Files
GaCy-GO/tools/preview_gameboy_tables.js
2026-10-28 18:54:43 +07:00

27 lines
2.2 KiB
JavaScript

// Research capture for SHA 4aa08a0706f27d2cf9951b61920c179140ece9ea94f7e34422c18e46922939b6.
// Exact FromBinary -> Add dictionary -> GetAll chains are retained in
// battle_diagnostics/preview-*-*.asm. Only flat config getters are called.
const mod = Process.getModuleByName('GameAssembly.dll');
const tables = [['gameboy',0x1ded33b0]];
for (const [name,rva] of tables) {
if (mod.base.add(rva).readU32() !== 0x28ec8348) throw Error('Getter prologue differs: '+name);
}
const serialize = new NativeFunction(mod.base.add(0x1d46c040),'pointer',['pointer','pointer']);
let table=0,index=0,count=0,entries=null,done=false,busy=false;
const marker='GAC_PREVIEW_TICK';
Interceptor.attach(mod.base.add(0x1d46c040),{
onEnter(args){this.trigger=false;if(done||busy)return;try{this.trigger=args[0].add(0x10).readS32()===marker.length&&args[0].add(0x14).readUtf16String(marker.length)===marker;}catch(e){}},
onLeave(){if(!this.trigger||busy||done)return;busy=true;try{
if(entries===null){const get=new NativeFunction(mod.base.add(tables[table][1]),'pointer',['pointer']);const dict=get(ptr(0));if(dict.isNull())throw Error('Missing dictionary');count=dict.add(0x20).readU32();entries=dict.add(0x18).readPointer();index=0;if(count>4096||entries.isNull()||entries.add(0x18).readU32()<count)throw Error('Dictionary bound');send({kind:'BEGIN',table:tables[table][0],count});}
const rows=[];
for(let batch=0;batch<16&&index<count;batch++){
const entry=entries.add(0x20+24*index++);if(entry.readS32()<0)continue;
const row=entry.add(16).readPointer();if(row.isNull())continue;
const value=serialize(row,ptr(0));if(value.isNull())throw Error('Missing serialization');const length=value.add(0x10).readS32();if(length<2||length>32768)throw Error('Serialization bound');rows.push(value.add(0x14).readUtf16String(length));
}
send({kind:'ROWS',table:tables[table][0],rows});
if(index>=count){send({kind:'TABLE_END',table:tables[table][0],count});table++;entries=null;if(table===tables.length){done=true;send({kind:'END'});}}
}catch(e){done=true;send({kind:'ERROR',error:String(e)});}finally{busy=false;}}
});
send({kind:'ATTACHED',path:mod.path,scope:'four named flat config tables, 16 rows per managed marker callback'});