silphscope 1.2.28 → 1.2.29
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/package.json +1 -1
- package/rom-configs/firered.js +3 -0
- package/src/get-rom-config.js +3 -0
- package/src/graphics/graphics-extractor-main.js +10 -26
- package/src/graphics/icons/resolvers/item-icons-resolver.js +3 -0
- package/src/graphics/mons/render-mons.js +35 -22
- package/src/graphics/mons/resolvers/mon-footprint-sprite-resolver.js +3 -0
- package/src/graphics/mons/resolvers/mon-icon-palette-resolver.js +3 -0
- package/src/graphics/mons/resolvers/mon-icon-resolver.js +3 -0
- package/src/graphics/mons/resolvers/mon-sprite-palette-resolver.js +3 -0
- package/src/graphics/mons/resolvers/mon-sprite-resolver.js +3 -0
- package/src/graphics/trainers/render-trainer-back-pics.js +5 -15
- package/src/graphics/trainers/render-trainers.js +6 -7
- package/src/graphics/trainers/resolvers/trainer-back-pic-pal-resolver.js +10 -0
- package/src/graphics/trainers/resolvers/trainer-back-pic-resolver.js +9 -0
- package/src/graphics/trainers/resolvers/trainer-front-pic-pal-resolver.js +10 -0
- package/src/graphics/trainers/resolvers/trainer-front-pic-resolver.js +9 -0
- package/src/rom-reader.js +3 -0
- package/src/trainer-data-parser.js +1 -1
- package/trainer-data/trainerBackData.json +26 -0
- package/trainer-data/trainerData.json +741 -613
package/package.json
CHANGED
package/rom-configs/firered.js
CHANGED
package/src/get-rom-config.js
CHANGED
|
@@ -21,6 +21,7 @@ const assets = loadDefaultJson("../graphics-maps/fr-graphic-map.json");
|
|
|
21
21
|
const mons = loadDefaultJson("../mon-data/monData.json");
|
|
22
22
|
const icons = loadDefaultJson("../item-data/itemData.json");
|
|
23
23
|
const trainers = loadDefaultJson("../trainer-data/trainerData.json");
|
|
24
|
+
const trainersBack = loadDefaultJson("../trainer-data/trainerBackData.json");
|
|
24
25
|
|
|
25
26
|
export async function renderAllMons(rom, options = {}) {
|
|
26
27
|
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
@@ -40,34 +41,13 @@ export async function renderAllMons(rom, options = {}) {
|
|
|
40
41
|
const reader = new RomReader(rom, config);
|
|
41
42
|
|
|
42
43
|
for (const monName of Object.keys(providedMons)) {
|
|
43
|
-
await renderMon(monName, providedMons, reader, rom, {
|
|
44
|
-
side: "front",
|
|
45
|
-
variant: "normal",
|
|
44
|
+
await renderMon(monName, providedMons, reader, rom, { // hopefully this is faster since we are no longer calling the function 4 times lol
|
|
45
|
+
side: ["front", "back"],
|
|
46
|
+
variant: ["normal", "shiny"],
|
|
46
47
|
icon,
|
|
47
48
|
footprint,
|
|
48
49
|
outputDir,
|
|
49
50
|
});
|
|
50
|
-
await renderMon(monName, providedMons, reader, rom, {
|
|
51
|
-
side: "front",
|
|
52
|
-
variant: "shiny",
|
|
53
|
-
icon: false,
|
|
54
|
-
footprint: false,
|
|
55
|
-
outputDir,
|
|
56
|
-
});
|
|
57
|
-
await renderMon(monName, providedMons, reader, rom, {
|
|
58
|
-
side: "back",
|
|
59
|
-
variant: "normal",
|
|
60
|
-
icon: false,
|
|
61
|
-
footprint: false,
|
|
62
|
-
outputDir,
|
|
63
|
-
});
|
|
64
|
-
await renderMon(monName, providedMons, reader, rom, {
|
|
65
|
-
side: "back",
|
|
66
|
-
variant: "shiny",
|
|
67
|
-
icon: false,
|
|
68
|
-
footprint: false,
|
|
69
|
-
outputDir,
|
|
70
|
-
});
|
|
71
51
|
console.log(`Done: ${monName}`);
|
|
72
52
|
}
|
|
73
53
|
}
|
|
@@ -100,16 +80,20 @@ export async function renderAllTrainers(rom, options = {}) {
|
|
|
100
80
|
}
|
|
101
81
|
|
|
102
82
|
const {
|
|
103
|
-
assets: providedAssets = assets,
|
|
104
83
|
trainers: providedTrainers = trainers,
|
|
84
|
+
trainersBack: providedBackTrainers = trainersBack,
|
|
105
85
|
trainerBackPics = true,
|
|
106
86
|
outputDir = "./out",
|
|
107
87
|
} = options;
|
|
108
88
|
|
|
109
89
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
90
|
+
const backTrainerName = Object.keys(providedBackTrainers);
|
|
91
|
+
|
|
92
|
+
const config = getRomConfig(rom);
|
|
93
|
+
const reader = new RomReader(rom, config);
|
|
110
94
|
|
|
111
95
|
for (const trainerName of Object.keys(providedTrainers)) {
|
|
112
|
-
await renderTrainer(trainerName, providedTrainers,
|
|
96
|
+
await renderTrainer(trainerName, providedTrainers, backTrainerName, providedBackTrainers, reader, rom, {
|
|
113
97
|
trainerBackPics,
|
|
114
98
|
outputDir
|
|
115
99
|
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
1
4
|
export function resolveItemIconObject(item, reader, itemName, gfxOrPal) {
|
|
2
5
|
const table = reader.getTable("itemIconTable");
|
|
3
6
|
const entrySize = 8; // why did I make this variable... hmmm... I really can't remember... eh it doesn't do any harm other than being kinda useless since we could just pass 8 as is...
|
|
@@ -30,6 +30,9 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
|
|
|
30
30
|
throw new TypeError("renderMon(..., rom) requires a ROM Buffer/Uint8Array");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
const sides = Array.isArray(side) ? side : [side];
|
|
34
|
+
const variants = Array.isArray(variant) ? variant : [variant];
|
|
35
|
+
|
|
33
36
|
if (icon === true) {
|
|
34
37
|
await renderMonIcon(monName, mons, reader, rom, { outputDir });
|
|
35
38
|
}
|
|
@@ -42,35 +45,45 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
|
|
|
42
45
|
throw new Error(`Missing mon: ${monName}`);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error(`Missing
|
|
48
|
+
const picCache = {};
|
|
49
|
+
const palCache = {};
|
|
50
|
+
for (const side of sides) {
|
|
51
|
+
const monPic = resolveMonSprite(mon, reader, monName, side);
|
|
52
|
+
if (!monPic) throw new Error(`Missing sprite data for: ${monName} ${side}`);
|
|
53
|
+
picCache[side] = extract(monPic, rom).data;
|
|
54
|
+
}
|
|
55
|
+
for (const variant of variants) {
|
|
56
|
+
const monPal = resolveMonPalette(mon, reader, monName, variant);
|
|
57
|
+
if (!monPal) throw new Error(`Missing palette data for: ${monName} ${variant}`);
|
|
58
|
+
palCache[variant] = extract(monPal, rom).data;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
|
-
const
|
|
53
|
-
const rawMonPalData = extract(monPal, rom);
|
|
61
|
+
const results = [];
|
|
54
62
|
const width = 64;
|
|
55
63
|
const height = 64;
|
|
64
|
+
for (const side of sides) {
|
|
65
|
+
for (const variant of variants) {
|
|
66
|
+
const image = render4bppImage({
|
|
67
|
+
tileData: picCache[side],
|
|
68
|
+
paletteData: palCache[variant],
|
|
69
|
+
width,
|
|
70
|
+
height,
|
|
71
|
+
});
|
|
56
72
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
width,
|
|
61
|
-
height,
|
|
62
|
-
});
|
|
73
|
+
const png = new PNG({ width, height });
|
|
74
|
+
png.data = image;
|
|
75
|
+
const pngBuffer = await streamToBuffer(png.pack());
|
|
63
76
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
if (outputDir) {
|
|
78
|
+
const dir = `${outputDir}/${monName}`;
|
|
79
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); // I suppose it is better to have this out of the loop for performance but erm I would say its fine for now :p
|
|
80
|
+
const fileName = `${dir}/${side}${variant === "shiny" ? "_shiny" : ""}.png`;
|
|
81
|
+
fs.writeFileSync(fileName, pngBuffer);
|
|
82
|
+
}
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
71
|
-
const fileName = `${dir}/${side}${variant === "shiny" ? "_shiny" : ""}.png`;
|
|
72
|
-
fs.writeFileSync(fileName, pngBuffer);
|
|
84
|
+
results.push(pngBuffer);
|
|
85
|
+
}
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
return
|
|
88
|
+
return results;
|
|
76
89
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
1
4
|
export function resolveMonIconPalette(mon, reader, monName) { // I kinda think this might be wrong... but erm who knows :o maybe I got it right first try lol
|
|
2
5
|
const indexTable = reader.getTable("monIconPaletteIndices");
|
|
3
6
|
const paletteIndex = reader.readU8(indexTable + mon.index);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
1
4
|
// so the idea of "resolvers" is to essentially replace this line in the asset extraction:
|
|
2
5
|
// const monPic = assets.find(a => a.name === picName);
|
|
3
6
|
// of course though thats for the mon stuff specifically however!
|
|
@@ -5,6 +5,8 @@ import { PNG } from "pngjs";
|
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import { extract } from "../extract.js";
|
|
7
7
|
import { render4bppImage } from "../render-4bpp-image.js";
|
|
8
|
+
import { resolveTrainerBackPic } from "./resolvers/trainer-back-pic-resolver.js";
|
|
9
|
+
import { resolveTrainerBackPicPal } from "./resolvers/trainer-back-pic-pal-resolver.js"
|
|
8
10
|
|
|
9
11
|
const streamToBuffer = (stream) => new Promise((resolve, reject) => {
|
|
10
12
|
const chunks = [];
|
|
@@ -13,7 +15,7 @@ const streamToBuffer = (stream) => new Promise((resolve, reject) => {
|
|
|
13
15
|
stream.on("error", reject);
|
|
14
16
|
});
|
|
15
17
|
|
|
16
|
-
export async function renderTrainerBackPic(trainerName, trainers,
|
|
18
|
+
export async function renderTrainerBackPic(trainerName, trainers, reader, rom, options = {}) {
|
|
17
19
|
const { outputDir = null } = options;
|
|
18
20
|
|
|
19
21
|
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
@@ -25,20 +27,8 @@ export async function renderTrainerBackPic(trainerName, trainers, assets, rom, o
|
|
|
25
27
|
throw new Error(`Missing trainer entry for ${trainerName}`);
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
trainerName !== "RS_BRENDAN_1" &&
|
|
31
|
-
trainerName !== "RS_BRENDAN_2" &&
|
|
32
|
-
trainerName !== "RS_MAY_1" &&
|
|
33
|
-
trainerName !== "RS_MAY_2" && // I wonder if there is a more compact way to write this without using a array and `.includes`
|
|
34
|
-
trainerName !== "RED" &&
|
|
35
|
-
trainerName !== "LEAF"
|
|
36
|
-
) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const trainerBackPic = assets.find(a => a.name === trainer.BackPic);
|
|
41
|
-
const trainerBackPal = assets.find(a => a.name === trainer.BackPal);
|
|
30
|
+
const trainerBackPic = resolveTrainerBackPic(trainer, reader, trainerName);
|
|
31
|
+
const trainerBackPal = resolveTrainerBackPicPal(trainer, reader, trainerName);
|
|
42
32
|
if (!trainerBackPic || !trainerBackPal) {
|
|
43
33
|
throw new Error(`Missing assets for: ${trainerName}`);
|
|
44
34
|
}
|
|
@@ -6,6 +6,8 @@ import { render4bppImage } from "../render-4bpp-image.js";
|
|
|
6
6
|
import { PNG } from "pngjs";
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import { renderTrainerBackPic } from "./render-trainer-back-pics.js";
|
|
9
|
+
import { resolveTrainerFrontPic } from "./resolvers/trainer-front-pic-resolver.js";
|
|
10
|
+
import { resolveTrainerFrontPicPal } from "./resolvers/trainer-front-pic-pal-resolver.js";
|
|
9
11
|
|
|
10
12
|
const streamToBuffer = (stream) => new Promise((resolve, reject) => {
|
|
11
13
|
const chunks = [];
|
|
@@ -14,7 +16,7 @@ const streamToBuffer = (stream) => new Promise((resolve, reject) => {
|
|
|
14
16
|
stream.on("error", reject);
|
|
15
17
|
});
|
|
16
18
|
|
|
17
|
-
export async function renderTrainer(trainerName, trainers,
|
|
19
|
+
export async function renderTrainer(trainerName, trainers, backTrainerName, backTrainers, reader, rom, options = {}) {
|
|
18
20
|
const {
|
|
19
21
|
trainerBackPics = false,
|
|
20
22
|
outputDir = null,
|
|
@@ -28,13 +30,10 @@ export async function renderTrainer(trainerName, trainers, assets, rom, options
|
|
|
28
30
|
throw new Error(`Missing Trainer: ${trainerName}`);
|
|
29
31
|
}
|
|
30
32
|
if (trainerBackPics) {
|
|
31
|
-
renderTrainerBackPic(
|
|
33
|
+
renderTrainerBackPic(backTrainerName, backTrainers, reader, rom, { outputDir });
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
const trainerPal = assets.find(a => a.name === trainer.Palette);
|
|
37
|
-
const trainerPic = assets.find(a => a.name === trainer.Pic);
|
|
35
|
+
const trainerPal = resolveTrainerFrontPicPal(trainer, reader, trainerName);
|
|
36
|
+
const trainerPic = resolveTrainerFrontPic(trainer, reader, trainerName);
|
|
38
37
|
|
|
39
38
|
if (!trainerPal || !trainerPic) {
|
|
40
39
|
throw new Error(`Missing assets for: ${trainerName}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function resolveTrainerBackPicPal(trainer, reader, trainerName) { // I probably could generalize this stuff but erm... thats more work :p
|
|
2
|
+
const table = reader.getTable("trainerBackPicPaletteTable");
|
|
3
|
+
const entryOffset = table + trainer.index * 8;
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
|
+
return {
|
|
6
|
+
name: `trainer_${trainerName}_back_pic_pal`,
|
|
7
|
+
offset: ptr,
|
|
8
|
+
size: 40,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function resolveTrainerBackPic(trainer, reader, trainerName) {
|
|
2
|
+
const table = reader.getTable("trainerBackPicTable");
|
|
3
|
+
const entryOffset = table + trainer.index * 8;
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
|
+
return {
|
|
6
|
+
name: `trainer_${trainerName}_back_pic`,
|
|
7
|
+
offset: ptr,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function resolveTrainerFrontPicPal(trainer, reader, trainerName) {
|
|
2
|
+
const table = reader.getTable("trainerFrontPicPaletteTable");
|
|
3
|
+
const entryOffset = table + trainer.index * 8;
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
|
+
return {
|
|
6
|
+
name: `trainer_${trainerName}_front_pic_pal`,
|
|
7
|
+
offset: ptr,
|
|
8
|
+
size: 40,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function resolveTrainerFrontPic(trainer, reader, trainerName) {
|
|
2
|
+
const table = reader.getTable("trainerFrontPicTable");
|
|
3
|
+
const entryOffset = table + trainer.index * 8;
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
|
+
return {
|
|
6
|
+
name: `trainer_${trainerName}_front_pic`,
|
|
7
|
+
offset: ptr,
|
|
8
|
+
}
|
|
9
|
+
}
|
package/src/rom-reader.js
CHANGED
|
@@ -19,7 +19,7 @@ for (let i = 0; i < frontPicLines.length; i++) {
|
|
|
19
19
|
} else {
|
|
20
20
|
trainerData[trainerName]["Palette"] = match[3];
|
|
21
21
|
}
|
|
22
|
-
if (
|
|
22
|
+
if (trainerData[trainerName]["index"] === undefined) {
|
|
23
23
|
trainerData[trainerName]["index"] = trainerIndex;
|
|
24
24
|
trainerIndex++;
|
|
25
25
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"RED": {
|
|
3
|
+
"index": 0
|
|
4
|
+
},
|
|
5
|
+
"LEAF": {
|
|
6
|
+
"index": 1
|
|
7
|
+
},
|
|
8
|
+
"RS_BRENDAN_1": {
|
|
9
|
+
"index": 2
|
|
10
|
+
},
|
|
11
|
+
"RS_BRENDAN_2": {
|
|
12
|
+
"index": 2
|
|
13
|
+
},
|
|
14
|
+
"RS_MAY_1": {
|
|
15
|
+
"index": 3
|
|
16
|
+
},
|
|
17
|
+
"RS_MAY_2": {
|
|
18
|
+
"index": 3
|
|
19
|
+
},
|
|
20
|
+
"POKEDUDE": {
|
|
21
|
+
"index": 4
|
|
22
|
+
},
|
|
23
|
+
"OLDMAN": {
|
|
24
|
+
"index": 5
|
|
25
|
+
}
|
|
26
|
+
}
|