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()); // 避免使用迭代器方法,减少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 => { valid.sort((a, b) => a.serial.localeCompare(b.serial)); console.info(`valid rom has ${valid.length}`); const enc = new TextEncoder(); //const size = Symbol("length"); const concatUnit = 10240; const align = (n, base) => base * Math.floor((n + base - 1) / base); const size = new WeakMap() const concat = (a, b) => { const sizea = size.get(a); let len = (sizea || 0) + b.length; if (len > a.length) { let n = new (Object.getPrototypeOf(a).constructor)(align(len, concatUnit) * 2); n.set(a, 0); n.set(b, sizea); size.set(n, len); return n; } else { a.set(b, sizea); size.set(a, len); return a; } } // 避免使用迭代器方法,减少CPU(已走过performance对比流程) // ---------------原来的代码在这里------------------ const getnametable = fastly === false ? list => list.reduce((r, { serial, cheat }, idx, arr) => { const val = serial.slice(0, -1); const size1 = size.get(r[1]); if (val != r[4]) { if (size1 > 0 && r[2] - r[1].at(size1 - 1) > r[3]) r[3] = r[2] - r[1].at(size1 - 1); r[0] = concat(r[0], enc.encode(val)) r[1] = concat(r[1], [r[2]]); r[4] = val; } r[2] = r[2] + cheat.length; if (idx + 1 == arr.length) { if (r[2] - r[1].at(size1 - 1) > r[3]) r[3] = r[2] - r[1].at(size1 - 1); r[1] = concat(r[1], [r[2]]); } return r; }, [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; }; // ---------------修改的代码完结处------------------ // 避免使用迭代器方法,减少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]); r[0] = concat(r[0], [val | (off << 3), id]) r[1] = concat(r[1], bin); return r; }, r); }, [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) { const arrBa = arr[i]; arr[i] = arrBa.slice(0, size.get(arrBa)); } return arr; } const [sers, offs, chtc, maxl] = linkbuffer(getnametable(valid), 0, 1); const [cheats, expanded] = linkbuffer(expandcheat(valid, 8 + sers.length + offs.length * 2 + chtc * 8), 0, 1); console.info(`name: ${sers.length} cheats: ${chtc} maxl: ${maxl}`); const serialbase = 8; const offsetbase = serialbase + sers.length; const cheatbase = offsetbase + offs.length * 2; const expandbase = align(cheatbase + cheats.length * 4, 32); const total = expandbase + expanded.length; const output = new ArrayBuffer(total); // header: magic number: ACL\1-serial length(u32) const writter = new DataView(output); writter.setUint8(0, "A".charCodeAt()); writter.setUint8(1, "C".charCodeAt()); writter.setUint8(2, "L".charCodeAt()); writter.setUint8(3, 1); const slen = sers.length / 3; writter.setUint16(4, slen, true); writter.setUint16(6, maxl, true); let ret = new Uint8Array(output); ret.set(sers, serialbase); ret.set(new Uint8Array(offs.buffer), offsetbase); ret.set(new Uint8Array(cheats.buffer), cheatbase); ret.set(expanded, expandbase); return ret; } const start = async () => { let begin = performance.now(); let list = await loadlist(); let roms = await transform(list); console.log(`all rom has ${roms.length} and time ${performance.now() - begin}`); // 避免使用迭代器方法,减少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 ) 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 ); } start()