summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcoco875 <pereira.jannin@gmail.com>2025-08-21 03:29:13 +0200
committerLywx <kiritodev01@gmail.com>2025-12-01 12:39:42 -0600
commit35bf098074262de07063e3cf8c3ea5503f46ad68 (patch)
tree2f9eefefeb5a0e1231eb7c2113ca5eada1352093 /src
parentb47ba0a853801be397f69b856f22f9788e2b5f30 (diff)
add Packed_gfx
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp2
-rw-r--r--src/factories/mk64/PackedDisplayListFactory.cpp877
-rw-r--r--src/factories/mk64/PackedDisplayListFactory.h17
3 files changed, 896 insertions, 0 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index d5cb096..c4fb5a1 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -55,6 +55,7 @@
#include "factories/mk64/DrivingBehaviour.h"
#include "factories/mk64/ItemCurve.h"
#include "factories/mk64/CourseMetadata.h"
+#include "factories/mk64/PackedDisplayListFactory.h"
#endif
#ifdef SF64_SUPPORT
@@ -173,6 +174,7 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("MK64:DRIVING_BEHAVIOUR", std::make_shared<MK64::DrivingBehaviourFactory>());
this->RegisterFactory("MK64:ITEM_CURVE", std::make_shared<MK64::ItemCurveFactory>()); // Item curve for decomp only
this->RegisterFactory("MK64:METADATA", std::make_shared<MK64::CourseMetadataFactory>());
+ this->RegisterFactory("MK64:PACKED_GFX", std::make_shared<PackedDListFactory>());
#endif
#ifdef SF64_SUPPORT
diff --git a/src/factories/mk64/PackedDisplayListFactory.cpp b/src/factories/mk64/PackedDisplayListFactory.cpp
new file mode 100644
index 0000000..946c972
--- /dev/null
+++ b/src/factories/mk64/PackedDisplayListFactory.cpp
@@ -0,0 +1,877 @@
+#include "PackedDisplayListFactory.h"
+#include "utils/Decompressor.h"
+#include "spdlog/spdlog.h"
+#include "../DisplayListOverrides.h" // pour N64Gfx
+#include <string>
+
+#ifdef STANDALONE
+#include <gfxd.h>
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define G_SPNOOP 0
+#define G_MTX 1
+#define G_RESERVED0 2
+#define G_MOVEMEM 3
+#define G_VTX 4
+#define G_RESERVED1 5
+#define G_DL 6
+#define G_RESERVED2 7
+#define G_RESERVED3 8
+#define G_SPRITE2D_BASE 9
+
+#define G_MV_VIEWPORT 0x80
+#define G_MV_LOOKATY 0x82
+#define G_MV_LOOKATX 0x84
+#define G_MV_L0 0x86
+#define G_MV_L1 0x88
+#define G_MV_L2 0x8a
+#define G_MV_L3 0x8c
+#define G_MV_L4 0x8e
+#define G_MV_L5 0x90
+#define G_MV_L6 0x92
+#define G_MV_L7 0x94
+#define G_MV_TXTATT 0x96
+#define G_MV_MATRIX_1 0x9e
+#define G_MV_MATRIX_2 0x98
+#define G_MV_MATRIX_3 0x9a
+#define G_MV_MATRIX_4 0x9c
+
+#define G_SPNOOP 0
+#define G_MTX 1
+#define G_RESERVED0 2
+#define G_MOVEMEM 3
+#define G_VTX 4
+#define G_RESERVED1 5
+#define G_DL 6
+#define G_RESERVED2 7
+#define G_RESERVED3 8
+#define G_SPRITE2D_BASE 9
+
+#define G_IMMFIRST -65
+#define G_TRI1 (G_IMMFIRST - 0)
+#define G_CULLDL (G_IMMFIRST - 1)
+#define G_POPMTX (G_IMMFIRST - 2)
+#define G_MOVEWORD (G_IMMFIRST - 3)
+#define G_TEXTURE (G_IMMFIRST - 4)
+#define G_SETOTHERMODE_H (G_IMMFIRST - 5)
+#define G_SETOTHERMODE_L (G_IMMFIRST - 6)
+#define G_ENDDL (G_IMMFIRST - 7)
+#define G_SETGEOMETRYMODE (G_IMMFIRST - 8)
+#define G_CLEARGEOMETRYMODE (G_IMMFIRST - 9)
+#define G_LINE3D (G_IMMFIRST - 10)
+#define G_RDPHALF_1 (G_IMMFIRST - 11)
+#define G_RDPHALF_2 (G_IMMFIRST - 12)
+#define G_MODIFYVTX (G_IMMFIRST - 13)
+#define G_TRI2 (G_IMMFIRST - 14)
+#define G_BRANCH_Z (G_IMMFIRST - 15)
+#define G_LOAD_UCODE (G_IMMFIRST - 16)
+#define G_QUAD (G_IMMFIRST - 10)
+
+#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE
+#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE
+#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE
+#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0
+#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0
+#define G_CC_MODULATERGB G_CC_MODULATEI
+#define G_CC_MODULATERGBA G_CC_MODULATEIA
+#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA
+#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE
+#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0
+#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0
+#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM
+#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM
+#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM
+#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE
+#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0
+#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
+#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0
+#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE
+#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_ADDRGB 1, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
+#define G_CC_ADDRGBDECALA 1, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
+#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
+#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE
+#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0
+#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0
+#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0
+
+#define G_IM_SIZ_16b_BYTES 2
+
+#define G_SETCIMG 0xff /* -1 */
+#define G_SETZIMG 0xfe /* -2 */
+#define G_SETTIMG 0xfd /* -3 */
+#define G_SETCOMBINE 0xfc /* -4 */
+#define G_SETENVCOLOR 0xfb /* -5 */
+#define G_SETPRIMCOLOR 0xfa /* -6 */
+#define G_SETBLENDCOLOR 0xf9 /* -7 */
+#define G_SETFOGCOLOR 0xf8 /* -8 */
+#define G_SETFILLCOLOR 0xf7 /* -9 */
+#define G_FILLRECT 0xf6 /* -10 */
+#define G_SETTILE 0xf5 /* -11 */
+#define G_LOADTILE 0xf4 /* -12 */
+#define G_LOADBLOCK 0xf3 /* -13 */
+#define G_SETTILESIZE 0xf2 /* -14 */
+#define G_LOADTLUT 0xf0 /* -16 */
+#define G_RDPSETOTHERMODE 0xef /* -17 */
+#define G_SETPRIMDEPTH 0xee /* -18 */
+#define G_SETSCISSOR 0xed /* -19 */
+#define G_SETCONVERT 0xec /* -20 */
+#define G_SETKEYR 0xeb /* -21 */
+#define G_SETKEYGB 0xea /* -22 */
+#define G_RDPFULLSYNC 0xe9 /* -23 */
+#define G_RDPTILESYNC 0xe8 /* -24 */
+#define G_RDPPIPESYNC 0xe7 /* -25 */
+#define G_RDPLOADSYNC 0xe6 /* -26 */
+#define G_TEXRECTFLIP 0xe5 /* -27 */
+#define G_TEXRECT 0xe4 /* -28 */
+
+#define gsDPNoParam(cmd) \
+ { \
+ { _SHIFTL(cmd, 24, 8), 0 } \
+ }
+
+#define G_CCMUX_COMBINED 0
+#define G_CCMUX_TEXEL0 1
+#define G_CCMUX_TEXEL1 2
+#define G_CCMUX_PRIMITIVE 3
+#define G_CCMUX_SHADE 4
+#define G_CCMUX_ENVIRONMENT 5
+#define G_CCMUX_CENTER 6
+#define G_CCMUX_SCALE 6
+#define G_CCMUX_COMBINED_ALPHA 7
+#define G_CCMUX_TEXEL0_ALPHA 8
+#define G_CCMUX_TEXEL1_ALPHA 9
+#define G_CCMUX_PRIMITIVE_ALPHA 10
+#define G_CCMUX_SHADE_ALPHA 11
+#define G_CCMUX_ENV_ALPHA 12
+#define G_CCMUX_LOD_FRACTION 13
+#define G_CCMUX_PRIM_LOD_FRAC 14
+#define G_CCMUX_NOISE 7
+#define G_CCMUX_K4 7
+#define G_CCMUX_K5 15
+#define G_CCMUX_1 6
+#define G_CCMUX_0 31
+
+#define G_ACMUX_COMBINED 0
+#define G_ACMUX_TEXEL0 1
+#define G_ACMUX_TEXEL1 2
+#define G_ACMUX_PRIMITIVE 3
+#define G_ACMUX_SHADE 4
+#define G_ACMUX_ENVIRONMENT 5
+#define G_ACMUX_LOD_FRACTION 0
+#define G_ACMUX_PRIM_LOD_FRAC 6
+#define G_ACMUX_1 6
+#define G_ACMUX_0 7
+
+#define gsDPTileSync() gsDPNoParam(G_RDPTILESYNC)
+#define gsDPLoadSync() gsDPNoParam(G_RDPLOADSYNC)
+
+#define G_IM_SIZ_16b 2
+
+#define G_TX_RENDERTILE 0
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define G_TX_DXT_FRAC 11
+
+#define TXL2WORDS(txls, b_txl) MAX(1, ((txls) * (b_txl) / 8))
+#define CALC_DXT(width, b_txl) (((1 << G_TX_DXT_FRAC) + TXL2WORDS(width, b_txl) - 1) / TXL2WORDS(width, b_txl))
+
+#define G_CC_MODULATERGBA G_CC_MODULATEIA
+
+#define BOWTIE_VAL 0
+
+#define gsSPTexture(s, t, level, tile, on) \
+ { \
+ (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \
+ _SHIFTL((on), 0, 8)), \
+ (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)) \
+ }
+
+#define gsDPSetCombineLERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \
+ { \
+ { \
+ _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \
+ GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1), \
+ 0, 24), \
+ (unsigned int) (GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, G_ACMUX_##Ab0, G_ACMUX_##Ad0) | \
+ GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, G_ACMUX_##Ac1, G_CCMUX_##d1, G_ACMUX_##Ab1, \
+ G_ACMUX_##Ad1)) \
+ } \
+ }
+
+#define GCCc0w0(saRGB0, mRGB0, saA0, mA0) \
+ (_SHIFTL((saRGB0), 20, 4) | _SHIFTL((mRGB0), 15, 5) | _SHIFTL((saA0), 12, 3) | _SHIFTL((mA0), 9, 3))
+
+#define GCCc1w0(saRGB1, mRGB1) (_SHIFTL((saRGB1), 5, 4) | _SHIFTL((mRGB1), 0, 5))
+
+#define GCCc0w1(sbRGB0, aRGB0, sbA0, aA0) \
+ (_SHIFTL((sbRGB0), 28, 4) | _SHIFTL((aRGB0), 15, 3) | _SHIFTL((sbA0), 12, 3) | _SHIFTL((aA0), 9, 3))
+
+#define GCCc1w1(sbRGB1, saA1, mA1, aRGB1, sbA1, aA1) \
+ (_SHIFTL((sbRGB1), 24, 4) | _SHIFTL((saA1), 21, 3) | _SHIFTL((mA1), 18, 3) | _SHIFTL((aRGB1), 6, 3) | \
+ _SHIFTL((sbA1), 3, 3) | _SHIFTL((aA1), 0, 3))
+
+#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b)
+
+#define gsSPSetOtherMode(cmd, sft, len, data) \
+ { \
+ { _SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8), (unsigned int) (data) } \
+ }
+
+#define gsDPSetRenderMode(c0, c1) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, (c0) | (c1))
+
+#define G_MDSFT_ALPHACOMPARE 0
+#define G_MDSFT_ZSRCSEL 2
+#define G_MDSFT_RENDERMODE 3
+#define G_MDSFT_BLENDER 16
+
+#define AA_EN 0x8
+#define Z_CMP 0x10
+#define Z_UPD 0x20
+#define IM_RD 0x40
+#define CLR_ON_CVG 0x80
+#define CVG_DST_CLAMP 0
+#define CVG_DST_WRAP 0x100
+#define CVG_DST_FULL 0x200
+#define CVG_DST_SAVE 0x300
+#define ZMODE_OPA 0
+#define ZMODE_INTER 0x400
+#define ZMODE_XLU 0x800
+#define ZMODE_DEC 0xc00
+#define CVG_X_ALPHA 0x1000
+#define ALPHA_CVG_SEL 0x2000
+#define FORCE_BL 0x4000
+#define TEX_EDGE 0x0000 /* used to be 0x8000 */
+
+#define G_BL_CLR_IN 0
+#define G_BL_CLR_MEM 1
+#define G_BL_CLR_BL 2
+#define G_BL_CLR_FOG 3
+#define G_BL_1MA 0
+#define G_BL_A_MEM 1
+#define G_BL_A_IN 0
+#define G_BL_A_FOG 1
+#define G_BL_A_SHADE 2
+#define G_BL_1 2
+#define G_BL_0 3
+
+#define GBL_c1(m1a, m1b, m2a, m2b) (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
+#define GBL_c2(m1a, m1b, m2a, m2b) (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16
+
+#define RM_AA_ZB_OPA_SURF(clk) \
+ AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \
+ GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
+
+#define RM_AA_ZB_TEX_EDGE(clk) \
+ AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
+ GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
+
+#define RM_AA_ZB_XLU_SURF(clk) \
+ AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_XLU | \
+ GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
+
+#define RM_AA_ZB_OPA_DECAL(clk) \
+ AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \
+ GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
+
+#define RM_AA_ZB_XLU_DECAL(clk) \
+ AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_DEC | \
+ GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
+
+#define G_RM_AA_ZB_OPA_SURF RM_AA_ZB_OPA_SURF(1)
+#define G_RM_AA_ZB_OPA_SURF2 RM_AA_ZB_OPA_SURF(2)
+#define G_RM_AA_ZB_TEX_EDGE RM_AA_ZB_TEX_EDGE(1)
+#define G_RM_AA_ZB_TEX_EDGE2 RM_AA_ZB_TEX_EDGE(2)
+#define G_RM_AA_ZB_XLU_SURF RM_AA_ZB_XLU_SURF(1)
+#define G_RM_AA_ZB_XLU_SURF2 RM_AA_ZB_XLU_SURF(2)
+#define G_RM_AA_ZB_OPA_DECAL RM_AA_ZB_OPA_DECAL(1)
+#define G_RM_AA_ZB_OPA_DECAL2 RM_AA_ZB_OPA_DECAL(2)
+#define G_RM_AA_ZB_XLU_DECAL RM_AA_ZB_XLU_DECAL(1)
+#define G_RM_AA_ZB_XLU_DECAL2 RM_AA_ZB_XLU_DECAL(2)
+
+#define gsSPGeometryMode(c, s) \
+ { \
+ { (_SHIFTL(G_GEOMETRYMODE, 24, 8) | _SHIFTL(~(u32) (c), 0, 24)), (u32) (s) } \
+ }
+
+#define G_TEXTURE_ENABLE 0x00000002 /* Microcode use only */
+#define G_SHADING_SMOOTH 0x00000200 /* flat or smooth shaded */
+#define G_CULL_FRONT 0x00001000
+#define G_CULL_BACK 0x00002000
+#define G_CULL_BOTH 0x00003000 /* To make code cleaner */
+#define gsSPSetGeometryMode(word) \
+ { \
+ { _SHIFTL(G_SETGEOMETRYMODE, 24, 8), (unsigned int) (word) } \
+ }
+
+#define gsSPClearGeometryMode(word) \
+ { \
+ { _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (unsigned int) (word) } \
+ }
+#define gsSPCullDisplayList(vstart, vend) \
+ { \
+ { _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart) * 2, 0, 16), _SHIFTL((vend) * 2, 0, 16) } \
+ }
+#define G_ON (1)
+#define G_OFF (0)
+
+#define NUML(n) (((n) + 1) * 32 + 0x80000000)
+#define NUMLIGHTS_0 1
+#define NUMLIGHTS_1 1
+#define NUMLIGHTS_2 2
+#define NUMLIGHTS_3 3
+#define NUMLIGHTS_4 4
+#define NUMLIGHTS_5 5
+#define NUMLIGHTS_6 6
+#define NUMLIGHTS_7 7
+
+#define G_MW_NUMLIGHT 0x02
+
+#define G_MWO_NUMLIGHT 0x00
+
+#define gsImmp21(c, p0, p1, dat) \
+ { _SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8), (uintptr_t)(dat) }
+#define gsMoveWd(index, offset, data) gsImmp21(G_MOVEWORD, offset, index, data)
+#define gsSPNumLights(n) gsMoveWd(G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n))
+
+#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts) \
+ { \
+ (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) | \
+ _SHIFTL(tmem, 0, 9)), \
+ (_SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \
+ _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4)) \
+ }
+
+#define G_TX_RENDERTILE 0
+
+#define gsDPLoadTileGeneric(c, tile, uls, ult, lrs, lrt) \
+ { \
+ _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12), \
+ _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12) \
+ }
+
+#define gsDPSetTileSize(t, uls, ult, lrs, lrt) gsDPLoadTileGeneric(G_SETTILESIZE, t, uls, ult, lrs, lrt)
+
+#define gsDma1p(c, s, l, p) \
+ { (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)), (uintptr_t)(s) }
+#define gsSPVertex(v, n, v0) gsDma1p(G_VTX, (v), ((n) << 10) | (0x10 * (n)-1), (v0)*2)
+
+#define __gsSP1Triangle_w1(v0, v1, v2) (_SHIFTL((v0)*2, 16, 8) | _SHIFTL((v1)*2, 8, 8) | _SHIFTL((v2)*2, 0, 8))
+#define __gsSP1Triangle_w1f(v0, v1, v2, flag) \
+ (((flag) == 0) ? __gsSP1Triangle_w1(v0, v1, v2) \
+ : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v0) \
+ : __gsSP1Triangle_w1(v2, v0, v1))
+#define gsSP1Triangle(v0, v1, v2, flag) \
+ { _SHIFTL(G_TRI1, 24, 8), __gsSP1Triangle_w1f(v0, v1, v2, flag) }
+
+#define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \
+ { (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Triangle_w1f(v00, v01, v02, flag0)), __gsSP1Triangle_w1f(v10, v11, v12, flag1) }
+
+#define G_DL_PUSH 0x00
+#define gsSPDisplayList(dl) gsDma1p(G_DL, dl, 0, G_DL_PUSH)
+
+#define gsSetImage(cmd, fmt, siz, width, i) \
+ { _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12), (uintptr_t)(i) }
+
+#define gsDPSetTextureImage(f, s, w, i) gsSetImage(G_SETTIMG, f, s, w, i)
+
+#define G_TX_LDBLK_MAX_TXL 4095
+
+#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) \
+ { \
+ (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12)), \
+ (_SHIFTL(tile, 24, 3) | _SHIFTL((MIN(lrs, G_TX_LDBLK_MAX_TXL)), 12, 12) | _SHIFTL(dxt, 0, 12)) \
+ }
+
+#define G_TX_NOMIRROR 0
+#define G_TX_WRAP 0
+
+#define G_TX_NOMASK 0
+#define G_TX_NOLOD 0
+
+// Packed opcodes (alignés avec src/racing/memory.c)
+enum PackedOp : uint8_t {
+ PG_LIGHTS_0 = 0x00, // 0..0x14 mappés sur unpack_lights côté runtime
+ PG_SETCOMBINE_CC_MODULATERGBA = 0x15,
+ PG_SETCOMBINE_CC_MODULATERGBDECALA = 0x16,
+ PG_SETCOMBINE_CC_SHADE = 0x17,
+ PG_RMODE_OPA = 0x18,
+ PG_RMODE_TEXEDGE = 0x19,
+ PG_TILECFG_A = 0x1A,
+ PG_TILECFG_B = 0x1B,
+ PG_TILECFG_C = 0x1C,
+ PG_TILECFG_D = 0x1D,
+ PG_TILECFG_E = 0x1E,
+ PG_TILECFG_F = 0x1F,
+ PG_TIMG_LOADBLOCK_0 = 0x20,
+ PG_TIMG_LOADBLOCK_1 = 0x21,
+ PG_TIMG_LOADBLOCK_2 = 0x22,
+ PG_TIMG_LOADBLOCK_3 = 0x23,
+ PG_TIMG_LOADBLOCK_4 = 0x24,
+ PG_TIMG_LOADBLOCK_5 = 0x25,
+ PG_TEXTURE_ON = 0x26,
+ PG_TEXTURE_OFF = 0x27,
+ PG_VTX1 = 0x28,
+ PG_TRI1 = 0x29,
+ PG_ENDDL = 0x2A,
+ PG_DL = 0x2B,
+ PG_TILECFG_G = 0x2C,
+ PG_CULLDL = 0x2D,
+ PG_SETCOMBINE_ALT = 0x2E,
+ PG_RMODE_XLU = 0x2F,
+ PG_SPLINE3D = 0x30,
+ PG_VTX_BASE = 0x32, // 0x33..0x52 VTX2 variants
+ PG_SETCOMBINE_CC_DECALRGBA= 0x53,
+ PG_RMODE_OPA_DECAL = 0x54,
+ PG_RMODE_XLU_DECAL = 0x55,
+ PG_SETGEOMETRYMODE = 0x56,
+ PG_CLEARGEOMETRYMODE = 0x57,
+ PG_TRI2 = 0x58,
+ PG_EOF = 0xFF,
+};
+
+std::string opcode_to_string(uint8_t op) {
+ if (PG_LIGHTS_0 <= op && op <= PG_LIGHTS_0 + 0x20) {
+ return "PG_LIGHTS_" + std::to_string(op - PG_LIGHTS_0);
+ }
+ if (PG_VTX_BASE <= op && op <= PG_VTX_BASE + 0x20) {
+ return "PG_VTX_" + std::to_string(op - PG_VTX_BASE);
+ }
+ switch (op) {
+ case PG_SETCOMBINE_CC_MODULATERGBA: return "PG_SETCOMBINE_CC_MODULATERGBA";
+ case PG_SETCOMBINE_CC_MODULATERGBDECALA: return "PG_SETCOMBINE_CC_MODULATERGBDECALA";
+ case PG_SETCOMBINE_CC_SHADE: return "PG_SETCOMBINE_CC_SHADE";
+ case PG_RMODE_OPA: return "PG_RMODE_OPA";
+ case PG_RMODE_TEXEDGE: return "PG_RMODE_TEXEDGE";
+ case PG_TILECFG_A: return "PG_TILECFG_A";
+ case PG_TILECFG_B: return "PG_TILECFG_B";
+ case PG_TILECFG_C: return "PG_TILECFG_C";
+ case PG_TILECFG_D: return "PG_TILECFG_D";
+ case PG_TILECFG_E: return "PG_TILECFG_E";
+ case PG_TILECFG_F: return "PG_TILECFG_F";
+ case PG_TIMG_LOADBLOCK_0: return "PG_TIMG_LOADBLOCK_0";
+ case PG_TIMG_LOADBLOCK_1: return "PG_TIMG_LOADBLOCK_1";
+ case PG_TIMG_LOADBLOCK_2: return "PG_TIMG_LOADBLOCK_2";
+ case PG_TIMG_LOADBLOCK_3: return "PG_TIMG_LOADBLOCK_3";
+ case PG_TIMG_LOADBLOCK_4: return "PG_TIMG_LOADBLOCK_4";
+ case PG_TIMG_LOADBLOCK_5: return "PG_TIMG_LOADBLOCK_5";
+ case PG_TEXTURE_ON: return "PG_TEXTURE_ON";
+ case PG_TEXTURE_OFF: return "PG_TEXTURE_OFF";
+ case PG_VTX1: return "PG_VTX1";
+ case PG_TRI1: return "PG_TRI1";
+ case PG_ENDDL: return "PG_ENDDL";
+ case PG_DL: return "PG_DL";
+ case PG_TILECFG_G: return "PG_TILECFG_G";
+ case PG_CULLDL: return "PG_CULLDL";
+ case PG_SETCOMBINE_ALT: return "PG_SETCOMBINE_ALT";
+ case PG_RMODE_XLU: return "PG_RMODE_XLU";
+ case PG_SPLINE3D: return "PG_SPLINE3D";
+ case PG_SETCOMBINE_CC_DECALRGBA: return "PG_SETCOMBINE_CC_DECALRGBA";
+ case PG_RMODE_OPA_DECAL: return "PG_RMODE_OPA_DECAL";
+ case PG_RMODE_XLU_DECAL: return "PG_RMODE_XLU_DECAL";
+ case PG_SETGEOMETRYMODE: return "PG_SETGEOMETRYMODE";
+ case PG_CLEARGEOMETRYMODE: return "PG_CLEARGEOMETRYMODE";
+ case PG_TRI2: return "PG_TRI2";
+ case PG_EOF: return "PG_EOF";
+ default: return "PG_UNKNOWN_" + std::to_string(op);
+ }
+}
+
+static inline uint32_t ImmediateSize(uint8_t op) {
+ switch (op) {
+ case PG_TRI1: return 2; // packed 2 bytes
+ case PG_TRI2: return 4; // packed 4 bytes (2 tris)
+ case PG_DL: return 2; // index
+ case PG_VTX1: return 4; // vtx1: 4 bytes
+ case PG_SPLINE3D: return 3; // spline3d: 3 bytes
+ case PG_SETCOMBINE_CC_MODULATERGBA:
+ case PG_SETCOMBINE_CC_MODULATERGBDECALA:
+ case PG_SETCOMBINE_CC_SHADE:
+ case PG_RMODE_OPA:
+ case PG_RMODE_TEXEDGE:
+ case PG_SETCOMBINE_ALT:
+ case PG_RMODE_XLU:
+ case PG_SETCOMBINE_CC_DECALRGBA:
+ case PG_RMODE_OPA_DECAL:
+ case PG_RMODE_XLU_DECAL:
+ case PG_SETGEOMETRYMODE:
+ case PG_CLEARGEOMETRYMODE:
+ return 0; // pas d’immediate dans notre format packé
+ case PG_TIMG_LOADBLOCK_0:
+ case PG_TIMG_LOADBLOCK_1:
+ case PG_TIMG_LOADBLOCK_2:
+ case PG_TIMG_LOADBLOCK_3:
+ case PG_TIMG_LOADBLOCK_4:
+ case PG_TIMG_LOADBLOCK_5:
+ return 3; // fmt-dep tex, 3 bytes
+ case PG_TILECFG_A:
+ case PG_TILECFG_B:
+ case PG_TILECFG_C:
+ case PG_TILECFG_D:
+ case PG_TILECFG_E:
+ case PG_TILECFG_F:
+ case PG_TILECFG_G:
+ return 2; // 2 bytes
+ default:
+ // VTX2 banked range: 0x33..0x52 → 2 bytes
+ if (op >= (uint8_t)(PG_VTX_BASE) && op <= (uint8_t)(PG_VTX_BASE + 0x20)) {
+ // base (0x32) unused, but if encountered, treat as 0
+ return (op == PG_VTX_BASE) ? 0 : 2;
+ }
+ return 0;
+ }
+}
+
+static inline uint16_t RD16(const std::vector<uint8_t>& b, size_t i) {
+ return (uint16_t)((b[i + 1] << 8) | b[i]);
+}
+
+std::optional<std::shared_ptr<IParsedData>> PackedDListFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& data) {
+ auto [_, segment] = Decompressor::AutoDecode(data, buffer);
+ std::vector<uint8_t> decoded(segment.data, segment.data + segment.size);
+
+ const auto start = 0;
+ size_t i = start;
+ std::vector<uint32_t> gfx;
+
+ auto emit = [&](uint32_t w0, uint32_t w1) {
+ gfx.push_back(w0);
+ gfx.push_back(w1);
+ };
+
+ // Minimal expansion for export purposes
+ std::vector<uint8_t>& raw = buffer; // pour compatibilité éventuelle avec le reste du code
+ YAML::Node& node = data;
+
+ while (i < decoded.size()) {
+ uint8_t op = decoded[i++];
+ SPDLOG_INFO("PackedDListFactory: opcode 0x{:02X} ({})", op, opcode_to_string(op));
+ if (op == PG_EOF) { break; }
+
+ // LIGHTS: 0x00..0x14
+ if (op >= PG_LIGHTS_0 && op <= PG_LIGHTS_0 + 0x14) {
+ int a = (op * 0x18) + 0x9000008;
+ int b = (op * 0x18) + 0x9000000;
+ N64Gfx macro = gsSPNumLights(NUMLIGHTS_1);
+ emit(macro.words.w0, macro.words.w1);
+ emit(0x3860010, a);
+ emit(0x3880010, b);
+ continue;
+ }
+
+ if (op >= (uint8_t)(PG_VTX_BASE + 0x01) && op <= (uint8_t)(PG_VTX_BASE + 0x20)) {
+ // Mimic unpack_vtx2: banked variant encodes count in opcode
+ if (i + 2 > decoded.size()) { goto done; }
+ uintptr_t vtxOff = RD16(decoded, i);
+ i += 2;
+ vtxOff *= 0x10; // bytes
+
+ uint8_t n = op - PG_VTX_BASE; // map 0x33..→1.. etc.
+ // In runtime, w0 uses (n<<10)+((n*0x10)-1); start index implied 0
+ // uint32_t w0 = (_SHIFTL(G_VTX, 24, 8)
+ // | ((n << 10) + ((n * 0x10) - 1)));
+ // uint32_t w1 = vtxOff;
+ N64Gfx macro = gsSPVertex(vtxOff, n, 0);
+ emit(macro.words.w0, macro.words.w1);
+ continue;
+ }
+
+ switch (op) {
+ case PG_VTX1: {
+ // Mimic unpack_vtx1
+ if (i + 4 > decoded.size()) { goto done; }
+ uint8_t t0 = decoded[i++];
+ uint16_t vtxOff = ((uint16_t)decoded[i++] << 8) | t0;
+ vtxOff *= 0x10; // bytes
+
+ uint8_t b0 = decoded[i++];
+ uint8_t start = (uint8_t)(b0 & 0x3F);
+
+ uint8_t b1 = decoded[i++];
+ uint8_t n = (uint8_t)(b1 & 0x3F);
+
+ // G_VTX encoding matches DisplayListFactory exporter expectations
+ uint32_t w0 = (_SHIFTL(G_VTX, 24, 8)
+ | _SHIFTL((n * 2), 16, 8)
+ | ((start << 10) + ((0x10 * start) - 1)));
+ // Use segmented address style: segment 0x04 is vertex pool in runtime; here we keep raw offset so exporter can attempt resolution
+ uint32_t w1 = vtxOff; // exporter will patch/resolve if possible
+
+ emit(w0, w1);
+ } break;
+ case PG_TILECFG_A:
+ case PG_TILECFG_B:
+ case PG_TILECFG_C:
+ case PG_TILECFG_D:
+ case PG_TILECFG_E:
+ case PG_TILECFG_F:
+ case PG_TILECFG_G: {
+ // Mirror de unpack_tile_sync
+ if (i + 2 > decoded.size()) { goto done; }
+ int width = 32, height = 32, fmt = 0, tmem = 0;
+ switch (op) {
+ case PG_TILECFG_A: width = 32; height = 32; fmt = 0; tmem = 0; break;
+ case PG_TILECFG_B: width = 64; height = 32; fmt = 0; tmem = 0; break;
+ case PG_TILECFG_C: width = 32; height = 64; fmt = 0; tmem = 0; break;
+ case PG_TILECFG_D: width = 32; height = 32; fmt = 3; tmem = 0; break;
+ case PG_TILECFG_E: width = 64; height = 32; fmt = 3; tmem = 0; break;
+ case PG_TILECFG_F: width = 32; height = 64; fmt = 3; tmem = 0; break;
+ case PG_TILECFG_G: width = 32; height = 32; fmt = 0; tmem = 256; break;
+ }
+ const int sizBytes = G_IM_SIZ_16b_BYTES; // 2
+ const int line = (((width * 2) + 7) >> 3);
+
+ uint8_t b0 = decoded[i++];
+ uint8_t b1 = decoded[i++];
+ const int cms = b0 & 0xF;
+ const int masks = (b0 >> 4) & 0xF;
+ const int cmt = b1 & 0xF;
+ const int maskt = (b1 >> 4) & 0xF;
+
+ // gsDPTileSync
+ {
+ N64Gfx macro = gsDPTileSync();
+ emit(macro.words.w0, macro.words.w1);
+ }
+
+ // G_SETTILE
+ // uint32_t w0 = (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(G_IM_SIZ_16b_BYTES, 19, 2) | _SHIFTL(line, 9, 9) | _SHIFTL(tmem, 0, 9));
+ // uint32_t w1 = (_SHIFTL(cmt, 18, 3) | _SHIFTL(maskt, 14, 4) | _SHIFTL(cms, 8, 3) | _SHIFTL(masks, 4, 4));
+ N64Gfx macro = gsDPSetTile(fmt, G_IM_SIZ_16b, line, tmem, G_TX_RENDERTILE, 0, cmt, maskt, 0, cms, masks, 0);
+ emit(macro.words.w0, macro.words.w1);
+
+ // G_SETTILESIZE
+ // const int lrs = (width - 1) << 2;
+ // const int lrt = (height - 1) << 2;
+ // emit(_SHIFTL(G_SETTILESIZE, 24, 8), _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12));
+ macro = gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, (width - 1) << 2, (height - 1) << 2);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_TIMG_LOADBLOCK_0:
+ case PG_TIMG_LOADBLOCK_1:
+ case PG_TIMG_LOADBLOCK_2:
+ case PG_TIMG_LOADBLOCK_3:
+ case PG_TIMG_LOADBLOCK_4:
+ case PG_TIMG_LOADBLOCK_5: {
+ // Mirror de unpack_tile_load_sync
+ if (i + 3 > decoded.size()) { goto done; }
+ uint32_t width = 32, height = 32, fmt = 0;
+ switch (op) {
+ case PG_TIMG_LOADBLOCK_0: width = 32; height = 32; fmt = 0; break;
+ case PG_TIMG_LOADBLOCK_1: width = 64; height = 32; fmt = 0; break;
+ case PG_TIMG_LOADBLOCK_2: width = 32; height = 64; fmt = 0; break;
+ case PG_TIMG_LOADBLOCK_3: width = 32; height = 32; fmt = 3; break;
+ case PG_TIMG_LOADBLOCK_4: width = 64; height = 32; fmt = 3; break;
+ case PG_TIMG_LOADBLOCK_5: width = 32; height = 64; fmt = 3; break;
+ }
+ uint32_t offset = ((uint32_t)decoded[i++]) << 11; // index << 11
+
+ i++;
+ uint8_t arg = decoded[i++];
+ const uint32_t siz = G_IM_SIZ_16b;
+ const uint32_t tmem = (arg & 0x0F);
+ const uint32_t tile = ((arg >> 4) & 0x0F);
+
+ // G_SETTIMG (on garde un pointeur brut = offset; l'exporteur patchera en OTR)
+ // Utiliser width=1 par défaut pour le champ "width-1" du w0
+ // uint32_t w0 = (_SHIFTL(G_SETTIMG, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2));
+ N64Gfx macro = gsDPSetTextureImage(fmt, siz, 1, offset);
+
+ emit(macro.words.w0, macro.words.w1);
+
+ // gsDPTileSync
+ {
+ N64Gfx macro = gsDPTileSync();
+ emit(macro.words.w0, macro.words.w1);
+ }
+
+ // G_SETTILE (fmt/siz/tmem) + tile en w1
+ // w0 = (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(tmem, 0, 9));
+ // uint32_t w1 = _SHIFTL(tile, 24, 3);
+ macro = gsDPSetTile(fmt, siz, 0, tmem, tile, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
+ emit(macro.words.w0, macro.words.w1);
+
+ // gsDPLoadSync
+ {
+ N64Gfx macro = gsDPLoadSync();
+ emit(macro.words.w0, macro.words.w1);
+ }
+
+ // G_LOADBLOCK
+ // const uint32_t count = std::min((width * height) - 1, 0x7FFu);
+ const uint32_t dxt = CALC_DXT(width, G_IM_SIZ_16b_BYTES);
+ // w0 = _SHIFTL(G_LOADBLOCK, 24, 8);
+ // w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(count, 12, 12) | _SHIFTL(dxt, 0, 12));
+ macro = gsDPLoadBlock(tile, 0, 0, (width * height) - 1, dxt);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_TEXTURE_ON: {
+ N64Gfx macro = gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_TEXTURE_OFF: {
+ // gsSPTexture(0x1, 0x1, 0, G_TX_RENDERTILE, G_OFF)
+ N64Gfx macro = gsSPTexture(0x1, 0x1, 0, G_TX_RENDERTILE, G_OFF);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETCOMBINE_CC_MODULATERGBA: {
+ // gsDPSetCombineMode(G_CC_MODULATERGBA, G_CC_MODULATERGBA)
+ N64Gfx macro = gsDPSetCombineMode(G_CC_MODULATERGBA, G_CC_MODULATERGBA);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETCOMBINE_CC_MODULATERGBDECALA: {
+ N64Gfx macro = gsDPSetCombineMode(G_CC_MODULATERGBDECALA, G_CC_MODULATERGBDECALA);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETCOMBINE_CC_SHADE: {
+ N64Gfx macro = gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETCOMBINE_ALT: {
+ // Alias historique → même que MODULATERGBDECALA côté runtime
+ N64Gfx macro = gsDPSetCombineMode(G_CC_MODULATERGBDECALA, G_CC_MODULATERGBDECALA);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETCOMBINE_CC_DECALRGBA: {
+ N64Gfx macro = gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_RMODE_OPA: {
+ N64Gfx macro = gsDPSetRenderMode(G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_RMODE_TEXEDGE: {
+ N64Gfx macro = gsDPSetRenderMode(G_RM_AA_ZB_TEX_EDGE, G_RM_AA_ZB_TEX_EDGE2);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_RMODE_XLU: {
+ N64Gfx macro = gsDPSetRenderMode(G_RM_AA_ZB_XLU_SURF, G_RM_AA_ZB_XLU_SURF2);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_RMODE_OPA_DECAL: {
+ N64Gfx macro = gsDPSetRenderMode(G_RM_AA_ZB_OPA_DECAL, G_RM_AA_ZB_OPA_DECAL);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_RMODE_XLU_DECAL: {
+ N64Gfx macro = gsDPSetRenderMode(G_RM_AA_ZB_XLU_DECAL, G_RM_AA_ZB_XLU_DECAL);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SETGEOMETRYMODE: {
+ N64Gfx macro = gsSPSetGeometryMode(G_CULL_BACK);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_CLEARGEOMETRYMODE: {
+ N64Gfx macro = gsSPClearGeometryMode(G_CULL_BACK);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_CULLDL: {
+ N64Gfx macro = gsSPCullDisplayList(0, 7);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_TRI1: {
+ if (i + 2 > decoded.size()) { goto done; }
+ uint16_t c = RD16(decoded, i); i += 2;
+ uint8_t a = (c & 0x1F);
+ uint8_t b = ((c >> 5) & 0x1F);
+ uint8_t d = ((c >> 10) & 0x1F);
+ N64Gfx macro = gsSP1Triangle(a, b, d, 0);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_TRI2: {
+ if (i + 4 > decoded.size()) { goto done; }
+ uint16_t c0 = RD16(decoded, i); i += 2;
+ uint16_t c1 = RD16(decoded, i); i += 2;
+ uint8_t a0 = (c0 & 0x1F);
+ uint8_t b0 = ((c0 >> 5) & 0x1F);
+ uint8_t d0 = ((c0 >> 10) & 0x1F);
+ uint8_t a1 = (c1 & 0x1F);
+ uint8_t b1 = ((c1 >> 5) & 0x1F);
+ uint8_t d1 = ((c1 >> 10) & 0x1F);
+ N64Gfx macro = gsSP2Triangles(a0, b0, d0, 0, a1, b1, d1, 0);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_SPLINE3D: {
+ // Mimic unpack_spline_3D: map packed 3 bytes into a QUAD macro layout
+ if (i + 3 > decoded.size()) { goto done; }
+ uint8_t b0 = decoded[i++];
+ uint8_t b1 = decoded[i++];
+ uint8_t b2 = decoded[i++];
+
+ // Follow non-mirror path from memory.c
+ uint8_t t0 = (uint8_t)(b0 & 0x1F);
+ uint8_t a3 = (uint8_t)((b0 >> 5) & 0x7);
+ a3 |= (uint8_t)((b1 & 0x3) * 8);
+ uint8_t a2 = (uint8_t)((b1 >> 2) & 0x1F);
+ uint8_t a0 = (uint8_t)(((b1 >> 7) & 0x1) | ((b2 & 0xF) * 2));
+
+ uint32_t w0 = _SHIFTL(G_QUAD, 24, 8);
+ uint32_t w1 = (_SHIFTL((uint8_t)(a0 * 2), 24, 8)
+ | _SHIFTL((uint8_t)(t0 * 2), 16, 8)
+ | _SHIFTL((uint8_t)(a3 * 2), 8, 8)
+ | _SHIFTL((uint8_t)(a2 * 2), 0, 8));
+ emit(w0, w1);
+ } break;
+ case PG_DL: {
+ if (i + 2 > decoded.size()) { goto done; }
+ uint16_t idx = RD16(decoded, i); i += 2;
+ // uint32_t w0 = (_SHIFTL(G_DL, 24, 8)); // push
+ // // Encode as segmented address into segment 0x07 so exporter treats it as an index into packed DL buffer
+ // uint32_t w1 = 0x07000000u | (idx * 8);
+ N64Gfx macro = gsSPDisplayList(idx);
+ emit(macro.words.w0, macro.words.w1);
+ } break;
+ case PG_ENDDL:
+ emit(_SHIFTL(G_ENDDL, 24, 8), 0);
+ SPDLOG_INFO("Size of packed DL: 0x{:X}", i - start);
+ goto done;
+ default: {
+ // Skip immediates for any known opcode (tilecfg, timg, spline, etc.)
+ uint32_t imm = ImmediateSize(op);
+ if (imm) {
+ if (i + imm > decoded.size()) { goto done; }
+ i += imm;
+ }
+ // Other 1-byte ops (combine, render, texture on/off, geom modes) produce no gfx here
+ } break;
+ }
+ }
+
+done:
+ if (gfx.empty()) {
+ return std::nullopt;
+ }
+
+ // Print gfx command output for debugging/export visualization
+ for (size_t j = 0; j + 1 < gfx.size(); j += 2) {
+ SPDLOG_INFO("PackedDL gfx[{}]: W0=0x{:08X} W1=0x{:08X}", j / 2, gfx[j], gfx[j + 1]);
+ }
+
+#ifdef STANDALONE
+ // Pretty-print the generated commands using gfxd (same tooling as DisplayListFactory)
+ gfxd_input_buffer(gfx.data(), sizeof(uint32_t) * gfx.size());
+ gfxd_output_fd(fileno(stdout));
+ gfxd_endian(gfxd_endian_host, sizeof(uint32_t));
+ gfxd_macro_fn([] {
+ gfxd_puts("> ");
+ gfxd_macro_dflt();
+ gfxd_puts("\n");
+ return 0;
+ });
+ // No overrides needed here; just default decode
+ // Version selection mirrors DisplayListFactory
+ gfxd_target(gfxd_f3dex);
+ gfxd_execute();
+#endif
+ return std::make_shared<DListData>(gfx);
+}
diff --git a/src/factories/mk64/PackedDisplayListFactory.h b/src/factories/mk64/PackedDisplayListFactory.h
new file mode 100644
index 0000000..696fff0
--- /dev/null
+++ b/src/factories/mk64/PackedDisplayListFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "../BaseFactory.h"
+#include "../DisplayListFactory.h" // Reuse DList exporters and data shape
+
+// Factory to expand MK64 packed DL bytecode into regular Gfx commands
+class PackedDListFactory : public BaseFactory {
+public:
+ std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override;
+ std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
+ return {
+ REGISTER(Header, DListHeaderExporter)
+ REGISTER(Binary, DListBinaryExporter)
+ };
+ }
+ uint32_t GetAlignment() override { return 0; }
+};