silphscope 1.4.22 → 1.4.23

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.
@@ -27,7 +27,8 @@ export async function renderMonIcon(monName, mons, reader, rom, options = {}) {
27
27
  throw new TypeError("renderMonIcon(..., rom) requires a ROM Buffer/Uint8Array");
28
28
  }
29
29
 
30
- let fileCount = 0;
30
+ let fullFileCount = 0;
31
+ const results = returnFileBuffer? [] : null;
31
32
  const mon = mons[monName];
32
33
  if (!mon) {
33
34
  throw new Error(`Missing mon entry for ${monName}`);
@@ -71,12 +72,32 @@ export async function renderMonIcon(monName, mons, reader, rom, options = {}) {
71
72
  await fs.promises.mkdir(dir, { recursive: true });
72
73
  await fs.promises.writeFile(`${dir}/icon_frame1.png`, buffer1);
73
74
  await fs.promises.writeFile(`${dir}/icon_frame2.png`, buffer2);
74
- fileCount += 2;
75
+ fullFileCount += 2;
76
+ }
77
+ if (returnFileBuffer) {
78
+ results.push({
79
+ name: `${monName}-icon-frame1`,
80
+ category: "mon",
81
+ asset: "icon",
82
+ path: `out/mons/${monName}/icon_frame1`,
83
+ buffer: buffer1,
84
+ meta: {
85
+ frame: 1,
86
+ }
87
+ });
88
+ results.push({
89
+ name: `${monName}-icon-frame2`,
90
+ category: "mon",
91
+ asset: "icon",
92
+ path: `out/mons/${monName}/icon_frame2`,
93
+ buffer: buffer2,
94
+ meta: {
95
+ frame: 2,
96
+ },
97
+ });
75
98
  }
76
-
77
99
  return {
78
- ...(returnFileBuffer && { frame1: buffer1 }),
79
- ...(returnFileBuffer && { frame2: buffer2 }),
80
- fileCount,
100
+ ...(returnFileBuffer && { results }),
101
+ fullFileCount,
81
102
  };
82
103
  }
@@ -45,10 +45,9 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
45
45
  returnFileBuffer,
46
46
  outputDir,
47
47
  });
48
- fullFileCount += monIconData.fileCount;
48
+ fullFileCount += monIconData.fullFileCount;
49
49
  if (returnFileBuffer) {
50
- results.push(monIconData.frame1);
51
- results.push(monIconData.frame2);
50
+ results.push(...monIconData.results);
52
51
  }
53
52
  }
54
53
  if (footprint === true) {
@@ -58,9 +57,9 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
58
57
  returnFileBuffer,
59
58
  outputDir,
60
59
  });
61
- fullFileCount += monFootData.fileCount;
60
+ fullFileCount += monFootData.fullFileCount;
62
61
  if (returnFileBuffer) {
63
- results.push(monFootData.pngBuffer);
62
+ results.push(...monFootData.results);
64
63
  }
65
64
  }
66
65
 
@@ -112,7 +111,17 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
112
111
  }
113
112
 
114
113
  if (returnFileBuffer) {
115
- results.push(pngBuffer);
114
+ results.push({
115
+ name: `${monName}-${variant === "shiny"? "shiny" : "normal"}-${side}-sprite`,
116
+ category: "mon",
117
+ asset: "sprite",
118
+ path: `out/mons/${monName}/${side}_${variant === "shiny"? "shiny" : "normal"}`,
119
+ buffer: pngBuffer,
120
+ meta: {
121
+ side: side,
122
+ variant: variant,
123
+ },
124
+ });
116
125
  }
117
126
  }
118
127
  }
@@ -49,7 +49,7 @@ export async function renderMove(moveName, moves, reader, rom, options = {}) {
49
49
  }
50
50
 
51
51
  let fullFileCount = 0;
52
- const results = returnFileBuffer? [] : null;
52
+ const results = returnFileBuffer ? [] : null;
53
53
  const move = moves[moveName];
54
54
  if (!move) {
55
55
  throw new Error(`Missing move: ${moveName}`)
@@ -75,48 +75,63 @@ export async function renderMove(moveName, moves, reader, rom, options = {}) {
75
75
 
76
76
  const png = new PNG({ width, height });
77
77
  png.data = image;
78
- const pngBuffer = PNG.sync.write(png, {
78
+ const pngBuffer = PNG.sync.write(png, {
79
79
  filterType: pngFilterType,
80
- deflateLevel: pngCompressionLevel,
80
+ deflateLevel: pngCompressionLevel,
81
81
  });
82
82
 
83
- 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
84
- const rootDir = (sortUnused && move?.unused === true)? `${outputDir}/unused` : `${outputDir}`
85
- const dir = `${rootDir}/${moveName}`;
86
- await fs.promises.mkdir(dir, { recursive: true });
87
- if (renderMasterImage) {
88
- const png = new PNG({ width, height });
89
- png.data = image;
90
- const pngBuffer = PNG.sync.write(png, {
91
- filterType: pngFilterType,
92
- deflateLevel: pngCompressionLevel,
93
- });
83
+ const rootDir = (sortUnused && move?.unused === true) ? `${outputDir}/unused` : `${outputDir}` // yes this could become null if outputDir is null (who would have thought :p) but since nothing uses this unless outputDir is true then it isn't really an issue... although it is a bit messy...
84
+ const dir = `${rootDir}/${moveName}`;
85
+
86
+ if (renderMasterImage) {
87
+ if (outputDir) {
94
88
  await fs.promises.writeFile(`${dir}/master.png`, pngBuffer);
95
89
  fullFileCount += 1;
96
- if (returnFileBuffer) {
97
- results.push(pngBuffer);
98
- }
99
90
  }
100
- for (let i = 0; i < move.frames.length; i++) {
101
- const frame = move.frames[i];
102
- const frameImageData = extractFrameFromImage(image, width, frame);
103
-
104
- const png = new PNG({ width: frame.width, height: frame.height });
105
- png.data = frameImageData;
106
- const pngBuffer = PNG.sync.write(png, {
107
- filterType: pngFilterType,
108
- deflateLevel: pngCompressionLevel,
91
+ if (returnFileBuffer) {
92
+ results.push({
93
+ name: `${moveName}-full-sprite`,
94
+ category: "move",
95
+ asset: "sprite",
96
+ path: `out/moves/${(sortUnused && move?.unused) === true? `unused/${moveName}` : `${moveName}`}/master`,
97
+ buffer: pngBuffer,
98
+ meta: { },
109
99
  });
100
+ }
101
+ }
110
102
 
103
+ 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
104
+ await fs.promises.mkdir(dir, { recursive: true });
105
+ }
106
+ for (let i = 0; i < move.frames.length; i++) {
107
+ const frame = move.frames[i];
108
+ const frameImageData = extractFrameFromImage(image, width, frame);
109
+
110
+ const png = new PNG({ width: frame.width, height: frame.height });
111
+ png.data = frameImageData;
112
+ const pngBuffer = PNG.sync.write(png, {
113
+ filterType: pngFilterType,
114
+ deflateLevel: pngCompressionLevel,
115
+ });
116
+
117
+ if (outputDir) {
111
118
  const fileName = `${dir}/frame-${i}.png`;
112
119
  await fs.promises.writeFile(fileName, pngBuffer);
113
120
  fullFileCount += 1;
114
- if (returnFileBuffer) {
115
- results.push(pngBuffer);
116
- }
121
+ }
122
+ if (returnFileBuffer) {
123
+ results.push({
124
+ name: `${moveName}-frame${i}`,
125
+ category: "move",
126
+ asset: "frame",
127
+ path: `out/moves/${(sortUnused && move?.unused) === true ? `unused/${moveName}` : `${moveName}`}/frame-${i}`,
128
+ buffer: pngBuffer,
129
+ meta: { },
130
+ });
117
131
  }
118
132
  }
119
133
 
134
+
120
135
  return {
121
136
  ...(returnFileBuffer && { results }),
122
137
  fullFileCount,
@@ -27,7 +27,8 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
27
27
  throw new TypeError("renderTrainerBackPic(..., rom) requires a ROM Buffer/Uint8Array");
28
28
  }
29
29
 
30
- let fileCount = 0;
30
+ let fullFileCount = 0;
31
+ const results = returnFileBuffer? [] : null;
31
32
  const trainer = trainers[trainerName];
32
33
  if (!trainer) {
33
34
  throw new Error(`Missing trainer entry for ${trainerName}`);
@@ -98,7 +99,7 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
98
99
  await fs.promises.writeFile(`${dir}/trainer_back_frame_2.png`, buffer2);
99
100
  await fs.promises.writeFile(`${dir}/trainer_back_frame_3.png`, buffer3);
100
101
  await fs.promises.writeFile(`${dir}/trainer_back_frame_4.png`, buffer4);
101
- fileCount += 4;
102
+ fullFileCount += 4;
102
103
  if (frame5) {
103
104
  const pngFrame5 = new PNG({ width, height: frameHeight });
104
105
  pngFrame5.data = frame5;
@@ -106,17 +107,58 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
106
107
  filterType: pngFilterType,
107
108
  deflateLevel: pngCompressionLevel,
108
109
  });
109
- fs.writeFileSync(`${dir}/trainer_back_frame_5.png`, buffer5);
110
- fileCount += 1;
110
+ await fs.promises.writeFile(`${dir}/trainer_back_frame_5.png`, buffer5);
111
+ fullFileCount += 1;
112
+ }
113
+ }
114
+
115
+ if (returnFileBuffer) {
116
+ results.push({
117
+ name: `${trainerName}-back-frame1`,
118
+ category: "trainer",
119
+ asset: "frame",
120
+ path: `out/trainers/${trainerName}/back_frame_1`,
121
+ buffer: buffer1,
122
+ meta: {},
123
+ });
124
+ results.push({
125
+ name: `${trainerName}-back-frame2`,
126
+ category: "trainer",
127
+ asset: "frame",
128
+ path: `out/trainers/${trainerName}/back_frame_2`,
129
+ buffer: buffer2,
130
+ meta: {},
131
+ });
132
+ results.push({
133
+ name: `${trainerName}-back-frame3`,
134
+ category: "trainer",
135
+ asset: "frame",
136
+ path: `out/trainers/${trainerName}/back_frame_3`,
137
+ buffer: buffer3,
138
+ meta: {},
139
+ });
140
+ results.push({
141
+ name: `${trainerName}-back-frame4`,
142
+ category: "trainer",
143
+ asset: "frame",
144
+ path: `out/trainers/${trainerName}/back_frame_4`,
145
+ buffer: buffer4,
146
+ meta: {},
147
+ });
148
+ if (frame5) {
149
+ results.push({
150
+ name: `${trainerName}-back-frame5`,
151
+ category: "trainer",
152
+ asset: "frame",
153
+ path: `out/trainers/${trainerName}/back_frame_5`,
154
+ buffer: buffer5,
155
+ meta: {},
156
+ });
111
157
  }
112
158
  }
113
159
 
114
160
  return {
115
- ...(returnFileBuffer && { frame1: buffer1 }),
116
- ...(returnFileBuffer && { frame2: buffer2 }),
117
- ...(returnFileBuffer && { frame3: buffer3 }),
118
- ...(returnFileBuffer && { frame4: buffer4 }),
119
- ...(returnFileBuffer && { frame5: buffer5 }),
120
- fileCount,
161
+ ...(returnFileBuffer && { results }),
162
+ fullFileCount,
121
163
  };
122
164
  }
@@ -45,15 +45,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
45
45
  returnFileBuffer,
46
46
  outputDir,
47
47
  });
48
- fullFileCount += trainerBackPicData.fileCount;
48
+ fullFileCount += trainerBackPicData.fullFileCount;
49
49
  if (returnFileBuffer) {
50
- results.push(trainerBackPicData.frame1);
51
- results.push(trainerBackPicData.frame2);
52
- results.push(trainerBackPicData.frame3);
53
- results.push(trainerBackPicData.frame4);
54
- if (trainerBackPicData?.frame5) {
55
- results.push(trainerBackPicData.frame5);
56
- }
50
+ results.push(...trainerBackPicData.results);
57
51
  }
58
52
  }
59
53
  else if (backTrainerName === false && trainerName === "PAINTER") {
@@ -63,12 +57,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
63
57
  returnFileBuffer,
64
58
  outputDir,
65
59
  });
66
- fullFileCount += trainerBackPicData.fileCount;
60
+ fullFileCount += trainerBackPicData.fullFileCount;
67
61
  if (returnFileBuffer) {
68
- results.push(trainerBackPicData.frame1);
69
- results.push(trainerBackPicData.frame2);
70
- results.push(trainerBackPicData.frame3);
71
- results.push(trainerBackPicData.frame4);
62
+ results.push(...trainerBackPicData.results);
72
63
  }
73
64
  const trainerBackPicData2 = await renderTrainerBackPic("POKEDUDE", backTrainers, reader, rom, {
74
65
  pngFilterType,
@@ -76,12 +67,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
76
67
  returnFileBuffer,
77
68
  outputDir,
78
69
  });
79
- fullFileCount += trainerBackPicData2.fileCount;
70
+ fullFileCount += trainerBackPicData2.fullFileCount;
80
71
  if (returnFileBuffer) {
81
- results.push(trainerBackPicData.frame1);
82
- results.push(trainerBackPicData.frame2);
83
- results.push(trainerBackPicData.frame3);
84
- results.push(trainerBackPicData.frame4);
72
+ results.push(...trainerBackPicData2.results);
85
73
  }
86
74
  }
87
75
  }
@@ -112,7 +100,16 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
112
100
  });
113
101
 
114
102
  if (returnFileBuffer) {
115
- results.push(pngBuffer);
103
+ results.push({
104
+ name: `${trainerName}-front-sprite`,
105
+ category: "trainer",
106
+ asset: "sprite",
107
+ path: `out/trainers/${trainerName}/front`,
108
+ buffer: pngBuffer,
109
+ meta: {
110
+ side: "front" // now some of you may be wondering why I added this... well it's because eventually I want you to be able to make a master image of the back sprites so we need to be able to differentiate the buffers here... anyway that was a long explanation :p
111
+ },
112
+ });
116
113
  }
117
114
 
118
115
  if (outputDir) {
@@ -0,0 +1,15 @@
1
+ // Copyright (c) 2026 chickenPoo
2
+ // Licensed under the MIT License. See LICENSE file in project root.
3
+
4
+ export function isValidFilterType(filterTypeValue) {
5
+ if (Number.isInteger(filterTypeValue)) {
6
+ return filterTypeValue >= -1 && filterTypeValue <= 4;
7
+ }
8
+ if (Array.isArray(filterTypeValue)) {
9
+ return filterTypeValue.length > 0 && filterTypeValue.every(entry =>
10
+ Number.isInteger(entry) && entry >= 0 && entry <= 4
11
+ );
12
+ }
13
+
14
+ return false;
15
+ }
@@ -0,0 +1,12 @@
1
+ import { mapLimit } from "./map-limit.js";
2
+
3
+ export async function runWithConcurrency(items, concurrency, callback) { // this seems simple... but I have no idea if this will do what I am thinking it will do... (don't be a dum dum like me who decided to code kids!)
4
+ if (concurrency <= 1) {
5
+ for (const item of items) {
6
+ await callback(item);
7
+ }
8
+
9
+ return;
10
+ }
11
+ await mapLimit(items, concurrency, callback);
12
+ }
@@ -0,0 +1,28 @@
1
+ // Copyright (c) 2026 chickenPoo
2
+ // Licensed under the MIT License. See LICENSE file in project root.
3
+
4
+ import { isValidFilterType } from "./is-valid-filter-type.js";
5
+
6
+ export function validateRenderOptions(functionName, options = {}) {
7
+ const {
8
+ concurrency = null,
9
+ filterType = null,
10
+ compressionLevel = null,
11
+ } = options;
12
+
13
+ if (concurrency) {
14
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
15
+ throw new TypeError(`${functionName}(rom, options = { concurrency, ... }) requires concurrency to be a integer greater than 0 (recieved ${concurrency})`);
16
+ }
17
+ }
18
+ if (filterType) {
19
+ if (!isValidFilterType(filterType)) {
20
+ throw new TypeError(`${functionName}renderAllMons(rom, options = { pngFilterType, ... }) requires pngFilterType to be a integer between -1 and 4 (received ${pngFilterType})`)
21
+ }
22
+ }
23
+ if (compressionLevel) {
24
+ if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
25
+ throw new TypeError(`${functionName}renderAllMons(rom, options = { pngCompressionLevel, ... }) requires pngCompressionLevel to be a integer between 0 and 9 (received ${pngCompressionLevel})`)
26
+ }
27
+ }
28
+ }