diff --git a/tools_pc/d88_emit.py b/tools_pc/d88_emit.py index 691a764e..5e9789ef 100644 --- a/tools_pc/d88_emit.py +++ b/tools_pc/d88_emit.py @@ -80,8 +80,10 @@ existing sidecar+manifest are present -- it loads and extends them). import csv, struct, zlib, os, re, sys REGION = "ntsc-final" -if len(sys.argv) > 1 and sys.argv[1] in ("ntsc-final", "pal-final", "jpn-final"): - REGION = sys.argv[1] +REGEN = "--regen" in sys.argv[1:] +for _a in sys.argv[1:]: + if _a in ("ntsc-final", "pal-final", "jpn-final"): + REGION = _a ROM_PATH = f"data/ge007.{REGION}.z64" OUT_DIR = f"data/pccg-{REGION}" @@ -461,19 +463,8 @@ existing_names = set() os.makedirs(OUT_DIR, exist_ok=True) bin_path = os.path.join(OUT_DIR, "pccg.bin") man_path = os.path.join(OUT_DIR, "manifest.csv") -if os.path.exists(bin_path) and os.path.exists(man_path): - with open(man_path, newline="") as f: - r = csv.reader(f) - next(r, None) - for row in r: - if len(row) == 3: - manifest.append((row[0], int(row[1]), int(row[2]))) - existing_names.add(row[0]) - with open(bin_path, "rb") as f: - base = f.read() - chunks.append(base) - cur_off = len(base) - print(f"extending existing sidecar: {len(base)} bytes, {len(manifest)} rows") +def is_usetup_row(nm): + return nm.startswith("Usetup") and nm.endswith("Z") def emit(name, data): global cur_off @@ -485,6 +476,36 @@ def emit(name, data): chunks.append(data) cur_off += len(data) +if os.path.exists(bin_path) and os.path.exists(man_path): + old_rows = [] + with open(man_path, newline="") as f: + r = csv.reader(f) + next(r, None) + for row in r: + if len(row) == 3: + old_rows.append((row[0], int(row[1]), int(row[2]))) + with open(bin_path, "rb") as f: + base = f.read() + if REGEN: + # Drop every Usetup*Z row (manifest entry + its bytes) and rebuild the + # sidecar from the surviving d69 rows only, so converter iteration + # starts from a clean slate every run. Don't hand-edit manifest.csv. + dropped = [nm for (nm, _o, _s) in old_rows if is_usetup_row(nm)] + for (nm, o, s) in old_rows: + if is_usetup_row(nm): + continue + emit(nm, base[o:o + s]) + existing_names.add(nm) + print(f"--regen: kept {len(manifest)} d69 rows, dropped " + f"{len(dropped)} Usetup*Z rows; sidecar rebuilt to {cur_off} bytes") + else: + manifest.extend(old_rows) + for (nm, _o, _s) in old_rows: + existing_names.add(nm) + chunks.append(base) + cur_off = len(base) + print(f"extending existing sidecar: {len(base)} bytes, {len(manifest)} rows") + n_ok = 0 for name in usetup_names: if name in existing_names: diff --git a/tools_pc/d88_layoutprobe.c b/tools_pc/d88_layoutprobe.c new file mode 100644 index 00000000..392ede12 --- /dev/null +++ b/tools_pc/d88_layoutprobe.c @@ -0,0 +1,108 @@ +/* D88.4: compiler-verified PC layout probe for propDef record types. + * + * Prints sizeof (and key offsets) for every PROPDEF_* record struct the + * offline converter (d88_emit.py) must reproduce byte-for-byte, compiled with + * the SAME toolchain + include chain + flags as the PC game build. Run: + * + * export PATH="/c/msys64/mingw64/bin:$PATH" + * x86_64-w64-mingw32-gcc -std=gnu11 -DPORT=1 -DPLATFORM_64BIT=1 \ + * -D_LANGUAGE_C=1 -DVERSION_US -DLANG_US -DREFRESH_NTSC \ + * -Iport/shim -I. -Iinclude -Iinclude/PR -Isrc -Isrc/game -Isrc/libultra \ + * -Isrc/libultra/audio -Iport/include -Ibuild-pc/port/include \ + * -include versioninfo.h \ + * tools_pc/d88_layoutprobe.c -o build-pc/d88_layoutprobe.exe + * ./build-pc/d88_layoutprobe.exe + */ +#include +#include +#include + +#ifndef offsetof +#define offsetof(type, member) __builtin_offsetof(type, member) +#endif + +#define P_SZ(t) printf("SZ %-34s %3lu bytes %2lu words\n", #t, \ + (unsigned long)sizeof(t), (unsigned long)sizeof(t) / 4) +#define P_OFF(t, f) printf("OFF %-28s %-16s %3lu\n", #t, #f, \ + (unsigned long)offsetof(t, f)) + +int main(void) +{ + /* --- the base header every record embeds --- */ + P_SZ(PropDefHeaderRecord); + P_OFF(PropDefHeaderRecord, extrascale); + P_OFF(PropDefHeaderRecord, state); + P_OFF(PropDefHeaderRecord, type); + + /* --- ObjectRecord family (PROP/GLASS/ALARM/RACK/HAT/SAFE/GAS/KEY) --- */ + P_SZ(ObjectRecord); + P_OFF(ObjectRecord, obj); + P_OFF(ObjectRecord, pad); + P_OFF(ObjectRecord, flags); + P_OFF(ObjectRecord, flags2); + P_OFF(ObjectRecord, prop); + P_OFF(ObjectRecord, model); + P_OFF(ObjectRecord, mtx); + P_OFF(ObjectRecord, runtime_pos); + P_OFF(ObjectRecord, runtime_bitflags); + P_OFF(ObjectRecord, ptr_allocated_collisiondata_block); + P_OFF(ObjectRecord, projectile); + P_OFF(ObjectRecord, maxdamage); + P_OFF(ObjectRecord, damage); + P_OFF(ObjectRecord, shadecol); + P_OFF(ObjectRecord, nextcol); + + P_SZ(DoorRecord); + P_OFF(DoorRecord, linkedDoorOffset); + P_OFF(DoorRecord, maxFrac); + P_OFF(DoorRecord, doorFlags); + P_OFF(DoorRecord, doorType); + P_OFF(DoorRecord, keyflags); + P_OFF(DoorRecord, autoCloseFrames); + P_OFF(DoorRecord, doorOpenSound); + P_OFF(DoorRecord, frac); + P_OFF(DoorRecord, openstate); + P_OFF(DoorRecord, calculatedopacity); + P_OFF(DoorRecord, TintDist); + P_OFF(DoorRecord, CullDist); + P_OFF(DoorRecord, soundType); + P_OFF(DoorRecord, linkedDoor); + P_OFF(DoorRecord, unkcc); + P_OFF(DoorRecord, bbox); + P_OFF(DoorRecord, openedTime); + P_OFF(DoorRecord, portalNumber); + P_OFF(DoorRecord, openSoundState); + P_OFF(DoorRecord, closeSoundState); + + P_SZ(GuardRecord); + P_OFF(GuardRecord, chrnum); + P_OFF(GuardRecord, PadID); + P_OFF(GuardRecord, bitflags); + P_OFF(GuardRecord, HeadID); + P_OFF(GuardRecord, Data); + + P_SZ(GlobalDoorScaleRecord); + P_SZ(KeyRecord); + P_OFF(KeyRecord, keyflags); + P_SZ(TintedGlassRecord); + P_SZ(TagObjectRecord); + P_OFF(TagObjectRecord, NextTag); + P_OFF(TagObjectRecord, TaggedObject); + + /* --- records whose sizepropdef() arm is a hardcoded literal --- */ + /* Wrapped so the probe still builds if a type is missing; each line that + * fails to compile tells us that type has no PC struct yet. */ +#define TRY_SZ(t) P_SZ(t) + TRY_SZ(CutsceneRecord); + TRY_SZ(RenameObjectRecord); + TRY_SZ(LockDoorRecord); + TRY_SZ(SafeObjectRecord); + TRY_SZ(PhotographObjectRecord); + TRY_SZ(DestroyObjectRecord); + TRY_SZ(CollectObjectRecord); + TRY_SZ(DepositObjectRecord); + TRY_SZ(NULLObjectRecord); + TRY_SZ(CoopyObjectRecord); + + return 0; +}