silphscope 1.1.5 → 1.2.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/README.md
CHANGED
|
@@ -20,10 +20,11 @@ const rom = fs.readFileSync("pokefirered.gba"); // replace with path to your own
|
|
|
20
20
|
await renderAllGraphics(rom, {
|
|
21
21
|
outputMonDir: "./Assets/monImages", // must I explain?
|
|
22
22
|
outputIconDir: "./Assets/Icons", // same thing here :p
|
|
23
|
+
outputTrainerDir: "./Assets/Trainers", // ...
|
|
23
24
|
});
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
Of course though the above is for extracting all graphics (which is kinda a lie... In reality it only extracts mon images and
|
|
27
|
+
Of course though the above is for extracting all graphics (which is kinda a lie... In reality it only extracts mon images, item icons, and trainer images... but like I said this is a WIP :p so wait a bit please!).
|
|
27
28
|
|
|
28
29
|
But if you want say just the mon images or item icons refer below:
|
|
29
30
|
|
|
@@ -49,4 +50,15 @@ const rom = fs.readFileSync("pokefirered.gba")// find your own rom and so on :l
|
|
|
49
50
|
await renderAllIcons(rom, {
|
|
50
51
|
outputDir: "./Assets/Icons" // no comment (wait... that was a comment :p)
|
|
51
52
|
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
trainer image extraction:
|
|
56
|
+
```JavaScript
|
|
57
|
+
import fs from "fs";
|
|
58
|
+
import { renderAllTrainers } from "silphscope" // :O
|
|
59
|
+
|
|
60
|
+
const rom = fs.readFileSync("pokefirered.gba") // stuff stuff stuff
|
|
61
|
+
await renderAllTrainers(rom, {
|
|
62
|
+
outputDir: "./Assets/trainers" // more stuff
|
|
63
|
+
})
|
|
52
64
|
```
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import path from "path";
|
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { renderMon } from "./render-mons.js";
|
|
8
8
|
import { renderIcon } from "./icons/render-icons.js";
|
|
9
|
+
import { renderTrainer } from "./trainers/render-trainers.js";
|
|
9
10
|
|
|
10
11
|
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
|
|
@@ -17,6 +18,7 @@ function loadDefaultJson(relativePath) {
|
|
|
17
18
|
const assets = loadDefaultJson("../graphics-maps/fr-graphic-map.json");
|
|
18
19
|
const mons = loadDefaultJson("../mon-data/monData.json");
|
|
19
20
|
const icons = loadDefaultJson("../item-data/itemData.json");
|
|
21
|
+
const trainers = loadDefaultJson("../trainer-data/trainerData.json");
|
|
20
22
|
|
|
21
23
|
export async function renderAllMons(rom, options = {}) {
|
|
22
24
|
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
@@ -85,6 +87,25 @@ export async function renderAllIcons(rom, options = {}) {
|
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
export async function renderAllTrainers(rom, options = {}) {
|
|
91
|
+
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
92
|
+
throw new TypeError("renderAllTrainers(rom, options) requires rom Buffer/Uint8Array as first argument");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const {
|
|
96
|
+
assets: providedAssets = assets,
|
|
97
|
+
trainers: providedTrainers = trainers,
|
|
98
|
+
outputDir = "./out",
|
|
99
|
+
} = options;
|
|
100
|
+
|
|
101
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
102
|
+
|
|
103
|
+
for (const trainerName of Object.keys(providedTrainers)) {
|
|
104
|
+
await renderTrainer(trainerName, providedTrainers, providedAssets, rom, { outputDir });
|
|
105
|
+
console.log(`Done: ${trainerName}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
88
109
|
export async function renderAllGraphics(rom, options = {}) { // eventually I will speed this up instead of doing it sequentially :p but for now its fine I guess
|
|
89
110
|
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
90
111
|
throw new TypeError("renderAllGraphics(rom, options) requires rom Buffer/Uint8Array as first argument");
|
|
@@ -93,6 +114,7 @@ export async function renderAllGraphics(rom, options = {}) { // eventually I wil
|
|
|
93
114
|
const {
|
|
94
115
|
outputMonDir = "./out/mons",
|
|
95
116
|
outputIconDir = "./out/icons",
|
|
117
|
+
outputTrainerDir = "./out/trainers",
|
|
96
118
|
} = options;
|
|
97
119
|
|
|
98
120
|
await renderAllMons(rom, {
|
|
@@ -104,6 +126,10 @@ export async function renderAllGraphics(rom, options = {}) { // eventually I wil
|
|
|
104
126
|
await renderAllIcons(rom, {
|
|
105
127
|
outputDir: outputIconDir,
|
|
106
128
|
});
|
|
129
|
+
|
|
130
|
+
await renderTrainer(rom, {
|
|
131
|
+
outputDir: outputTrainerDir,
|
|
132
|
+
});
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
export function loadDefaultRom() {
|
|
@@ -37,8 +37,8 @@ export async function renderIcon(itemName, items, assets, rom, options = {}) {
|
|
|
37
37
|
const height = 24;
|
|
38
38
|
|
|
39
39
|
const image = render4bppImage({
|
|
40
|
-
tileData: iconImageData,
|
|
41
|
-
paletteData: rawIconPalData,
|
|
40
|
+
tileData: iconImageData.data,
|
|
41
|
+
paletteData: rawIconPalData.data,
|
|
42
42
|
width,
|
|
43
43
|
height
|
|
44
44
|
});
|
|
@@ -57,8 +57,8 @@ export async function renderMon(monName, mons, assets, rom, options = {}) {
|
|
|
57
57
|
const height = 64;
|
|
58
58
|
|
|
59
59
|
const image = render4bppImage({
|
|
60
|
-
tileData: monImageData,
|
|
61
|
-
paletteData: rawMonPalData,
|
|
60
|
+
tileData: monImageData.data,
|
|
61
|
+
paletteData: rawMonPalData.data,
|
|
62
62
|
width,
|
|
63
63
|
height,
|
|
64
64
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
4
|
+
import { extract } from "../extract.js";
|
|
5
|
+
import { render4bppImage } from "../render-4bpp-image.js";
|
|
6
|
+
import { PNG } from "pngjs";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
|
|
9
|
+
const streamToBuffer = (stream) => new Promise((resolve, reject) => {
|
|
10
|
+
const chunks = [];
|
|
11
|
+
stream.on("data", chunk => chunks.push(chunk));
|
|
12
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
13
|
+
stream.on("error", reject);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export async function renderTrainer(trainerName, trainers, assets, rom, options = {}) {
|
|
17
|
+
const { outputDir = null } = options;
|
|
18
|
+
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
19
|
+
throw new TypeError("renderTrainer(..., rom) requires a ROM Buffer/Uint8Array");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const trainer = trainers[trainerName];
|
|
23
|
+
if (!trainer) {
|
|
24
|
+
throw new Error(`Missing Trainer: ${trainerName}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const trainerPal = assets.find(a => a.name === trainer.Palette);
|
|
28
|
+
const trainerPic = assets.find(a => a.name === trainer.Pic);
|
|
29
|
+
|
|
30
|
+
if (!trainerPal || !trainerPic) {
|
|
31
|
+
throw new Error(`Missing assets for: ${trainerName}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const trainerImageData = extract(trainerPic, rom);
|
|
35
|
+
const rawTrainerPalData = extract(trainerPal, rom);
|
|
36
|
+
const width = 64;
|
|
37
|
+
const height = 64;
|
|
38
|
+
|
|
39
|
+
const image = render4bppImage({
|
|
40
|
+
tileData: trainerImageData.data,
|
|
41
|
+
paletteData: rawTrainerPalData.data,
|
|
42
|
+
width,
|
|
43
|
+
height
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const png = new PNG({ width, height });
|
|
47
|
+
png.data = image;
|
|
48
|
+
const pngBuffer = await streamToBuffer(png.pack());
|
|
49
|
+
|
|
50
|
+
if (outputDir) {
|
|
51
|
+
const dir = `${outputDir}/${trainerName}`;
|
|
52
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
53
|
+
const fileName = `${dir}/trainer_front.png`;
|
|
54
|
+
fs.writeFileSync(fileName, pngBuffer);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return pngBuffer;
|
|
58
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const trainerFrontPicMap = path.resolve("../trainer_graphics/front_pic_tables.h");
|
|
5
|
+
const frontPicLines = fs.readFileSync(trainerFrontPicMap, "utf-8").split("\n");
|
|
6
|
+
|
|
7
|
+
const trainerData = {};
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < frontPicLines.length; i++) {
|
|
10
|
+
const match = frontPicLines[i].match(/TRAINER_(SPRITE|PAL)\(\s*([A-Z0-9_]+)\s*,\s*(gTrainer(?:FrontPic|Palette)_[A-Za-z0-9_]+)/);
|
|
11
|
+
if (match) {
|
|
12
|
+
let trainerName = match[2];
|
|
13
|
+
if (!trainerData[trainerName]) {
|
|
14
|
+
trainerData[trainerName] = {};
|
|
15
|
+
}
|
|
16
|
+
if (match[1] === "SPRITE") {
|
|
17
|
+
trainerData[trainerName]["Pic"] = match[3];
|
|
18
|
+
} else {
|
|
19
|
+
trainerData[trainerName]["Palette"] = match[3];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fs.writeFileSync("../trainer-data/trainerData.json", JSON.stringify(trainerData, null, 2));
|