JARVIS-MAGIC
Update 2.124
6/16/2026
v2.124.0.0MINORJarvis-MAGIC
Item Stack Cap 99→255: new `ItemStackCapHook` in `FfxHooksDll` (new capability, flag-gated)
- (new capability: a new runtime hook + writer-like patch that lifts the per-slot stack cap above vanilla; first time we touch
FFX_Inventory_AddItem). The finding (proven via idalib MCP onFFX_recon.i642026-06-16): the 99-per-slot inventory limit is clamped in a single central function inFFX.exe—FFX_Inventory_AddItem@0x003905A0(IDA flat0x7905A0) — through TWOpush 63hsites feeding the generic helperFFX_Math_ClampInt(v, 0, 99)@0x0039A0D0. All 14 callers (steal/drop/mix/shop/treasure/event/menu) all funnel through this one function — there is no copy-pasted clamp anywhere else. No save-load normalization (proved onFFX_Btl_PrepareSaveCommandState@0x786BC0: only initializes empty slots, does not reclamp existing counts). Storage (1 byte atQuantityBase+slotin the save +byte[112]in RAM at0xD30B5C) already supports 0..255 without realloc. Chosen strategy: byte-narrow patch CANNOT reach 255 (the6A FFpush imm8 would be sign-extended to -1) → 5-byte trampoline detour + heap stub per site (same pattern asNovaSuperDamageHook). Each stub:push imm32 <cap>replaces the originalpush 63h, replays the 2..3 displaced bytes, returns viajmp rel32to0x00390622/0x00390652. Cap is configurable via theFFXHOOKS_ITEM_STACK_CAPenv var (default 255, clamped to 1..255). Gating:item_stack_cap_255.flag(off by default = vanilla behavior preserved, natural regression test). Sentinel bytes verified before patching:kExpectedNew[5] = {6A 63 6A 00 53}(site #1 / new slot) andkExpectedExist[5] = {6A 63 8D 04 1E}(site #2 / existing slot). Automatic rollback if the second site write fails (no half-installed states). PolyHook build PASS (11/11 cpp including the newItemStackCapHook.cpp), Release DLL deployed (932352→935936 bytes, +3584 from the new hook)..i64renames+comments applied (IDA GOLDEN RULE):0x7905A0→FFX_Inventory_AddItem,0x79A0D0→FFX_Math_ClampInt,0x790500→FFX_Inventory_GetItemCount,0x784A90→FFX_Inventory_DebugMaxAll,g_CmdAggregateAvailArrays→g_FFX_InventoryAggregate, comments on0x79061D/0x79064D(clamp sites with the trampoline recipe). UI verdict (from the spike):safe-above-99-likely(the getter returns the raw byte without reclamp; visual risk = the 2-digit layout may overflow on "100"+ but the%dformatter handles 3 digits without crashing). Phase 3 UI patch not applied proactively — RT2 confirms. Phase 4 RT2 recipe with 6 scenarios (save-edit Quantity=200, steal/drop, shop buy, mix, Potion use, UI render) indocs/reverse/FFX_ITEM_STACK_CAP_99_RESEARCH_2026-06-16.md§8. In-game RT2 Needs Testing (Halyson). Doc:docs/reverse/FFX_ITEM_STACK_CAP_99_RESEARCH_2026-06-16.md. New files:RuntimeTools/FfxHooksDll/hooks/ItemStackCapHook.{h,cpp}. Touched files:shared/ffx_addresses.h(10 new RVAs+constants),dllmain.cpp(include + flag enablers + InstallItemStackCapHook insideInstallHooks+ RemoveItemStackCapHook),FfxHooksDll.vcxproj,build_hooks.ps1
v2.123.5.0
v2.124.0.1REVISIONJarvis-MAGIC
Ronso Mana (5th RE pass): the "full bar" theory is DEAD — the real gate is MENU-TREE NODE RESOLUTION (`ResolveMenuTreeNode`), not the gauge
- (RE/docs + renames/comments on the real
.i64; NO behavior change, DLL untouched — the other room can use it). ThehudSafe=24(v2.123.4.1) RT2 FAILED and the log PROVES why the entire gauge line (hudSafe 23/24) was wrong:P0 dispatch #1..#48 charge=100 max=100across ALL 48 frames (the persistent pin worked, the bar was genuinely full every frame) + bits0x590=0x0D+IsOdReady ->1(evenvanilla=1) +311Ain the ring — and OD stayed hidden / LEFT stayed blocked. ⇒charge==maxis NOT the gate. (Bonus: the log floodsWARN: OD ring header NOT FOUND in commands 0-49—ScanForOdRingHeaderscans the wrong range; the OD header is cmd282.) The REAL gate (proven by idalib decompile):FFX_Btl_UI_BuildCommandRing@0x7ACEC0builds the main ring (Attack/Skill/Special) viaFFX_Btl_UI_BuildMainCommandRingTree@0x7A07D0only whenkind<=8; the Overdrive ring iskind=12→ it SKIPS7A07D0and depends 100% onResolveMenuTreeNode(2,1,slot+41)>=0(only then doessub_7979E0finalize). For Kimahri (slot 2) that'sResolve(2,1,43), which returns −1. The exact cause is inFFX_Btl_UI_WalkMenuBlobIndex@0x797420:idx=blob[2+treeId]; cnt=blob[1]; if(idx==0xFF || treeId>=cnt) return 0→LookupMenuBlob=0→Resolve=−1→ the OD ring never displays. Two-axis mapping: main=Resolve(2,**0**,slot+109)on blobg_FFX_MenuTreeBlob_MainRing(*0x112A994) — resolves OK (user sees it); OD=Resolve(2,**1**,slot+41)on blobg_FFX_MenuTreeBlob_OdRing(*0x112A9B4) — Kimahri testsblob[45]. Both blobs are STATICsystem_01resource data (viaFFX_Btl_UI_InitMenuBlobPointers@0x783ED0:blob = base + *(base+N)), identical with/without OD →blob[2+treeId]is a node index, not a bool (which is whyhudSafe=19got the semantics wrong). None of this reads0x5BC/0x5BD(charge/max)..i64renames+comments applied and saved (GOLDEN RULE):0x112A9B4→g_FFX_MenuTreeBlob_OdRing,0x112A994→g_FFX_MenuTreeBlob_MainRing,0x112A9A8→g_FFX_MenuBlobBase_system01+ comments on0x797420(gate formula),0x7985A0,0x7ACEC0,0x7A07D0,0x783ED0. DECISIVE EXPERIMENT (next, needs 1 DLL session): a DIAG hook inResolveMenuTreeNodewhena1==2&&a2==1loggingtreeId,blobPtr=*0x112A9B4,cnt=blob[1],idx=blob[2+treeId], vanilla return — across 2 scenarios (proven save-edit full-OD vs our forced) and diff theidx/cnt→ reveals the exact value for the fix (write a validblob[45], or redirect the treeId, or populate the per-actorcase 3actor+0xF7C). Recommendation: revert/neutralize thehudSafe=24gauge pin (it's not the path), keeping onlygateMin/drainCost(consumption already confirmed correct). Doc:docs/reverse/FFX_RONSO_MANA_COMMAND_RING_PIPELINE_RE_2026-06-16.md§11
v2.124.0.0
v2.124.0.2REVISIONJarvis-MAGIC
Build+deploy of `ffx-hooks.dll` (DLL freed): materializes the Nul Ward menu-bound fix + arms the Nul Ward and Item Stack Cap flags for RT2; offline preflight GREEN
- (operational: rebuild+deploy of already-versioned code + flag creation; NO new source behavior this pass — the menu-bound fix is from
v2.123.4.0, the editor LearnedMove encoding fix had its own entry). Halyson freed the DLL (the Ronso Mana lane finished using it). Actions: (1)build_hooks.ps1 -WithPolyHook -Release→ 10/10 hooks compiled, includingNulWardTeachHook.cppwith the multi-site fix (cmp r32,140h/cmp eax,140h→322, patches ALL sites incl. the PLACEMENT loop81 FF=edi) that was previously only coded; (2) deploy viainstall_to_modules.ps1 -EnableApply -EnableTeach— backup of the prior DLL (ffx-hooks.dll.backup-nul-ward-20260616-071450), new SHA-prefix95E1D56A8B9269F7, flagsnul_ward.flag+nul_ward_apply.flag+nul_ward_teach.flag+nul_ward_teach_grant.flag; (3) at another lane's request, createdmodules/item_stack_cap_255.flag(empty) → armsItemStackCapHook(stack cap 99→255; hook authored by the other lane, already compiled into the shared DLL, defaultFFX_ITEM_STACK_CAP_EXTENDED=255). The--nul-ward-staticgate is now VERDICT: PASS (exe bytes + 322 rows +engine_lookup_resolvesRadiant@0x7814/Umbral@0x7874inRange + DLL strings + deploy flags + nova clear). In-game RT2 unblocked (Nul Ward: casting 320/321 not-becoming-Attack already proven offline, pending white-magic menu surfacing + grid teaching + persistence; Item Stack Cap: the 6 scenarios in doc §8 — stack 100 Potions, Steal/Drop/Mix/Shop/Treasure, flag-off regression). Docs:FFX_NUL_WARD_TEACH_SURFACE_RE_VERDICT_2026-06-16,FFX_ITEM_STACK_CAP_99_RESEARCH_2026-06-16§8
v2.124.0.1