mirror of
https://gitea.tendokyu.moe/beerpsi/x.git
synced 2024-11-24 07:10:11 +01:00
bemanipatcher -> sp2x
This commit is contained in:
parent
562e616704
commit
20c41426c6
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
Miscellaneous scripts that don't need their own repo.
|
Miscellaneous scripts that don't need their own repo.
|
||||||
|
|
||||||
|
## Patchers
|
||||||
|
- `patchers/b2spatch.mjs`: Convert from BemaniPatcher to Spice2x JSON
|
||||||
|
- Requires `cheerio`: `npm i cheerio`
|
||||||
|
- You will need to rename the resulting JSON manually so that they match the
|
||||||
|
`{gameCode}-{timestamp}_{entrypoint}` format expected by Spice2x.
|
||||||
|
|
||||||
## Arcaea
|
## Arcaea
|
||||||
- `acaca/arcpack.py`: Script to extract Arcaea Nintendo Switch `arc.pack` files
|
- `acaca/arcpack.py`: Script to extract Arcaea Nintendo Switch `arc.pack` files
|
||||||
|
|
||||||
|
165
patchers/b2spatch.mjs
Normal file
165
patchers/b2spatch.mjs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* Script to convert a BemaniPatcher HTML to a spice2x patch JSON.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* - node b2spatch.js <patcher URL | local patcher HTML> <gameCode>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import vm from "vm";
|
||||||
|
import * as cheerio from "cheerio";
|
||||||
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number[]} bytes
|
||||||
|
*/
|
||||||
|
function bytesToHex(bytes) {
|
||||||
|
return bytes.reduce((acc, c) => acc + c.toString(16).padStart(2, "0").toUpperCase(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a BemaniPatcher HTML for patches.
|
||||||
|
* @param {string} contents
|
||||||
|
*/
|
||||||
|
function parsePatcherHtml(filename, contents) {
|
||||||
|
const $ = cheerio.load(contents);
|
||||||
|
|
||||||
|
let script = "";
|
||||||
|
|
||||||
|
for (const element of $("script").get()) {
|
||||||
|
const code = $(element).html();
|
||||||
|
|
||||||
|
if (!code.includes("new Patcher")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
script += code;
|
||||||
|
script += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script.length === 0) {
|
||||||
|
console.warn(`Failed to find any BemaniPatcher patches in ${filename}.`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchers = [];
|
||||||
|
const context = {
|
||||||
|
window: {
|
||||||
|
addEventListener: (type, cb) => cb(),
|
||||||
|
},
|
||||||
|
Patcher: class {
|
||||||
|
constructor(fname, description, patches) {
|
||||||
|
patchers.push({ fname, description, patches });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PatchContainer: class {
|
||||||
|
constructor(patchers) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.createContext(context);
|
||||||
|
vm.runInContext(script, context);
|
||||||
|
|
||||||
|
return patchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToSpicePatch(bmPatch, gameCode, dllName) {
|
||||||
|
let description = "";
|
||||||
|
|
||||||
|
if (bmPatch.tooltip) {
|
||||||
|
description += bmPatch.tooltip + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bmPatch.danger) {
|
||||||
|
description += `WARNING: ${bmPatch.danger}`
|
||||||
|
}
|
||||||
|
|
||||||
|
description = description.trim()
|
||||||
|
|
||||||
|
const patch = {
|
||||||
|
name: bmPatch.name,
|
||||||
|
description,
|
||||||
|
gameCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bmPatch.type) {
|
||||||
|
if (bmPatch.type === "union") {
|
||||||
|
patch.type = "union";
|
||||||
|
patch.patches = bmPatch.patches.map((p) => ({
|
||||||
|
name: p.name,
|
||||||
|
patch: {
|
||||||
|
dllName,
|
||||||
|
data: bytesToHex(p.patch),
|
||||||
|
offset: bmPatch.offset,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (bmPatch.type === "number") {
|
||||||
|
patch.type = "number";
|
||||||
|
patch.patch = {
|
||||||
|
dllName,
|
||||||
|
offset: bmPatch.offset,
|
||||||
|
min: bmPatch.min,
|
||||||
|
max: bmPatch.max,
|
||||||
|
size: bmPatch.size,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.warn(`Unsupported BemaniPatcher patch type ${bmPatch.type}`, bmPatch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
patch.type = "memory";
|
||||||
|
patch.patches = bmPatch.patches.map((p) => ({
|
||||||
|
offset: p.offset,
|
||||||
|
dllName,
|
||||||
|
dataDisabled: bytesToHex(p.off),
|
||||||
|
dataEnabled: bytesToHex(p.on),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
if (process.argv.length < 4) {
|
||||||
|
console.log("Usage: node b2spatch.js <patcher URL | local patcher HTML> <gameCode> [output dir]")
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const location = process.argv[2];
|
||||||
|
const gameCode = process.argv[3];
|
||||||
|
const output = process.argv[4] ?? ".";
|
||||||
|
const locationIsUrl = location.startsWith("http://") || location.startsWith("https://");
|
||||||
|
let html = "";
|
||||||
|
|
||||||
|
if (locationIsUrl) {
|
||||||
|
html = await fetch(location).then((r) => r.text());
|
||||||
|
} else {
|
||||||
|
html = readFileSync(location, { encoding: "utf-8" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const patchers = parsePatcherHtml(location, html);
|
||||||
|
|
||||||
|
for (const patcher of patchers) {
|
||||||
|
const lastUpdated = new Date()
|
||||||
|
const spice = [
|
||||||
|
{
|
||||||
|
gameCode,
|
||||||
|
version: patcher.description,
|
||||||
|
lastUpdated: `${lastUpdated.getFullYear()}-${(lastUpdated.getMonth() + 1).toString().padStart(2, "0")}-${lastUpdated.getDate().toString().padStart(2, "0")} ${lastUpdated.getHours().toString().padStart(2, "0")}:${lastUpdated.getMinutes().toString().padStart(2, "0")}:${lastUpdated.getSeconds().toString().padStart(2, "0")}`,
|
||||||
|
source: locationIsUrl ? location : "https://sp2x.two-torial.xyz/",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const patch of patcher.patches) {
|
||||||
|
spice.push(convertToSpicePatch(patch, gameCode, patcher.fname));
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
path.join(output, `${gameCode}-${patcher.description}.json`),
|
||||||
|
JSON.stringify(spice, null, 4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user