// Research only: current assembly SHA c5e02e6e984f7e5577c78b995a8efeee2d06ee22e67d484ad5e859c8bf21e144. // Create(UInt32,...) calls this flat row getter at RVA 1dee0820 before // allocating MonsterData. Only that getter and the existing serializer run. const ids=globalThis.CW_MONSTER_IDS; if(!Array.isArray(ids)||!ids.length||ids.length>128||ids.some(x=>!Number.isInteger(x)||x<=0))throw Error('Bounded monster ID plan required'); const m=Process.getModuleByName('GameAssembly.dll'); const check=(rva,expected)=>{const b=Array.from(new Uint8Array(m.base.add(rva).readByteArray(expected.length)));if(b.some((x,i)=>x!==expected[i]))throw Error('Entry identity differs');}; check(0x1dee0820,[0x56,0x48,0x83,0xec,0x60]);check(0x1d3efb60,[0x56,0x57,0x48,0x83,0xec,0x28]); const read=new NativeFunction(m.base.add(0x1dee0820),'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{for(let n=0;n<4&&index65536)throw Error('Row bound');send({kind:'ROW',id,json:v.add(0x14).readUtf16String(length)});}if(index===ids.length){done=true;send({kind:'END',count:index});}}catch(e){done=true;send({kind:'ERROR',error:String(e)});}}}); send({kind:'BEGIN',count:ids.length});