mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 05:44:11 +08:00
214 lines
8.0 KiB
JavaScript
214 lines
8.0 KiB
JavaScript
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] != "????");
|
||
}
|
||
// ---------------修改的代码在这里------------------
|
||
else {
|
||
const entries = Object.entries(datas);
|
||
const retval = new Array(entries.length);
|
||
let l = 0;
|
||
for( let i=0; i < entries.length; ++i ){
|
||
if( entries[i][1] !== "????" ) retval[l++] = entries[i];
|
||
}
|
||
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])// 为什么移位是3不是8,看看align
|
||
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(6400), idx = {};
|
||
let l = 0;
|
||
for( let i=0; i < roms.length; ++i ){
|
||
const data = roms[i];
|
||
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 );
|
||
process.exit(0);
|
||
}
|
||
|
||
start()
|