Read the English 1.0 and Australian cartridges besides 1.1 (#84)
PRET's crystal and crystal_au builds reproduce both retail cartridges, and every label the profile pins keeps its address in all three; check:reference now verifies that when the rebuilds are present. The revision comes from the header. Revision 1.0's Battle Tower draws only the first 21 trainers and picks its texts' gender from the name's sixth character, checked against each cartridge. A native backup imports across the three, which share every map, event and object. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
acdaf62eaa
commit
3c480a28f0
17 files changed
+317
-26
No files matched your search
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { CrystalAssets } from "../src/game/rom/assets";
|
||||
import { romRevision } from "../src/game/rom/revision";
|
||||
import { CRYSTAL_ROMS, validateRom } from "../src/rom/validate";
|
||||
import { emptyBattleTowerState, loadOpponent } from "../src/game/scripts/battle-tower";
|
||||
import type { SpecialContext } from "../src/game/scripts/special-context";
|
||||
|
||||
/** The user's 1.1 cartridge and PRET's rebuilds of the other English ones
|
||||
* (`make crystal crystal_au` in .reference/pokecrystal), each skipped when
|
||||
* absent. */
|
||||
const cartridges = [
|
||||
{ revision: "1.1", path: process.env.CRYSTAL_ROM_PATH ?? "Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc", rom: CRYSTAL_ROMS[0] },
|
||||
{ revision: "1.0", path: ".reference/pokecrystal/pokecrystal.gbc", rom: CRYSTAL_ROMS[1] },
|
||||
{ revision: "au", path: ".reference/pokecrystal/pokecrystal_au.gbc", rom: CRYSTAL_ROMS[2] },
|
||||
] as const;
|
||||
|
||||
for (const { revision, path, rom } of cartridges)
|
||||
describe.skipIf(!existsSync(path))(`the ${revision} cartridge`, () => {
|
||||
const bytes = existsSync(path) ? new Uint8Array(readFileSync(path)) : new Uint8Array();
|
||||
it("is accepted under its name and recognised from its header", async () => {
|
||||
// CRYSTAL_ROM_PATH may point the 1.1 entry at another cartridge.
|
||||
const assets = new CrystalAssets(bytes);
|
||||
const own = cartridges.find((c) => c.revision === romRevision(assets.reader))!;
|
||||
expect(await validateRom(new File([bytes], "crystal.gbc"))).toMatchObject({ sha1: own.rom.sha1, name: own.rom.name });
|
||||
if (revision !== "1.1" || !process.env.CRYSTAL_ROM_PATH) expect(own.revision).toBe(revision);
|
||||
// One changed byte is a modified ROM.
|
||||
const changed = bytes.slice();
|
||||
changed[0x7fff] ^= 1;
|
||||
await expect(validateRom(new File([changed], "crystal.gbc"))).rejects.toThrow("not a supported English");
|
||||
});
|
||||
|
||||
it("draws Battle Tower trainers from all 70, or from the first 21 on 1.0", () => {
|
||||
const assets = new CrystalAssets(bytes);
|
||||
let seed = 84;
|
||||
const context = {
|
||||
reader: assets.reader,
|
||||
data: assets.pokemon,
|
||||
random: () => {
|
||||
seed = (seed * 1103515245 + 12345) >>> 0;
|
||||
return (seed >>> 16) & 255;
|
||||
},
|
||||
text: () => ({ player: "KAWA", rival: "SILVER", mom: "MOM", strings: {}, weekday: "SUNDAY" }),
|
||||
} as unknown as SpecialContext;
|
||||
const drawn = new Set<number>();
|
||||
for (let draw = 0; draw < 2000; draw++) {
|
||||
const bt = { ...emptyBattleTowerState(), levelGroup: 1 + (draw % 10) };
|
||||
drawn.add(loadOpponent(context, bt).trainer);
|
||||
}
|
||||
const limit = romRevision(assets.reader) === "1.0" ? 21 : 70;
|
||||
expect(Math.max(...drawn)).toBe(limit - 1);
|
||||
expect(drawn.size).toBe(limit);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user