diff --git a/tools/cheat-builder/build.js b/tools/cheat-builder/build.js index cb31425..32a1fd4 100644 --- a/tools/cheat-builder/build.js +++ b/tools/cheat-builder/build.js @@ -1,10 +1,25 @@ const { readFile, writeFile } = require("fs/promises"); const transform = require("./worker"); +const fastly = process.env.OAFCHT_NO_ITER==='1'; const loadlist = async () => { const datfile = await readFile("./serial.json"); const datas = JSON.parse(datfile.toString()); - return Object.entries(datas).filter(n => n[1] != "????"); + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + if( fastly === false ){ + return Object.entries(datas).filter(n => n[1] != "????"); + } + // ---------------修改的代码在这里------------------ + const entries = Object.entries(datas); + const retval = new Array(entries.length); + let l = 0; + for( const data of entries ){ + if( data[1] !== "????" ) retval[l++] = data; + } + retval.length = l; + return retval; + // ---------------修改的代码完结处------------------ } const format = valid => { @@ -32,9 +47,10 @@ const format = valid => { return a; } } - const getnametable = list => list.reduce((r, { serial, cheat }, idx, arr) => { + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + const getnametable = fastly === false ? list => list.reduce((r, { serial, cheat }, idx, arr) => { const val = serial.slice(0, -1); -//console.log("looping", val, r[4], r[2], cheat.length) const size1 = size.get(r[1]); if (val != r[4]) { if (size1 > 0 && r[2] - r[1].at(size1 - 1) > r[3]) @@ -50,9 +66,37 @@ const format = valid => { r[1] = concat(r[1], [r[2]]); } return r; - }, [concat(new Uint8Array(concatUnit), []), concat(new Uint16Array(concatUnit), []), 0, 0, ""]); + }, [concat(new Uint8Array(concatUnit), []), concat(new Uint16Array(concatUnit), []), 0, 0, ""]) + // ---------------修改的代码在这里------------------ + : list => { + const ret = [concat(new Uint8Array(concatUnit), []), concat(new Uint16Array(concatUnit), []), 0, 0, ""]; + const {length} = list; + for( let idx=0; idx < length; ++idx ){ + const { serial, cheat } = list[idx]; + const val = serial.slice(0, -1); + const size1 = size.get(ret[1]); + if( val != ret[4] ){ + if( size1 > 0 && ret[2] - ret[1].at(size1 - 1) > ret[3] ) + ret[3] = ret[2] - ret[1].at(size1 - 1); + ret[0] = concat(ret[0], enc.encode(val)) + ret[1] = concat(ret[1], [ret[2]]); + ret[4] = val; + } + ret[2] = ret[2] + cheat.length; + if( idx + 1 == length ){ + if( ret[2] - ret[1].at(size1 - 1) > ret[3] ) + ret[3] = ret[2] - ret[1].at(size1 - 1); + ret[1] = concat(ret[1], [ret[2]]); + } + } + return ret; + }; + // ---------------修改的代码完结处------------------ - const expandcheat = (list, base) => list.reduce((r, { cheat, serial }) => { + + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + const expandcheat = fastly === false ? (list, base) => list.reduce((r, { cheat, serial }) => { const val = serial.charCodeAt(3); return cheat.reduce((r, { id, bin }) => { const off = r[2] + size.get(r[1]); @@ -60,7 +104,23 @@ const format = valid => { r[1] = concat(r[1], bin); return r; }, r); - }, [concat(new Uint32Array(concatUnit), []), concat(new Uint8Array(concatUnit), []), align(base, 32)]); + }, [concat(new Uint32Array(concatUnit), []), concat(new Uint8Array(concatUnit), []), align(base, 32)]) + // ---------------修改的代码在这里------------------ + : (list, base) => { + const ret = [concat(new Uint32Array(concatUnit), []), concat(new Uint8Array(concatUnit), []), align(base, 32)]; + const {length} = list; + for( let idx=0; idx < length; ++idx ){ + const { cheat, serial } = list[idx]; + const val = serial.charCodeAt(3); + for( const { id, bin } of cheat ){ + const off = ret[2] + size.get(ret[1]); + ret[0] = concat(ret[0], [val | (off << 3), id]) + ret[1] = concat(ret[1], bin); + } + } + return ret; + }; + // ---------------修改的代码完结处------------------ const linkbuffer = (arr, ...l) => { for (let i of l) { @@ -106,15 +166,41 @@ const start = async () => { let list = await loadlist(); let roms = await transform(list); console.log(`all rom has ${roms.length} and time ${performance.now() - begin}`); - roms = Object.entries(roms.reduce((r, data) => { - if (!data) return r; + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + if( fastly === false ){ + roms = Object.entries(roms.reduce((r, data) => { + if (!data) return r; + + const { serial, cheat } = data; + if (!cheat || cheat.length == 0) return r; + if (serial in r) r[serial] = [...r[serial], ...cheat]; + else r[serial] = cheat; + return r; + }, {})).map(([serial, cheat]) => ({ serial, cheat })); + } + // ---------------修改的代码在这里------------------ + else + { + const res = new Array(10240), idx = {}; + let l = 0; + for( const data of roms ){ + if( !data ) continue; - const { serial, cheat } = data; - if (!cheat || cheat.length == 0) return r; - if (serial in r) r[serial] = [...r[serial], ...cheat]; - else r[serial] = cheat; - return r; - }, {})).map(([serial, cheat]) => ({ serial, cheat })); + const { serial, cheat } = data; + if( !cheat || cheat.length == 0 ) continue; + + const cheats = idx[serial] ? idx[serial] : []; + cheats.push(...cheat); + if( !idx[serial] ){ + idx[serial] = cheats; + res[l++] = { serial, cheat: cheats }; + } + } + res.length = l; + roms = res; + } + // ---------------修改的代码完结处------------------ const content = await format( roms ); await writeFile( "gba.acl", content ); diff --git a/tools/cheat-builder/cht.js b/tools/cheat-builder/cht.js index 5c8c047..d4939c4 100644 --- a/tools/cheat-builder/cht.js +++ b/tools/cheat-builder/cht.js @@ -18,7 +18,8 @@ const nine = "9".charCodeAt(); const a = "a".charCodeAt(); const f = "f".charCodeAt(); const conv = new TextEncoder(); -const fastly = process.env.OAFCHT_NO_REUSE==='1'; +const noiter = process.env.OAFCHT_NO_ITER==='1'; +const noreuse = process.env.OAFCHT_NO_REUSE==='1'; /** * 这个函数做了一些数据复用处理,能省下来一点文件容量 @@ -224,34 +225,76 @@ const assembleCheat = (list, dup, hole) => { else if (0 == (n & 0xf000000)) return (n & 0xffff) | 0x3000000; else return n; }; - const addrval = list.reduce((r, command) => { - let addr = fromTaddr(command[0]); - let len = command.length - 1; - for( let pos=0; pos < len; ++pos) { - r.set(addr+pos, parseInt(command[pos+1], 16)) - } - return r; - }, new Map()); - const ordered = Array.from(addrval).sort((a, b) => a[0] - b[0]); - const blocks = ordered.reduce((arr, [addr, value]) => { - dup.set(addr, hole); - if (arr.length == 0) { - arr.push({ addr, value, count: 1 });// cnt addr value - } - else { - let cur = arr.at(-1); - if (cur.value == value) { - ++cur.count; + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + const addrval = new Map(); + if( noiter === false ){ + list.forEach((command) => { + let addr = fromTaddr(command[0]); + let len = command.length - 1; + for( let pos=0; pos < len; ++pos) { + addrval.set(addr+pos, parseInt(command[pos+1], 16)) + } + }); + } + // ---------------修改的代码在这里------------------ + else { + for( const command of list ){ + const addr = fromTaddr(command[0]); + const len = command.length - 1; + for( let pos=0; pos < len; ++pos) { + addrval.set(addr+pos, parseInt(command[pos+1], 16)); } - else arr.push({ addr, value, count: 1 }); } - return arr; - }, []); - return Uint32Array.from(blocks.reduce((r, block) => { - r.push( block.addr ); - r.push( (block.count << 8) | block.value ) - return r; - }, [])) + } + // ---------------修改的代码完结处------------------ + const ordered = Array.from(addrval).sort((a, b) => a[0] - b[0]); + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + if( noiter === false ){ + const blocks = ordered.reduce((arr, [addr, value]) => { + dup.set(addr, hole); + if (arr.length == 0) { + arr.push({ addr, value, count: 1 });// cnt addr value + } + else { + let cur = arr.at(-1); + if (cur.value == value && cur.addr + cur.count == addr) { + ++cur.count; + } + else arr.push({ addr, value, count: 1 }); + } + return arr; + }, []); + return Uint32Array.from(blocks.reduce((r, block) => { + r.push( block.addr ); + r.push( (block.count << 8) | block.value ) + return r; + }, [])); + } + // ---------------修改的代码在这里------------------ + else { + const blocks = new Array(); + let curr = null; + for( const [addr, value] of ordered ){ + dup.set(addr, hole); + if( !curr ) { + curr = { addr, value, count: 1 }; + } + else { + if( curr.value == value && curr.addr + curr.count == addr ) { + ++curr.count; + } + else { + blocks.push( curr.addr, (curr.count << 8) | curr.value ); + curr = { addr, value, count: 1 }; + } + } + } + if( curr ) blocks.push( curr.addr, (curr.count << 8) | curr.value ); + return Uint32Array.from(blocks); + } + // ---------------修改的代码完结处------------------ } const pack = (entlist, strlist, idxlist, cmdlist, xvalue) => { @@ -338,7 +381,7 @@ const lade = (list, info, order) => { size.str = len; return off; }; - const ut_addtr = fastly ? (() => { + const ut_addtr = noreuse ? (() => { const cache = {}; return tr => {// 不要直接ut_pushtr,因为有些秘籍会撑爆字符串表 if (cache[tr] != undefined) return cache[tr]; @@ -354,7 +397,20 @@ const lade = (list, info, order) => { const off = findIndex(strtable, code, size.str); return off < 0 ? ut_pushtr(code) : off; }; - return list.map(ut_addtr); + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + if( noiter === false ){ + return list.map(ut_addtr); + } + // ---------------修改的代码在这里------------------ + else { + let res = new Array(list.length); + for( let i=0; i < list.length; ++i ){ + res[i] = ut_addtr(list[i]); + } + return res; + } + // ---------------修改的代码完结处------------------ } const addStrIndex = list => { @@ -400,19 +456,42 @@ const lade = (list, info, order) => { // pack let holedup = new Map(); - let entbytelist = enttable.map(entry => { - let { id, data } = entry; - if (id < 0x10000) { // 收集str - let idxlist = id == 0 ? addString( data ) : addString(data.length > 1 ? data : []); // length==1就是开启 - let location = addStrIndex(idxlist); - return Uint32Array.of(id, location, idxlist.length); + // 避免使用迭代器方法,减少CPU(已走过performance对比流程) + // ---------------原来的代码在这里------------------ + let entbytelist; + if( noiter === false ){ + entbytelist = enttable.map(entry => { + let { id, data } = entry; + if (id < 0x10000) { // 收集str + let idxlist = id == 0 ? addString( data ) : addString(data.length > 1 ? data : []); // length==1就是开启 + let location = addStrIndex(idxlist); + return Uint32Array.of(id, location, idxlist.length); + } + else { + let armbytecode = assembleCheat(data, holedup, id & 0xffff); + let location = addCommand(armbytecode); + return Uint32Array.of(id, location, armbytecode.length); + } + }); + } + // ---------------修改的代码在这里------------------ + else { + entbytelist = new Array(enttable.length); + for( let i=0; i < enttable.length; i++ ){ + let { id, data } = enttable[i]; + if (id < 0x10000) { + let idxlist = id == 0 ? addString( data ) : addString(data.length > 1 ? data : []); // length==1就是开启 + let location = addStrIndex(idxlist); + entbytelist[i] = Uint32Array.of(id, location, idxlist.length); + } + else { + let armbytecode = assembleCheat(data, holedup, id & 0xffff); + let location = addCommand(armbytecode); + entbytelist[i] = Uint32Array.of(id, location, armbytecode.length); + } } - else { - let armbytecode = assembleCheat(data, holedup, id & 0xffff); - let location = addCommand(armbytecode); - return Uint32Array.of(id, location, armbytecode.length); - } - }); + } + // ---------------修改的代码完结处------------------ cmdtable = cmdtable.slice(0, size.cmd); strtable = strtable.slice(0, size.str); idxtable = idxtable.slice(0, size.idx); diff --git a/tools/cheat-builder/worker.js b/tools/cheat-builder/worker.js index 2ecb31f..af50dd6 100644 --- a/tools/cheat-builder/worker.js +++ b/tools/cheat-builder/worker.js @@ -3,6 +3,21 @@ const {Worker, isMainThread, parentPort, workerData} = require("worker_threads") const readCht = require("./cht"); const { readFileSync } = require("fs"); const ncpu = require("os").availableParallelism(); +const fastly = process.env.OAFCHT_NO_ITER==='1'; + +const handlelist = fastly === false ? list => list.map( game => { + const [order, serial, title] = game; + const file = `./gba/${order}.u8`; + return loadcheat(order, serial, title ?? order, file); +}) : list => { + const res = new Array(list.length); + for( let i=0; i < list.length; i++ ) { + const [order, serial, title] = list[i]; + const file = `./gba/${order}.u8`; + res[i] = loadcheat(order, serial, title ?? order, file) ; + } + return res; +}; const loadcheat = (order, serial, title, file) => { try { @@ -32,11 +47,7 @@ if( isMainThread ) { }); } ); }); - } : list => Promise.resolve( list.map( game => { - const [order, serial, title] = game; - const file = `./gba/${order}.u8`; - return loadcheat(order, serial, title ?? order, file); - } ) ); + } : list => Promise.resolve( handlelist(list) ); } else { const list = workerData; @@ -45,11 +56,5 @@ else { const file = `./gba/${order}.u8`; return loadcheat(order, serial, title ?? order, file); } ) ).then( res => parentPort.postMessage(res) );*/ - parentPort.postMessage( - list.map( game => { - const [order, serial, title] = game; - const file = `./gba/${order}.u8`; - return loadcheat(order, serial, title ?? order, file); - }) - ); + parentPort.postMessage( handlelist(list) ); }