From e0474ff92ad5b3966e183adae524f507234f355b Mon Sep 17 00:00:00 2001 From: arcattack Date: Fri, 17 Jul 2026 09:50:23 -0500 Subject: [PATCH] Per-pilot mech paint: wire the color/badge/patch substitution end-to-end - Mech::resourceNameA/B/C -> real CString members (the binary's 16-byte CStringRepresentation; deep-copy bind from the MakeMessage = FUN_00402a98, implicit member dtors) + paint-name accessors - SetupMaterialSubstitutionList reads the real egg names ([paint] log); TearDown clears the callback first (FUN_004d11e8) - dpl_SetMaterialNameCallback is real now (L4VIDEO registry); bgfload MaterialResolver::resolve() applies it to every material name -- the port analogue of the dpl board rewriting names at load - MP_BHMC.EGG: color=Red -> Crimson (vehicletable has no Red; binary Fail()ed) Verified live 2-node MP: crimson MadCat with hip hazard stripes + yellow VGL leg emblems; white Blackhawk + emblems; replicants painted on both nodes. Co-Authored-By: Claude Fable 5 --- content/MP_BHMC.EGG | 244 +++++++++++++++++++++++++++++++++ context/open-questions.md | 8 +- context/rendering.md | 23 +++- engine/MUNGA_L4/L4VIDEO.cpp | 33 +++++ engine/MUNGA_L4/L4VIDEO.h | 7 + engine/MUNGA_L4/bgfload.cpp | 11 +- game/reconstructed/btl4vid.cpp | 42 +++--- game/reconstructed/btstubs.cpp | 16 +-- game/reconstructed/mech.cpp | 25 ++-- game/reconstructed/mech.hpp | 26 +++- 10 files changed, 382 insertions(+), 53 deletions(-) create mode 100644 content/MP_BHMC.EGG diff --git a/content/MP_BHMC.EGG b/content/MP_BHMC.EGG new file mode 100644 index 0000000..ff91cb1 --- /dev/null +++ b/content/MP_BHMC.EGG @@ -0,0 +1,244 @@ +[mission] +adventure=BattleTech +map=cavern +scenario=freeforall +time=night +weather=clear +temperature=27 +length=600 +[ordinals] +bitmap=Ordinal::BitMap::1 +bitmap=Ordinal::BitMap::2 +bitmap=Ordinal::BitMap::3 +bitmap=Ordinal::BitMap::4 +[Ordinal::BitMap::1] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000038000003C000001FF00000000F00 +bitmap=0000F8000003C000003FF80000000F00 +bitmap=0001F8000003C00000707C0000000F00 +bitmap=0001F8000003C00000603C0000000F00 +bitmap=00007801FC0FF00000003C3DF807FF00 +bitmap=00007803FE0FF00000003C3FFC0FFF00 +bitmap=00007803C703C00000003C3E3C1F0F00 +bitmap=000078078303C0000000783C1E1E0F00 +bitmap=000078078003C0000000783C1E1E0F00 +bitmap=00007807C003C0000000F03C1E1E0F00 +bitmap=00007807F003C0000001E03C1E1E0F00 +bitmap=00007803FC03C0000003C03C1E1E0F00 +bitmap=00007801FE03C0000007803C1E1E0F00 +bitmap=000078007F03C000000F003C1E1E0F00 +bitmap=000078001F03C000001E003C1E1E0F00 +bitmap=000078000F03C000003C003C1E1E0F00 +bitmap=000078060F03C0000078003C1E1E0F00 +bitmap=000078071E03E0000078003C1E1F1F00 +bitmap=00007803FE01F000007FFC3C1E0FFF00 +bitmap=00007801FC00F000007FFC3C1E07EF00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::2] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF0000003C0000003C03C0780000 +bitmap=001FFF0000003C0000007C03C0780000 +bitmap=00000E0000003C000000FC03C0780000 +bitmap=00003C0000003C000001BC03C0780000 +bitmap=0000700F3E1FFC000003BC0FF07BF000 +bitmap=0001E00F7E3FFC0000073C0FF07FF800 +bitmap=0003800FFE7C3C0000063C03C07C7800 +bitmap=0007F80FFE783C00000C3C03C0783C00 +bitmap=0007FE0F80783C0000183C03C0783C00 +bitmap=00001E0F00783C0000383C03C0783C00 +bitmap=00000F0F00783C0000703C03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00180F0F00783C0000003C03C0783C00 +bitmap=001C1F0F007C7C0000003C03E0783C00 +bitmap=000FFE0F003FFC0000003C01F0783C00 +bitmap=0007FC0F001FBC0000003C00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::3] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000000FC03C0780000 +bitmap=001FFF03C07800000003FC03C0780000 +bitmap=001E0003C07800000007C003C0780000 +bitmap=001E0003C0780000000F0003C0780000 +bitmap=001E000FF07BF000000F000FF07BF000 +bitmap=001E000FF07FF800001E000FF07FF800 +bitmap=001E0003C07C7800001E0003C07C7800 +bitmap=001FFC03C0783C00001EFC03C0783C00 +bitmap=001FFE03C0783C00001FFE03C0783C00 +bitmap=00001F03C0783C00001F1F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00180F03C0783C00001E0F03C0783C00 +bitmap=001C1F03E0783C00001F1F03E0783C00 +bitmap=000FFE01F0783C00000FFE01F0783C00 +bitmap=0007FC00F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::4] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000007FC03C0780000 +bitmap=001FFF03C0780000000FFE03C0780000 +bitmap=00000F03C0780000001F1F03C0780000 +bitmap=00000F03C0780000001E0F03C0780000 +bitmap=00000F0FF07BF000001E0F0FF07BF000 +bitmap=00001F0FF07FF800001E0F0FF07FF800 +bitmap=00001E03C07C7800001E0F03C07C7800 +bitmap=00003E03C0783C00001E0F03C0783C00 +bitmap=00003C03C0783C00000F1E03C0783C00 +bitmap=00003C03C0783C000007FC03C0783C00 +bitmap=00007803C0783C000007FC03C0783C00 +bitmap=00007803C0783C00000F1E03C0783C00 +bitmap=00007803C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003E0783C00000F1E03E0783C00 +bitmap=0000F001F0783C00000FFE01F0783C00 +bitmap=0000F000F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[pilots] +pilot=127.0.0.1:1502 +pilot=127.0.0.1:1602 +[127.0.0.1:1502] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Aeolus +bitmapindex=1 +experience=expert +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=bhk1 +vehicleValue=1000 +color=White +[127.0.0.1:1602] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Boreas +bitmapindex=2 +experience=expert +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=madcat +vehicleValue=1000 +color=Crimson +[largebitmap] +bitmap=BitMap::Large::Aeolus +[BitMap::Large::Aeolus] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000003FE0000000001E0000000000000 +bitmap=000007FF0000000001E0000000000000 +bitmap=00000F8F8000000001E0000000000000 +bitmap=00000F078000000001E0000000000000 +bitmap=00000F0781FF00FF81E0F0781FC00000 +bitmap=00000F0783FF81FFC1E0F0783FE00000 +bitmap=00000F0787C7C3E3E1E0F0783C700000 +bitmap=00000F078783C3C1E1E0F07878300000 +bitmap=00000F078783C3C1E1E0F07878000000 +bitmap=00000FFF8783C3C1E1E0F0787C000000 +bitmap=00000FFF8783C3C1E1E0F0787F000000 +bitmap=00000F0787FFC3C1E1E0F0783FC00000 +bitmap=00000F0787FFC3C1E1E0F0781FE00000 +bitmap=00000F07878003C1E1E0F07807F00000 +bitmap=00000F07878003C1E1E0F07801F00000 +bitmap=00000F07878003C1E1E0F07800F00000 +bitmap=00000F078780C3C1E1E0F07860F00000 +bitmap=00000F0787C1C3E3E1E078F871E00000 +bitmap=00000F0783FF81FFC1E07FF83FE00000 +bitmap=00000F0781FF00FF81E03F781FC00000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[smallbitmap] +bitmap=BitMap::Small::Aeolus +[BitMap::Small::Aeolus] +bitmap=00000000000000000000000000000000 +bitmap=0000000000000000003E00000C000000 +bitmap=006300000C00000000631F0F8CC63C00 +bitmap=00633198CCC66200007F3198CCC66000 +bitmap=00633F98CCC6780000633018CCC61E00 +bitmap=00633018CCC6060000633098CCC64600 +bitmap=00631F0F8C7E3C000000000000000000 +bitmap=00000000000000000000000000000000 +x=64 +y=16 +width=4 +[Role::Default] +model=dfltrole +[Role::NoReturn] +model=noretun diff --git a/context/open-questions.md b/context/open-questions.md index f027f97..57f6955 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -644,10 +644,10 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR LIVENESS audit (does a live call path reach each reconstructed fn?) would catch this class. ## Rendering follow-ups (non-blocking) -- **Per-pilot mech PAINT (color/badge/patch) — art ships, port never engages it** (2026-07-17 audit; - full mechanics + fix path in [[rendering]] §Per-pilot mech PAINT). Wire egg color/badge/patch into - `SetupMaterialSubstitutionList`, apply `substituteMaterial` in `bgfload resolve()`, per-variant - mesh cache. This is the "missing warning stripes / coloration" report. +- ~~Per-pilot mech PAINT (color/badge/patch)~~ — **DONE 2026-07-17, verified live** (crimson MadCat + + yellow VGL emblems + hip hazard stripes). Mechanics + the vehicletable color/badge/patch name + tables in [[rendering]] §Per-pilot mech PAINT. Residual nit: eggs saying `color=Red` mean + **Crimson** (table has no Red; the binary Fail()ed on it, the port warns + drops). - **.PFX effect-layer polish (deferred 2026-07-08; the layer itself is LIVE + verified).** The reconstructed BT particle layer (L4VIDEO.cpp, see its banner) renders the authentic .PFX content (fire + smoke, premultiplied blend, impact-frame oriented — weapon hits AND damage bands via diff --git a/context/rendering.md b/context/rendering.md index ed857ae..b5852b3 100644 --- a/context/rendering.md +++ b/context/rendering.md @@ -126,13 +126,22 @@ never engaged by the port. Mechanics (all [T1] from the BMF/BGF/vehicletable con top-left), shown through the paintable-zone materials and tinted by the pilot color. `EMBLEM0.BMF` carries `lgoN__dz_ld/rdleg` materials (8 patch tints incl. yellow .89,.73,0) over `LGO.BSL` slices 0-5 (slice 5 = VGL winged logo). -- PORT GAP: `SetupMaterialSubstitutionList` (btl4vid.cpp:2094) feeds NULL egg names (Mech's - resourceNameA/B/C not yet safely readable), and `substituteMaterial` (L4VIDEO.cpp:2565) is consumed - only by the legacy .X loader (L4D3D.cpp:151) — NOT by `bgfload`'s `MaterialResolver::resolve`. So - every mech renders base untinted paint + the hardwired `lgo6` (VGL) emblem. Verified live 2026-07-17: - MP MadCat renders white/gray, no tint/emblem/stripes. FIX PATH: (a) wire real egg color/badge/patch - into the substitution list; (b) apply the substitution in `resolve()` (BGF path); (c) per-variant - mesh caching (BgfData is cached per filename — two pilots' colors must not share a baked mesh). [T2] +- **WIRED 2026-07-17 (same-day fix), verified live**: MP MadCat renders crimson with hip hazard + stripes + yellow VGL leg emblems; Blackhawk white + emblems; replicants painted on both nodes. + The reconstruction: (a) `Mech::resourceNameA/B/C` are real `CString` members (the binary's exact + 16-byte `CStringRepresentation` {size,len,text@+8,refs@+0xc}; ctor = part_012.c:9841-71, deep-copy + bind from the MakeMessage = `CString::operator=(const char*)` == FUN_00402a98 @10427-41, implicit + member dtors = 11388-403) with paint-name accessors; (b) `SetupMaterialSubstitutionList` reads them + (real egg names; `[paint]` log line); (c) `dpl_SetMaterialNameCallback` is REAL now + (L4VIDEO.cpp registry, cleared first in TearDown as FUN_004d11e8 does) and + `bgfload MaterialResolver::resolve()` runs every name through `dpl_ApplyMaterialNameCallback`; + no mesh-cache issue (LoadObject/LoadBgfFile don't cache — every load resolves fresh). [T2] +- DEVIATION (documented, btl4vid.cpp): on a vehicletable miss the binary Fail()ed ("Exiting" + @BTL4VID.CPP:0xbeb); the port warns + drops that placeholder (RP analogue). Real case: dev eggs + ship `color=Red` but the `[color]` table only has **Crimson** — MP_BHMC.EGG fixed to Crimson; + LAST.EGG pilot-2 still says Red (unused slot). The 7 valid egg colors: + Black/Brown/Crimson/Green/Grey/Tan/White; badges Davion/Kurita/Liao/Marik/None/Steiner/VGL; + patches Black/Blue/Green/Grey/Red/Violet/White/Yellow. [T1] ## The OLD-STYLE SPECIALFX layer (the weapon-impact action) — RECONSTRUCTED 2026-07-12 (firmware-decoded) The per-weapon impact visuals are fully AUTHORED in `content/BTDPL.INI` `[effects_to_load]`: diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index bbc9ef3..af2b08a 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -2631,6 +2631,39 @@ char* return source; } +//############################################################################# +// dpl material-name callback registry (FUN_0049664c / DAT_00527f24). +//----------------------------------------------------------------------------- +// The 1995 dpl board applied the installed callback to every material name as +// a model loaded (dpl_LoadObject). The port analogue: the BGF loader +// (bgfload.cpp MaterialResolver::resolve) runs every "lib:material" name +// through dpl_ApplyMaterialNameCallback before resolving it, so the per-pilot +// paint substitutions (skin: -> _:, skin:lgo -> emblem:) +// installed by SetupMaterialSubstitutionList take effect on the mech model +// loads that happen between the Setup/TearDown bracket. +//############################################################################# +static char *(*gMaterialNameCallback)(char *source) = NULL; + +void dpl_SetMaterialNameCallback(char *(*callback)(char *source)) +{ + gMaterialNameCallback = callback; +} + +// C++-friendly wrapper honouring the callback's legacy protocol (the callback +// may delete[] the input and return a new[] buffer). Returns the (possibly +// substituted) name. +std::string dpl_ApplyMaterialNameCallback(const std::string &name) +{ + if (gMaterialNameCallback == NULL) + return name; + char *buf = new char[name.size() + 1]; + strcpy(buf, name.c_str()); + buf = gMaterialNameCallback(buf); + std::string result(buf); + delete [] buf; + return result; +} + vector DPLRenderer::MonitorsCreateAll(int &monitorCount) { monitorCount = gD3D->GetAdapterCount(); diff --git a/engine/MUNGA_L4/L4VIDEO.h b/engine/MUNGA_L4/L4VIDEO.h index 6d3d455..f15c101 100644 --- a/engine/MUNGA_L4/L4VIDEO.h +++ b/engine/MUNGA_L4/L4VIDEO.h @@ -40,6 +40,13 @@ class NotationFile; char* substituteMaterial(char *source); +// dpl material-name callback registry (FUN_0049664c): install/clear the +// per-load substitution hook; dpl_ApplyMaterialNameCallback runs the installed +// hook over a name (identity when none installed). Consumed by the BGF +// loader's material resolver. +void dpl_SetMaterialNameCallback(char *(*callback)(char *source)); +std::string dpl_ApplyMaterialNameCallback(const std::string &name); + void loadTables(); const char* opMaterialName(const char *fileName, int opId); diff --git a/engine/MUNGA_L4/bgfload.cpp b/engine/MUNGA_L4/bgfload.cpp index 049b8a7..0918420 100644 --- a/engine/MUNGA_L4/bgfload.cpp +++ b/engine/MUNGA_L4/bgfload.cpp @@ -11,6 +11,11 @@ #include #include +// L4VIDEO.cpp: the dpl material-name callback registry. While a mech loads +// (the SetupMaterialSubstitutionList / TearDown bracket) this rewrites +// "blhskin:..." -> "blh_wht0:..." / "emblem0:..." -- the per-pilot paint. +std::string dpl_ApplyMaterialNameCallback(const std::string &name); + namespace { // ---- DIV-BIZ2 tag ids (PFBIZTAG.H), masked form (tagword & 0x2fff) ---- @@ -503,7 +508,11 @@ struct MaterialResolver { return libs.emplace(lib, std::move(table)).first->second; } - MatInfo resolve(const std::string& full) { + MatInfo resolve(const std::string& fullIn) { + // Per-pilot paint: run the name through the installed dpl material-name + // callback first (identity when none) -- the board rewrote names at + // load time, before any material lookup. + std::string full = dpl_ApplyMaterialNameCallback(fullIn); auto cached = resolvedCache.find(full); if (cached != resolvedCache.end()) return cached->second; MatInfo info; diff --git a/game/reconstructed/btl4vid.cpp b/game/reconstructed/btl4vid.cpp index d7a384c..75fe058 100644 --- a/game/reconstructed/btl4vid.cpp +++ b/game/reconstructed/btl4vid.cpp @@ -2128,23 +2128,25 @@ void // // Look up this mech's colour / badge / patch codes from the table, using // the egg-supplied names carried on the entity (badge=resourceNameA @0x844, - // color=resourceNameB @0x848, patch=resourceNameC @0x84c). + // color=resourceNameB @0x848, patch=resourceNameC @0x84c). The binary + // reads the backing text straight off the ref-counted name objects + // (*(char**)(*(int*)(mech+0x848)+8) etc.); the reconstructed Mech now + // carries them as real CString members deep-copied from the MakeMessage, + // exposed via the paint-name accessors. // - // The recovered code read these directly off the BattleTech mech egg - // (Mech::vehicleColor / vehicleBadge / vehiclePatch) and Fail()ed on a miss. - // TODO(bring-up): the reconstructed Mech carries those names as ref-counted - // creation-name objects (resourceNameA/B/C) whose backing string is the - // transient MakeMessage buffer -- not safely readable here yet, and the - // attribute-index path ("VehicleColor"/...) is not wired, so it returns - // garbage that FindNote then deref-crashed on (btl4vid.cpp:808). Follow the - // RP analogue (RPL4VID.cpp:1562) which simply tolerates a missing colour/ - // badge: a NULL egg name or a table miss leaves veh_* == NULL and the - // placeholder substitution below drops to default materials. Re-wire the - // real egg names (a named Mech accessor) once the Mech layout is mapped. + // DEVIATION (documented): on a table miss the binary Fail()ed hard + // ("Exiting" @BTL4VID.CPP:0xbeb/0xbf2/0xbf9). We follow the RP analogue + // (RPL4VID.cpp:1562) and tolerate it -- a NULL/missing name leaves veh_* == + // NULL and that placeholder expands empty (base materials) -- so a sparse + // test egg degrades to the old gray-metal look instead of killing the pod. // - const char *egg_color = NULL; // [0x848] resourceNameB (color) - const char *egg_badge = NULL; // [0x844] resourceNameA (badge) - const char *egg_patch = NULL; // [0x84c] resourceNameC (patch) + Mech *mech = (Mech *)entity; // caller dispatched on MechClassID + const char *egg_color = mech->GetVehicleColorName(); // [0x848] resourceNameB + const char *egg_badge = mech->GetVehicleBadgeName(); // [0x844] resourceNameA + const char *egg_patch = mech->GetVehiclePatchName(); // [0x84c] resourceNameC + if (egg_color && !*egg_color) egg_color = NULL; + if (egg_badge && !*egg_badge) egg_badge = NULL; + if (egg_patch && !*egg_patch) egg_patch = NULL; const char *veh_color = NULL, *veh_badge = NULL, *veh_patch = NULL; if (egg_color && !veh_tbl->GetEntry("color", egg_color, &veh_color)) // @0051d94e @@ -2166,6 +2168,14 @@ void veh_patch = NULL; } + DEBUG_STREAM << "[paint] mech egg color='" << (egg_color ? egg_color : "") + << "' badge='" << (egg_badge ? egg_badge : "") + << "' patch='" << (egg_patch ? egg_patch : "") + << "' -> codes color=" << (veh_color ? veh_color : "-") + << " badge=" << (veh_badge ? veh_badge : "-") + << " patch=" << (veh_patch ? veh_patch : "-") + << " serno=" << gSerno << "\n" << std::flush; + // // Generic substitution list, then expand placeholders per entry. // @@ -2257,6 +2267,7 @@ void void BTL4VideoRenderer::TearDownMaterialSubstitutionList() { + dpl_SetMaterialNameCallback(NULL); // FUN_0049664c(0) -- first, as the binary does if (materialSubstitutionList != NULL) { for (NameList::Entry *entry = materialSubstitutionList->GetFirstEntry(); @@ -2269,7 +2280,6 @@ void delete materialSubstitutionList; materialSubstitutionList = NULL; } - // dpl_SetMaterialNameCallback(NULL); } //===========================================================================// diff --git a/game/reconstructed/btstubs.cpp b/game/reconstructed/btstubs.cpp index f4d9daa..503a025 100644 --- a/game/reconstructed/btstubs.cpp +++ b/game/reconstructed/btstubs.cpp @@ -247,17 +247,11 @@ LWord BT_GetSegmentFlags(Mech *owner) return owner ? owner->simulationFlags : 0; } -//===========================================================================// -// Legacy libDPL material-name callback. -//---------------------------------------------------------------------------// -// The dpl2d_ 2D display-list API (NewDisplayList/Begin/SetColor/Circle/ -// PushMatrix/PopMatrix/MoveTo/End/Compile) was MOVED OUT of this file and -// reimplemented over Direct3D 9 in decomp/reconstructed/dpl2d.cpp (the HUD/ -// reticle 2D layer). Only the 3D material-name-substitution hook -- which is -// part of the world renderer, not the 2D HUD -- remains stubbed here. -// TODO(bring-up): wire the material substitution table (FUN_00459eb8). -//===========================================================================// -void dpl_SetMaterialNameCallback(char *(*)(char *)) {} +// dpl_SetMaterialNameCallback is now REAL (per-pilot mech paint): it lives in +// engine/MUNGA_L4/L4VIDEO.cpp next to substituteMaterial, and the BGF loader +// (bgfload.cpp MaterialResolver::resolve) applies the installed callback to +// every material name -- the port analogue of the dpl board rewriting names +// at dpl_LoadObject time (FUN_0049664c / FUN_00459eb8). //===========================================================================// // BTL4VideoRenderer helper methods whose bodies were not reconstructed. diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index 6ed9e9c..44770f2 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -888,10 +888,11 @@ Mech::Mech( // // Three ref-counted creation-name holders (badge/color/insignia). + // CString members now: their default ctors run in the member-init phase and + // allocate the empty CStringRepresentation + addref -- exactly the binary's + // new(0x10)/FUN_004023f4/refs++ triple @part_012.c:9841-9871. Nothing to + // do here. // - resourceNameA = NewRefCounted(); // Wword(0x211) - resourceNameB = NewRefCounted(); // Wword(0x212) - resourceNameC = NewRefCounted(); // Wword(0x213) instanceFlags |= 0x10; // this[10] |= 0x10 // Zero the optional-subsystem back-pointers up front (binary @9873-9875). @@ -1710,11 +1711,14 @@ Mech::Mech( } // - // Bind the three creation-name strings from the MakeMessage. + // Bind the three creation-name strings from the MakeMessage. CString:: + // operator=(const char*) releases the empty ctor rep and allocates a fresh + // rep COPYING the message's inline char[20] -- byte-for-byte FUN_00402a98 + // (part_012.c:10427-10441); the names survive the transient message. // - resourceNameA = creation_message->resourceNameA; // param_2+0x7c -> Wword(0x211) - resourceNameB = creation_message->resourceNameB; // param_2+0x90 -> Wword(0x212) - resourceNameC = creation_message->resourceNameC; // param_2+0xa4 -> Wword(0x213) + resourceNameA = creation_message->resourceNameA; // badge param_2+0x7c -> Wword(0x211) + resourceNameB = creation_message->resourceNameB; // color param_2+0x90 -> Wword(0x212) + resourceNameC = creation_message->resourceNameC; // patch param_2+0xa4 -> Wword(0x213) // // Register this Mech in the global "Mechs" directory. @@ -1810,9 +1814,10 @@ Mech::~Mech() delete (MechDeathHandler *)deathHandler; // real class -> plain delete } - ReleaseRefCounted(resourceNameC); // Wword(0x213) - ReleaseRefCounted(resourceNameB); // Wword(0x212) - ReleaseRefCounted(resourceNameA); // Wword(0x211) + // resourceNameC/B/A: the binary's inline refcount-decrement/delete triple + // (part_012.c:11388-11403) is the CString member dtors -- implicit at the + // closing brace now that the members are real CStrings (dtor-epilogue rule: + // do NOT also write them out here). // // ⚠ RECONSTRUCTION RULE (root cause of the P5 death-row crash AND the app-exit diff --git a/game/reconstructed/mech.hpp b/game/reconstructed/mech.hpp index 291393a..3b079cf 100644 --- a/game/reconstructed/mech.hpp +++ b/game/reconstructed/mech.hpp @@ -50,6 +50,7 @@ # include // GaugeAlarm #endif #include // PlatformTool +#include // CString (the vehicle badge/color/patch creation names) #include // AffineMatrix #include // AverageOf #include // Scalar @@ -764,10 +765,27 @@ protected: // eye-slew system (HUD freeAimSlew / gyro eye chain) writes it. EulerAngles eyepointRotation; - // Three ref-counted creation-name objects (badge/color/insignia). - void *resourceNameA; // @0x844 this[0x211] - void *resourceNameB; // @0x848 this[0x212] - void *resourceNameC; // @0x84c this[0x213] + // Three ref-counted creation-name strings (badge/color/insignia). + // CString members: each slot is a CStringRepresentation* -- the exact + // 16-byte object {size,len,text@+8,refs@+0xc} the binary's Mech ctor + // allocates (part_012.c:9841-9871) and the make handler re-binds from + // the MakeMessage via CString::operator=(const char*) == FUN_00402a98 + // (part_012.c:10427-10441). SetupMaterialSubstitutionList reads the + // backing text (*(char**)(this+0x844..84c)+8) for the vehicle-table + // color/badge/patch lookups. + CString resourceNameA; // @0x844 this[0x211] (vehicle badge) + CString resourceNameB; // @0x848 this[0x212] (vehicle color) + CString resourceNameC; // @0x84c this[0x213] (vehicle patch) + + public: + // Paint-name accessors (SetupMaterialSubstitutionList). CString's + // operator char*() returns the rep's stringText -- NULL for a + // default-constructed (never-bound) name; callers must tolerate NULL. + const char *GetVehicleBadgeName() const { return (const char *)resourceNameA; } + const char *GetVehicleColorName() const { return (const char *)resourceNameB; } + const char *GetVehiclePatchName() const { return (const char *)resourceNameC; } + + protected: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Reconstruction additions -- members & methods whose bodies live in the