mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 22:04:10 +08:00
更换了数据来源的build.js
This commit is contained in:
parent
a1a4b345f1
commit
32f573514b
@ -1,72 +1,16 @@
|
|||||||
const readxml = require("./xml");
|
const readxml = require("./xml");
|
||||||
const readCht = require("./cht");
|
const readCht = require("./cht");
|
||||||
const { readFile, writeFile } = require("fs/promises");
|
const fs = require("fs");
|
||||||
|
const { readFile, writeFile, access } = require("fs/promises");
|
||||||
const XML = node => {
|
|
||||||
const helper = {
|
|
||||||
get: function(obj, key) {
|
|
||||||
if( !(key in obj) ){
|
|
||||||
return map => {
|
|
||||||
if( !map ) return XML( obj.children.find( n => n.name==key ) );
|
|
||||||
else return XML( obj.children.find( n => n.name == key && Object.keys(map).every(k=>n.attributes[k]==map[k]) ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else return obj[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node ? new Proxy(node, helper) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const slotmap = (type,size) => {
|
|
||||||
const map = {
|
|
||||||
gba_eeprom_4k:0, gba_yoshiug: 0, gba_eeprom: 0,
|
|
||||||
gba_eeprom_64k: 2, gba_boktai: 2,
|
|
||||||
gba_flash_rtc: 8,
|
|
||||||
gba_flash: 9, gba_flash_512: 9,
|
|
||||||
gba_flash_1m_rtc: 10,
|
|
||||||
gba_flash_1m: 11,
|
|
||||||
gba_sram: 14, gba_drilldoz: 14, gba_wariotws: 14
|
|
||||||
}
|
|
||||||
return type in map ? (0 == (map[type] & 4) ? (size>0x1000000 ? map[type]+1 : map[type]) : map[type] ) : 15
|
|
||||||
}
|
|
||||||
|
|
||||||
const romdesc = data => {
|
|
||||||
const getserial = tr => ["AGB-","AGP-"].includes(tr.slice(0,4).toUpperCase())?tr.slice(4,8).toUpperCase() : "";
|
|
||||||
const getslot = n => n ? slotmap( n.attributes.value ) : 15
|
|
||||||
|
|
||||||
const rom = XML(data);
|
|
||||||
|
|
||||||
// title
|
|
||||||
let title = rom.info( {name: "alt_title"} );
|
|
||||||
// description
|
|
||||||
let desc = rom.description();
|
|
||||||
// serial
|
|
||||||
let serial = rom.info( {name: "serial"} );
|
|
||||||
// slot
|
|
||||||
let part = rom.part( {name: "cart"} );
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: title ? title.attributes.value : desc.children[0],
|
|
||||||
desc: desc.children[0],
|
|
||||||
serial: serial ? getserial(serial.attributes.value) : "",
|
|
||||||
slot: part ? getslot( part.feature({name: "slot"}), part.dataarea().attributes.size ) : 15,
|
|
||||||
hash: part ? part.dataarea().rom().attributes.crc : "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadroms = async () => {
|
|
||||||
const xmlfile = await readFile("./gba.xml")
|
|
||||||
const xmldata = readxml( xmlfile.toString("utf-8") );
|
|
||||||
return xmldata.children.map( romdesc ).filter( n => !!n )
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadlist = async () => {
|
const loadlist = async () => {
|
||||||
const datfile = await readFile("./gba.dat");
|
const datfile = await readFile("./list.dat");
|
||||||
const records = datfile.toString("utf-8").split("\r\n");
|
const records = readxml( datfile.toString("utf-8") );
|
||||||
return records.filter( rec => rec.includes(";") ).map( rec => rec.split(";") );
|
const inform = rec => [rec.attributes.name.slice(0,4), rec.children[1].attributes.serial||"????", rec.attributes.name.slice(7)]
|
||||||
|
return records.children.filter( rec => rec.name=="game" && rec.attributes.name.charAt()!="x" ).map( inform );
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimcheat = (dat, rom, order) => {
|
const trimcheat = (dat, info, order) => {
|
||||||
let parts = [], start = 0, done = false;
|
let parts = [], start = 0, done = false;
|
||||||
while( !done ){
|
while( !done ){
|
||||||
let pos = dat.indexOf("[GameInfo]", start);
|
let pos = dat.indexOf("[GameInfo]", start);
|
||||||
@ -86,24 +30,29 @@ const trimcheat = (dat, rom, order) => {
|
|||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parts.map( tr => readCht(tr, rom, order) );
|
return parts.map( tr => readCht(tr, info, order) );
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadcheat = async (rom, order) => {
|
const loadcheat = async (order, serial, title, file) => {
|
||||||
try{
|
try{
|
||||||
const chtfile = await readFile(`./gba/${order.padStart(4,"0").slice(-4)}.u8`);
|
const chtfile = await readFile( file );
|
||||||
const cheats = chtfile.toString("utf-8");
|
const cheats = chtfile.toString("utf-8");
|
||||||
|
let cheat = null;
|
||||||
if( cheats.indexOf("[GameInfo]") != cheats.lastIndexOf("[GameInfo]") )
|
if( cheats.indexOf("[GameInfo]") != cheats.lastIndexOf("[GameInfo]") )
|
||||||
rom.cheat = trimcheat( cheats, rom, order );
|
cheat = trimcheat( cheats, {serial}, order );
|
||||||
else rom.cheat = [ readCht( cheats, rom, order ) ];
|
else cheat = [ readCht( cheats, {serial}, order ) ];
|
||||||
|
return { serial, cheat }
|
||||||
}
|
}
|
||||||
catch( e ) { console.log( "bad cheat: %s[order=%d]\n%s", rom.title, order, e.stack ) };
|
catch( e ) {
|
||||||
|
console.log( "bad cheat: %s[order=%d]\n%s", title, order, e.stack )
|
||||||
|
return { serial }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const format = roms => {
|
const format = valid => {
|
||||||
roms.sort( (a, b) => a.serial.localeCompare(b.serial) );
|
valid.sort( (a, b) => a.serial.localeCompare(b.serial) );
|
||||||
const valid = roms.filter( r => r.serial.length == 4 && !!r.cheat );
|
console.info( `valid rom has ${valid.length}` );
|
||||||
console.info( `all rom has ${roms.length}, valid rom has ${valid.length}` );
|
valid.forEach( ({serial, cheat}) => console.debug(`${cheat.length} in ${serial}`) );
|
||||||
|
|
||||||
const enc = new TextEncoder();
|
const enc = new TextEncoder();
|
||||||
const align = (n, base) => base * Math.floor( (n+base-1) / base );
|
const align = (n, base) => base * Math.floor( (n+base-1) / base );
|
||||||
@ -173,16 +122,36 @@ const format = roms => {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasfile = f => {
|
||||||
|
try{
|
||||||
|
fs.accessSync(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch( e ){
|
||||||
|
console.error( e.stack )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
let roms = await loadroms();
|
|
||||||
let indx = roms.reduce( (r,v) => (v.hash && r.set(v.hash,v), r), new Map() );
|
|
||||||
let list = await loadlist();
|
let list = await loadlist();
|
||||||
await Promise.all( list.map( game => {
|
let roms = await Promise.all( list.map( game => {
|
||||||
if( !indx.has(game[8].toLowerCase()) ) {
|
const [order, serial, title] = game;
|
||||||
return Promise.resolve();
|
const file = `./gba/${order}.u8`;
|
||||||
}
|
if( hasfile(file) )
|
||||||
else return loadcheat( indx.get(game[8].toLowerCase()), game[0] );
|
return loadcheat( order, serial, title, file );
|
||||||
|
else return Promise.resolve();
|
||||||
} ) );
|
} ) );
|
||||||
|
console.log( `all rom has ${roms.length}` );
|
||||||
|
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}) );
|
||||||
|
|
||||||
const content = await format( roms );
|
const content = await format( roms );
|
||||||
await writeFile( "gba.acl", content );
|
await writeFile( "gba.acl", content );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user