// Native table getter used by GridFightStageNode._RefreshCachedFields with // the route node's ChapterID. Source hash and function bounds are retained in // cw-chapter-table-getter.asm. Run only on the managed handbook caller thread. const ids = globalThis.CW_CHAPTER_IDS; if (!Array.isArray(ids) || ids.length === 0 || ids.length > 128 || ids.some(x=>!Number.isInteger(x)||x<=0)) throw Error('Supply captured route chapter IDs'); const m = Process.getModuleByName('GameAssembly.dll'); const bytes = Array.from(new Uint8Array(m.base.add(0x1de40ce0).readByteArray(5))); if (bytes.join(',') !== [0x56,0x48,0x83,0xec,0x60].join(',')) throw Error('Chapter getter prefix mismatch'); const read = new NativeFunction(m.base.add(0x1de40ce0),'pointer',['uint32','pointer']); const serialize = new NativeFunction(m.base.add(0x1d3efb60),'pointer',['pointer','pointer']); let index=0, done=false; Interceptor.attach(m.base.add(0xdb5f860), {onLeave() { if(done)return; try { const key=ids[index++], row=read(key,ptr(0)); if(row.isNull())throw Error('Missing chapter row '+key); const encoded=serialize(row,ptr(0)); if(encoded.isNull())throw Error('Serializer returned null'); const length=encoded.add(0x10).readS32(); if(length<2||length>65536)throw Error('Chapter row exceeds capture bound'); const json=encoded.add(0x14).readUtf16String(length), parsed=JSON.parse(json); if(parsed.ChapterID!==key && parsed.ID!==key)throw Error('Chapter row identity differs: '+Object.keys(parsed).join(',')); send({kind:'ROW',key,json,thread:Process.getCurrentThreadId()}); if(index===ids.length){done=true;send({kind:'END',count:ids.length});} }catch(e){done=true;send({kind:'ERROR',error:String(e)});} }}); send({kind:'BEGIN',count:ids.length});