silphscope 1.3.0 → 1.3.2
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 +17 -1
- package/package.json +6 -2
- package/src/graphics/graphics-extractor-main.js +6 -1
- package/src/graphics/moves/render-moves.js +8 -2
- package/src/graphics/moves/resolvers/move-pal-resolver.js +1 -1
- package/src/graphics/moves/resolvers/move-pic-resolver.js +1 -1
- package/src/icon-data-parser.js +0 -30
- package/src/mon-data-parser.js +0 -118
- package/src/move-data-parser.js +0 -169
- package/src/trainer-data-parser.js +0 -29
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
still a WIP however hopefully with some more work I can get this into a workable state...
|
|
2
2
|
|
|
3
|
+
(Even Newer!) Update:
|
|
4
|
+
|
|
5
|
+
moves now work... kinda... still working on getting it all the way done but it mostly works!
|
|
6
|
+
|
|
3
7
|
(Newer!) Update:
|
|
4
8
|
|
|
5
9
|
still a WIP :p but erm you can extract more graphics!
|
|
@@ -21,10 +25,11 @@ await renderAllGraphics(rom, {
|
|
|
21
25
|
outputMonDir: "./Assets/monImages", // must I explain?
|
|
22
26
|
outputIconDir: "./Assets/Icons", // same thing here :p
|
|
23
27
|
outputTrainerDir: "./Assets/Trainers", // ...
|
|
28
|
+
outputMoveDir: "./Assets/Moves"
|
|
24
29
|
});
|
|
25
30
|
```
|
|
26
31
|
|
|
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
|
|
32
|
+
Of course though the above is for extracting all graphics (which is kinda a lie... In reality it only extracts mon images, item icons, trainer images, and move images... but like I said this is a WIP :p so wait a bit please!).
|
|
28
33
|
|
|
29
34
|
But if you want say just the mon images or item icons refer below:
|
|
30
35
|
|
|
@@ -62,4 +67,15 @@ await renderAllTrainers(rom, {
|
|
|
62
67
|
outputDir: "./Assets/trainers", // more stuff
|
|
63
68
|
trainerBackPics: true, // renders the like 8 trainer back pics
|
|
64
69
|
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
move image extraction:
|
|
73
|
+
```JavaScript
|
|
74
|
+
import fs from "fs";
|
|
75
|
+
import { renderAllMoves } from "silphscope" // :O
|
|
76
|
+
|
|
77
|
+
const rom = fs.readFileSync("pokefirered.gba") // stuff stuff stuff (more stuff!)
|
|
78
|
+
await renderAllMoves(rom, {
|
|
79
|
+
outputDir: "./Assets/trainers", // (incredibly) more stuff
|
|
80
|
+
})
|
|
65
81
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silphscope",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A firered/leafgreen ROM asset extractor for use in web applications",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"exports": {
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
"item-data/",
|
|
19
19
|
"trainer-data",
|
|
20
20
|
"rom-configs/",
|
|
21
|
-
"move-data/"
|
|
21
|
+
"move-data/",
|
|
22
|
+
"!src/icon-data-parser.js",
|
|
23
|
+
"!src/mon-data-parser.js",
|
|
24
|
+
"!src/trainer-data-parser.js",
|
|
25
|
+
"!src/move-data-parser.js"
|
|
22
26
|
],
|
|
23
27
|
"keywords": [],
|
|
24
28
|
"author": "chickenPoo",
|
|
@@ -104,6 +104,7 @@ export async function renderAllMoves(rom, options = {}) {
|
|
|
104
104
|
const {
|
|
105
105
|
moves: providedMoves = moves,
|
|
106
106
|
outputDir = "./out",
|
|
107
|
+
renderMasterImage = true,
|
|
107
108
|
} = options;
|
|
108
109
|
|
|
109
110
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
@@ -112,7 +113,10 @@ export async function renderAllMoves(rom, options = {}) {
|
|
|
112
113
|
const reader = new RomReader(rom, config);
|
|
113
114
|
|
|
114
115
|
for (const moveName of Object.keys(providedMoves)) {
|
|
115
|
-
await renderMove(moveName, providedMoves, reader, rom, {
|
|
116
|
+
await renderMove(moveName, providedMoves, reader, rom, {
|
|
117
|
+
outputDir,
|
|
118
|
+
renderMasterImage,
|
|
119
|
+
});
|
|
116
120
|
console.log(`Done: ${moveName}`);
|
|
117
121
|
}
|
|
118
122
|
}
|
|
@@ -146,6 +150,7 @@ export async function renderAllGraphics(rom, options = {}) { // eventually I wil
|
|
|
146
150
|
|
|
147
151
|
await renderAllMoves(rom, {
|
|
148
152
|
outputDir: outputMoveDir,
|
|
153
|
+
renderMasterImage: true,
|
|
149
154
|
})
|
|
150
155
|
}
|
|
151
156
|
|
|
@@ -37,7 +37,8 @@ const extractFrameFromImage = (imageData, fullWidth, frameData) => { // in theor
|
|
|
37
37
|
|
|
38
38
|
export async function renderMove(moveName, moves, reader, rom, options = {}) {
|
|
39
39
|
const {
|
|
40
|
-
outputDir = null
|
|
40
|
+
outputDir = null,
|
|
41
|
+
renderMasterImage = false,
|
|
41
42
|
} = options;
|
|
42
43
|
if (!rom || !(rom instanceof Uint8Array || Buffer.isBuffer(rom))) {
|
|
43
44
|
throw new TypeError("renderMove(..., rom) requires a ROM Buffer/Uint8Array");
|
|
@@ -69,7 +70,12 @@ export async function renderMove(moveName, moves, reader, rom, options = {}) {
|
|
|
69
70
|
if (outputDir) { // I will update this later but in theory it should also work... eventually though it will need a split inside to handle full image generation :p
|
|
70
71
|
const dir = `${outputDir}/${moveName}`;
|
|
71
72
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
72
|
-
|
|
73
|
+
if (renderMasterImage) {
|
|
74
|
+
const png = new PNG({ width, height });
|
|
75
|
+
png.data = image;
|
|
76
|
+
const pngBuffer = await streamToBuffer(png.pack());
|
|
77
|
+
fs.writeFileSync(`${dir}/master.png`, pngBuffer);
|
|
78
|
+
}
|
|
73
79
|
for (let i = 0; i < move.frames.length; i++) {
|
|
74
80
|
const frame = move.frames[i];
|
|
75
81
|
const frameImageData = extractFrameFromImage(image, width, frame);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function resolveMovePal(move, reader, moveName) {
|
|
2
2
|
const table = reader.getTable("moveAnimPaletteTable");
|
|
3
3
|
const entryOffset = table + move.index * 8;
|
|
4
|
-
const ptr = reader.
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
5
|
return {
|
|
6
6
|
name: `move_${moveName}_pal`,
|
|
7
7
|
offset: ptr,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function resolveMovePic(move, reader, moveName) {
|
|
2
2
|
const table = reader.getTable("moveAnimPicTable");
|
|
3
3
|
const entryOffset = table + move.index * 8;
|
|
4
|
-
const ptr = reader.
|
|
4
|
+
const ptr = reader.readPointer(entryOffset);
|
|
5
5
|
return {
|
|
6
6
|
name: `move_${moveName}_pic`,
|
|
7
7
|
offset: ptr
|
package/src/icon-data-parser.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
const itemIconDataMap = path.resolve("../item_graphics/item_icon_table.h");
|
|
5
|
-
const iconDataLines = fs.readFileSync(itemIconDataMap, "utf-8").split("\n");
|
|
6
|
-
|
|
7
|
-
const itemData = {};
|
|
8
|
-
let itemIndex = 0;
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < iconDataLines.length; i++) {
|
|
11
|
-
const match = iconDataLines[i].match(/\[(ITEMS?_[A-Z0-9_]+)\]\s*=\s*\{\s*([^,]+),\s*([^}]+)\}/);
|
|
12
|
-
if (match) {
|
|
13
|
-
let itemName = match[1]
|
|
14
|
-
.replace("ITEM_", "")
|
|
15
|
-
.replace("ITEMS_", "");
|
|
16
|
-
const icon = match[2].trim();
|
|
17
|
-
const palette = match[3].trim();
|
|
18
|
-
if (!itemData[itemName]) {
|
|
19
|
-
itemData[itemName] = {};
|
|
20
|
-
}
|
|
21
|
-
if (!itemData[itemName]["index"]) {
|
|
22
|
-
itemData[itemName]["index"] = itemIndex;
|
|
23
|
-
itemIndex++;
|
|
24
|
-
}
|
|
25
|
-
itemData[itemName]["icon"] = icon;
|
|
26
|
-
itemData[itemName]["palette"] = palette;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
fs.writeFileSync("../item-data/itemData.json", JSON.stringify(itemData, null, 2));
|
package/src/mon-data-parser.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
const monFrontPicMap = path.resolve("../pokemon_graphics/front_pic_table.h");
|
|
5
|
-
const monBackPicMap = path.resolve("../pokemon_graphics/back_pic_table.h");
|
|
6
|
-
const monFootprintMap = path.resolve("../pokemon_graphics/footprint_table.h");
|
|
7
|
-
const monPaletteMap = path.resolve("../pokemon_graphics/palette_table.h");
|
|
8
|
-
const monShinyPaletteMap = path.resolve("../pokemon_graphics/shiny_palette_table.h");
|
|
9
|
-
const monImageDimensionMap = path.resolve("../pokemon_graphics/front_pic_coordinates.h");
|
|
10
|
-
const monIconPaletteIdxMap = path.resolve("../pokemon_icon.c");
|
|
11
|
-
const frontPicLines = fs.readFileSync(monFrontPicMap, "utf-8").split("\n");
|
|
12
|
-
const backPicLines = fs.readFileSync(monBackPicMap, "utf-8").split("\n");
|
|
13
|
-
const footprintLines = fs.readFileSync(monFootprintMap, "utf8").split("\n");
|
|
14
|
-
const paletteLines = fs.readFileSync(monPaletteMap, "utf-8").split("\n");
|
|
15
|
-
const shinyPaletteLines = fs.readFileSync(monShinyPaletteMap, "utf-8").split("\n");
|
|
16
|
-
const imageDimLines = fs.readFileSync(monImageDimensionMap, "utf-8").split("\n");
|
|
17
|
-
const iconPalIdxLines = fs.readFileSync(monIconPaletteIdxMap, "utf-8").split("\n");
|
|
18
|
-
const monData = {};
|
|
19
|
-
|
|
20
|
-
function extractMonData(lines, category) {
|
|
21
|
-
let monIndex = 0;
|
|
22
|
-
for (let i = 0; i < lines.length; i++) {
|
|
23
|
-
let match;
|
|
24
|
-
let monName;
|
|
25
|
-
let secondArgument;
|
|
26
|
-
if (category !== "footprintPics" && category !== "imageDimensions" && category !== "iconPalIndex") {
|
|
27
|
-
match = lines[i].match(/\(([^,]+),\s*([^,]+)\)/);
|
|
28
|
-
if (match) {
|
|
29
|
-
monName = match[1];
|
|
30
|
-
secondArgument = match[2];
|
|
31
|
-
}
|
|
32
|
-
} else if (category === "footprintPics") {
|
|
33
|
-
match = lines[i].match(/\[(SPECIES_\w+)\]\s*=\s*(gMonFootprint_\w+)/);
|
|
34
|
-
if (match) {
|
|
35
|
-
monName = match[1].replace("SPECIES_", "");
|
|
36
|
-
secondArgument = match[2];
|
|
37
|
-
}
|
|
38
|
-
} else if (category === "imageDimensions" && lines[i].includes(".size = MON_COORDS_SIZE")) {
|
|
39
|
-
const prevLine = lines[i - 2];
|
|
40
|
-
match = lines[i].match(/MON_COORDS_SIZE\((\d+),\s*(\d+)\)/);
|
|
41
|
-
if (match) {
|
|
42
|
-
monName = prevLine.match(/\[(SPECIES_\w+)\]/)[1].replace("SPECIES_", "");
|
|
43
|
-
const width = match[1];
|
|
44
|
-
const height = match[2];
|
|
45
|
-
if (!monData[monName]) {
|
|
46
|
-
monData[monName] = {};
|
|
47
|
-
}
|
|
48
|
-
monData[monName]["width"] = width;
|
|
49
|
-
monData[monName]["height"] = height;
|
|
50
|
-
}
|
|
51
|
-
} else if (category === "iconPalIndex") {
|
|
52
|
-
match = lines[i].match(/\[(SPECIES_\w+)\]\s*=\s*(\d+)\s*,?/);
|
|
53
|
-
if (match) {
|
|
54
|
-
monName = match[1].replace("SPECIES_", "");
|
|
55
|
-
secondArgument = match[2];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (monName && secondArgument) {
|
|
59
|
-
if (!monData[monName]) {
|
|
60
|
-
monData[monName] = {};
|
|
61
|
-
}
|
|
62
|
-
if (!monData[monName][category]) {
|
|
63
|
-
monData[monName][category] = secondArgument;
|
|
64
|
-
}
|
|
65
|
-
if (!monData[monName]["index"]) {
|
|
66
|
-
monData[monName]["index"] = monIndex;
|
|
67
|
-
monIndex++
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
extractMonData(frontPicLines, "frontPics");
|
|
74
|
-
extractMonData(backPicLines, "backPics");
|
|
75
|
-
extractMonData(paletteLines, "normPalette");
|
|
76
|
-
extractMonData(shinyPaletteLines, "shinyPalette");
|
|
77
|
-
extractMonData(footprintLines, "footprintPics");
|
|
78
|
-
extractMonData(imageDimLines, "imageDimensions");
|
|
79
|
-
extractMonData(iconPalIdxLines, "iconPalIndex");
|
|
80
|
-
function uncapitalize(string) {
|
|
81
|
-
return string.charAt(0) + string.slice(1).toLowerCase();
|
|
82
|
-
}
|
|
83
|
-
Object.entries(monData).forEach(([monName]) => {
|
|
84
|
-
let monName2 = uncapitalize(monName);
|
|
85
|
-
if (monName2.includes("Old_unown_") || monName2.includes("None")) {
|
|
86
|
-
monName2 = "QuestionMark";
|
|
87
|
-
}
|
|
88
|
-
if (monName2.includes("Unown_")) {
|
|
89
|
-
monName2 = monName2.slice(0, 5) + monName2[6].toUpperCase() + monName2.slice(7);
|
|
90
|
-
if (monName2 === "UnownEmark") {
|
|
91
|
-
monName2 = "UnownExclamationMark";
|
|
92
|
-
} else if (monName2 === "UnownQmark") {
|
|
93
|
-
monName2 = "UnownQuestionMark";
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (monName2.includes("Nidoran")) {
|
|
97
|
-
if (monName2.includes("Nidoran_f")) {
|
|
98
|
-
monName2 = "NidoranF";
|
|
99
|
-
}
|
|
100
|
-
if (monName2.includes("Nidoran_m")) {
|
|
101
|
-
monName2 = "NidoranM";
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (monName2.includes("Mr_mime")) {
|
|
105
|
-
monName2 = "Mrmime";
|
|
106
|
-
}
|
|
107
|
-
if (monName2 === "Unown") {
|
|
108
|
-
monName2 = "UnownA";
|
|
109
|
-
}
|
|
110
|
-
if (monName2 === "Ho_oh") {
|
|
111
|
-
monName2 = "HoOh";
|
|
112
|
-
}
|
|
113
|
-
console.log(monName2);
|
|
114
|
-
const iconName = `gMonIcon_${monName2}`
|
|
115
|
-
monData[monName]["Icon"] = iconName;
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
fs.writeFileSync("../mon-data/monData.json", JSON.stringify(monData, null, 2));
|
package/src/move-data-parser.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
// === CONFIG ===
|
|
5
|
-
const USE_PREBUILT_TEMPLATES = true; // Set to true to load from moveStructData.json instead of parsing .c files
|
|
6
|
-
// ===
|
|
7
|
-
|
|
8
|
-
// Paths
|
|
9
|
-
const movePicMap = path.resolve("../move_graphics/gBattleAnimPicTable.h");
|
|
10
|
-
const moveDataPath = "../move-data/moveData.json";
|
|
11
|
-
const unusedTagData = JSON.parse(fs.readFileSync(path.resolve("../move-data/unusedTags.json"), "utf-8"));
|
|
12
|
-
|
|
13
|
-
// Read animation pic table to get move animation tags
|
|
14
|
-
const movePicLines = fs.readFileSync(movePicMap, "utf-8").split("\n");
|
|
15
|
-
|
|
16
|
-
const moveData = {};
|
|
17
|
-
let moveIndex = 0;
|
|
18
|
-
|
|
19
|
-
// Tags that are defined but never assigned a SpriteTemplate
|
|
20
|
-
const unusedTags = new Set(Object.keys(unusedTagData));
|
|
21
|
-
|
|
22
|
-
// Parse gBattleAnimPicTable.h for animation tags
|
|
23
|
-
for (const line of movePicLines) {
|
|
24
|
-
const match = line.match(/\{\s*(gBattleAnimSpriteGfx_[A-Za-z0-9_]+)\s*,\s*(0x[0-9A-Fa-f]+)\s*,\s*(ANIM_TAG_[A-Za-z0-9_]+)\s*\},/);
|
|
25
|
-
if (match) {
|
|
26
|
-
const moveName = match[3].replace("ANIM_TAG_", "");
|
|
27
|
-
const moveTag = match[3];
|
|
28
|
-
|
|
29
|
-
if (!moveData[moveName]) {
|
|
30
|
-
moveData[moveName] = { index: moveIndex++ };
|
|
31
|
-
}
|
|
32
|
-
moveData[moveName].animTag = moveTag;
|
|
33
|
-
|
|
34
|
-
if (unusedTagData[moveTag]) {
|
|
35
|
-
Object.assign(moveData[moveName], unusedTagData[moveTag]);
|
|
36
|
-
moveData[moveName].unused = true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Parse SpriteTemplate structs from battle_anim_*.c files (or load prebuilt)
|
|
42
|
-
let templates = {};
|
|
43
|
-
|
|
44
|
-
if (USE_PREBUILT_TEMPLATES) {
|
|
45
|
-
// Load from prebuilt JSON
|
|
46
|
-
const prebuiltPath = "../move-data/moveStructData.json";
|
|
47
|
-
templates = JSON.parse(fs.readFileSync(prebuiltPath, "utf-8"));
|
|
48
|
-
console.log(`Loaded ${Object.keys(templates).length} templates from prebuilt file.`);
|
|
49
|
-
} else {
|
|
50
|
-
// Parse from source files
|
|
51
|
-
const animFiles = [
|
|
52
|
-
"../move_graphics/battle_anim_bug.c", "../move_graphics/battle_anim_dark.c",
|
|
53
|
-
"../move_graphics/battle_anim_dragon.c", "../move_graphics/battle_anim_effects_1.c",
|
|
54
|
-
"../move_graphics/battle_anim_effects_2.c", "../move_graphics/battle_anim_effects_3.c",
|
|
55
|
-
"../move_graphics/battle_anim_electric.c", "../move_graphics/battle_anim_fight.c",
|
|
56
|
-
"../move_graphics/battle_anim_fire.c", "../move_graphics/battle_anim_flying.c",
|
|
57
|
-
"../move_graphics/battle_anim_ghost.c", "../move_graphics/battle_anim_ground.c",
|
|
58
|
-
"../move_graphics/battle_anim_ice.c", "../move_graphics/battle_anim_normal.c",
|
|
59
|
-
"../move_graphics/battle_anim_poison.c", "../move_graphics/battle_anim_psychic.c",
|
|
60
|
-
"../move_graphics/battle_anim_rock.c", "../move_graphics/battle_anim_water.c",
|
|
61
|
-
"../move_graphics/battle_anim_status_effects.c",
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for (const file of animFiles) {
|
|
65
|
-
const lines = fs.readFileSync(file, "utf-8").split("\n");
|
|
66
|
-
Object.assign(templates, parseStructs(lines));
|
|
67
|
-
}
|
|
68
|
-
console.log(`Parsed ${Object.keys(templates).length} templates from source files.`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Build template lookup by tileTag
|
|
72
|
-
const templateByTag = Object.fromEntries(
|
|
73
|
-
Object.values(templates).map(t => [t.tileTag, t])
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Check for missing animTags
|
|
77
|
-
const animTagsInPicTable = Object.values(moveData).map(item => item.animTag);
|
|
78
|
-
const tileTagsInTemplates = Object.values(templates).map(t => t.tileTag);
|
|
79
|
-
const missingTags = animTagsInPicTable.filter(tag => !tileTagsInTemplates.includes(tag));
|
|
80
|
-
|
|
81
|
-
if (missingTags.length > 0) {
|
|
82
|
-
console.log("Missing animTags from templates:");
|
|
83
|
-
missingTags.forEach(tag => {
|
|
84
|
-
if (!unusedTags.has(tag)) console.log(tag);
|
|
85
|
-
});
|
|
86
|
-
} else {
|
|
87
|
-
console.log("No missing animTags.");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Extract OAM size info for each move
|
|
91
|
-
for (const key in moveData) {
|
|
92
|
-
const item = moveData[key];
|
|
93
|
-
const template = templateByTag[item.animTag];
|
|
94
|
-
|
|
95
|
-
if (template?.oam) {
|
|
96
|
-
const size = extractOamSize(template.oam);
|
|
97
|
-
if (size) {
|
|
98
|
-
item.possibleFrameSize = size.raw;
|
|
99
|
-
item.possibleFrameWidth = size.width;
|
|
100
|
-
item.possibleFrameHeight = size.height;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Write final output
|
|
106
|
-
fs.writeFileSync(moveDataPath, JSON.stringify(moveData, null, 2));
|
|
107
|
-
console.log(`Wrote ${Object.keys(moveData).length} moves to ${moveDataPath}`);
|
|
108
|
-
|
|
109
|
-
// --- Helper Functions ---
|
|
110
|
-
|
|
111
|
-
function parseStructs(fileLines) {
|
|
112
|
-
const results = {};
|
|
113
|
-
let current = null;
|
|
114
|
-
let braceDepth = 0;
|
|
115
|
-
let inStruct = false;
|
|
116
|
-
|
|
117
|
-
for (const line of fileLines) {
|
|
118
|
-
const trimmed = line.trim();
|
|
119
|
-
const startMatch = trimmed.match(/^(static\s+)?const\s+struct\s+SpriteTemplate\s+(\w+)/);
|
|
120
|
-
|
|
121
|
-
if (startMatch) {
|
|
122
|
-
current = { name: startMatch[2] };
|
|
123
|
-
inStruct = true;
|
|
124
|
-
braceDepth = trimmed.includes("{") ? 1 : 0;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (!inStruct) continue;
|
|
129
|
-
|
|
130
|
-
// Track brace depth
|
|
131
|
-
for (const char of trimmed) {
|
|
132
|
-
if (char === "{") braceDepth++;
|
|
133
|
-
if (char === "}") braceDepth--;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Parse struct fields
|
|
137
|
-
if (trimmed.startsWith(".")) {
|
|
138
|
-
const eqIndex = trimmed.indexOf("=");
|
|
139
|
-
if (eqIndex === -1) continue;
|
|
140
|
-
|
|
141
|
-
const key = trimmed.slice(1, eqIndex).trim();
|
|
142
|
-
let value = trimmed.slice(eqIndex + 1).replace(",", "").trim();
|
|
143
|
-
if (value === "NULL") value = null;
|
|
144
|
-
else if (value.startsWith("&")) value = value.slice(1);
|
|
145
|
-
|
|
146
|
-
current[key] = value;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// End of struct
|
|
150
|
-
if (inStruct && braceDepth === 0) {
|
|
151
|
-
results[current.name] = current;
|
|
152
|
-
current = null;
|
|
153
|
-
inStruct = false;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return results;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function extractOamSize(oam) {
|
|
161
|
-
const match = oam?.match(/(\d+)x(\d+)$/);
|
|
162
|
-
if (!match) return null;
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
width: parseInt(match[1], 10),
|
|
166
|
-
height: parseInt(match[2], 10),
|
|
167
|
-
raw: match[0]
|
|
168
|
-
};
|
|
169
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
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
|
-
let trainerIndex = 0;
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < frontPicLines.length; i++) {
|
|
11
|
-
const match = frontPicLines[i].match(/TRAINER_(SPRITE|PAL)\(\s*([A-Z0-9_]+)\s*,\s*(gTrainer(?:FrontPic|Palette)_[A-Za-z0-9_]+)/);
|
|
12
|
-
if (match) {
|
|
13
|
-
let trainerName = match[2];
|
|
14
|
-
if (!trainerData[trainerName]) {
|
|
15
|
-
trainerData[trainerName] = {};
|
|
16
|
-
}
|
|
17
|
-
if (match[1] === "SPRITE") {
|
|
18
|
-
trainerData[trainerName]["Pic"] = match[3];
|
|
19
|
-
} else {
|
|
20
|
-
trainerData[trainerName]["Palette"] = match[3];
|
|
21
|
-
}
|
|
22
|
-
if (trainerData[trainerName]["index"] === undefined) {
|
|
23
|
-
trainerData[trainerName]["index"] = trainerIndex;
|
|
24
|
-
trainerIndex++;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
fs.writeFileSync("../trainer-data/trainerData.json", JSON.stringify(trainerData, null, 2));
|