@gba-kit/gba-node 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/headless-runtime.d.ts +26 -0
- package/dist/headless-runtime.d.ts.map +1 -0
- package/dist/headless-runtime.js +121 -0
- package/dist/headless-runtime.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/node-scripting-host.d.ts +19 -0
- package/dist/node-scripting-host.d.ts.map +1 -0
- package/dist/node-scripting-host.js +47 -0
- package/dist/node-scripting-host.js.map +1 -0
- package/dist/snapshot-serializer.d.ts +10 -0
- package/dist/snapshot-serializer.d.ts.map +1 -0
- package/dist/snapshot-serializer.js +167 -0
- package/dist/snapshot-serializer.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bruno Macabeus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @gba-kit/gba-node
|
|
2
|
+
|
|
3
|
+
Headless GBA emulator runtime for Node.js. Load a ROM, execute JavaScript scripts against the emulator, and capture screenshots, memory snapshots, and save states to disk.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @gba-kit/gba-node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Headless Runtime
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { HeadlessRuntime } from '@gba-kit/gba-node';
|
|
17
|
+
|
|
18
|
+
const runtime = await HeadlessRuntime.create({
|
|
19
|
+
romPath: './game.gba',
|
|
20
|
+
outputDir: './output',
|
|
21
|
+
logFn: console.log,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Execute a script against the emulator
|
|
25
|
+
await runtime.executeScript(`
|
|
26
|
+
await wait({ frames: 60 });
|
|
27
|
+
await press('start');
|
|
28
|
+
await wait({ frames: 30 });
|
|
29
|
+
await takeScreenshot({ name: 'title' });
|
|
30
|
+
`);
|
|
31
|
+
|
|
32
|
+
// Write final save state
|
|
33
|
+
await runtime.writeFinalSaveState();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Script API
|
|
37
|
+
|
|
38
|
+
Scripts run inside a sandboxed `vm.Context` with these functions available.
|
|
39
|
+
|
|
40
|
+
See the **[Scripting Guide](https://github.com/macabeus/gba-kit/blob/main/docs/scripting.md)** for the full API reference and examples.
|
|
41
|
+
|
|
42
|
+
### Snapshot Serialization
|
|
43
|
+
|
|
44
|
+
Convert `GbaSnapshot` objects (with TypedArrays) to/from JSON using base64 encoding:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { deserializeSnapshot, serializeSnapshot } from '@gba-kit/gba-node';
|
|
48
|
+
|
|
49
|
+
// Save to JSON
|
|
50
|
+
const json = JSON.stringify(serializeSnapshot(snapshot));
|
|
51
|
+
|
|
52
|
+
// Restore from JSON
|
|
53
|
+
const snapshot = deserializeSnapshot(JSON.parse(json));
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Custom Scripting Host
|
|
57
|
+
|
|
58
|
+
Implement `ScriptingHost` to control where screenshots and snapshots are written:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { NodeScriptingHost } from '@gba-kit/gba-node';
|
|
62
|
+
|
|
63
|
+
const host = new NodeScriptingHost('./output', console.log);
|
|
64
|
+
// host.generatedFiles — list of files written during the session
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Exports
|
|
68
|
+
|
|
69
|
+
| Export | Type | Description |
|
|
70
|
+
| ----------------------- | -------- | ------------------------------------------------------------------------- |
|
|
71
|
+
| `HeadlessRuntime` | class | Main runtime — loads ROM, executes scripts, manages GBA instance |
|
|
72
|
+
| `NodeScriptingHost` | class | File-system scripting host (PNG screenshots via fast-png, JSON snapshots) |
|
|
73
|
+
| `serializeSnapshot()` | function | Convert `GbaSnapshot` to JSON-safe object (base64 TypedArrays) |
|
|
74
|
+
| `deserializeSnapshot()` | function | Restore `GbaSnapshot` from serialized JSON |
|
|
75
|
+
|
|
76
|
+
Type: `HeadlessRuntimeOptions`
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless GBA Runtime for Node.js
|
|
3
|
+
*
|
|
4
|
+
* Creates a Gba instance, loads ROM, and evaluates user scripts
|
|
5
|
+
* via the ScriptingEngine.
|
|
6
|
+
*/
|
|
7
|
+
import { Gba, ScriptingEngine, type ScriptingHost } from '@gba-kit/gba-emulator';
|
|
8
|
+
export interface HeadlessRuntimeOptions {
|
|
9
|
+
romPath: string;
|
|
10
|
+
loadSavePath?: string;
|
|
11
|
+
outputDir: string;
|
|
12
|
+
logFn: (message: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare class HeadlessRuntime {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(gba: Gba, host: ScriptingHost, outputDir: string);
|
|
17
|
+
get engine(): ScriptingEngine;
|
|
18
|
+
get gba(): Gba;
|
|
19
|
+
get host(): ScriptingHost;
|
|
20
|
+
static create(options: HeadlessRuntimeOptions): Promise<HeadlessRuntime>;
|
|
21
|
+
/** Execute a script string against the emulator */
|
|
22
|
+
executeScript(scriptCode: string, scriptPath?: string): Promise<void>;
|
|
23
|
+
/** Write the final save state */
|
|
24
|
+
writeFinalSaveState(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=headless-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-runtime.d.ts","sourceRoot":"","sources":["../src/headless-runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAOjF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,eAAe;;gBAMd,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM;IAc5D,IAAI,MAAM,IAAI,eAAe,CAE5B;IAED,IAAI,GAAG,IAAI,GAAG,CAEb;IAED,IAAI,IAAI,IAAI,aAAa,CAExB;WAEY,MAAM,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkC9E,mDAAmD;IAC7C,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+D3E,iCAAiC;IAC3B,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;CAU3C"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless GBA Runtime for Node.js
|
|
3
|
+
*
|
|
4
|
+
* Creates a Gba instance, loads ROM, and evaluates user scripts
|
|
5
|
+
* via the ScriptingEngine.
|
|
6
|
+
*/
|
|
7
|
+
import { Gba, ScriptingEngine } from '@gba-kit/gba-emulator';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import vm from 'vm';
|
|
10
|
+
import { NodeScriptingHost } from './node-scripting-host.js';
|
|
11
|
+
import { deserializeSnapshot, serializeSnapshot } from './snapshot-serializer.js';
|
|
12
|
+
export class HeadlessRuntime {
|
|
13
|
+
#gba;
|
|
14
|
+
#engine;
|
|
15
|
+
#host;
|
|
16
|
+
#outputDir;
|
|
17
|
+
constructor(gba, host, outputDir) {
|
|
18
|
+
this.#gba = gba;
|
|
19
|
+
this.#host = host;
|
|
20
|
+
this.#outputDir = outputDir;
|
|
21
|
+
this.#engine = new ScriptingEngine(gba, host);
|
|
22
|
+
// Wire CPU access for scripting engine
|
|
23
|
+
const cpu = gba.armCpu;
|
|
24
|
+
this.#engine.cpuRegisters = cpu.registers;
|
|
25
|
+
this.#engine.cpuCpsr = () => cpu.cpsr;
|
|
26
|
+
this.#engine.cpuSerialize = () => cpu.serialize();
|
|
27
|
+
this.#engine.cpuDeserialize = (snap) => cpu.deserialize(snap);
|
|
28
|
+
}
|
|
29
|
+
get engine() {
|
|
30
|
+
return this.#engine;
|
|
31
|
+
}
|
|
32
|
+
get gba() {
|
|
33
|
+
return this.#gba;
|
|
34
|
+
}
|
|
35
|
+
get host() {
|
|
36
|
+
return this.#host;
|
|
37
|
+
}
|
|
38
|
+
static async create(options) {
|
|
39
|
+
const host = new NodeScriptingHost(options.outputDir, options.logFn);
|
|
40
|
+
const gba = new Gba();
|
|
41
|
+
// Load ROM
|
|
42
|
+
const romData = await fs.readFile(options.romPath);
|
|
43
|
+
gba.loadRom(new Uint8Array(romData));
|
|
44
|
+
// Set up initial CPU state (post-BIOS boot)
|
|
45
|
+
const cpu = gba.armCpu;
|
|
46
|
+
cpu.switchMode(0x12); // IRQ mode
|
|
47
|
+
cpu.registers[13] = 0x03007fa0;
|
|
48
|
+
cpu.switchMode(0x13); // SVC mode
|
|
49
|
+
cpu.registers[13] = 0x03007fe0;
|
|
50
|
+
cpu.switchMode(0x1f); // System mode
|
|
51
|
+
cpu.registers[13] = 0x03007f00;
|
|
52
|
+
cpu.cpsr = 0x1f; // SYS mode, IRQs enabled, ARM state
|
|
53
|
+
cpu.registers[15] = 0x08000000; // ROM entry point
|
|
54
|
+
// Load save state if provided
|
|
55
|
+
if (options.loadSavePath) {
|
|
56
|
+
const saveData = await fs.readFile(options.loadSavePath, 'utf-8');
|
|
57
|
+
const snapshot = deserializeSnapshot(JSON.parse(saveData));
|
|
58
|
+
gba.deserialize(snapshot);
|
|
59
|
+
}
|
|
60
|
+
return new HeadlessRuntime(gba, host, options.outputDir);
|
|
61
|
+
}
|
|
62
|
+
/** Execute a script string against the emulator */
|
|
63
|
+
async executeScript(scriptCode, scriptPath) {
|
|
64
|
+
const engine = this.#engine;
|
|
65
|
+
// Build the script context with all API functions bound
|
|
66
|
+
const context = vm.createContext({
|
|
67
|
+
// Timing
|
|
68
|
+
wait: (condition) => engine.wait(condition),
|
|
69
|
+
// Input
|
|
70
|
+
press: (buttons, options) => engine.press(buttons, options),
|
|
71
|
+
release: (button) => engine.release(button),
|
|
72
|
+
pressSequence: (inputs) => engine.pressSequence(inputs),
|
|
73
|
+
// Screenshots / memory
|
|
74
|
+
takeScreenshot: (options) => engine.takeScreenshot(options),
|
|
75
|
+
takeMemorySnapshot: (options) => engine.takeMemorySnapshot(options),
|
|
76
|
+
getRegisters: () => engine.getRegisters(),
|
|
77
|
+
getMemory: (address, length) => engine.getMemory(address, length),
|
|
78
|
+
read16: (address) => engine.read16(address),
|
|
79
|
+
read32: (address) => engine.read32(address),
|
|
80
|
+
disassemble: (address, count, mode) => engine.disassemble(address, count, mode),
|
|
81
|
+
disassembleFunction: (address, mode) => engine.disassembleFunction(address, mode),
|
|
82
|
+
readString: (address, maxLen) => engine.readString(address, maxLen),
|
|
83
|
+
getPixel: (x, y) => engine.getPixel(x, y),
|
|
84
|
+
getScreenRegion: (x, y, width, height) => engine.getScreenRegion(x, y, width, height),
|
|
85
|
+
record: (options) => engine.record(options),
|
|
86
|
+
readOAM: () => engine.readOAM(),
|
|
87
|
+
readBgScroll: (layer) => engine.readBgScroll(layer),
|
|
88
|
+
readBgTilemap: (layer) => engine.readBgTilemap(layer),
|
|
89
|
+
readDisplayControl: () => engine.readDisplayControl(),
|
|
90
|
+
hashRegion: (x, y, w, h) => engine.hashRegion(x, y, w, h),
|
|
91
|
+
onFrame: (cb) => engine.onFrame(cb),
|
|
92
|
+
searchMemory: (options) => engine.searchMemory(options),
|
|
93
|
+
filterMemory: (addresses, options) => engine.filterMemory(addresses, options),
|
|
94
|
+
// Save states
|
|
95
|
+
saveState: (options) => engine.saveState(options),
|
|
96
|
+
loadState: (path) => engine.loadState(path),
|
|
97
|
+
// Assertions
|
|
98
|
+
assert: (condition) => engine.assert(condition),
|
|
99
|
+
// Console
|
|
100
|
+
console: {
|
|
101
|
+
log: (...args) => this.#host.log(args.map(String).join(' ')),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// Wrap in an async IIFE so top-level await works
|
|
105
|
+
const wrappedCode = `(async () => {\n${scriptCode}\n})()`;
|
|
106
|
+
const script = new vm.Script(wrappedCode, {
|
|
107
|
+
filename: scriptPath ?? '<inline>',
|
|
108
|
+
});
|
|
109
|
+
await script.runInContext(context);
|
|
110
|
+
}
|
|
111
|
+
/** Write the final save state */
|
|
112
|
+
async writeFinalSaveState() {
|
|
113
|
+
const snapshot = this.#gba.serialize();
|
|
114
|
+
const serialized = serializeSnapshot(snapshot);
|
|
115
|
+
const { default: fs } = await import('fs/promises');
|
|
116
|
+
const { default: path } = await import('path');
|
|
117
|
+
const filePath = path.join(this.#outputDir, 'final_save.json');
|
|
118
|
+
await fs.writeFile(filePath, JSON.stringify(serialized, null, 2));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=headless-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-runtime.js","sourceRoot":"","sources":["../src/headless-runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GBA Node.js Package — Public API
|
|
3
|
+
*
|
|
4
|
+
* Headless GBA emulator runtime for Node.js scripting.
|
|
5
|
+
*/
|
|
6
|
+
export { HeadlessRuntime } from './headless-runtime.js';
|
|
7
|
+
export type { HeadlessRuntimeOptions } from './headless-runtime.js';
|
|
8
|
+
export { NodeScriptingHost } from './node-scripting-host.js';
|
|
9
|
+
export { serializeSnapshot, deserializeSnapshot } from './snapshot-serializer.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GBA Node.js Package — Public API
|
|
3
|
+
*
|
|
4
|
+
* Headless GBA emulator runtime for Node.js scripting.
|
|
5
|
+
*/
|
|
6
|
+
export { HeadlessRuntime } from './headless-runtime.js';
|
|
7
|
+
export { NodeScriptingHost } from './node-scripting-host.js';
|
|
8
|
+
export { serializeSnapshot, deserializeSnapshot } from './snapshot-serializer.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Scripting Host
|
|
3
|
+
*
|
|
4
|
+
* Implements ScriptingHost for Node.js: writes PNG screenshots via fast-png,
|
|
5
|
+
* JSON memory snapshots, and save states to disk.
|
|
6
|
+
*/
|
|
7
|
+
import type { ScriptingHost } from '@gba-kit/gba-emulator';
|
|
8
|
+
import type { GbaSnapshot } from '@gba-kit/gba-emulator/savestate';
|
|
9
|
+
export declare class NodeScriptingHost implements ScriptingHost {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(outputDir: string, logFn: (message: string) => void);
|
|
12
|
+
get generatedFiles(): readonly string[];
|
|
13
|
+
writeScreenshot(name: string, rgbaData: Uint8Array, width: number, height: number): Promise<void>;
|
|
14
|
+
writeMemorySnapshot(name: string, data: Record<string, unknown>): Promise<void>;
|
|
15
|
+
writeSaveState(name: string, snapshot: GbaSnapshot): Promise<void>;
|
|
16
|
+
readSaveState(filePath: string): Promise<GbaSnapshot>;
|
|
17
|
+
log(message: string): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=node-scripting-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-scripting-host.d.ts","sourceRoot":"","sources":["../src/node-scripting-host.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAOnE,qBAAa,iBAAkB,YAAW,aAAa;;gBAKzC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAK/D,IAAI,cAAc,IAAI,SAAS,MAAM,EAAE,CAEtC;IAEK,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjG,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/E,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAM3D,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAG3B"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { encode } from 'fast-png';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { deserializeSnapshot, serializeSnapshot } from './snapshot-serializer.js';
|
|
5
|
+
export class NodeScriptingHost {
|
|
6
|
+
#outputDir;
|
|
7
|
+
#logFn;
|
|
8
|
+
#generatedFiles = [];
|
|
9
|
+
constructor(outputDir, logFn) {
|
|
10
|
+
this.#outputDir = outputDir;
|
|
11
|
+
this.#logFn = logFn;
|
|
12
|
+
}
|
|
13
|
+
get generatedFiles() {
|
|
14
|
+
return this.#generatedFiles;
|
|
15
|
+
}
|
|
16
|
+
async writeScreenshot(name, rgbaData, width, height) {
|
|
17
|
+
const pngBytes = encode({
|
|
18
|
+
width,
|
|
19
|
+
height,
|
|
20
|
+
data: rgbaData,
|
|
21
|
+
channels: 4,
|
|
22
|
+
});
|
|
23
|
+
const filePath = path.join(this.#outputDir, `screenshot-${name}.png`);
|
|
24
|
+
await fs.writeFile(filePath, pngBytes);
|
|
25
|
+
this.#generatedFiles.push(`screenshot-${name}.png`);
|
|
26
|
+
}
|
|
27
|
+
async writeMemorySnapshot(name, data) {
|
|
28
|
+
const filePath = path.join(this.#outputDir, `memory-${name}.json`);
|
|
29
|
+
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
|
|
30
|
+
this.#generatedFiles.push(`memory-${name}.json`);
|
|
31
|
+
}
|
|
32
|
+
async writeSaveState(name, snapshot) {
|
|
33
|
+
const filePath = path.join(this.#outputDir, `savestate-${name}.json`);
|
|
34
|
+
const serialized = serializeSnapshot(snapshot);
|
|
35
|
+
await fs.writeFile(filePath, JSON.stringify(serialized, null, 2));
|
|
36
|
+
this.#generatedFiles.push(`savestate-${name}.json`);
|
|
37
|
+
}
|
|
38
|
+
async readSaveState(filePath) {
|
|
39
|
+
const data = await fs.readFile(filePath, 'utf-8');
|
|
40
|
+
const parsed = JSON.parse(data);
|
|
41
|
+
return deserializeSnapshot(parsed);
|
|
42
|
+
}
|
|
43
|
+
log(message) {
|
|
44
|
+
this.#logFn(message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=node-scripting-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-scripting-host.js","sourceRoot":"","sources":["../src/node-scripting-host.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElF,MAAM,OAAO,iBAAiB;IACnB,UAAU,CAAS;IACnB,MAAM,CAA4B;IAClC,eAAe,GAAa,EAAE,CAAC;IAExC,YAAY,SAAiB,EAAE,KAAgC;QAC7D,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,QAAoB,EAAE,KAAa,EAAE,MAAc;QACrF,MAAM,QAAQ,GAAG,MAAM,CAAC;YACtB,KAAK;YACL,MAAM;YACN,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,CAAC;SACZ,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,IAAI,MAAM,CAAC,CAAC;QACtE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,IAAY,EAAE,IAA6B;QACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,IAAI,OAAO,CAAC,CAAC;QACnE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,QAAqB;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,IAAI,OAAO,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB;QAClC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GbaSnapshot JSON Serializer
|
|
3
|
+
*
|
|
4
|
+
* Converts GbaSnapshot (with TypedArrays) to/from plain JSON objects
|
|
5
|
+
* using base64 encoding for binary data.
|
|
6
|
+
*/
|
|
7
|
+
import type { GbaSnapshot } from '@gba-kit/gba-emulator/savestate';
|
|
8
|
+
export declare function serializeSnapshot(snap: GbaSnapshot): any;
|
|
9
|
+
export declare function deserializeSnapshot(data: any): GbaSnapshot;
|
|
10
|
+
//# sourceMappingURL=snapshot-serializer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-serializer.d.ts","sourceRoot":"","sources":["../src/snapshot-serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAuBnE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,GAAG,CA2ExD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,WAAW,CAwE1D"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/** Encode a TypedArray to base64 string */
|
|
2
|
+
function typedArrayToBase64(arr) {
|
|
3
|
+
const bytes = arr instanceof Uint8Array ? arr : new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
4
|
+
return Buffer.from(bytes).toString('base64');
|
|
5
|
+
}
|
|
6
|
+
/** Decode a base64 string to a buffer, then wrap in the target TypedArray constructor */
|
|
7
|
+
function base64ToUint8Array(b64) {
|
|
8
|
+
return new Uint8Array(Buffer.from(b64, 'base64'));
|
|
9
|
+
}
|
|
10
|
+
function base64ToUint32Array(b64) {
|
|
11
|
+
const buf = Buffer.from(b64, 'base64');
|
|
12
|
+
return new Uint32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
13
|
+
}
|
|
14
|
+
function base64ToInt8Array(b64) {
|
|
15
|
+
const buf = Buffer.from(b64, 'base64');
|
|
16
|
+
return new Int8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
17
|
+
}
|
|
18
|
+
export function serializeSnapshot(snap) {
|
|
19
|
+
return {
|
|
20
|
+
version: snap.version,
|
|
21
|
+
cpu: {
|
|
22
|
+
registers: typedArrayToBase64(snap.cpu.registers),
|
|
23
|
+
cpsr: snap.cpu.cpsr,
|
|
24
|
+
bankedSP: typedArrayToBase64(snap.cpu.bankedSP),
|
|
25
|
+
bankedLR: typedArrayToBase64(snap.cpu.bankedLR),
|
|
26
|
+
fiqBankedR8to12: typedArrayToBase64(snap.cpu.fiqBankedR8to12),
|
|
27
|
+
usrBankedR8to12: typedArrayToBase64(snap.cpu.usrBankedR8to12),
|
|
28
|
+
spsr: typedArrayToBase64(snap.cpu.spsr),
|
|
29
|
+
halted: snap.cpu.halted,
|
|
30
|
+
haltedBySWI: snap.cpu.haltedBySWI,
|
|
31
|
+
},
|
|
32
|
+
currentScanline: snap.currentScanline,
|
|
33
|
+
inIrqHandler: snap.inIrqHandler,
|
|
34
|
+
scheduler: {
|
|
35
|
+
currentCycle: snap.scheduler.currentCycle,
|
|
36
|
+
events: snap.scheduler.events,
|
|
37
|
+
},
|
|
38
|
+
interrupts: snap.interrupts,
|
|
39
|
+
timers: snap.timers,
|
|
40
|
+
dma: snap.dma,
|
|
41
|
+
input: snap.input,
|
|
42
|
+
bus: {
|
|
43
|
+
ewram: typedArrayToBase64(snap.bus.ewram),
|
|
44
|
+
iwram: typedArrayToBase64(snap.bus.iwram),
|
|
45
|
+
palette: typedArrayToBase64(snap.bus.palette),
|
|
46
|
+
vram: typedArrayToBase64(snap.bus.vram),
|
|
47
|
+
oam: typedArrayToBase64(snap.bus.oam),
|
|
48
|
+
sram: typedArrayToBase64(snap.bus.sram),
|
|
49
|
+
mmioRegisters: typedArrayToBase64(snap.bus.mmioRegisters),
|
|
50
|
+
hasSram: snap.bus.hasSram,
|
|
51
|
+
waitcnt: snap.bus.waitcnt,
|
|
52
|
+
postflg: snap.bus.postflg,
|
|
53
|
+
lastBiosRead: snap.bus.lastBiosRead,
|
|
54
|
+
eeprom: {
|
|
55
|
+
data: typedArrayToBase64(snap.bus.eeprom.data),
|
|
56
|
+
addrBits: snap.bus.eeprom.addrBits,
|
|
57
|
+
state: snap.bus.eeprom.state,
|
|
58
|
+
command: snap.bus.eeprom.command,
|
|
59
|
+
address: snap.bus.eeprom.address,
|
|
60
|
+
bitBuffer: snap.bus.eeprom.bitBuffer,
|
|
61
|
+
bitsReceived: snap.bus.eeprom.bitsReceived,
|
|
62
|
+
sendBuffer: snap.bus.eeprom.sendBuffer,
|
|
63
|
+
sendPos: snap.bus.eeprom.sendPos,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
ppu: {
|
|
67
|
+
framebuffer: typedArrayToBase64(snap.ppu.framebuffer),
|
|
68
|
+
bg2RefX: snap.ppu.bg2RefX,
|
|
69
|
+
bg2RefY: snap.ppu.bg2RefY,
|
|
70
|
+
bg3RefX: snap.ppu.bg3RefX,
|
|
71
|
+
bg3RefY: snap.ppu.bg3RefY,
|
|
72
|
+
bg2RefLatched: snap.ppu.bg2RefLatched,
|
|
73
|
+
bg3RefLatched: snap.ppu.bg3RefLatched,
|
|
74
|
+
},
|
|
75
|
+
apu: snap.apu
|
|
76
|
+
? {
|
|
77
|
+
...snap.apu,
|
|
78
|
+
dsA: {
|
|
79
|
+
...snap.apu.dsA,
|
|
80
|
+
buffer: typedArrayToBase64(snap.apu.dsA.buffer),
|
|
81
|
+
},
|
|
82
|
+
dsB: {
|
|
83
|
+
...snap.apu.dsB,
|
|
84
|
+
buffer: typedArrayToBase64(snap.apu.dsB.buffer),
|
|
85
|
+
},
|
|
86
|
+
ch3: {
|
|
87
|
+
...snap.apu.ch3,
|
|
88
|
+
waveRam: typedArrayToBase64(snap.apu.ch3.waveRam),
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
: undefined,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export function deserializeSnapshot(data) {
|
|
95
|
+
return {
|
|
96
|
+
version: data.version,
|
|
97
|
+
cpu: {
|
|
98
|
+
registers: base64ToUint32Array(data.cpu.registers),
|
|
99
|
+
cpsr: data.cpu.cpsr,
|
|
100
|
+
bankedSP: base64ToUint32Array(data.cpu.bankedSP),
|
|
101
|
+
bankedLR: base64ToUint32Array(data.cpu.bankedLR),
|
|
102
|
+
fiqBankedR8to12: base64ToUint32Array(data.cpu.fiqBankedR8to12),
|
|
103
|
+
usrBankedR8to12: base64ToUint32Array(data.cpu.usrBankedR8to12),
|
|
104
|
+
spsr: base64ToUint32Array(data.cpu.spsr),
|
|
105
|
+
halted: data.cpu.halted,
|
|
106
|
+
haltedBySWI: data.cpu.haltedBySWI,
|
|
107
|
+
},
|
|
108
|
+
currentScanline: data.currentScanline,
|
|
109
|
+
inIrqHandler: data.inIrqHandler,
|
|
110
|
+
scheduler: data.scheduler,
|
|
111
|
+
interrupts: data.interrupts,
|
|
112
|
+
timers: data.timers,
|
|
113
|
+
dma: data.dma,
|
|
114
|
+
input: data.input,
|
|
115
|
+
bus: {
|
|
116
|
+
ewram: base64ToUint8Array(data.bus.ewram),
|
|
117
|
+
iwram: base64ToUint8Array(data.bus.iwram),
|
|
118
|
+
palette: base64ToUint8Array(data.bus.palette),
|
|
119
|
+
vram: base64ToUint8Array(data.bus.vram),
|
|
120
|
+
oam: base64ToUint8Array(data.bus.oam),
|
|
121
|
+
sram: base64ToUint8Array(data.bus.sram),
|
|
122
|
+
mmioRegisters: base64ToUint8Array(data.bus.mmioRegisters),
|
|
123
|
+
hasSram: data.bus.hasSram,
|
|
124
|
+
waitcnt: data.bus.waitcnt,
|
|
125
|
+
postflg: data.bus.postflg,
|
|
126
|
+
lastBiosRead: data.bus.lastBiosRead,
|
|
127
|
+
eeprom: {
|
|
128
|
+
data: base64ToUint8Array(data.bus.eeprom.data),
|
|
129
|
+
addrBits: data.bus.eeprom.addrBits,
|
|
130
|
+
state: data.bus.eeprom.state,
|
|
131
|
+
command: data.bus.eeprom.command,
|
|
132
|
+
address: data.bus.eeprom.address,
|
|
133
|
+
bitBuffer: data.bus.eeprom.bitBuffer,
|
|
134
|
+
bitsReceived: data.bus.eeprom.bitsReceived,
|
|
135
|
+
sendBuffer: data.bus.eeprom.sendBuffer,
|
|
136
|
+
sendPos: data.bus.eeprom.sendPos,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
ppu: {
|
|
140
|
+
framebuffer: base64ToUint32Array(data.ppu.framebuffer),
|
|
141
|
+
bg2RefX: data.ppu.bg2RefX,
|
|
142
|
+
bg2RefY: data.ppu.bg2RefY,
|
|
143
|
+
bg3RefX: data.ppu.bg3RefX,
|
|
144
|
+
bg3RefY: data.ppu.bg3RefY,
|
|
145
|
+
bg2RefLatched: data.ppu.bg2RefLatched,
|
|
146
|
+
bg3RefLatched: data.ppu.bg3RefLatched,
|
|
147
|
+
},
|
|
148
|
+
apu: data.apu
|
|
149
|
+
? {
|
|
150
|
+
...data.apu,
|
|
151
|
+
dsA: {
|
|
152
|
+
...data.apu.dsA,
|
|
153
|
+
buffer: base64ToInt8Array(data.apu.dsA.buffer),
|
|
154
|
+
},
|
|
155
|
+
dsB: {
|
|
156
|
+
...data.apu.dsB,
|
|
157
|
+
buffer: base64ToInt8Array(data.apu.dsB.buffer),
|
|
158
|
+
},
|
|
159
|
+
ch3: {
|
|
160
|
+
...data.apu.ch3,
|
|
161
|
+
waveRam: base64ToUint8Array(data.apu.ch3.waveRam),
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
: undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=snapshot-serializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-serializer.js","sourceRoot":"","sources":["../src/snapshot-serializer.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gba-kit/gba-node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Headless Node.js runtime for scripted GBA emulation",
|
|
5
|
+
"author": "macabeus",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/macabeus/gba-kit",
|
|
19
|
+
"directory": "packages/gba-node"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/macabeus/gba-kit",
|
|
22
|
+
"bugs": "https://github.com/macabeus/gba-kit/issues",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"gba",
|
|
25
|
+
"gameboy-advance",
|
|
26
|
+
"emulator",
|
|
27
|
+
"arm7tdmi",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"fast-png": "^8.0.0",
|
|
35
|
+
"@gba-kit/arm-emulator": "0.1.0",
|
|
36
|
+
"@gba-kit/gba-emulator": "0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^25.5.0",
|
|
40
|
+
"typescript": "^6.0.2"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"check-types": "tsc --noEmit",
|
|
45
|
+
"lint": "eslint src/"
|
|
46
|
+
}
|
|
47
|
+
}
|