@gaialabs/core 0.1.0 → 0.1.1
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 +674 -0
- package/README.md +27 -0
- package/dist/browser.d.mts +0 -0
- package/dist/browser.d.ts +0 -0
- package/dist/browser.js +0 -0
- package/dist/browser.js.map +0 -0
- package/dist/browser.mjs +0 -0
- package/dist/browser.mjs.map +0 -0
- package/dist/index.d.mts +367 -499
- package/dist/index.d.ts +367 -499
- package/dist/index.js +2094 -1635
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1996 -1534
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +0 -0
- package/dist/node.d.ts +0 -0
- package/dist/node.js +0 -0
- package/dist/node.js.map +0 -0
- package/dist/node.mjs +0 -0
- package/dist/node.mjs.map +0 -0
- package/dist/worker.d.mts +0 -0
- package/dist/worker.d.ts +0 -0
- package/dist/worker.js +0 -0
- package/dist/worker.js.map +0 -0
- package/dist/worker.mjs +0 -0
- package/dist/worker.mjs.map +0 -0
- package/package.json +16 -16
package/dist/index.mjs
CHANGED
|
@@ -1,309 +1,6 @@
|
|
|
1
|
-
import { BitStream, RomProcessingConstants,
|
|
2
|
-
import { promises } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
1
|
+
import { BitStream, RomProcessingConstants, Op, RegisterType, Address, ChunkFileUtils, BlockReaderConstants, createTableEntry, ChunkFile, BinType, createChunkFileFromDbFile, createChunkFileFromDbBlock, LocationWrapper, CompressionRegistry, Word, Byte, StatusFlags, AddressSpace, AddressType, MemberType, readJsonFile, getDirectory, TypedNumber, AsmBlock, Long, crc32_buffer, DbRootUtils } from '@gaialabs/shared';
|
|
4
2
|
|
|
5
|
-
// src/
|
|
6
|
-
|
|
7
|
-
// src/sprites/SpriteFrame.ts
|
|
8
|
-
var SpriteFrame = class {
|
|
9
|
-
constructor(duration = 0, groupIndex = 0, groupOffset = 0) {
|
|
10
|
-
this.duration = duration;
|
|
11
|
-
this.groupIndex = groupIndex;
|
|
12
|
-
this.groupOffset = groupOffset;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// src/sprites/SpritePart.ts
|
|
17
|
-
var SpritePart = class {
|
|
18
|
-
constructor() {
|
|
19
|
-
this.isLarge = false;
|
|
20
|
-
this.xOffset = 0;
|
|
21
|
-
this.xOffsetMirror = 0;
|
|
22
|
-
this.yOffset = 0;
|
|
23
|
-
this.yOffsetMirror = 0;
|
|
24
|
-
this.vMirror = false;
|
|
25
|
-
this.hMirror = false;
|
|
26
|
-
this.someOffset = 0;
|
|
27
|
-
this.paletteIndex = 0;
|
|
28
|
-
this.tileIndex = 0;
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// src/sprites/SpriteGroup.ts
|
|
33
|
-
var SpriteGroup = class {
|
|
34
|
-
constructor() {
|
|
35
|
-
this.xOffset = 0;
|
|
36
|
-
this.xOffsetMirror = 0;
|
|
37
|
-
this.yOffset = 0;
|
|
38
|
-
this.yOffsetMirror = 0;
|
|
39
|
-
this.xRecoilHitboxOffset = 0;
|
|
40
|
-
this.yRecoilHitboxOffset = 0;
|
|
41
|
-
this.xRecoilHitboxTilesize = 0;
|
|
42
|
-
this.yRecoilHitboxTilesize = 0;
|
|
43
|
-
this.xHostileHitboxOffset = 0;
|
|
44
|
-
this.xHostileHitboxSize = 0;
|
|
45
|
-
this.yHostileHitboxOffset = 0;
|
|
46
|
-
this.yHostileHitboxSize = 0;
|
|
47
|
-
this.parts = [];
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// src/sprites/SpriteMap.ts
|
|
52
|
-
var SpriteMap = class _SpriteMap {
|
|
53
|
-
constructor() {
|
|
54
|
-
this.frameSets = [];
|
|
55
|
-
this.groups = [];
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Create a SpriteMap from binary data
|
|
59
|
-
* @param data Binary data buffer
|
|
60
|
-
* @returns SpriteMap instance
|
|
61
|
-
*/
|
|
62
|
-
static fromBytes(data) {
|
|
63
|
-
let position = 0;
|
|
64
|
-
const getByte = () => {
|
|
65
|
-
if (position >= data.length) return 0;
|
|
66
|
-
return data[position++];
|
|
67
|
-
};
|
|
68
|
-
const getUshort = () => {
|
|
69
|
-
if (position + 1 >= data.length) return 0;
|
|
70
|
-
const low = data[position++];
|
|
71
|
-
const high = data[position++];
|
|
72
|
-
return low | high << 8;
|
|
73
|
-
};
|
|
74
|
-
const spriteMap = new _SpriteMap();
|
|
75
|
-
const setOffsets = /* @__PURE__ */ new Set();
|
|
76
|
-
const groupOffsets = /* @__PURE__ */ new Set();
|
|
77
|
-
position = 0;
|
|
78
|
-
while (!setOffsets.has(position)) {
|
|
79
|
-
const offset = getUshort() - 16384;
|
|
80
|
-
setOffsets.add(offset);
|
|
81
|
-
}
|
|
82
|
-
for (const offStart of setOffsets) {
|
|
83
|
-
position = offStart;
|
|
84
|
-
const frameList = [];
|
|
85
|
-
while (true) {
|
|
86
|
-
const duration = getUshort();
|
|
87
|
-
if (duration === 65535) {
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
const groupOffset = getUshort() - 16384;
|
|
91
|
-
frameList.push(new SpriteFrame(duration, 0, groupOffset));
|
|
92
|
-
groupOffsets.add(groupOffset);
|
|
93
|
-
}
|
|
94
|
-
spriteMap.frameSets.push(frameList);
|
|
95
|
-
}
|
|
96
|
-
let grpIx = 0;
|
|
97
|
-
const sortedGroupOffsets = Array.from(groupOffsets).sort((a, b) => a - b);
|
|
98
|
-
for (const offStart of sortedGroupOffsets) {
|
|
99
|
-
position = offStart;
|
|
100
|
-
for (const set of spriteMap.frameSets) {
|
|
101
|
-
for (const frame of set) {
|
|
102
|
-
if (frame.groupOffset === offStart) {
|
|
103
|
-
frame.groupIndex = grpIx;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
const grp = new SpriteGroup();
|
|
108
|
-
grp.xOffset = getByte();
|
|
109
|
-
grp.xOffsetMirror = getByte();
|
|
110
|
-
grp.yOffset = getByte();
|
|
111
|
-
grp.yOffsetMirror = getByte();
|
|
112
|
-
grp.xRecoilHitboxOffset = getByte();
|
|
113
|
-
grp.yRecoilHitboxOffset = getByte();
|
|
114
|
-
grp.xRecoilHitboxTilesize = getByte();
|
|
115
|
-
grp.yRecoilHitboxTilesize = getByte();
|
|
116
|
-
grp.xHostileHitboxOffset = getByte();
|
|
117
|
-
grp.xHostileHitboxSize = getByte();
|
|
118
|
-
grp.yHostileHitboxOffset = getByte();
|
|
119
|
-
grp.yHostileHitboxSize = getByte();
|
|
120
|
-
let numParts = getByte();
|
|
121
|
-
while (numParts-- > 0) {
|
|
122
|
-
const part = new SpritePart();
|
|
123
|
-
part.isLarge = getByte() !== 0;
|
|
124
|
-
part.xOffset = getByte();
|
|
125
|
-
part.xOffsetMirror = getByte();
|
|
126
|
-
part.yOffset = getByte();
|
|
127
|
-
part.yOffsetMirror = getByte();
|
|
128
|
-
const props = getUshort();
|
|
129
|
-
part.vMirror = (props & 32768) !== 0;
|
|
130
|
-
part.hMirror = (props & 16384) !== 0;
|
|
131
|
-
part.someOffset = props >> 12 & 3;
|
|
132
|
-
part.paletteIndex = props >> 9 & 7;
|
|
133
|
-
part.tileIndex = props & 511;
|
|
134
|
-
grp.parts.push(part);
|
|
135
|
-
}
|
|
136
|
-
spriteMap.groups.push(grp);
|
|
137
|
-
grpIx++;
|
|
138
|
-
}
|
|
139
|
-
return spriteMap;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Convert this SpriteMap to binary data
|
|
143
|
-
* @returns Binary data buffer
|
|
144
|
-
*/
|
|
145
|
-
toBytes() {
|
|
146
|
-
let size = this.frameSets.length * 2;
|
|
147
|
-
for (const set of this.frameSets) {
|
|
148
|
-
size += set.length * 4 + 2;
|
|
149
|
-
}
|
|
150
|
-
for (const grp of this.groups) {
|
|
151
|
-
size += grp.parts.length * 7 + 13;
|
|
152
|
-
}
|
|
153
|
-
const buffer = new Uint8Array(size);
|
|
154
|
-
let position = 0;
|
|
155
|
-
const writeShort = (val) => {
|
|
156
|
-
buffer[position++] = val & 255;
|
|
157
|
-
buffer[position++] = val >> 8 & 255;
|
|
158
|
-
};
|
|
159
|
-
const writeLoc = (val) => writeShort(val + 16384);
|
|
160
|
-
let pos = this.frameSets.length << 1;
|
|
161
|
-
const groupPos = new Array(this.groups.length);
|
|
162
|
-
for (const set of this.frameSets) {
|
|
163
|
-
writeLoc(pos);
|
|
164
|
-
pos += (set.length << 2) + 2;
|
|
165
|
-
}
|
|
166
|
-
for (let i = 0; i < this.groups.length; i++) {
|
|
167
|
-
groupPos[i] = pos;
|
|
168
|
-
pos += this.groups[i].parts.length * 7 + 13;
|
|
169
|
-
}
|
|
170
|
-
for (const set of this.frameSets) {
|
|
171
|
-
for (const frm of set) {
|
|
172
|
-
writeShort(frm.duration);
|
|
173
|
-
writeLoc(groupPos[frm.groupIndex]);
|
|
174
|
-
}
|
|
175
|
-
writeShort(65535);
|
|
176
|
-
}
|
|
177
|
-
for (const grp of this.groups) {
|
|
178
|
-
buffer[position++] = grp.xOffset;
|
|
179
|
-
buffer[position++] = grp.xOffsetMirror;
|
|
180
|
-
buffer[position++] = grp.yOffset;
|
|
181
|
-
buffer[position++] = grp.yOffsetMirror;
|
|
182
|
-
buffer[position++] = grp.xRecoilHitboxOffset;
|
|
183
|
-
buffer[position++] = grp.yRecoilHitboxOffset;
|
|
184
|
-
buffer[position++] = grp.xRecoilHitboxTilesize;
|
|
185
|
-
buffer[position++] = grp.yRecoilHitboxTilesize;
|
|
186
|
-
buffer[position++] = grp.xHostileHitboxOffset;
|
|
187
|
-
buffer[position++] = grp.xHostileHitboxSize;
|
|
188
|
-
buffer[position++] = grp.yHostileHitboxOffset;
|
|
189
|
-
buffer[position++] = grp.yHostileHitboxSize;
|
|
190
|
-
buffer[position++] = grp.parts.length;
|
|
191
|
-
for (const prt of grp.parts) {
|
|
192
|
-
buffer[position++] = prt.isLarge ? 1 : 0;
|
|
193
|
-
buffer[position++] = prt.xOffset;
|
|
194
|
-
buffer[position++] = prt.xOffsetMirror;
|
|
195
|
-
buffer[position++] = prt.yOffset;
|
|
196
|
-
buffer[position++] = prt.yOffsetMirror;
|
|
197
|
-
const accum = (prt.vMirror ? 32768 : 0) | (prt.hMirror ? 16384 : 0) | (prt.someOffset & 3) << 12 | (prt.paletteIndex & 7) << 9 | prt.tileIndex;
|
|
198
|
-
writeShort(accum);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return buffer.slice(0, position);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// src/rom/state.ts
|
|
206
|
-
var RomState = class _RomState {
|
|
207
|
-
constructor() {
|
|
208
|
-
// Hardware memory buffers
|
|
209
|
-
this.cgram = new Uint8Array(512);
|
|
210
|
-
// Color palette RAM
|
|
211
|
-
this.vram = new Uint8Array(65536);
|
|
212
|
-
// Video RAM
|
|
213
|
-
// Tileset data
|
|
214
|
-
this.mainTileset = new Uint8Array(2048);
|
|
215
|
-
this.effectTileset = new Uint8Array(2048);
|
|
216
|
-
// Tilemap data
|
|
217
|
-
this.mainTilemap = new Uint8Array(8192);
|
|
218
|
-
this.mainTilemapW = 0;
|
|
219
|
-
this.mainTilemapH = 0;
|
|
220
|
-
this.effectTilemap = new Uint8Array(8192);
|
|
221
|
-
this.effectTilemapW = 0;
|
|
222
|
-
this.effectTilemapH = 0;
|
|
223
|
-
}
|
|
224
|
-
static {
|
|
225
|
-
// Sprite data
|
|
226
|
-
this.spriteMap = null;
|
|
227
|
-
}
|
|
228
|
-
static {
|
|
229
|
-
this.spriteMapPath = null;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Strip address space prefixes from names
|
|
233
|
-
*/
|
|
234
|
-
static stripName(name) {
|
|
235
|
-
const addressSpace = ["@", "&", "^", "#", "$", "%", "*"];
|
|
236
|
-
while (name.length > 0 && addressSpace.includes(name[0])) {
|
|
237
|
-
name = name.substring(1);
|
|
238
|
-
}
|
|
239
|
-
return name;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Create ROM state from scene metadata
|
|
243
|
-
* Simplified version of the C# implementation
|
|
244
|
-
*/
|
|
245
|
-
static async fromScene(baseDir, root, metaFile, id) {
|
|
246
|
-
const state = new _RomState();
|
|
247
|
-
return state;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Process scene command (simplified version)
|
|
251
|
-
* TODO: Implement full command processing
|
|
252
|
-
*/
|
|
253
|
-
static async processCommand(state, command, params, getResource) {
|
|
254
|
-
switch (command) {
|
|
255
|
-
case 2:
|
|
256
|
-
break;
|
|
257
|
-
case 3:
|
|
258
|
-
break;
|
|
259
|
-
case 4:
|
|
260
|
-
break;
|
|
261
|
-
case 5:
|
|
262
|
-
break;
|
|
263
|
-
case 6:
|
|
264
|
-
break;
|
|
265
|
-
case 16:
|
|
266
|
-
if (params.length > 0) {
|
|
267
|
-
const spritePath = getResource(params[0], BinType.Spritemap);
|
|
268
|
-
try {
|
|
269
|
-
const data = await readFileAsBinary(spritePath);
|
|
270
|
-
_RomState.spriteMap = SpriteMap.fromBytes(data);
|
|
271
|
-
_RomState.spriteMapPath = spritePath;
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.warn(`Failed to load sprite map: ${spritePath}`, error);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
var RomStateUtils = class {
|
|
281
|
-
/**
|
|
282
|
-
* Create empty ROM state
|
|
283
|
-
*/
|
|
284
|
-
static createEmpty() {
|
|
285
|
-
return new RomState();
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Clear all data in ROM state
|
|
289
|
-
*/
|
|
290
|
-
static clear(state) {
|
|
291
|
-
state.cgram.fill(0);
|
|
292
|
-
state.vram.fill(0);
|
|
293
|
-
state.mainTileset.fill(0);
|
|
294
|
-
state.effectTileset.fill(0);
|
|
295
|
-
state.mainTilemap.fill(0);
|
|
296
|
-
state.effectTilemap.fill(0);
|
|
297
|
-
state.mainTilesetPath = void 0;
|
|
298
|
-
state.effectTilesetPath = void 0;
|
|
299
|
-
state.mainTilemapPath = void 0;
|
|
300
|
-
state.effectTilemapPath = void 0;
|
|
301
|
-
state.mainTilemapW = 0;
|
|
302
|
-
state.mainTilemapH = 0;
|
|
303
|
-
state.effectTilemapW = 0;
|
|
304
|
-
state.effectTilemapH = 0;
|
|
305
|
-
}
|
|
306
|
-
};
|
|
3
|
+
// src/rom/project.ts
|
|
307
4
|
var QuintetLZ = class _QuintetLZ {
|
|
308
5
|
static {
|
|
309
6
|
this.DICTIONARY_SIZE = 256;
|
|
@@ -427,13 +124,111 @@ var QuintetLZ = class _QuintetLZ {
|
|
|
427
124
|
return outputBuffer.slice(0, finalSize);
|
|
428
125
|
}
|
|
429
126
|
};
|
|
127
|
+
function registerCompressionProviders() {
|
|
128
|
+
CompressionRegistry.register("QuintetLZ", () => new QuintetLZ());
|
|
129
|
+
}
|
|
130
|
+
registerCompressionProviders();
|
|
430
131
|
|
|
431
|
-
// src/rom/
|
|
432
|
-
var
|
|
433
|
-
constructor() {
|
|
434
|
-
this.
|
|
435
|
-
this.
|
|
436
|
-
this.
|
|
132
|
+
// src/rom/project.ts
|
|
133
|
+
var ProjectRoot = class _ProjectRoot {
|
|
134
|
+
constructor(config) {
|
|
135
|
+
this.config = config;
|
|
136
|
+
this.name = config.name;
|
|
137
|
+
this.romPath = config.romPath;
|
|
138
|
+
this.baseDir = config.baseDir;
|
|
139
|
+
this.system = config.system;
|
|
140
|
+
this.database = config.database;
|
|
141
|
+
this.flipsPath = config.flipsPath;
|
|
142
|
+
this.resources = config.resources;
|
|
143
|
+
this.databasePath = config.databasePath;
|
|
144
|
+
this.systemPath = config.systemPath;
|
|
145
|
+
this.compression = config.compression;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Load project from file or directory
|
|
149
|
+
*/
|
|
150
|
+
static async load(path) {
|
|
151
|
+
path = path || "./project.json";
|
|
152
|
+
try {
|
|
153
|
+
const config = await readJsonFile(path);
|
|
154
|
+
if (!config.baseDir) {
|
|
155
|
+
config.baseDir = await getDirectory(path);
|
|
156
|
+
}
|
|
157
|
+
return new _ProjectRoot(config);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.warn(`Failed to load project file: ${error}. Using directory-based configuration.`);
|
|
160
|
+
}
|
|
161
|
+
const defaultConfig = {
|
|
162
|
+
name: "GaiaLabs",
|
|
163
|
+
baseDir: path,
|
|
164
|
+
flipsPath: process?.env?.flips_path,
|
|
165
|
+
romPath: process?.env?.rom_path || "",
|
|
166
|
+
database: "us",
|
|
167
|
+
databasePath: `${path}/db/us`,
|
|
168
|
+
systemPath: `${path}/db/snes`,
|
|
169
|
+
resources: {}
|
|
170
|
+
};
|
|
171
|
+
return new _ProjectRoot(defaultConfig);
|
|
172
|
+
}
|
|
173
|
+
// /**
|
|
174
|
+
// * Build the ROM (simplified)
|
|
175
|
+
// */
|
|
176
|
+
// public async build(files: ChunkFile[], entryPoints: DbEntryPoint[]): Promise<void> {
|
|
177
|
+
// // Build ROM using rebuild writer
|
|
178
|
+
// const writer = new RomWriter(entryPoints, this.config.cartName, this.config.makerCode);
|
|
179
|
+
// await writer.repack(files);
|
|
180
|
+
// }
|
|
181
|
+
/**
|
|
182
|
+
* Dump database and extract ROM data with comprehensive assembly processing
|
|
183
|
+
*/
|
|
184
|
+
// public async dumpDatabase(): Promise<ChunkFile[]> {
|
|
185
|
+
// // Load database and ROM data
|
|
186
|
+
// const root = await DbRootUtils.fromFolder(this.databasePath!, this.systemPath!);
|
|
187
|
+
// const rom = await readFileAsBinary(this.romPath);
|
|
188
|
+
// //root.paths = this.resources;
|
|
189
|
+
// const chunkReader = new BlockReader(rom, root);
|
|
190
|
+
// const chunkFiles = chunkReader.analyzeAndResolve();
|
|
191
|
+
// const blockWriter = new BlockWriter(chunkReader);
|
|
192
|
+
// const writtenFiles = blockWriter.generateAllAsm(chunkFiles);
|
|
193
|
+
// return chunkFiles;
|
|
194
|
+
// }
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/rom/state.ts
|
|
198
|
+
var RomState = class _RomState {
|
|
199
|
+
constructor() {
|
|
200
|
+
// Basic memory buffers
|
|
201
|
+
this.cgram = new Uint8Array(512);
|
|
202
|
+
// Color palette RAM
|
|
203
|
+
this.vram = new Uint8Array(65536);
|
|
204
|
+
}
|
|
205
|
+
// Video RAM
|
|
206
|
+
/**
|
|
207
|
+
* Create ROM state from scene metadata.
|
|
208
|
+
* Parameters are currently unused but kept for future implementation.
|
|
209
|
+
*/
|
|
210
|
+
static async fromScene(_baseDir, _root, _metaFile, _id) {
|
|
211
|
+
return new _RomState();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var RomStateUtils = class {
|
|
215
|
+
/** Create an empty ROM state */
|
|
216
|
+
static createEmpty() {
|
|
217
|
+
return new RomState();
|
|
218
|
+
}
|
|
219
|
+
/** Clear all data in the given ROM state */
|
|
220
|
+
static clear(state) {
|
|
221
|
+
state.cgram.fill(0);
|
|
222
|
+
state.vram.fill(0);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// src/rom/extraction/processor.ts
|
|
227
|
+
var ProcessorStateManager = class {
|
|
228
|
+
constructor() {
|
|
229
|
+
this.accumulatorFlags = /* @__PURE__ */ new Map();
|
|
230
|
+
this.indexFlags = /* @__PURE__ */ new Map();
|
|
231
|
+
this.bankNotes = /* @__PURE__ */ new Map();
|
|
437
232
|
this.stackPositions = /* @__PURE__ */ new Map();
|
|
438
233
|
}
|
|
439
234
|
/**
|
|
@@ -443,15 +238,15 @@ var ProcessorStateManager = class {
|
|
|
443
238
|
hydrateRegisters(position, reg) {
|
|
444
239
|
const acc = this.accumulatorFlags.get(position);
|
|
445
240
|
if (acc !== void 0) {
|
|
446
|
-
reg.accumulatorFlag = acc;
|
|
241
|
+
reg.accumulatorFlag = acc ?? void 0;
|
|
447
242
|
}
|
|
448
243
|
const ind = this.indexFlags.get(position);
|
|
449
244
|
if (ind !== void 0) {
|
|
450
|
-
reg.indexFlag = ind;
|
|
245
|
+
reg.indexFlag = ind ?? void 0;
|
|
451
246
|
}
|
|
452
247
|
const bnk = this.bankNotes.get(position);
|
|
453
248
|
if (bnk !== void 0) {
|
|
454
|
-
reg.dataBank = bnk;
|
|
249
|
+
reg.dataBank = bnk ?? void 0;
|
|
455
250
|
}
|
|
456
251
|
const stack = this.stackPositions.get(position);
|
|
457
252
|
if (stack !== void 0) {
|
|
@@ -811,7 +606,7 @@ var CopCommandProcessor = class {
|
|
|
811
606
|
for (const partStr of copDef.parts) {
|
|
812
607
|
const addrType = Address.typeFromCode(partStr[0]);
|
|
813
608
|
const isPtr = addrType !== AddressType.Unknown;
|
|
814
|
-
const referenceType = isPtr ? partStr.substring(1) : this._blockReader.
|
|
609
|
+
const referenceType = isPtr ? partStr.substring(1) : this._blockReader._currentAsmBlock.structName ?? "Binary";
|
|
815
610
|
const memberTypeName = isPtr ? addrType.toString() : partStr;
|
|
816
611
|
const memberType = this.tryParseMemberType(memberTypeName);
|
|
817
612
|
if (memberType === null) {
|
|
@@ -851,9 +646,9 @@ var CopCommandProcessor = class {
|
|
|
851
646
|
readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType) {
|
|
852
647
|
switch (memberType) {
|
|
853
648
|
case MemberType.Byte:
|
|
854
|
-
return
|
|
649
|
+
return new Byte(this._romDataReader.readByte());
|
|
855
650
|
case MemberType.Word:
|
|
856
|
-
return
|
|
651
|
+
return new Word(this._romDataReader.readUShort());
|
|
857
652
|
case MemberType.Offset:
|
|
858
653
|
return this.createCopLocation(this._romDataReader.readUShort(), null, partStr, isPtr, referenceType, addrType);
|
|
859
654
|
case MemberType.Address:
|
|
@@ -864,7 +659,7 @@ var CopCommandProcessor = class {
|
|
|
864
659
|
}
|
|
865
660
|
createCopLocation(offset, bank, partStr, isPtr, otherStr, type) {
|
|
866
661
|
if (bank === null && offset === 0) {
|
|
867
|
-
return
|
|
662
|
+
return new Word(offset);
|
|
868
663
|
}
|
|
869
664
|
const addr = new Address(bank ?? this._romDataReader.position >> 16, offset);
|
|
870
665
|
if (addr.space === AddressSpace.ROM) {
|
|
@@ -910,51 +705,54 @@ var AddressingModeHandler = class {
|
|
|
910
705
|
processAddressingMode(code, context, reg) {
|
|
911
706
|
const operands = [];
|
|
912
707
|
switch (code.mode) {
|
|
913
|
-
case
|
|
914
|
-
case
|
|
708
|
+
case "Stack":
|
|
709
|
+
case "Implied":
|
|
710
|
+
case "Accumulator":
|
|
915
711
|
this.handleStackOrImpliedMode(code.mnem, reg);
|
|
916
712
|
break;
|
|
917
|
-
case
|
|
713
|
+
case "Immediate":
|
|
918
714
|
this.handleImmediateMode(code.mnem, context.size, operands, reg);
|
|
919
715
|
break;
|
|
920
|
-
case
|
|
921
|
-
case
|
|
922
|
-
case
|
|
923
|
-
case
|
|
924
|
-
case
|
|
925
|
-
case
|
|
716
|
+
case "AbsoluteIndirect":
|
|
717
|
+
case "AbsoluteIndirectLong":
|
|
718
|
+
case "AbsoluteIndexedIndirect":
|
|
719
|
+
case "Absolute":
|
|
720
|
+
case "AbsoluteIndexedX":
|
|
721
|
+
case "AbsoluteIndexedY":
|
|
926
722
|
this.handleAbsoluteMode(code.mnem, void 0, context.nextAddress, reg.dataBank, operands, reg);
|
|
927
723
|
break;
|
|
928
|
-
case
|
|
929
|
-
case
|
|
724
|
+
case "AbsoluteLong":
|
|
725
|
+
case "AbsoluteLongIndexedX":
|
|
930
726
|
this.handleAbsoluteLongMode(code.mnem, operands, reg);
|
|
931
727
|
break;
|
|
932
|
-
case
|
|
728
|
+
case "BlockMove":
|
|
933
729
|
this.handleBlockMoveMode(operands, context);
|
|
934
730
|
break;
|
|
935
|
-
case
|
|
936
|
-
case
|
|
937
|
-
case
|
|
938
|
-
case
|
|
939
|
-
case
|
|
940
|
-
case
|
|
941
|
-
case
|
|
942
|
-
case
|
|
731
|
+
case "DirectPage":
|
|
732
|
+
case "DirectPageIndexedIndirectX":
|
|
733
|
+
case "DirectPageIndexedX":
|
|
734
|
+
case "DirectPageIndexedY":
|
|
735
|
+
case "DirectPageIndirect":
|
|
736
|
+
case "DirectPageIndirectIndexedY":
|
|
737
|
+
case "DirectPageIndirectLong":
|
|
738
|
+
case "DirectPageIndirectLongIndexedY":
|
|
943
739
|
this.handleDirectPageMode(operands);
|
|
944
740
|
break;
|
|
945
|
-
case
|
|
741
|
+
case "PCRelative":
|
|
946
742
|
this.handlePCRelativeMode(operands, context.nextAddress, reg, false);
|
|
947
743
|
break;
|
|
948
|
-
case
|
|
744
|
+
case "PCRelativeLong":
|
|
949
745
|
this.handlePCRelativeMode(operands, context.nextAddress, reg, true);
|
|
950
746
|
break;
|
|
951
|
-
case
|
|
952
|
-
case
|
|
747
|
+
case "StackRelative":
|
|
748
|
+
case "StackRelativeIndirectIndexedY":
|
|
953
749
|
this.handleStackRelativeMode(operands);
|
|
954
750
|
break;
|
|
955
|
-
case
|
|
751
|
+
case "StackInterrupt":
|
|
956
752
|
this.handleStackInterruptMode(code.mnem, operands, context);
|
|
957
753
|
break;
|
|
754
|
+
default:
|
|
755
|
+
throw new Error(`Unknown addressing mode: ${code}`);
|
|
958
756
|
}
|
|
959
757
|
return operands;
|
|
960
758
|
}
|
|
@@ -964,7 +762,7 @@ var AddressingModeHandler = class {
|
|
|
964
762
|
this.updateRegisterForImmediateInstruction(mnemonic, operand, reg);
|
|
965
763
|
}
|
|
966
764
|
readImmediateOperand(size) {
|
|
967
|
-
return size === 3 ?
|
|
765
|
+
return size === 3 ? new Word(this._dataReader.readUShort()) : new Byte(this._dataReader.readByte());
|
|
968
766
|
}
|
|
969
767
|
updateRegisterForImmediateInstruction(mnemonic, operand, reg) {
|
|
970
768
|
const value = typeof operand === "number" ? operand : operand.value;
|
|
@@ -1016,12 +814,12 @@ var AddressingModeHandler = class {
|
|
|
1016
814
|
}
|
|
1017
815
|
}
|
|
1018
816
|
handleBlockMoveMode(operands, context) {
|
|
1019
|
-
operands.push(
|
|
817
|
+
operands.push(new Byte(this._dataReader.readByte()));
|
|
1020
818
|
context.xForm2 = this._transformProcessor.getTransform();
|
|
1021
|
-
operands.push(
|
|
819
|
+
operands.push(new Byte(this._dataReader.readByte()));
|
|
1022
820
|
}
|
|
1023
821
|
handleDirectPageMode(operands) {
|
|
1024
|
-
operands.push(
|
|
822
|
+
operands.push(new Byte(this._dataReader.readByte()));
|
|
1025
823
|
}
|
|
1026
824
|
handlePCRelativeMode(operands, nextAddress, reg, isLong) {
|
|
1027
825
|
const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
|
|
@@ -1029,11 +827,11 @@ var AddressingModeHandler = class {
|
|
|
1029
827
|
operands.push(new LocationWrapper(relative, AddressType.Relative));
|
|
1030
828
|
}
|
|
1031
829
|
handleStackRelativeMode(operands) {
|
|
1032
|
-
operands.push(
|
|
830
|
+
operands.push(new Byte(this._dataReader.readByte()));
|
|
1033
831
|
}
|
|
1034
832
|
handleStackInterruptMode(mnemonic, operands, context) {
|
|
1035
833
|
const cmd = this._dataReader.readByte();
|
|
1036
|
-
operands.push(cmd);
|
|
834
|
+
operands.push(new Byte(cmd));
|
|
1037
835
|
if (mnemonic === "COP") {
|
|
1038
836
|
const copDef = this._blockReader._root.copDef[cmd];
|
|
1039
837
|
if (!copDef) {
|
|
@@ -1167,169 +965,6 @@ var TransformProcessor = class _TransformProcessor {
|
|
|
1167
965
|
return match ? parseInt(match[1], 16) : null;
|
|
1168
966
|
}
|
|
1169
967
|
};
|
|
1170
|
-
var SfxReader = class {
|
|
1171
|
-
constructor(romData, dbRoot) {
|
|
1172
|
-
this._romData = romData;
|
|
1173
|
-
this._dbRoot = dbRoot;
|
|
1174
|
-
this._location = dbRoot.config.sfxLocation;
|
|
1175
|
-
this._count = dbRoot.config.sfxCount;
|
|
1176
|
-
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Reads a byte from the rom data
|
|
1179
|
-
*/
|
|
1180
|
-
readByte() {
|
|
1181
|
-
if ((this._location & Address.UPPER_BANK) !== 0) {
|
|
1182
|
-
this._location += Address.UPPER_BANK;
|
|
1183
|
-
}
|
|
1184
|
-
return this._romData[this._location++];
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Reads a short from the rom data
|
|
1188
|
-
*/
|
|
1189
|
-
readShort() {
|
|
1190
|
-
return this.readByte() | this.readByte() << 8;
|
|
1191
|
-
}
|
|
1192
|
-
/**
|
|
1193
|
-
* Extracts the sfx from the rom data to the given output path
|
|
1194
|
-
*/
|
|
1195
|
-
async extract(outPath) {
|
|
1196
|
-
const res = this.getPath(BinType.Sound);
|
|
1197
|
-
if (res?.folder) {
|
|
1198
|
-
outPath = `${outPath}/${res.folder}`;
|
|
1199
|
-
}
|
|
1200
|
-
const isNode = typeof window === "undefined";
|
|
1201
|
-
const fs2 = isNode ? await import('fs') : null;
|
|
1202
|
-
if (isNode) {
|
|
1203
|
-
fs2.mkdirSync(outPath, { recursive: true });
|
|
1204
|
-
}
|
|
1205
|
-
for (let i = 0; i < this._count; i++) {
|
|
1206
|
-
const size = this.readShort();
|
|
1207
|
-
const filePath = `${outPath}/sfx${i.toString(16).padStart(2, "0").toUpperCase()}.${res?.extension || "bin"}`;
|
|
1208
|
-
if (isNode) {
|
|
1209
|
-
if (fs2.existsSync(filePath)) {
|
|
1210
|
-
this._location += size;
|
|
1211
|
-
} else {
|
|
1212
|
-
const sfxData = new Uint8Array(size);
|
|
1213
|
-
for (let x = 0; x < size; x++) {
|
|
1214
|
-
sfxData[x] = this.readByte();
|
|
1215
|
-
}
|
|
1216
|
-
fs2.writeFileSync(filePath, sfxData);
|
|
1217
|
-
}
|
|
1218
|
-
} else {
|
|
1219
|
-
this._location += size;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
/**
|
|
1224
|
-
* Gets the path information for a binary type
|
|
1225
|
-
* TODO: This should be implemented in DbRoot
|
|
1226
|
-
*/
|
|
1227
|
-
getPath(binType) {
|
|
1228
|
-
switch (binType) {
|
|
1229
|
-
case BinType.Sound:
|
|
1230
|
-
return { folder: "sound", extension: "sfc" };
|
|
1231
|
-
default:
|
|
1232
|
-
return null;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
};
|
|
1236
|
-
var FileReader = class _FileReader {
|
|
1237
|
-
static {
|
|
1238
|
-
this.PALETTE_MIN_SIZE = 512;
|
|
1239
|
-
}
|
|
1240
|
-
constructor(romData, dbRoot, provider) {
|
|
1241
|
-
this._romData = romData;
|
|
1242
|
-
this._dbRoot = dbRoot;
|
|
1243
|
-
this._compression = provider;
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Extracts all files from the ROM to the given output path
|
|
1247
|
-
*/
|
|
1248
|
-
async extract(outPath) {
|
|
1249
|
-
const isNode = typeof window === "undefined";
|
|
1250
|
-
const fs2 = isNode ? await import('fs') : null;
|
|
1251
|
-
for (const file of this._dbRoot.files) {
|
|
1252
|
-
let start = file.start;
|
|
1253
|
-
const res = this.getPath(file.type);
|
|
1254
|
-
let filePath = outPath;
|
|
1255
|
-
if (res.folder) {
|
|
1256
|
-
filePath = `${outPath}/${res.folder}`;
|
|
1257
|
-
if (isNode) {
|
|
1258
|
-
fs2.mkdirSync(filePath, { recursive: true });
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
filePath = `${filePath}/${file.name}.${res.extension}`;
|
|
1262
|
-
if (isNode && fs2.existsSync(filePath)) {
|
|
1263
|
-
continue;
|
|
1264
|
-
}
|
|
1265
|
-
let header = null;
|
|
1266
|
-
if (file.type === BinType.Tilemap) {
|
|
1267
|
-
header = new Uint8Array([this._romData[start++], this._romData[start++]]);
|
|
1268
|
-
} else if (file.type === BinType.Meta17) {
|
|
1269
|
-
header = new Uint8Array([
|
|
1270
|
-
this._romData[start++],
|
|
1271
|
-
this._romData[start++],
|
|
1272
|
-
this._romData[start++],
|
|
1273
|
-
this._romData[start++]
|
|
1274
|
-
]);
|
|
1275
|
-
}
|
|
1276
|
-
let length = file.end - start;
|
|
1277
|
-
let fileData;
|
|
1278
|
-
if (file.compressed === true) {
|
|
1279
|
-
const data = this._compression.expand(this._romData, start, length);
|
|
1280
|
-
fileData = this.processFileData(data, 0, data.length, header, file.type);
|
|
1281
|
-
} else {
|
|
1282
|
-
if (file.compressed !== null) {
|
|
1283
|
-
start += 2;
|
|
1284
|
-
length -= 2;
|
|
1285
|
-
}
|
|
1286
|
-
fileData = this.processFileData(this._romData, start, length, header, file.type);
|
|
1287
|
-
}
|
|
1288
|
-
if (isNode) {
|
|
1289
|
-
fs2.writeFileSync(filePath, fileData);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
processFileData(data, position, length, header, type) {
|
|
1294
|
-
let totalLength = length;
|
|
1295
|
-
let headerLength = 0;
|
|
1296
|
-
if (header) {
|
|
1297
|
-
headerLength = header.length;
|
|
1298
|
-
totalLength += headerLength;
|
|
1299
|
-
}
|
|
1300
|
-
if (type === BinType.Palette) {
|
|
1301
|
-
const remain = _FileReader.PALETTE_MIN_SIZE - length;
|
|
1302
|
-
if (remain > 0) {
|
|
1303
|
-
totalLength += remain;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
const result = new Uint8Array(totalLength);
|
|
1307
|
-
let offset = 0;
|
|
1308
|
-
if (header) {
|
|
1309
|
-
result.set(header, offset);
|
|
1310
|
-
offset += headerLength;
|
|
1311
|
-
}
|
|
1312
|
-
result.set(data.slice(position, position + length), offset);
|
|
1313
|
-
offset += length;
|
|
1314
|
-
if (type === BinType.Palette) {
|
|
1315
|
-
const remain = _FileReader.PALETTE_MIN_SIZE - length;
|
|
1316
|
-
if (remain > 0) {
|
|
1317
|
-
result.fill(0, offset, offset + remain);
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
return result;
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Gets the path information for a binary type from DbRoot configuration
|
|
1324
|
-
*/
|
|
1325
|
-
getPath(binType) {
|
|
1326
|
-
const pathConfig = this._dbRoot.paths[binType] || this._dbRoot.paths[BinType.Unknown];
|
|
1327
|
-
return {
|
|
1328
|
-
folder: pathConfig.folder,
|
|
1329
|
-
extension: pathConfig.extension
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
1333
968
|
|
|
1334
969
|
// src/utils/index.ts
|
|
1335
970
|
function indexOfAny(str, chars, startIndex = 0) {
|
|
@@ -1480,12 +1115,11 @@ var StringReader = class _StringReader {
|
|
|
1480
1115
|
}
|
|
1481
1116
|
name.substring(0, opix);
|
|
1482
1117
|
const target = sloc - offset;
|
|
1483
|
-
const [isOutside,
|
|
1484
|
-
if (
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
entry.Object.marker = offset;
|
|
1118
|
+
const [isOutside, block, part] = ChunkFileUtils.isOutsideWithPart(this._blockReader._enrichedChunks, this._blockReader._currentChunk, sloc);
|
|
1119
|
+
if (part != null) {
|
|
1120
|
+
const entry = part.objList?.find((x) => typeof x === "object" && x !== null && "location" in x && "object" in x && x.location === target);
|
|
1121
|
+
if (entry && entry.object) {
|
|
1122
|
+
entry.object.marker = offset;
|
|
1489
1123
|
}
|
|
1490
1124
|
}
|
|
1491
1125
|
}
|
|
@@ -1519,9 +1153,9 @@ var TypeParser = class {
|
|
|
1519
1153
|
if (mType !== null) {
|
|
1520
1154
|
switch (mType) {
|
|
1521
1155
|
case MemberType.Byte:
|
|
1522
|
-
return
|
|
1156
|
+
return new Byte(this._romDataReader.readByte());
|
|
1523
1157
|
case MemberType.Word:
|
|
1524
|
-
return
|
|
1158
|
+
return new Word(this.parseWordSafe());
|
|
1525
1159
|
case MemberType.Offset:
|
|
1526
1160
|
return this.parseLocation(this._romDataReader.readUShort(), bank, null, AddressType.Offset);
|
|
1527
1161
|
case MemberType.Address:
|
|
@@ -1563,7 +1197,7 @@ var TypeParser = class {
|
|
|
1563
1197
|
const parts = new Array(memberCount);
|
|
1564
1198
|
const def = { name: targetType.name, parts };
|
|
1565
1199
|
for (let i = 0; i < memberCount; i++) {
|
|
1566
|
-
parts[i] = this.parseType(types[i], null, depth + 1);
|
|
1200
|
+
parts[i] = this.parseType(types[i], null, depth + 1, bank);
|
|
1567
1201
|
}
|
|
1568
1202
|
if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) {
|
|
1569
1203
|
this._romDataReader.position++;
|
|
@@ -1611,8 +1245,8 @@ var TypeParser = class {
|
|
|
1611
1245
|
return outBuffer;
|
|
1612
1246
|
}
|
|
1613
1247
|
parseLocation(offset, bank, typeName, addrType) {
|
|
1614
|
-
if (bank === void 0 && offset === 0) {
|
|
1615
|
-
return
|
|
1248
|
+
if ((bank === void 0 || bank === null) && offset === 0) {
|
|
1249
|
+
return new Word(offset);
|
|
1616
1250
|
}
|
|
1617
1251
|
const resolvedBank = bank ?? this._romDataReader.position >> 16;
|
|
1618
1252
|
const adrs = new Address(resolvedBank, offset);
|
|
@@ -1620,8 +1254,8 @@ var TypeParser = class {
|
|
|
1620
1254
|
return adrs;
|
|
1621
1255
|
}
|
|
1622
1256
|
const loc = adrs.toInt();
|
|
1623
|
-
if (this._blockReader.
|
|
1624
|
-
const resolvedTypeName = typeName ?? this._blockReader.
|
|
1257
|
+
if (this._blockReader._currentChunk && ChunkFileUtils.isInside(this._blockReader._currentChunk, loc) && !this._blockReader._root.rewrites[loc]) {
|
|
1258
|
+
const resolvedTypeName = typeName ?? this._blockReader._currentAsmBlock.structName ?? "Binary";
|
|
1625
1259
|
this._referenceManager.tryAddStruct(loc, resolvedTypeName);
|
|
1626
1260
|
const referenceName = `${resolvedTypeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
|
|
1627
1261
|
this._referenceManager.tryAddName(loc, referenceName);
|
|
@@ -1638,7 +1272,7 @@ var TypeParser = class {
|
|
|
1638
1272
|
break;
|
|
1639
1273
|
}
|
|
1640
1274
|
if (reg) {
|
|
1641
|
-
this._blockReader.
|
|
1275
|
+
this._blockReader.hydrateRegisters(reg);
|
|
1642
1276
|
}
|
|
1643
1277
|
const op = this._blockReader._asmReader.parseAsm(reg);
|
|
1644
1278
|
opList.push(op);
|
|
@@ -1646,6 +1280,93 @@ var TypeParser = class {
|
|
|
1646
1280
|
return opList;
|
|
1647
1281
|
}
|
|
1648
1282
|
};
|
|
1283
|
+
var AsmReader = class _AsmReader {
|
|
1284
|
+
static {
|
|
1285
|
+
// Constants
|
|
1286
|
+
this.PROCESSOR_FLAG_MASK = 223;
|
|
1287
|
+
}
|
|
1288
|
+
static {
|
|
1289
|
+
this.ACCUMULATOR_OP_MASK = 15;
|
|
1290
|
+
}
|
|
1291
|
+
static {
|
|
1292
|
+
this.ACCUMULATOR_OP_VALUE = 9;
|
|
1293
|
+
}
|
|
1294
|
+
static {
|
|
1295
|
+
this.VARIABLE_SIZE_INDICATOR = -2;
|
|
1296
|
+
}
|
|
1297
|
+
static {
|
|
1298
|
+
this.TWO_BYTES_SIZE = 2;
|
|
1299
|
+
}
|
|
1300
|
+
static {
|
|
1301
|
+
this.THREE_BYTES_SIZE = 3;
|
|
1302
|
+
}
|
|
1303
|
+
constructor(blockReader) {
|
|
1304
|
+
this._blockReader = blockReader;
|
|
1305
|
+
this._transformProcessor = new TransformProcessor(blockReader);
|
|
1306
|
+
this._addressingModeHandler = new AddressingModeHandler(blockReader, this._transformProcessor);
|
|
1307
|
+
this._romDataReader = blockReader._romDataReader;
|
|
1308
|
+
}
|
|
1309
|
+
parseAsm(reg) {
|
|
1310
|
+
const opStart = this._romDataReader.position;
|
|
1311
|
+
const opCode = this._romDataReader.readByte();
|
|
1312
|
+
const code = this._blockReader._root.opCodes[opCode];
|
|
1313
|
+
if (!code) {
|
|
1314
|
+
throw new Error("Unknown OpCode");
|
|
1315
|
+
}
|
|
1316
|
+
const operationContext = this.initializeOperation(code, reg, opStart);
|
|
1317
|
+
const operands = this._addressingModeHandler.processAddressingMode(code, operationContext, reg);
|
|
1318
|
+
this._transformProcessor.applyTransforms(operationContext.xForm1, operationContext.xForm2, operands);
|
|
1319
|
+
const op = new Op(
|
|
1320
|
+
code,
|
|
1321
|
+
opStart,
|
|
1322
|
+
operands,
|
|
1323
|
+
this._romDataReader.position - opStart
|
|
1324
|
+
);
|
|
1325
|
+
if (operationContext.copDef) {
|
|
1326
|
+
op.copDef = operationContext.copDef;
|
|
1327
|
+
}
|
|
1328
|
+
return op;
|
|
1329
|
+
}
|
|
1330
|
+
clearDestinationRegister(code, reg) {
|
|
1331
|
+
switch (code.mnem) {
|
|
1332
|
+
case "LDA":
|
|
1333
|
+
reg.accumulator = void 0;
|
|
1334
|
+
break;
|
|
1335
|
+
case "LDX":
|
|
1336
|
+
reg.xIndex = void 0;
|
|
1337
|
+
break;
|
|
1338
|
+
case "LDY":
|
|
1339
|
+
reg.yIndex = void 0;
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
initializeOperation(code, reg, loc) {
|
|
1344
|
+
const size = this.calculateInstructionSize(code, reg);
|
|
1345
|
+
const next = loc + size;
|
|
1346
|
+
this.clearDestinationRegister(code, reg);
|
|
1347
|
+
const context = new OperationContext();
|
|
1348
|
+
context.size = size;
|
|
1349
|
+
context.nextAddress = next;
|
|
1350
|
+
context.xForm1 = this._transformProcessor.getTransform();
|
|
1351
|
+
context.xForm2 = null;
|
|
1352
|
+
context.copDef = null;
|
|
1353
|
+
return context;
|
|
1354
|
+
}
|
|
1355
|
+
calculateInstructionSize(code, reg) {
|
|
1356
|
+
const addrMode = this._blockReader._root.addrLookup[code.mode];
|
|
1357
|
+
let size = addrMode.size;
|
|
1358
|
+
if (size === _AsmReader.VARIABLE_SIZE_INDICATOR || code.mode === "Immediate") {
|
|
1359
|
+
if ((code.code & _AsmReader.PROCESSOR_FLAG_MASK) === 194) {
|
|
1360
|
+
size = _AsmReader.TWO_BYTES_SIZE;
|
|
1361
|
+
} else if ((code.code & _AsmReader.ACCUMULATOR_OP_MASK) === _AsmReader.ACCUMULATOR_OP_VALUE) {
|
|
1362
|
+
size = reg.accumulatorFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
1363
|
+
} else {
|
|
1364
|
+
size = reg.indexFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
return size;
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1649
1370
|
|
|
1650
1371
|
// src/assembly/Stack.ts
|
|
1651
1372
|
var Stack = class {
|
|
@@ -1726,510 +1447,49 @@ var Registers = class {
|
|
|
1726
1447
|
this.stack.reset();
|
|
1727
1448
|
}
|
|
1728
1449
|
};
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
this.
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
var OpCode = class {
|
|
1752
|
-
constructor(code, mnem, mode, size) {
|
|
1753
|
-
this.code = code;
|
|
1754
|
-
this.mnem = mnem;
|
|
1755
|
-
this.mode = mode;
|
|
1756
|
-
this.size = size;
|
|
1757
|
-
}
|
|
1758
|
-
};
|
|
1759
|
-
var ALL_OPCODES = {
|
|
1760
|
-
// ADC - Add with Carry
|
|
1761
|
-
105: new OpCode(105, "ADC", AddressingMode.Immediate, -2),
|
|
1762
|
-
109: new OpCode(109, "ADC", AddressingMode.Absolute, 3),
|
|
1763
|
-
111: new OpCode(111, "ADC", AddressingMode.AbsoluteLong, 4),
|
|
1764
|
-
101: new OpCode(101, "ADC", AddressingMode.DirectPage, 2),
|
|
1765
|
-
114: new OpCode(114, "ADC", AddressingMode.DirectPageIndirect, 2),
|
|
1766
|
-
103: new OpCode(103, "ADC", AddressingMode.DirectPageIndirectLong, 2),
|
|
1767
|
-
125: new OpCode(125, "ADC", AddressingMode.AbsoluteIndexedX, 3),
|
|
1768
|
-
127: new OpCode(127, "ADC", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1769
|
-
121: new OpCode(121, "ADC", AddressingMode.AbsoluteIndexedY, 3),
|
|
1770
|
-
117: new OpCode(117, "ADC", AddressingMode.DirectPageIndexedX, 2),
|
|
1771
|
-
97: new OpCode(97, "ADC", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1772
|
-
113: new OpCode(113, "ADC", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1773
|
-
119: new OpCode(119, "ADC", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1774
|
-
99: new OpCode(99, "ADC", AddressingMode.StackRelative, 2),
|
|
1775
|
-
115: new OpCode(115, "ADC", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1776
|
-
// AND - Logical AND
|
|
1777
|
-
41: new OpCode(41, "AND", AddressingMode.Immediate, -2),
|
|
1778
|
-
45: new OpCode(45, "AND", AddressingMode.Absolute, 3),
|
|
1779
|
-
47: new OpCode(47, "AND", AddressingMode.AbsoluteLong, 4),
|
|
1780
|
-
37: new OpCode(37, "AND", AddressingMode.DirectPage, 2),
|
|
1781
|
-
50: new OpCode(50, "AND", AddressingMode.DirectPageIndirect, 2),
|
|
1782
|
-
39: new OpCode(39, "AND", AddressingMode.DirectPageIndirectLong, 2),
|
|
1783
|
-
61: new OpCode(61, "AND", AddressingMode.AbsoluteIndexedX, 3),
|
|
1784
|
-
63: new OpCode(63, "AND", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1785
|
-
57: new OpCode(57, "AND", AddressingMode.AbsoluteIndexedY, 3),
|
|
1786
|
-
53: new OpCode(53, "AND", AddressingMode.DirectPageIndexedX, 2),
|
|
1787
|
-
33: new OpCode(33, "AND", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1788
|
-
49: new OpCode(49, "AND", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1789
|
-
55: new OpCode(55, "AND", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1790
|
-
35: new OpCode(35, "AND", AddressingMode.StackRelative, 2),
|
|
1791
|
-
51: new OpCode(51, "AND", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1792
|
-
// ASL - Arithmetic Shift Left
|
|
1793
|
-
10: new OpCode(10, "ASL", AddressingMode.Accumulator, 1),
|
|
1794
|
-
14: new OpCode(14, "ASL", AddressingMode.Absolute, 3),
|
|
1795
|
-
6: new OpCode(6, "ASL", AddressingMode.DirectPage, 2),
|
|
1796
|
-
30: new OpCode(30, "ASL", AddressingMode.AbsoluteIndexedX, 3),
|
|
1797
|
-
22: new OpCode(22, "ASL", AddressingMode.DirectPageIndexedX, 2),
|
|
1798
|
-
// Branch instructions
|
|
1799
|
-
16: new OpCode(16, "BPL", AddressingMode.PCRelative, 2),
|
|
1800
|
-
48: new OpCode(48, "BMI", AddressingMode.PCRelative, 2),
|
|
1801
|
-
128: new OpCode(128, "BRA", AddressingMode.PCRelative, 2),
|
|
1802
|
-
130: new OpCode(130, "BRL", AddressingMode.PCRelativeLong, 3),
|
|
1803
|
-
144: new OpCode(144, "BCC", AddressingMode.PCRelative, 2),
|
|
1804
|
-
176: new OpCode(176, "BCS", AddressingMode.PCRelative, 2),
|
|
1805
|
-
208: new OpCode(208, "BNE", AddressingMode.PCRelative, 2),
|
|
1806
|
-
240: new OpCode(240, "BEQ", AddressingMode.PCRelative, 2),
|
|
1807
|
-
80: new OpCode(80, "BVC", AddressingMode.PCRelative, 2),
|
|
1808
|
-
112: new OpCode(112, "BVS", AddressingMode.PCRelative, 2),
|
|
1809
|
-
// BIT - Bit Test
|
|
1810
|
-
137: new OpCode(137, "BIT", AddressingMode.Immediate, -2),
|
|
1811
|
-
44: new OpCode(44, "BIT", AddressingMode.Absolute, 3),
|
|
1812
|
-
36: new OpCode(36, "BIT", AddressingMode.DirectPage, 2),
|
|
1813
|
-
60: new OpCode(60, "BIT", AddressingMode.AbsoluteIndexedX, 3),
|
|
1814
|
-
52: new OpCode(52, "BIT", AddressingMode.DirectPageIndexedX, 2),
|
|
1815
|
-
// BRK - Break
|
|
1816
|
-
0: new OpCode(0, "BRK", AddressingMode.StackInterrupt, 2),
|
|
1817
|
-
// Clear flag instructions
|
|
1818
|
-
24: new OpCode(24, "CLC", AddressingMode.Implied, 1),
|
|
1819
|
-
216: new OpCode(216, "CLD", AddressingMode.Implied, 1),
|
|
1820
|
-
88: new OpCode(88, "CLI", AddressingMode.Implied, 1),
|
|
1821
|
-
184: new OpCode(184, "CLV", AddressingMode.Implied, 1),
|
|
1822
|
-
// CMP - Compare Accumulator
|
|
1823
|
-
201: new OpCode(201, "CMP", AddressingMode.Immediate, -2),
|
|
1824
|
-
205: new OpCode(205, "CMP", AddressingMode.Absolute, 3),
|
|
1825
|
-
207: new OpCode(207, "CMP", AddressingMode.AbsoluteLong, 4),
|
|
1826
|
-
197: new OpCode(197, "CMP", AddressingMode.DirectPage, 2),
|
|
1827
|
-
210: new OpCode(210, "CMP", AddressingMode.DirectPageIndirect, 2),
|
|
1828
|
-
199: new OpCode(199, "CMP", AddressingMode.DirectPageIndirectLong, 2),
|
|
1829
|
-
221: new OpCode(221, "CMP", AddressingMode.AbsoluteIndexedX, 3),
|
|
1830
|
-
223: new OpCode(223, "CMP", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1831
|
-
217: new OpCode(217, "CMP", AddressingMode.AbsoluteIndexedY, 3),
|
|
1832
|
-
213: new OpCode(213, "CMP", AddressingMode.DirectPageIndexedX, 2),
|
|
1833
|
-
193: new OpCode(193, "CMP", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1834
|
-
209: new OpCode(209, "CMP", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1835
|
-
215: new OpCode(215, "CMP", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1836
|
-
195: new OpCode(195, "CMP", AddressingMode.StackRelative, 2),
|
|
1837
|
-
211: new OpCode(211, "CMP", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1838
|
-
// COP - Coprocessor Instruction
|
|
1839
|
-
2: new OpCode(2, "COP", AddressingMode.StackInterrupt, 2),
|
|
1840
|
-
// CPX - Compare X Register
|
|
1841
|
-
224: new OpCode(224, "CPX", AddressingMode.Immediate, -2),
|
|
1842
|
-
236: new OpCode(236, "CPX", AddressingMode.Absolute, 3),
|
|
1843
|
-
228: new OpCode(228, "CPX", AddressingMode.DirectPage, 2),
|
|
1844
|
-
// CPY - Compare Y Register
|
|
1845
|
-
192: new OpCode(192, "CPY", AddressingMode.Immediate, -2),
|
|
1846
|
-
204: new OpCode(204, "CPY", AddressingMode.Absolute, 3),
|
|
1847
|
-
196: new OpCode(196, "CPY", AddressingMode.DirectPage, 2),
|
|
1848
|
-
// DEC - Decrement
|
|
1849
|
-
58: new OpCode(58, "DEC", AddressingMode.Accumulator, 1),
|
|
1850
|
-
206: new OpCode(206, "DEC", AddressingMode.Absolute, 3),
|
|
1851
|
-
198: new OpCode(198, "DEC", AddressingMode.DirectPage, 2),
|
|
1852
|
-
222: new OpCode(222, "DEC", AddressingMode.AbsoluteIndexedX, 3),
|
|
1853
|
-
214: new OpCode(214, "DEC", AddressingMode.DirectPageIndexedX, 2),
|
|
1854
|
-
// DEX/DEY - Decrement Index Registers
|
|
1855
|
-
202: new OpCode(202, "DEX", AddressingMode.Implied, 1),
|
|
1856
|
-
136: new OpCode(136, "DEY", AddressingMode.Implied, 1),
|
|
1857
|
-
// EOR - Exclusive OR
|
|
1858
|
-
73: new OpCode(73, "EOR", AddressingMode.Immediate, -2),
|
|
1859
|
-
77: new OpCode(77, "EOR", AddressingMode.Absolute, 3),
|
|
1860
|
-
79: new OpCode(79, "EOR", AddressingMode.AbsoluteLong, 4),
|
|
1861
|
-
69: new OpCode(69, "EOR", AddressingMode.DirectPage, 2),
|
|
1862
|
-
82: new OpCode(82, "EOR", AddressingMode.DirectPageIndirect, 2),
|
|
1863
|
-
71: new OpCode(71, "EOR", AddressingMode.DirectPageIndirectLong, 2),
|
|
1864
|
-
93: new OpCode(93, "EOR", AddressingMode.AbsoluteIndexedX, 3),
|
|
1865
|
-
95: new OpCode(95, "EOR", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1866
|
-
89: new OpCode(89, "EOR", AddressingMode.AbsoluteIndexedY, 3),
|
|
1867
|
-
85: new OpCode(85, "EOR", AddressingMode.DirectPageIndexedX, 2),
|
|
1868
|
-
65: new OpCode(65, "EOR", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1869
|
-
81: new OpCode(81, "EOR", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1870
|
-
87: new OpCode(87, "EOR", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1871
|
-
67: new OpCode(67, "EOR", AddressingMode.StackRelative, 2),
|
|
1872
|
-
83: new OpCode(83, "EOR", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1873
|
-
// INC - Increment
|
|
1874
|
-
26: new OpCode(26, "INC", AddressingMode.Accumulator, 1),
|
|
1875
|
-
238: new OpCode(238, "INC", AddressingMode.Absolute, 3),
|
|
1876
|
-
230: new OpCode(230, "INC", AddressingMode.DirectPage, 2),
|
|
1877
|
-
254: new OpCode(254, "INC", AddressingMode.AbsoluteIndexedX, 3),
|
|
1878
|
-
246: new OpCode(246, "INC", AddressingMode.DirectPageIndexedX, 2),
|
|
1879
|
-
// INX/INY - Increment Index Registers
|
|
1880
|
-
232: new OpCode(232, "INX", AddressingMode.Implied, 1),
|
|
1881
|
-
200: new OpCode(200, "INY", AddressingMode.Implied, 1),
|
|
1882
|
-
// JMP/JML - Jump
|
|
1883
|
-
76: new OpCode(76, "JMP", AddressingMode.Absolute, 3),
|
|
1884
|
-
108: new OpCode(108, "JMP", AddressingMode.AbsoluteIndirect, 3),
|
|
1885
|
-
124: new OpCode(124, "JMP", AddressingMode.AbsoluteIndexedIndirect, 3),
|
|
1886
|
-
92: new OpCode(92, "JML", AddressingMode.AbsoluteLong, 4),
|
|
1887
|
-
220: new OpCode(220, "JML", AddressingMode.AbsoluteIndirectLong, 3),
|
|
1888
|
-
// JSR/JSL - Jump to Subroutine
|
|
1889
|
-
32: new OpCode(32, "JSR", AddressingMode.Absolute, 3),
|
|
1890
|
-
252: new OpCode(252, "JSR", AddressingMode.AbsoluteIndexedIndirect, 3),
|
|
1891
|
-
34: new OpCode(34, "JSL", AddressingMode.AbsoluteLong, 4),
|
|
1892
|
-
// LDA - Load Accumulator
|
|
1893
|
-
169: new OpCode(169, "LDA", AddressingMode.Immediate, -2),
|
|
1894
|
-
173: new OpCode(173, "LDA", AddressingMode.Absolute, 3),
|
|
1895
|
-
175: new OpCode(175, "LDA", AddressingMode.AbsoluteLong, 4),
|
|
1896
|
-
165: new OpCode(165, "LDA", AddressingMode.DirectPage, 2),
|
|
1897
|
-
178: new OpCode(178, "LDA", AddressingMode.DirectPageIndirect, 2),
|
|
1898
|
-
167: new OpCode(167, "LDA", AddressingMode.DirectPageIndirectLong, 2),
|
|
1899
|
-
189: new OpCode(189, "LDA", AddressingMode.AbsoluteIndexedX, 3),
|
|
1900
|
-
191: new OpCode(191, "LDA", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1901
|
-
185: new OpCode(185, "LDA", AddressingMode.AbsoluteIndexedY, 3),
|
|
1902
|
-
181: new OpCode(181, "LDA", AddressingMode.DirectPageIndexedX, 2),
|
|
1903
|
-
161: new OpCode(161, "LDA", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1904
|
-
177: new OpCode(177, "LDA", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1905
|
-
183: new OpCode(183, "LDA", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1906
|
-
163: new OpCode(163, "LDA", AddressingMode.StackRelative, 2),
|
|
1907
|
-
179: new OpCode(179, "LDA", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1908
|
-
// LDX - Load X Register
|
|
1909
|
-
162: new OpCode(162, "LDX", AddressingMode.Immediate, -2),
|
|
1910
|
-
174: new OpCode(174, "LDX", AddressingMode.Absolute, 3),
|
|
1911
|
-
166: new OpCode(166, "LDX", AddressingMode.DirectPage, 2),
|
|
1912
|
-
190: new OpCode(190, "LDX", AddressingMode.AbsoluteIndexedY, 3),
|
|
1913
|
-
182: new OpCode(182, "LDX", AddressingMode.DirectPageIndexedY, 2),
|
|
1914
|
-
// LDY - Load Y Register
|
|
1915
|
-
160: new OpCode(160, "LDY", AddressingMode.Immediate, -2),
|
|
1916
|
-
172: new OpCode(172, "LDY", AddressingMode.Absolute, 3),
|
|
1917
|
-
164: new OpCode(164, "LDY", AddressingMode.DirectPage, 2),
|
|
1918
|
-
188: new OpCode(188, "LDY", AddressingMode.AbsoluteIndexedX, 3),
|
|
1919
|
-
180: new OpCode(180, "LDY", AddressingMode.DirectPageIndexedX, 2),
|
|
1920
|
-
// LSR - Logical Shift Right
|
|
1921
|
-
74: new OpCode(74, "LSR", AddressingMode.Accumulator, 1),
|
|
1922
|
-
78: new OpCode(78, "LSR", AddressingMode.Absolute, 3),
|
|
1923
|
-
70: new OpCode(70, "LSR", AddressingMode.DirectPage, 2),
|
|
1924
|
-
94: new OpCode(94, "LSR", AddressingMode.AbsoluteIndexedX, 3),
|
|
1925
|
-
86: new OpCode(86, "LSR", AddressingMode.DirectPageIndexedX, 2),
|
|
1926
|
-
// MVN/MVP - Block Move
|
|
1927
|
-
84: new OpCode(84, "MVN", AddressingMode.BlockMove, 3),
|
|
1928
|
-
68: new OpCode(68, "MVP", AddressingMode.BlockMove, 3),
|
|
1929
|
-
// NOP - No Operation
|
|
1930
|
-
234: new OpCode(234, "NOP", AddressingMode.Implied, 1),
|
|
1931
|
-
// ORA - Logical OR
|
|
1932
|
-
9: new OpCode(9, "ORA", AddressingMode.Immediate, -2),
|
|
1933
|
-
13: new OpCode(13, "ORA", AddressingMode.Absolute, 3),
|
|
1934
|
-
15: new OpCode(15, "ORA", AddressingMode.AbsoluteLong, 4),
|
|
1935
|
-
5: new OpCode(5, "ORA", AddressingMode.DirectPage, 2),
|
|
1936
|
-
18: new OpCode(18, "ORA", AddressingMode.DirectPageIndirect, 2),
|
|
1937
|
-
7: new OpCode(7, "ORA", AddressingMode.DirectPageIndirectLong, 2),
|
|
1938
|
-
29: new OpCode(29, "ORA", AddressingMode.AbsoluteIndexedX, 3),
|
|
1939
|
-
31: new OpCode(31, "ORA", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1940
|
-
25: new OpCode(25, "ORA", AddressingMode.AbsoluteIndexedY, 3),
|
|
1941
|
-
21: new OpCode(21, "ORA", AddressingMode.DirectPageIndexedX, 2),
|
|
1942
|
-
1: new OpCode(1, "ORA", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1943
|
-
17: new OpCode(17, "ORA", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1944
|
-
23: new OpCode(23, "ORA", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1945
|
-
3: new OpCode(3, "ORA", AddressingMode.StackRelative, 2),
|
|
1946
|
-
19: new OpCode(19, "ORA", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1947
|
-
// Push instructions
|
|
1948
|
-
244: new OpCode(244, "PEA", AddressingMode.Absolute, 3),
|
|
1949
|
-
212: new OpCode(212, "PEI", AddressingMode.DirectPageIndirect, 2),
|
|
1950
|
-
98: new OpCode(98, "PER", AddressingMode.PCRelativeLong, 3),
|
|
1951
|
-
72: new OpCode(72, "PHA", AddressingMode.Stack, 1),
|
|
1952
|
-
139: new OpCode(139, "PHB", AddressingMode.Stack, 1),
|
|
1953
|
-
11: new OpCode(11, "PHD", AddressingMode.Stack, 1),
|
|
1954
|
-
75: new OpCode(75, "PHK", AddressingMode.Stack, 1),
|
|
1955
|
-
8: new OpCode(8, "PHP", AddressingMode.Stack, 1),
|
|
1956
|
-
218: new OpCode(218, "PHX", AddressingMode.Stack, 1),
|
|
1957
|
-
90: new OpCode(90, "PHY", AddressingMode.Stack, 1),
|
|
1958
|
-
// Pull instructions
|
|
1959
|
-
104: new OpCode(104, "PLA", AddressingMode.Stack, 1),
|
|
1960
|
-
171: new OpCode(171, "PLB", AddressingMode.Stack, 1),
|
|
1961
|
-
43: new OpCode(43, "PLD", AddressingMode.Stack, 1),
|
|
1962
|
-
40: new OpCode(40, "PLP", AddressingMode.Stack, 1),
|
|
1963
|
-
250: new OpCode(250, "PLX", AddressingMode.Stack, 1),
|
|
1964
|
-
122: new OpCode(122, "PLY", AddressingMode.Stack, 1),
|
|
1965
|
-
// REP - Reset Status Bits
|
|
1966
|
-
194: new OpCode(194, "REP", AddressingMode.Immediate, 2),
|
|
1967
|
-
// ROL - Rotate Left
|
|
1968
|
-
42: new OpCode(42, "ROL", AddressingMode.Accumulator, 1),
|
|
1969
|
-
46: new OpCode(46, "ROL", AddressingMode.Absolute, 3),
|
|
1970
|
-
38: new OpCode(38, "ROL", AddressingMode.DirectPage, 2),
|
|
1971
|
-
62: new OpCode(62, "ROL", AddressingMode.AbsoluteIndexedX, 3),
|
|
1972
|
-
54: new OpCode(54, "ROL", AddressingMode.DirectPageIndexedX, 2),
|
|
1973
|
-
// ROR - Rotate Right
|
|
1974
|
-
106: new OpCode(106, "ROR", AddressingMode.Accumulator, 1),
|
|
1975
|
-
110: new OpCode(110, "ROR", AddressingMode.Absolute, 3),
|
|
1976
|
-
102: new OpCode(102, "ROR", AddressingMode.DirectPage, 2),
|
|
1977
|
-
126: new OpCode(126, "ROR", AddressingMode.AbsoluteIndexedX, 3),
|
|
1978
|
-
118: new OpCode(118, "ROR", AddressingMode.DirectPageIndexedX, 2),
|
|
1979
|
-
// Return instructions
|
|
1980
|
-
64: new OpCode(64, "RTI", AddressingMode.Stack, 1),
|
|
1981
|
-
107: new OpCode(107, "RTL", AddressingMode.Stack, 1),
|
|
1982
|
-
96: new OpCode(96, "RTS", AddressingMode.Stack, 1),
|
|
1983
|
-
// SBC - Subtract with Carry
|
|
1984
|
-
233: new OpCode(233, "SBC", AddressingMode.Immediate, -2),
|
|
1985
|
-
237: new OpCode(237, "SBC", AddressingMode.Absolute, 3),
|
|
1986
|
-
239: new OpCode(239, "SBC", AddressingMode.AbsoluteLong, 4),
|
|
1987
|
-
229: new OpCode(229, "SBC", AddressingMode.DirectPage, 2),
|
|
1988
|
-
242: new OpCode(242, "SBC", AddressingMode.DirectPageIndirect, 2),
|
|
1989
|
-
231: new OpCode(231, "SBC", AddressingMode.DirectPageIndirectLong, 2),
|
|
1990
|
-
253: new OpCode(253, "SBC", AddressingMode.AbsoluteIndexedX, 3),
|
|
1991
|
-
255: new OpCode(255, "SBC", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
1992
|
-
249: new OpCode(249, "SBC", AddressingMode.AbsoluteIndexedY, 3),
|
|
1993
|
-
245: new OpCode(245, "SBC", AddressingMode.DirectPageIndexedX, 2),
|
|
1994
|
-
225: new OpCode(225, "SBC", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
1995
|
-
241: new OpCode(241, "SBC", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
1996
|
-
247: new OpCode(247, "SBC", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
1997
|
-
227: new OpCode(227, "SBC", AddressingMode.StackRelative, 2),
|
|
1998
|
-
243: new OpCode(243, "SBC", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
1999
|
-
// Set flag instructions
|
|
2000
|
-
56: new OpCode(56, "SEC", AddressingMode.Implied, 1),
|
|
2001
|
-
248: new OpCode(248, "SED", AddressingMode.Implied, 1),
|
|
2002
|
-
120: new OpCode(120, "SEI", AddressingMode.Implied, 1),
|
|
2003
|
-
226: new OpCode(226, "SEP", AddressingMode.Immediate, 2),
|
|
2004
|
-
// STA - Store Accumulator
|
|
2005
|
-
141: new OpCode(141, "STA", AddressingMode.Absolute, 3),
|
|
2006
|
-
143: new OpCode(143, "STA", AddressingMode.AbsoluteLong, 4),
|
|
2007
|
-
133: new OpCode(133, "STA", AddressingMode.DirectPage, 2),
|
|
2008
|
-
146: new OpCode(146, "STA", AddressingMode.DirectPageIndirect, 2),
|
|
2009
|
-
135: new OpCode(135, "STA", AddressingMode.DirectPageIndirectLong, 2),
|
|
2010
|
-
157: new OpCode(157, "STA", AddressingMode.AbsoluteIndexedX, 3),
|
|
2011
|
-
159: new OpCode(159, "STA", AddressingMode.AbsoluteLongIndexedX, 4),
|
|
2012
|
-
153: new OpCode(153, "STA", AddressingMode.AbsoluteIndexedY, 3),
|
|
2013
|
-
149: new OpCode(149, "STA", AddressingMode.DirectPageIndexedX, 2),
|
|
2014
|
-
129: new OpCode(129, "STA", AddressingMode.DirectPageIndexedIndirectX, 2),
|
|
2015
|
-
145: new OpCode(145, "STA", AddressingMode.DirectPageIndirectIndexedY, 2),
|
|
2016
|
-
151: new OpCode(151, "STA", AddressingMode.DirectPageIndirectLongIndexedY, 2),
|
|
2017
|
-
131: new OpCode(131, "STA", AddressingMode.StackRelative, 2),
|
|
2018
|
-
147: new OpCode(147, "STA", AddressingMode.StackRelativeIndirectIndexedY, 2),
|
|
2019
|
-
// STP - Stop
|
|
2020
|
-
219: new OpCode(219, "STP", AddressingMode.Implied, 1),
|
|
2021
|
-
// STX - Store X Register
|
|
2022
|
-
142: new OpCode(142, "STX", AddressingMode.Absolute, 3),
|
|
2023
|
-
134: new OpCode(134, "STX", AddressingMode.DirectPage, 2),
|
|
2024
|
-
150: new OpCode(150, "STX", AddressingMode.DirectPageIndexedY, 2),
|
|
2025
|
-
// STY - Store Y Register
|
|
2026
|
-
140: new OpCode(140, "STY", AddressingMode.Absolute, 3),
|
|
2027
|
-
132: new OpCode(132, "STY", AddressingMode.DirectPage, 2),
|
|
2028
|
-
148: new OpCode(148, "STY", AddressingMode.DirectPageIndexedX, 2),
|
|
2029
|
-
// STZ - Store Zero
|
|
2030
|
-
156: new OpCode(156, "STZ", AddressingMode.Absolute, 3),
|
|
2031
|
-
100: new OpCode(100, "STZ", AddressingMode.DirectPage, 2),
|
|
2032
|
-
158: new OpCode(158, "STZ", AddressingMode.AbsoluteIndexedX, 3),
|
|
2033
|
-
116: new OpCode(116, "STZ", AddressingMode.DirectPageIndexedX, 2),
|
|
2034
|
-
// Transfer instructions
|
|
2035
|
-
170: new OpCode(170, "TAX", AddressingMode.Implied, 1),
|
|
2036
|
-
168: new OpCode(168, "TAY", AddressingMode.Implied, 1),
|
|
2037
|
-
91: new OpCode(91, "TCD", AddressingMode.Implied, 1),
|
|
2038
|
-
27: new OpCode(27, "TCS", AddressingMode.Implied, 1),
|
|
2039
|
-
123: new OpCode(123, "TDC", AddressingMode.Implied, 1),
|
|
2040
|
-
28: new OpCode(28, "TRB", AddressingMode.Absolute, 3),
|
|
2041
|
-
20: new OpCode(20, "TRB", AddressingMode.DirectPage, 2),
|
|
2042
|
-
12: new OpCode(12, "TSB", AddressingMode.Absolute, 3),
|
|
2043
|
-
4: new OpCode(4, "TSB", AddressingMode.DirectPage, 2),
|
|
2044
|
-
59: new OpCode(59, "TSC", AddressingMode.Implied, 1),
|
|
2045
|
-
186: new OpCode(186, "TSX", AddressingMode.Implied, 1),
|
|
2046
|
-
138: new OpCode(138, "TXA", AddressingMode.Implied, 1),
|
|
2047
|
-
154: new OpCode(154, "TXS", AddressingMode.Implied, 1),
|
|
2048
|
-
155: new OpCode(155, "TXY", AddressingMode.Implied, 1),
|
|
2049
|
-
152: new OpCode(152, "TYA", AddressingMode.Implied, 1),
|
|
2050
|
-
187: new OpCode(187, "TYX", AddressingMode.Implied, 1),
|
|
2051
|
-
// Miscellaneous
|
|
2052
|
-
203: new OpCode(203, "WAI", AddressingMode.Implied, 1),
|
|
2053
|
-
66: new OpCode(66, "WDM", AddressingMode.Implied, 1),
|
|
2054
|
-
235: new OpCode(235, "XBA", AddressingMode.Implied, 1),
|
|
2055
|
-
251: new OpCode(251, "XCE", AddressingMode.Implied, 1)
|
|
2056
|
-
};
|
|
2057
|
-
var GROUPED_OPCODES = {};
|
|
2058
|
-
for (const opcode of Object.values(ALL_OPCODES)) {
|
|
2059
|
-
if (!GROUPED_OPCODES[opcode.mnem]) {
|
|
2060
|
-
GROUPED_OPCODES[opcode.mnem] = [];
|
|
2061
|
-
}
|
|
2062
|
-
GROUPED_OPCODES[opcode.mnem].push(opcode);
|
|
2063
|
-
}
|
|
2064
|
-
var ADDRESSING_REGEX = {
|
|
2065
|
-
[AddressingMode.DirectPageIndexedIndirectX]: /^\(\$([A-Fa-f0-9]{2}),\s?[Xx]\)$/,
|
|
2066
|
-
[AddressingMode.StackRelative]: /^\$([A-Fa-f0-9]{2}),\s?[Ss]$/,
|
|
2067
|
-
[AddressingMode.StackInterrupt]: /^#\$([A-Fa-f0-9]{2})$/,
|
|
2068
|
-
[AddressingMode.DirectPage]: /^\$([A-Fa-f0-9]{2})$/,
|
|
2069
|
-
[AddressingMode.DirectPageIndirectLong]: /^\[\$([A-Fa-f0-9]{2})\]$/,
|
|
2070
|
-
[AddressingMode.Immediate]: /^#(\$[A-Fa-f0-9]{2,4}|\$?[&^*][A-Za-z0-9-+_]+)$/,
|
|
2071
|
-
[AddressingMode.Absolute]: /^\$([A-Fa-f0-9]{4}|&[A-Za-z0-9-+_]+)$/,
|
|
2072
|
-
[AddressingMode.AbsoluteLong]: /^\$([A-Fa-f0-9]{6}|\@[A-Za-z0-9-+_]+)$/,
|
|
2073
|
-
[AddressingMode.DirectPageIndirectIndexedY]: /^\(\$([A-Fa-f0-9]{2})\),\s?[Yy]$/,
|
|
2074
|
-
[AddressingMode.DirectPageIndirect]: /^\(\$([A-Fa-f0-9]{2})\)$/,
|
|
2075
|
-
[AddressingMode.StackRelativeIndirectIndexedY]: /^\(\$([A-Fa-f0-9]{2}),\s?[Ss]\),\s?[Yy]$/,
|
|
2076
|
-
[AddressingMode.DirectPageIndexedX]: /^\$([A-Fa-f0-9]{2}),\s?[Xx]$/,
|
|
2077
|
-
[AddressingMode.DirectPageIndirectLongIndexedY]: /^\[\$([A-Fa-f0-9]{2})\],\s?[Yy]$/,
|
|
2078
|
-
[AddressingMode.AbsoluteIndexedY]: /^(\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s?[Yy]$/,
|
|
2079
|
-
[AddressingMode.AbsoluteIndexedX]: /^(\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s?[Xx]$/,
|
|
2080
|
-
[AddressingMode.AbsoluteLongIndexedX]: /^(\$[A-Fa-f0-9]{6}|\$?@[A-Za-z0-9-+_]+),\s?[Xx]$/,
|
|
2081
|
-
[AddressingMode.AbsoluteIndexedIndirect]: /^\((\$[A-Fa-f0-9]{4}|\$?&[A-Za-z0-9-+_]+),\s*[Xx]\)$/,
|
|
2082
|
-
[AddressingMode.BlockMove]: /^#\$([A-Fa-f0-9]{2}|\^[A-Za-z0-9-+_]+),\s?#\$([A-Fa-f0-9]{2}|\^[A-Za-z0-9-+_]+)$/,
|
|
2083
|
-
// Default entries for other addressing modes
|
|
2084
|
-
[AddressingMode.Implied]: /^$/,
|
|
2085
|
-
[AddressingMode.Accumulator]: /^[Aa]$/,
|
|
2086
|
-
[AddressingMode.PCRelative]: /^/,
|
|
2087
|
-
[AddressingMode.PCRelativeLong]: /^/,
|
|
2088
|
-
[AddressingMode.AbsoluteIndirect]: /^/,
|
|
2089
|
-
[AddressingMode.AbsoluteIndirectLong]: /^/,
|
|
2090
|
-
[AddressingMode.DirectPageIndexedY]: /^/,
|
|
2091
|
-
[AddressingMode.Stack]: /^/
|
|
2092
|
-
};
|
|
2093
|
-
var HEX_REGEX = /[^A-Fa-f0-9]/g;
|
|
2094
|
-
var OpCodeUtils = class {
|
|
2095
|
-
/**
|
|
2096
|
-
* Get all available opcodes
|
|
2097
|
-
*/
|
|
2098
|
-
static getAllOpcodes() {
|
|
2099
|
-
return Object.values(ALL_OPCODES);
|
|
1450
|
+
registerCompressionProviders();
|
|
1451
|
+
var BlockReader = class {
|
|
1452
|
+
constructor(romData, root) {
|
|
1453
|
+
// Current Processing State (Database)
|
|
1454
|
+
//public _currentBlock!: DbBlock;
|
|
1455
|
+
//public _currentPart: DbPart | null = null;
|
|
1456
|
+
this._partEnd = 0;
|
|
1457
|
+
// ChunkFile Processing State
|
|
1458
|
+
this._currentChunk = null;
|
|
1459
|
+
this._currentAsmBlock = null;
|
|
1460
|
+
this._enrichedChunks = [];
|
|
1461
|
+
this._romDataReader = new RomDataReader(romData);
|
|
1462
|
+
this._stateManager = new ProcessorStateManager();
|
|
1463
|
+
this._referenceManager = new ReferenceManager(root);
|
|
1464
|
+
this._root = root;
|
|
1465
|
+
this._stringReader = new StringReader(this);
|
|
1466
|
+
this._asmReader = new AsmReader(this);
|
|
1467
|
+
this._typeParser = new TypeParser(this);
|
|
1468
|
+
this.initializeOverrides();
|
|
1469
|
+
this.initializeFileReferences();
|
|
2100
1470
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
static getByMnemonic(mnemonic) {
|
|
2105
|
-
return GROUPED_OPCODES[mnemonic] || [];
|
|
1471
|
+
static {
|
|
1472
|
+
// Constants
|
|
1473
|
+
this.REF_SEARCH_MAX_RANGE = 416;
|
|
2106
1474
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
*/
|
|
2110
|
-
static findByCode(code) {
|
|
2111
|
-
return ALL_OPCODES[code];
|
|
1475
|
+
static {
|
|
1476
|
+
this.BANK_MASK_CHECK = 64;
|
|
2112
1477
|
}
|
|
2113
|
-
};
|
|
2114
|
-
|
|
2115
|
-
// src/rom/extraction/asm.ts
|
|
2116
|
-
var AsmReader = class _AsmReader {
|
|
2117
1478
|
static {
|
|
2118
|
-
|
|
2119
|
-
this.ACCUMULATOR_OP_MASK = 15;
|
|
1479
|
+
this.BYTE_DELIMITER_THRESHOLD = 256;
|
|
2120
1480
|
}
|
|
2121
1481
|
static {
|
|
2122
|
-
this.
|
|
1482
|
+
this.BANK_HIGH_MEMORY_1 = 126;
|
|
2123
1483
|
}
|
|
2124
1484
|
static {
|
|
2125
|
-
this.
|
|
1485
|
+
this.BANK_HIGH_MEMORY_2 = 127;
|
|
2126
1486
|
}
|
|
2127
1487
|
static {
|
|
2128
|
-
this.
|
|
1488
|
+
this.POINTER_CHARACTERS = ["&", "@"];
|
|
2129
1489
|
}
|
|
2130
1490
|
static {
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
constructor(blockReader) {
|
|
2134
|
-
this._blockReader = blockReader;
|
|
2135
|
-
this._transformProcessor = new TransformProcessor(blockReader);
|
|
2136
|
-
this._addressingModeHandler = new AddressingModeHandler(blockReader, this._transformProcessor);
|
|
2137
|
-
this._romDataReader = blockReader._romDataReader;
|
|
2138
|
-
}
|
|
2139
|
-
parseAsm(reg) {
|
|
2140
|
-
const opStart = this._romDataReader.position;
|
|
2141
|
-
const opCode = this._romDataReader.readByte();
|
|
2142
|
-
const code = this._blockReader._root.opCodes[opCode];
|
|
2143
|
-
if (!code) {
|
|
2144
|
-
throw new Error("Unknown OpCode");
|
|
2145
|
-
}
|
|
2146
|
-
const operationContext = this.initializeOperation(code, reg, opStart);
|
|
2147
|
-
const operands = this._addressingModeHandler.processAddressingMode(code, operationContext, reg);
|
|
2148
|
-
this._transformProcessor.applyTransforms(operationContext.xForm1, operationContext.xForm2, operands);
|
|
2149
|
-
const op = new Op(
|
|
2150
|
-
code,
|
|
2151
|
-
opStart,
|
|
2152
|
-
operands,
|
|
2153
|
-
this._romDataReader.position - opStart
|
|
2154
|
-
);
|
|
2155
|
-
if (operationContext.copDef) {
|
|
2156
|
-
op.copDef = operationContext.copDef;
|
|
2157
|
-
}
|
|
2158
|
-
return op;
|
|
2159
|
-
}
|
|
2160
|
-
clearDestinationRegister(code, reg) {
|
|
2161
|
-
switch (code.mnem) {
|
|
2162
|
-
case "LDA":
|
|
2163
|
-
reg.accumulator = void 0;
|
|
2164
|
-
break;
|
|
2165
|
-
case "LDX":
|
|
2166
|
-
reg.xIndex = void 0;
|
|
2167
|
-
break;
|
|
2168
|
-
case "LDY":
|
|
2169
|
-
reg.yIndex = void 0;
|
|
2170
|
-
break;
|
|
2171
|
-
}
|
|
2172
|
-
}
|
|
2173
|
-
initializeOperation(code, reg, loc) {
|
|
2174
|
-
const size = this.calculateInstructionSize(code, reg);
|
|
2175
|
-
const next = loc + size;
|
|
2176
|
-
this.clearDestinationRegister(code, reg);
|
|
2177
|
-
const context = new OperationContext();
|
|
2178
|
-
context.size = size;
|
|
2179
|
-
context.nextAddress = next;
|
|
2180
|
-
context.xForm1 = this._transformProcessor.getTransform();
|
|
2181
|
-
context.xForm2 = null;
|
|
2182
|
-
context.copDef = null;
|
|
2183
|
-
return context;
|
|
2184
|
-
}
|
|
2185
|
-
calculateInstructionSize(code, reg) {
|
|
2186
|
-
let size = code.size;
|
|
2187
|
-
if (size === _AsmReader.VARIABLE_SIZE_INDICATOR) {
|
|
2188
|
-
if ((code.code & _AsmReader.ACCUMULATOR_OP_MASK) === _AsmReader.ACCUMULATOR_OP_VALUE) {
|
|
2189
|
-
size = reg.accumulatorFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
2190
|
-
} else {
|
|
2191
|
-
size = reg.indexFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
return size;
|
|
2195
|
-
}
|
|
2196
|
-
};
|
|
2197
|
-
var BlockReader = class {
|
|
2198
|
-
constructor(romData, root) {
|
|
2199
|
-
this._currentPart = null;
|
|
2200
|
-
this._partEnd = 0;
|
|
2201
|
-
this._romDataReader = new RomDataReader(romData);
|
|
2202
|
-
this._stateManager = new ProcessorStateManager();
|
|
2203
|
-
this._referenceManager = new ReferenceManager(root);
|
|
2204
|
-
this._root = root;
|
|
2205
|
-
this._stringReader = new StringReader(this);
|
|
2206
|
-
this._asmReader = new AsmReader(this);
|
|
2207
|
-
this._typeParser = new TypeParser(this);
|
|
2208
|
-
this.initializeOverrides();
|
|
2209
|
-
this.initializeFileReferences();
|
|
2210
|
-
}
|
|
2211
|
-
static {
|
|
2212
|
-
// Constants
|
|
2213
|
-
this.REF_SEARCH_MAX_RANGE = 416;
|
|
2214
|
-
}
|
|
2215
|
-
static {
|
|
2216
|
-
this.BANK_MASK_CHECK = 64;
|
|
2217
|
-
}
|
|
2218
|
-
static {
|
|
2219
|
-
this.BYTE_DELIMITER_THRESHOLD = 256;
|
|
2220
|
-
}
|
|
2221
|
-
static {
|
|
2222
|
-
this.BANK_HIGH_MEMORY_1 = 126;
|
|
2223
|
-
}
|
|
2224
|
-
static {
|
|
2225
|
-
this.BANK_HIGH_MEMORY_2 = 127;
|
|
2226
|
-
}
|
|
2227
|
-
static {
|
|
2228
|
-
this.POINTER_CHARACTERS = ["&", "@"];
|
|
2229
|
-
}
|
|
2230
|
-
static {
|
|
2231
|
-
// Location regex pattern: _([A-Fa-f0-9]{6})
|
|
2232
|
-
this.LOCATION_REGEX = /_([A-Fa-f0-9]{6})/;
|
|
1491
|
+
// Location regex pattern: _([A-Fa-f0-9]{6})
|
|
1492
|
+
this.LOCATION_REGEX = /_([A-Fa-f0-9]{6})/;
|
|
2233
1493
|
}
|
|
2234
1494
|
// Backward Compatibility Properties
|
|
2235
1495
|
get AccumulatorFlags() {
|
|
@@ -2288,9 +1548,6 @@ var BlockReader = class {
|
|
|
2288
1548
|
return;
|
|
2289
1549
|
}
|
|
2290
1550
|
let offset = addr.offset;
|
|
2291
|
-
if (offset === 1750) {
|
|
2292
|
-
console.log(this._root.mnemonics[offset]);
|
|
2293
|
-
}
|
|
2294
1551
|
const label = this._root.mnemonics[offset];
|
|
2295
1552
|
if (!label) {
|
|
2296
1553
|
return;
|
|
@@ -2303,9 +1560,7 @@ var BlockReader = class {
|
|
|
2303
1560
|
opnd = -opnd;
|
|
2304
1561
|
offset -= opnd;
|
|
2305
1562
|
}
|
|
2306
|
-
|
|
2307
|
-
this._currentBlock.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
|
|
2308
|
-
}
|
|
1563
|
+
this._currentChunk.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
|
|
2309
1564
|
}
|
|
2310
1565
|
/**
|
|
2311
1566
|
* Resolves name for a location (delegated to ReferenceManager)
|
|
@@ -2317,21 +1572,9 @@ var BlockReader = class {
|
|
|
2317
1572
|
* Resolves include for a location
|
|
2318
1573
|
*/
|
|
2319
1574
|
resolveInclude(loc, isBranch) {
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
for (const part of block.parts) {
|
|
2324
|
-
if (loc >= part.start && loc < part.end) {
|
|
2325
|
-
foundPart = part;
|
|
2326
|
-
break;
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
if (foundPart) break;
|
|
2330
|
-
}
|
|
2331
|
-
if (foundPart) {
|
|
2332
|
-
this._currentPart.includes = this._currentPart.includes || /* @__PURE__ */ new Set();
|
|
2333
|
-
this._currentPart.includes.add(foundPart);
|
|
2334
|
-
}
|
|
1575
|
+
const [outside, foundBlock, foundPart] = ChunkFileUtils.isOutsideWithPart(this._enrichedChunks, this._currentChunk, loc);
|
|
1576
|
+
if (outside && foundBlock && foundPart) {
|
|
1577
|
+
this._currentAsmBlock.includes.add({ block: foundBlock, part: foundPart });
|
|
2335
1578
|
} else if (isBranch && !this._referenceManager.tryGetName(loc).found) {
|
|
2336
1579
|
const name = `loc_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
|
|
2337
1580
|
this._referenceManager.tryAddName(loc, name);
|
|
@@ -2390,34 +1633,37 @@ var BlockReader = class {
|
|
|
2390
1633
|
partCanContinue() {
|
|
2391
1634
|
return this._romDataReader.position < this._partEnd && !this._referenceManager.containsStruct(this._romDataReader.position);
|
|
2392
1635
|
}
|
|
2393
|
-
/**
|
|
2394
|
-
* Main analysis entry point
|
|
2395
|
-
*/
|
|
2396
1636
|
analyzeAndResolve() {
|
|
2397
|
-
this.
|
|
1637
|
+
this.createChunkFilesFromDatabase();
|
|
1638
|
+
this.initializeBlocksAndParts();
|
|
1639
|
+
this.analyzeChunkFiles();
|
|
2398
1640
|
this.resolveReferences();
|
|
1641
|
+
return this._enrichedChunks;
|
|
2399
1642
|
}
|
|
2400
1643
|
/**
|
|
2401
1644
|
* Analyzes all blocks in the ROM
|
|
2402
1645
|
*/
|
|
2403
|
-
analyzeBlocks() {
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
}
|
|
1646
|
+
// private analyzeBlocks(): void {
|
|
1647
|
+
// this.initializeBlocksAndParts();
|
|
1648
|
+
// for (const chunk of this._enrichedChunks) {
|
|
1649
|
+
// this._currentChunk = chunk;
|
|
1650
|
+
// for (const part of chunk.parts || []) {
|
|
1651
|
+
// this.processPart(part);
|
|
1652
|
+
// }
|
|
1653
|
+
// }
|
|
1654
|
+
// }
|
|
2412
1655
|
/**
|
|
2413
1656
|
* Initializes blocks and parts with base references
|
|
2414
1657
|
*/
|
|
2415
1658
|
initializeBlocksAndParts() {
|
|
2416
|
-
for (const block of this.
|
|
2417
|
-
for (const part of block.parts) {
|
|
2418
|
-
part.
|
|
2419
|
-
|
|
2420
|
-
|
|
1659
|
+
for (const block of this._enrichedChunks) {
|
|
1660
|
+
for (const part of block.parts || []) {
|
|
1661
|
+
if (part.structName) {
|
|
1662
|
+
this._referenceManager.tryAddStruct(part.location, part.structName);
|
|
1663
|
+
}
|
|
1664
|
+
if (part.label) {
|
|
1665
|
+
this._referenceManager.tryAddName(part.location, part.label);
|
|
1666
|
+
}
|
|
2421
1667
|
}
|
|
2422
1668
|
}
|
|
2423
1669
|
}
|
|
@@ -2425,10 +1671,10 @@ var BlockReader = class {
|
|
|
2425
1671
|
* Processes a single part
|
|
2426
1672
|
*/
|
|
2427
1673
|
processPart(part) {
|
|
2428
|
-
this.
|
|
2429
|
-
this._romDataReader.position = part.
|
|
2430
|
-
this._partEnd = part.
|
|
2431
|
-
let current = part.
|
|
1674
|
+
this._currentAsmBlock = part;
|
|
1675
|
+
this._romDataReader.position = part.location;
|
|
1676
|
+
this._partEnd = part.location + part.size;
|
|
1677
|
+
let current = part.structName || BlockReaderConstants.BINARY_TYPE;
|
|
2432
1678
|
const chunks = [];
|
|
2433
1679
|
const reg = new Registers();
|
|
2434
1680
|
const bank = part.bank;
|
|
@@ -2445,7 +1691,7 @@ var BlockReader = class {
|
|
|
2445
1691
|
chunks.push(last);
|
|
2446
1692
|
this.processNewEntry(current, reg, bank, last);
|
|
2447
1693
|
}
|
|
2448
|
-
part.
|
|
1694
|
+
part.objList = chunks;
|
|
2449
1695
|
}
|
|
2450
1696
|
/**
|
|
2451
1697
|
* Processes a continuous entry (same type as previous)
|
|
@@ -2468,14 +1714,110 @@ var BlockReader = class {
|
|
|
2468
1714
|
last.object = res;
|
|
2469
1715
|
}
|
|
2470
1716
|
/**
|
|
2471
|
-
*
|
|
1717
|
+
* Creates ChunkFile objects from database structure
|
|
1718
|
+
*/
|
|
1719
|
+
createChunkFilesFromDatabase() {
|
|
1720
|
+
this._enrichedChunks = [];
|
|
1721
|
+
this.createChunkFilesFromSfx();
|
|
1722
|
+
this.createChunkFilesFromDbFiles();
|
|
1723
|
+
this.createChunkFilesFromDbBlocks();
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Creates binary ChunkFiles from DbFiles (enriched with rawData)
|
|
1727
|
+
*/
|
|
1728
|
+
createChunkFilesFromSfx() {
|
|
1729
|
+
let pos = this._root.config.sfxLocation;
|
|
1730
|
+
const count = this._root.config.sfxCount;
|
|
1731
|
+
const romData = this._romDataReader.romData;
|
|
1732
|
+
const getSize = () => romData[pos++] | romData[pos++] << 8;
|
|
1733
|
+
for (let i = 0; i < count; i++) {
|
|
1734
|
+
const size = getSize();
|
|
1735
|
+
const startPos = pos;
|
|
1736
|
+
let remaining = size;
|
|
1737
|
+
let end = pos + remaining;
|
|
1738
|
+
let data;
|
|
1739
|
+
if (end & RomProcessingConstants.PAGE_SIZE) {
|
|
1740
|
+
const endLen = end & 32767;
|
|
1741
|
+
remaining -= endLen;
|
|
1742
|
+
data = romData.slice(pos, pos + remaining);
|
|
1743
|
+
pos += remaining + RomProcessingConstants.PAGE_SIZE;
|
|
1744
|
+
end = pos + endLen;
|
|
1745
|
+
const data2 = romData.slice(pos, end);
|
|
1746
|
+
data = new Uint8Array([...data, ...data2]);
|
|
1747
|
+
} else {
|
|
1748
|
+
data = romData.slice(pos, end);
|
|
1749
|
+
}
|
|
1750
|
+
pos = end;
|
|
1751
|
+
const chunk = new ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, BinType.Sound);
|
|
1752
|
+
chunk.rawData = data;
|
|
1753
|
+
this._enrichedChunks.push(chunk);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Creates binary ChunkFiles from DbFiles (enriched with rawData)
|
|
2472
1758
|
*/
|
|
2473
|
-
|
|
1759
|
+
createChunkFilesFromDbFiles() {
|
|
1760
|
+
for (const dbFile of this._root.files) {
|
|
1761
|
+
const chunkFile = createChunkFileFromDbFile(this._romDataReader.romData, this._root.compression, dbFile);
|
|
1762
|
+
this._enrichedChunks.push(chunkFile);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Creates assembly ChunkFiles from DbBlocks (enriched with parts)
|
|
1767
|
+
*/
|
|
1768
|
+
createChunkFilesFromDbBlocks() {
|
|
2474
1769
|
for (const block of this._root.blocks) {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
1770
|
+
const chunkFile = createChunkFileFromDbBlock(block);
|
|
1771
|
+
this._enrichedChunks.push(chunkFile);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Analyzes only assembly ChunkFiles (those with parts from DbBlocks)
|
|
1776
|
+
*/
|
|
1777
|
+
analyzeChunkFiles() {
|
|
1778
|
+
const assemblyChunks = this._enrichedChunks.filter((chunk) => chunk.type === BinType.Assembly);
|
|
1779
|
+
for (const chunkFile of assemblyChunks) {
|
|
1780
|
+
this._currentChunk = chunkFile;
|
|
1781
|
+
for (const asmBlock of chunkFile.parts || []) {
|
|
1782
|
+
this._currentAsmBlock = asmBlock;
|
|
1783
|
+
this.processPart(asmBlock);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
// /**
|
|
1788
|
+
// * Processes a single AsmBlock (similar to processPart)
|
|
1789
|
+
// */
|
|
1790
|
+
// private processAsmBlock(asmBlock: AsmBlock): void {
|
|
1791
|
+
// this._romDataReader.position = asmBlock.location;
|
|
1792
|
+
// this._partEnd = asmBlock.location + asmBlock.size;
|
|
1793
|
+
// let current = asmBlock.structName || BlockReaderConstants.BINARY_TYPE;
|
|
1794
|
+
// const chunks: TableEntry[] = [];
|
|
1795
|
+
// const reg = new Registers();
|
|
1796
|
+
// const bank = this._currentAsmBlock?.bank;
|
|
1797
|
+
// let last: TableEntry | null = null;
|
|
1798
|
+
// while (this._romDataReader.position < this._partEnd) {
|
|
1799
|
+
// const structResult = this._referenceManager.tryGetStruct(this._romDataReader.position);
|
|
1800
|
+
// if (structResult.found) {
|
|
1801
|
+
// current = structResult.chunkType!;
|
|
1802
|
+
// } else if (last !== null) {
|
|
1803
|
+
// this.processContinuousEntry(current, reg, bank, last);
|
|
1804
|
+
// continue;
|
|
1805
|
+
// }
|
|
1806
|
+
// last = createTableEntry(this._romDataReader.position);
|
|
1807
|
+
// chunks.push(last);
|
|
1808
|
+
// this.processNewEntry(current, reg, bank, last);
|
|
1809
|
+
// }
|
|
1810
|
+
// asmBlock.objList = chunks;
|
|
1811
|
+
// }
|
|
1812
|
+
/**
|
|
1813
|
+
* Resolves references in assembly ChunkFiles only
|
|
1814
|
+
*/
|
|
1815
|
+
resolveReferences() {
|
|
1816
|
+
for (const chunkFile of this._enrichedChunks) {
|
|
1817
|
+
this._currentChunk = chunkFile;
|
|
1818
|
+
for (const asmBlock of chunkFile.parts || []) {
|
|
1819
|
+
this._currentAsmBlock = asmBlock;
|
|
1820
|
+
this.resolveObject(asmBlock.objList, false);
|
|
2479
1821
|
}
|
|
2480
1822
|
}
|
|
2481
1823
|
}
|
|
@@ -2536,7 +1878,7 @@ var BlockReader = class {
|
|
|
2536
1878
|
* Checks if an operation is a branch operation
|
|
2537
1879
|
*/
|
|
2538
1880
|
isBranchOperation(op) {
|
|
2539
|
-
return op.
|
|
1881
|
+
return op.mode === "PCRelative" || op.mode === "PCRelativeLong" || op.mnem[0] === "J";
|
|
2540
1882
|
}
|
|
2541
1883
|
/**
|
|
2542
1884
|
* Hydrates registers with stored state
|
|
@@ -2544,12 +1886,6 @@ var BlockReader = class {
|
|
|
2544
1886
|
hydrateRegisters(reg) {
|
|
2545
1887
|
this._stateManager.hydrateRegisters(this._romDataReader.position, reg);
|
|
2546
1888
|
}
|
|
2547
|
-
/**
|
|
2548
|
-
* PascalCase wrapper for hydrateRegisters (for C# compatibility)
|
|
2549
|
-
*/
|
|
2550
|
-
HydrateRegisters(reg) {
|
|
2551
|
-
this.hydrateRegisters(reg);
|
|
2552
|
-
}
|
|
2553
1889
|
};
|
|
2554
1890
|
var PostProcessor = class {
|
|
2555
1891
|
constructor(reader) {
|
|
@@ -2591,7 +1927,10 @@ var PostProcessor = class {
|
|
|
2591
1927
|
Lookup(block, keyIx, valueIx) {
|
|
2592
1928
|
const kix = parseInt(keyIx.trim());
|
|
2593
1929
|
const vix = parseInt(valueIx.trim());
|
|
2594
|
-
|
|
1930
|
+
if (!block.parts || block.parts.length === 0) {
|
|
1931
|
+
throw new Error("Invalid block structure for Lookup post process");
|
|
1932
|
+
}
|
|
1933
|
+
const table = block.parts[0]?.objList;
|
|
2595
1934
|
const tableEntry = table && table[0];
|
|
2596
1935
|
const entries = tableEntry?.object;
|
|
2597
1936
|
if (!tableEntry || !entries) {
|
|
@@ -2629,16 +1968,17 @@ var PostProcessor = class {
|
|
|
2629
1968
|
newParts.push({ location: loc, object: value });
|
|
2630
1969
|
this._referenceManager.nameTable.set(loc, name);
|
|
2631
1970
|
while (newList.length <= key) {
|
|
2632
|
-
newList.push(
|
|
1971
|
+
newList.push(new Word(0));
|
|
2633
1972
|
}
|
|
2634
1973
|
newList[key] = `&${name}`;
|
|
2635
1974
|
eIx++;
|
|
2636
1975
|
}
|
|
2637
|
-
block.parts[0].
|
|
1976
|
+
block.parts[0].objList = newParts;
|
|
2638
1977
|
}
|
|
2639
1978
|
};
|
|
2640
1979
|
|
|
2641
1980
|
// src/rom/extraction/writer.ts
|
|
1981
|
+
var NEWLINE = process.platform === "win32" ? "\r\n" : "\n";
|
|
2642
1982
|
var ObjectType = /* @__PURE__ */ ((ObjectType2) => {
|
|
2643
1983
|
ObjectType2["TableEntryArray"] = "TableEntryArray";
|
|
2644
1984
|
ObjectType2["StructDef"] = "StructDef";
|
|
@@ -2662,28 +2002,46 @@ var BlockWriter = class {
|
|
|
2662
2002
|
this._referenceManager = reader._referenceManager;
|
|
2663
2003
|
this._postProcessor = new PostProcessor(reader);
|
|
2664
2004
|
}
|
|
2665
|
-
async writeBlocks(outPath) {
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2005
|
+
// async writeBlocks(outPath: string): Promise<void> {
|
|
2006
|
+
// const res = DbRootUtils.getPath(this._root, BinType.Assembly);
|
|
2007
|
+
// const folderPath = join(outPath, res.folder);
|
|
2008
|
+
// for (const block of this._root.blocks) {
|
|
2009
|
+
// const groupedFolderPath = block.group
|
|
2010
|
+
// ? join(folderPath, block.group)
|
|
2011
|
+
// : folderPath;
|
|
2012
|
+
// await fs.mkdir(groupedFolderPath, { recursive: true });
|
|
2013
|
+
// const outFile = join(groupedFolderPath, `${block.name}.${res.extension}`);
|
|
2014
|
+
// // Check if file exists, skip if it does
|
|
2015
|
+
// try {
|
|
2016
|
+
// await fs.access(outFile);
|
|
2017
|
+
// continue;
|
|
2018
|
+
// } catch {
|
|
2019
|
+
// // File doesn't exist, continue with creation
|
|
2020
|
+
// }
|
|
2021
|
+
// const content = this.generateAsm(block);
|
|
2022
|
+
// await fs.writeFile(outFile, content);
|
|
2023
|
+
// }
|
|
2024
|
+
// }
|
|
2025
|
+
generateAllAsm(chunkFiles) {
|
|
2026
|
+
const lines = [];
|
|
2027
|
+
for (const block of chunkFiles) {
|
|
2028
|
+
lines.push({
|
|
2029
|
+
name: block.name,
|
|
2030
|
+
group: block.group,
|
|
2031
|
+
text: this.generateAsm(block)
|
|
2032
|
+
});
|
|
2679
2033
|
}
|
|
2034
|
+
return lines;
|
|
2680
2035
|
}
|
|
2681
2036
|
generateAsm(block) {
|
|
2037
|
+
if (!block.parts) {
|
|
2038
|
+
throw new Error("Invalid block structure for generateAsm");
|
|
2039
|
+
}
|
|
2682
2040
|
const lines = [];
|
|
2683
|
-
if (
|
|
2684
|
-
lines.push(`?BANK ${
|
|
2041
|
+
if (block.bank !== void 0) {
|
|
2042
|
+
lines.push(`?BANK ${block.bank.toString(16).toUpperCase().padStart(2, "0")}`);
|
|
2685
2043
|
}
|
|
2686
|
-
const includes =
|
|
2044
|
+
const includes = ChunkFileUtils.getIncludes(block);
|
|
2687
2045
|
if (includes && includes.length > 0) {
|
|
2688
2046
|
lines.push("");
|
|
2689
2047
|
for (const inc of includes) {
|
|
@@ -2699,15 +2057,15 @@ var BlockWriter = class {
|
|
|
2699
2057
|
}
|
|
2700
2058
|
}
|
|
2701
2059
|
this._postProcessor.process(block);
|
|
2702
|
-
for (const part of block.parts) {
|
|
2060
|
+
for (const part of block.parts || []) {
|
|
2703
2061
|
this._currentPart = part;
|
|
2704
2062
|
this._isInline = true;
|
|
2705
2063
|
lines.push("");
|
|
2706
2064
|
lines.push("---------------------------------------------");
|
|
2707
|
-
const objectLines = this.writeObject(part.
|
|
2065
|
+
const objectLines = this.writeObject(part.objList, -1);
|
|
2708
2066
|
lines.push(...objectLines);
|
|
2709
2067
|
}
|
|
2710
|
-
let content = lines.join(
|
|
2068
|
+
let content = lines.join(NEWLINE);
|
|
2711
2069
|
if (block.transforms) {
|
|
2712
2070
|
for (const x of block.transforms) {
|
|
2713
2071
|
if (x.key && x.value) {
|
|
@@ -2716,6 +2074,9 @@ var BlockWriter = class {
|
|
|
2716
2074
|
}
|
|
2717
2075
|
}
|
|
2718
2076
|
}
|
|
2077
|
+
if (process.platform !== "win32") {
|
|
2078
|
+
content = content.replace(/\r/g, "");
|
|
2079
|
+
}
|
|
2719
2080
|
return content;
|
|
2720
2081
|
}
|
|
2721
2082
|
getMnemonicsForBlock(block) {
|
|
@@ -2725,10 +2086,12 @@ var BlockWriter = class {
|
|
|
2725
2086
|
return Object.entries(block.mnemonics).map(([k, v]) => [v, parseInt(k, 10)]).sort((a, b) => a[1] - b[1]);
|
|
2726
2087
|
}
|
|
2727
2088
|
resolveOperand(op, obj, isBranch = false) {
|
|
2728
|
-
|
|
2089
|
+
if (obj instanceof TypedNumber) {
|
|
2090
|
+
const len = obj.size * 2;
|
|
2091
|
+
return obj.value.toString(16).toUpperCase().padStart(len, "0");
|
|
2092
|
+
}
|
|
2729
2093
|
if (typeof obj === "number") {
|
|
2730
|
-
if (op.
|
|
2731
|
-
if (op.code.mode === AddressingMode.Immediate) {
|
|
2094
|
+
if (op.mode === "Immediate") {
|
|
2732
2095
|
return obj;
|
|
2733
2096
|
}
|
|
2734
2097
|
return this._blockReader.resolveName(obj, AddressType.Address, isBranch);
|
|
@@ -2750,15 +2113,7 @@ var BlockWriter = class {
|
|
|
2750
2113
|
}
|
|
2751
2114
|
return addr.offset;
|
|
2752
2115
|
}
|
|
2753
|
-
if (obj &&
|
|
2754
|
-
const typed = obj;
|
|
2755
|
-
if (typed._tag === "Byte" || typed._tag === "Word" || typed._tag === "TypedNumber") {
|
|
2756
|
-
return obj;
|
|
2757
|
-
}
|
|
2758
|
-
if (typeof typed.value === "number" && op.code.mode !== AddressingMode.Immediate) {
|
|
2759
|
-
const resolved = this._blockReader.resolveName(typed.value, AddressType.Address, isBranch);
|
|
2760
|
-
return { ...typed, value: resolved };
|
|
2761
|
-
}
|
|
2116
|
+
if (obj && obj instanceof TypedNumber) {
|
|
2762
2117
|
return obj;
|
|
2763
2118
|
}
|
|
2764
2119
|
return obj;
|
|
@@ -2768,11 +2123,11 @@ var BlockWriter = class {
|
|
|
2768
2123
|
return "String" /* String */;
|
|
2769
2124
|
}
|
|
2770
2125
|
if (obj._tag) {
|
|
2771
|
-
if (obj._tag === "Byte" || obj._tag === "Word") {
|
|
2772
|
-
return "TypedNumber" /* TypedNumber */;
|
|
2773
|
-
}
|
|
2774
2126
|
return obj._tag;
|
|
2775
2127
|
}
|
|
2128
|
+
if (obj instanceof TypedNumber) {
|
|
2129
|
+
return "TypedNumber" /* TypedNumber */;
|
|
2130
|
+
}
|
|
2776
2131
|
if (Array.isArray(obj)) {
|
|
2777
2132
|
if (obj.length > 0) {
|
|
2778
2133
|
if (obj[0] && typeof obj[0] === "object" && "location" in obj[0] && "object" in obj[0]) {
|
|
@@ -2887,7 +2242,7 @@ var BlockWriter = class {
|
|
|
2887
2242
|
this._isInline = true;
|
|
2888
2243
|
for (const part of structObj.parts) {
|
|
2889
2244
|
const partLines = this.writeObject(part, depth);
|
|
2890
|
-
parts.push(partLines.join(
|
|
2245
|
+
parts.push(partLines.join(NEWLINE));
|
|
2891
2246
|
}
|
|
2892
2247
|
const line = `${structObj.name} < ${parts.join(", ")} >`;
|
|
2893
2248
|
this._isInline = isInline;
|
|
@@ -2909,7 +2264,7 @@ var BlockWriter = class {
|
|
|
2909
2264
|
lines.push(` ${labelResult.referenceName}:`);
|
|
2910
2265
|
}
|
|
2911
2266
|
}
|
|
2912
|
-
let opLine = ` ${op.
|
|
2267
|
+
let opLine = ` ${op.mnem} `;
|
|
2913
2268
|
if (op.copDef) {
|
|
2914
2269
|
opLine += `[${op.copDef.mnem}]`;
|
|
2915
2270
|
if (op.operands && op.operands.length > 1) {
|
|
@@ -2921,26 +2276,18 @@ var BlockWriter = class {
|
|
|
2921
2276
|
opLine += ` ( ${operandStrings.join(", ")} )`;
|
|
2922
2277
|
}
|
|
2923
2278
|
} else if (op.operands && op.operands.length > 0) {
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2279
|
+
const isBr = op.mnem[0] === "J" || op.mode === "PCRelative" || op.mode === "PCRelativeLong";
|
|
2280
|
+
const resolvedOperand = this.resolveOperand(op, op.operands[0], isBr);
|
|
2281
|
+
const format = this._root.addrLookup[op.mode]?.formatString;
|
|
2282
|
+
if (format) {
|
|
2283
|
+
let actualFormat = format;
|
|
2284
|
+
if (op.mode === "Immediate" && op.size === 3) {
|
|
2285
|
+
actualFormat = format.replace("X2", "X4");
|
|
2930
2286
|
}
|
|
2287
|
+
const resolvedSecondOperand = op.operands.length > 1 ? this.resolveOperand(op, op.operands[1], isBr) : void 0;
|
|
2288
|
+
opLine += this.formatOperand(actualFormat, [resolvedOperand, resolvedSecondOperand]);
|
|
2931
2289
|
} else {
|
|
2932
|
-
|
|
2933
|
-
const resolvedOperand = this.resolveOperand(op, op.operands[0], isBr);
|
|
2934
|
-
const format = this._root.config.asmFormats?.[op.code.mode];
|
|
2935
|
-
if (format) {
|
|
2936
|
-
let actualFormat = format;
|
|
2937
|
-
if (op.code.mode === AddressingMode.Immediate && op.size === 3) {
|
|
2938
|
-
actualFormat = format.replace("X2", "X4");
|
|
2939
|
-
}
|
|
2940
|
-
opLine += this.formatOperand(actualFormat, [resolvedOperand, ...op.operands.slice(1)]);
|
|
2941
|
-
} else {
|
|
2942
|
-
opLine += this.formatDefaultOperand(resolvedOperand, op.size);
|
|
2943
|
-
}
|
|
2290
|
+
opLine += this.formatDefaultOperand(resolvedOperand, op.size);
|
|
2944
2291
|
}
|
|
2945
2292
|
}
|
|
2946
2293
|
lines.push(opLine);
|
|
@@ -2953,7 +2300,7 @@ var BlockWriter = class {
|
|
|
2953
2300
|
if (typeof operand === "string") {
|
|
2954
2301
|
return operand;
|
|
2955
2302
|
}
|
|
2956
|
-
if (operand &&
|
|
2303
|
+
if (operand && operand instanceof TypedNumber) {
|
|
2957
2304
|
return this.formatTypedNumber(operand);
|
|
2958
2305
|
}
|
|
2959
2306
|
if (typeof operand === "number") {
|
|
@@ -3031,7 +2378,7 @@ var BlockWriter = class {
|
|
|
3031
2378
|
}
|
|
3032
2379
|
writeArray(arr, depth) {
|
|
3033
2380
|
const lines = [];
|
|
3034
|
-
const indent = " ".repeat(depth);
|
|
2381
|
+
const indent = " ".repeat(Math.max(0, depth));
|
|
3035
2382
|
const isInline = this._isInline;
|
|
3036
2383
|
lines.push("[");
|
|
3037
2384
|
this._isInline = false;
|
|
@@ -3056,16 +2403,10 @@ var BlockWriter = class {
|
|
|
3056
2403
|
return [`#$${num.toString(16).toUpperCase().padStart(6, "0")}`];
|
|
3057
2404
|
}
|
|
3058
2405
|
formatTypedNumber(num) {
|
|
3059
|
-
let size = 1;
|
|
3060
|
-
if ("size" in num) {
|
|
3061
|
-
size = num.size;
|
|
3062
|
-
} else if (num._tag === "Word") {
|
|
3063
|
-
size = 2;
|
|
3064
|
-
}
|
|
3065
2406
|
if (typeof num.value === "number") {
|
|
3066
|
-
const width = size * 2;
|
|
2407
|
+
const width = num.size * 2;
|
|
3067
2408
|
const hex = num.value.toString(16).toUpperCase().padStart(width, "0");
|
|
3068
|
-
return size === 1 ? `#${hex}` : `#$${hex}`;
|
|
2409
|
+
return num.size === 1 ? `#${hex}` : `#$${hex}`;
|
|
3069
2410
|
}
|
|
3070
2411
|
return `#${num.value}`;
|
|
3071
2412
|
}
|
|
@@ -3093,469 +2434,1590 @@ var BlockWriter = class {
|
|
|
3093
2434
|
});
|
|
3094
2435
|
}
|
|
3095
2436
|
};
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
this.
|
|
3101
|
-
this.
|
|
3102
|
-
this.
|
|
3103
|
-
this.
|
|
3104
|
-
this.
|
|
3105
|
-
this.
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
2437
|
+
var RomLayout = class _RomLayout {
|
|
2438
|
+
constructor(files) {
|
|
2439
|
+
this.bestResult = new Array(512).fill(0);
|
|
2440
|
+
this.bestSample = new Array(512).fill(0);
|
|
2441
|
+
this.currentBank = 0;
|
|
2442
|
+
this.currentUpper = false;
|
|
2443
|
+
this.bestDepth = 0;
|
|
2444
|
+
this.bestOffset = 0;
|
|
2445
|
+
this.bestRemain = 0;
|
|
2446
|
+
this.unmatchedFiles = Array.from(files).filter((x) => (x.size || 0) > 0).sort((a, b) => {
|
|
2447
|
+
const aAsm = a.parts ? 0 : 1;
|
|
2448
|
+
const bAsm = b.parts ? 0 : 1;
|
|
2449
|
+
if (aAsm !== bAsm) return aAsm - bAsm;
|
|
2450
|
+
if (b.size !== a.size) return b.size - a.size;
|
|
2451
|
+
if (a.location !== b.location) return a.location - b.location;
|
|
2452
|
+
return a.name.localeCompare(b.name);
|
|
2453
|
+
});
|
|
3111
2454
|
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
2455
|
+
static {
|
|
2456
|
+
this.MIN_ACCEPTED_REMAINING = 32;
|
|
2457
|
+
}
|
|
2458
|
+
organize() {
|
|
2459
|
+
for (let page = 0; page < 128; page++) {
|
|
2460
|
+
if (this.unmatchedFiles.length === 0) break;
|
|
2461
|
+
let remain = RomProcessingConstants.PAGE_SIZE;
|
|
2462
|
+
if (page === 1) remain -= RomProcessingConstants.SNES_HEADER_SIZE;
|
|
2463
|
+
this.currentUpper = (page & 1) !== 0;
|
|
2464
|
+
this.currentBank = page >> 1;
|
|
2465
|
+
this.bestDepth = 0;
|
|
2466
|
+
this.bestRemain = remain;
|
|
2467
|
+
this.bestOffset = 0;
|
|
2468
|
+
const start = page << 15;
|
|
2469
|
+
this.testDepth(0, 0, remain, this.currentUpper);
|
|
2470
|
+
if (this.currentUpper) {
|
|
2471
|
+
this.bestOffset = this.bestDepth;
|
|
2472
|
+
this.testDepth(0, this.bestDepth, this.bestRemain, false);
|
|
2473
|
+
}
|
|
2474
|
+
let position = start;
|
|
2475
|
+
for (let i = 0; i < this.bestDepth; ) {
|
|
2476
|
+
const file = this.unmatchedFiles[this.bestResult[i++]];
|
|
2477
|
+
file.location = position;
|
|
2478
|
+
console.log(` ${position.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
|
|
2479
|
+
position += file.size || 0;
|
|
2480
|
+
}
|
|
2481
|
+
console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with ${this.bestDepth} files ${this.bestRemain} remaining`);
|
|
2482
|
+
this.commitPage();
|
|
2483
|
+
}
|
|
2484
|
+
if (this.unmatchedFiles.length > 0) {
|
|
2485
|
+
const names = this.unmatchedFiles.map((x) => x.name).join("\r\n");
|
|
2486
|
+
throw new Error(`Unable to match ${this.unmatchedFiles.length} files\r
|
|
2487
|
+
${names}`);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
testDepth(startIndex, depth, remain, asmMode) {
|
|
2491
|
+
for (let fileIndex = startIndex; fileIndex < this.unmatchedFiles.length; fileIndex++) {
|
|
2492
|
+
const file = this.unmatchedFiles[fileIndex];
|
|
2493
|
+
const fileSize = file.size || 0;
|
|
2494
|
+
if (fileSize > remain) continue;
|
|
2495
|
+
if (file.parts) {
|
|
2496
|
+
if (!asmMode) {
|
|
2497
|
+
if (!this.currentUpper || (file.bank ?? -1) >= 0) continue;
|
|
2498
|
+
} else if (file.bank !== this.currentBank) {
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
} else if (asmMode) {
|
|
2502
|
+
continue;
|
|
2503
|
+
} else if (file.upper && !this.currentUpper) {
|
|
2504
|
+
continue;
|
|
2505
|
+
}
|
|
2506
|
+
let inList = false;
|
|
2507
|
+
for (let y = this.bestOffset; --y >= 0; ) {
|
|
2508
|
+
if (this.bestResult[y] === fileIndex) {
|
|
2509
|
+
inList = true;
|
|
2510
|
+
break;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
if (inList) continue;
|
|
2514
|
+
this.bestSample[depth] = fileIndex;
|
|
2515
|
+
const newRemain = remain - fileSize;
|
|
2516
|
+
if (newRemain < this.bestRemain) {
|
|
2517
|
+
this.bestRemain = newRemain;
|
|
2518
|
+
this.bestDepth = depth + 1;
|
|
2519
|
+
for (let i = this.bestOffset; i < this.bestDepth; i++) {
|
|
2520
|
+
this.bestResult[i] = this.bestSample[i];
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (newRemain < _RomLayout.MIN_ACCEPTED_REMAINING) return true;
|
|
2524
|
+
if (this.testDepth(fileIndex + 1, depth + 1, newRemain, asmMode)) return true;
|
|
3121
2525
|
}
|
|
2526
|
+
return true;
|
|
3122
2527
|
}
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
2528
|
+
commitPage() {
|
|
2529
|
+
if (this.bestOffset > 0) {
|
|
2530
|
+
for (let i = this.bestDepth; --i >= 0; ) {
|
|
2531
|
+
let lastY = 0;
|
|
2532
|
+
let lastX = 0;
|
|
2533
|
+
let y = 0;
|
|
2534
|
+
for (let x = this.bestDepth; --x >= 0; ) {
|
|
2535
|
+
y = this.bestResult[x];
|
|
2536
|
+
if (y > lastY) {
|
|
2537
|
+
lastY = y;
|
|
2538
|
+
lastX = x;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
this.bestResult[lastX] = 0;
|
|
2542
|
+
this.unmatchedFiles.splice(lastY, 1);
|
|
2543
|
+
}
|
|
2544
|
+
} else {
|
|
2545
|
+
for (let i = this.bestDepth; --i >= 0; ) {
|
|
2546
|
+
this.unmatchedFiles.splice(this.bestResult[i], 1);
|
|
3132
2547
|
}
|
|
3133
|
-
return new _ProjectRoot(config);
|
|
3134
|
-
} catch (error) {
|
|
3135
|
-
console.warn(`Failed to load project file: ${error}. Using directory-based configuration.`);
|
|
3136
2548
|
}
|
|
3137
|
-
const defaultConfig = {
|
|
3138
|
-
name: "GaiaLabs",
|
|
3139
|
-
baseDir: path,
|
|
3140
|
-
flipsPath: process?.env?.flips_path,
|
|
3141
|
-
romPath: process?.env?.rom_path || "",
|
|
3142
|
-
database: "us",
|
|
3143
|
-
databasePath: `${path}/db/us`,
|
|
3144
|
-
systemPath: `${path}/db/snes`,
|
|
3145
|
-
resources: {}
|
|
3146
|
-
};
|
|
3147
|
-
return new _ProjectRoot(defaultConfig);
|
|
3148
|
-
}
|
|
3149
|
-
/**
|
|
3150
|
-
* Build the ROM (simplified)
|
|
3151
|
-
*/
|
|
3152
|
-
async build() {
|
|
3153
|
-
throw new Error("ROM building not yet implemented");
|
|
3154
|
-
}
|
|
3155
|
-
/**
|
|
3156
|
-
* Dump database and extract ROM data
|
|
3157
|
-
*/
|
|
3158
|
-
async dumpDatabase() {
|
|
3159
|
-
const root = await DbRootUtils.fromFolder(this.databasePath, this.systemPath);
|
|
3160
|
-
const data = await readFileAsBinary(this.romPath);
|
|
3161
|
-
root.paths = this.resources;
|
|
3162
|
-
const fileReader = new FileReader(data, root, this.getCompression());
|
|
3163
|
-
await fileReader.extract(this.baseDir);
|
|
3164
|
-
const sfxReader = new SfxReader(data, root);
|
|
3165
|
-
await sfxReader.extract(this.baseDir);
|
|
3166
|
-
const blockReader = new BlockReader(data, root);
|
|
3167
|
-
blockReader.analyzeAndResolve();
|
|
3168
|
-
const blockWriter = new BlockWriter(blockReader);
|
|
3169
|
-
await blockWriter.writeBlocks(this.baseDir);
|
|
3170
|
-
return root;
|
|
3171
2549
|
}
|
|
3172
2550
|
};
|
|
3173
|
-
|
|
3174
|
-
// src/api/RomProcessor.ts
|
|
3175
2551
|
var RomProcessor = class _RomProcessor {
|
|
3176
|
-
constructor(
|
|
3177
|
-
this.
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
const
|
|
3187
|
-
|
|
2552
|
+
constructor(writer) {
|
|
2553
|
+
this.writer = writer;
|
|
2554
|
+
}
|
|
2555
|
+
async repack(allFiles) {
|
|
2556
|
+
const patches = allFiles.filter((x) => x.type === BinType.Patch);
|
|
2557
|
+
const asmFiles = allFiles.filter((x) => !!x.parts);
|
|
2558
|
+
_RomProcessor.applyPatches(asmFiles, patches);
|
|
2559
|
+
for (const asm of asmFiles) {
|
|
2560
|
+
ChunkFileUtils.calculateSize(asm);
|
|
2561
|
+
}
|
|
2562
|
+
const layout = new RomLayout(allFiles);
|
|
2563
|
+
layout.organize();
|
|
2564
|
+
for (const file of asmFiles) {
|
|
2565
|
+
ChunkFileUtils.rebase(file);
|
|
2566
|
+
}
|
|
2567
|
+
for (const f of asmFiles) {
|
|
2568
|
+
const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
|
|
2569
|
+
f.includeLookup = /* @__PURE__ */ new Map();
|
|
2570
|
+
for (const b of includeBlocks) {
|
|
2571
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
2572
|
+
}
|
|
2573
|
+
for (const b of (f.parts || []).filter((x) => !!x.label)) {
|
|
2574
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
const blockLookup = /* @__PURE__ */ new Map();
|
|
2578
|
+
for (const f of allFiles) {
|
|
2579
|
+
blockLookup.set(f.name.toUpperCase(), f.location);
|
|
2580
|
+
}
|
|
2581
|
+
for (const file of allFiles) {
|
|
2582
|
+
await this.writer.writeFile(file, blockLookup);
|
|
2583
|
+
}
|
|
2584
|
+
const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
|
|
2585
|
+
for (const ep of this.writer.entryPoints) {
|
|
2586
|
+
const match = entryBlocks.find((b) => b.label === ep.name);
|
|
2587
|
+
if (match) this.writer.writeTransform(ep.location, match.location & 65535);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
static applyPatches(asmFiles, patches) {
|
|
2591
|
+
for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
|
|
2592
|
+
let file = null;
|
|
2593
|
+
let dstIx = -1;
|
|
2594
|
+
const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
|
|
2595
|
+
for (let ix = 0; patch.parts && ix < patch.parts.length; ) {
|
|
2596
|
+
const block = patch.parts[ix];
|
|
2597
|
+
let match = null;
|
|
2598
|
+
if (block.label) {
|
|
2599
|
+
for (const i of inc) {
|
|
2600
|
+
if (!i.parts) continue;
|
|
2601
|
+
for (let y = 0; y < i.parts.length; y++) {
|
|
2602
|
+
const check = i.parts[y];
|
|
2603
|
+
if (check.label === block.label) {
|
|
2604
|
+
file = i;
|
|
2605
|
+
dstIx = y;
|
|
2606
|
+
match = check;
|
|
2607
|
+
break;
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
if (match) {
|
|
2613
|
+
file.parts[dstIx++] = block;
|
|
2614
|
+
} else if (dstIx >= 0) {
|
|
2615
|
+
file.parts.splice(dstIx++, 0, block);
|
|
2616
|
+
} else {
|
|
2617
|
+
ix++;
|
|
2618
|
+
continue;
|
|
2619
|
+
}
|
|
2620
|
+
file.includes = file.includes || /* @__PURE__ */ new Set();
|
|
2621
|
+
file.includes.add(patch.name.toUpperCase());
|
|
2622
|
+
patch.parts.splice(ix, 1);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
3188
2625
|
}
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
2626
|
+
};
|
|
2627
|
+
var RomWriter = class {
|
|
2628
|
+
constructor(entryPoints, cartName, makerCode) {
|
|
2629
|
+
this.cartName = cartName;
|
|
2630
|
+
this.makerCode = makerCode;
|
|
2631
|
+
this.entryPoints = entryPoints;
|
|
2632
|
+
this.outBuffer = new Uint8Array(4194304);
|
|
2633
|
+
}
|
|
2634
|
+
async repack(files) {
|
|
2635
|
+
const processor = new RomProcessor(this);
|
|
2636
|
+
await processor.repack(files);
|
|
2637
|
+
this.writeHeader();
|
|
2638
|
+
this.writeEntryPoints(files.filter((x) => !!x.parts));
|
|
2639
|
+
this.writeChecksum();
|
|
2640
|
+
return this.outBuffer;
|
|
2641
|
+
}
|
|
2642
|
+
writeHeader() {
|
|
2643
|
+
const buf = this.outBuffer;
|
|
2644
|
+
let pos = 65456;
|
|
2645
|
+
this.writeAscii(this.makerCode.padEnd(6, " "), pos);
|
|
2646
|
+
pos += 6;
|
|
2647
|
+
for (let i = 0; i < 10; i++) buf[pos++] = 0;
|
|
2648
|
+
this.writeAscii(this.cartName.toUpperCase().padEnd(21, " "), pos);
|
|
2649
|
+
pos += 21;
|
|
2650
|
+
buf[pos++] = 49;
|
|
2651
|
+
buf[pos++] = 2;
|
|
2652
|
+
buf[pos++] = 12;
|
|
2653
|
+
buf[pos++] = 3;
|
|
2654
|
+
buf[pos++] = 1;
|
|
2655
|
+
buf[pos++] = 51;
|
|
2656
|
+
buf[pos++] = 0;
|
|
2657
|
+
}
|
|
2658
|
+
writeChecksum() {
|
|
2659
|
+
const buf = this.outBuffer;
|
|
2660
|
+
let sum = 0;
|
|
2661
|
+
for (let i = 0; i < buf.length; i++) sum += buf[i] & 255;
|
|
2662
|
+
buf[65502] = sum & 255;
|
|
2663
|
+
buf[65503] = sum >> 8 & 255;
|
|
2664
|
+
const comp = ~sum & 65535;
|
|
2665
|
+
buf[65500] = comp & 255;
|
|
2666
|
+
buf[65501] = comp >> 8 & 255;
|
|
2667
|
+
}
|
|
2668
|
+
// private async generatePatch(): Promise<void> {
|
|
2669
|
+
// const flips = this._projectRoot.flipsPath;
|
|
2670
|
+
// if (!flips) return;
|
|
2671
|
+
// if (typeof process === 'undefined' || !process.versions?.node) return;
|
|
2672
|
+
// const { spawn } = await import('child_process');
|
|
2673
|
+
// const { resolve } = await import('path');
|
|
2674
|
+
// this.bpsPath = `${this._projectRoot.baseDir}/${this._projectRoot.name}.bps`;
|
|
2675
|
+
// await new Promise<void>((resolvePromise) => {
|
|
2676
|
+
// const p = spawn(flips, ['--create', '--bps', `${this._projectRoot.romPath}`, `${this.romPath}`, `${this.bpsPath}`], {
|
|
2677
|
+
// stdio: ['ignore', 'pipe', 'pipe']
|
|
2678
|
+
// });
|
|
2679
|
+
// p.on('close', () => resolvePromise());
|
|
2680
|
+
// });
|
|
2681
|
+
// }
|
|
2682
|
+
writeEntryPoints(asmFiles) {
|
|
2683
|
+
this.outBuffer;
|
|
2684
|
+
const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
|
|
2685
|
+
for (const ep of this.entryPoints) {
|
|
2686
|
+
const match = entryBlocks.find((b) => b.label === ep.name);
|
|
2687
|
+
if (match) this.writeTransform(ep.location, match.location & 65535);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
writeTransform(location, value, size) {
|
|
2691
|
+
const buf = this.outBuffer;
|
|
2692
|
+
if (size === void 0) {
|
|
2693
|
+
if (value <= 255) {
|
|
2694
|
+
size = 1;
|
|
2695
|
+
} else if (value <= 65535) {
|
|
2696
|
+
size = 2;
|
|
2697
|
+
} else if (value <= 16777215) {
|
|
2698
|
+
size = 3;
|
|
2699
|
+
} else {
|
|
2700
|
+
size = 4;
|
|
2701
|
+
}
|
|
3196
2702
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
2703
|
+
switch (size) {
|
|
2704
|
+
case 1:
|
|
2705
|
+
buf[location] = value & 255;
|
|
2706
|
+
break;
|
|
2707
|
+
case 2:
|
|
2708
|
+
buf[location] = value & 255;
|
|
2709
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2710
|
+
break;
|
|
2711
|
+
case 3:
|
|
2712
|
+
buf[location] = value & 255;
|
|
2713
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2714
|
+
buf[location + 2] = value >> 16 & 255;
|
|
2715
|
+
break;
|
|
2716
|
+
case 4:
|
|
2717
|
+
buf[location] = value & 255;
|
|
2718
|
+
buf[location + 1] = value >> 8 & 255;
|
|
2719
|
+
buf[location + 2] = value >> 16 & 255;
|
|
2720
|
+
buf[location + 3] = value >> 24 & 255;
|
|
2721
|
+
break;
|
|
2722
|
+
default:
|
|
2723
|
+
throw new Error(`Invalid size ${size} for writeTransform`);
|
|
3201
2724
|
}
|
|
3202
2725
|
}
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
if (
|
|
3208
|
-
|
|
2726
|
+
async writeFile(file, _chunkLookup) {
|
|
2727
|
+
const start = file.location;
|
|
2728
|
+
let pos = start;
|
|
2729
|
+
const buf = this.outBuffer;
|
|
2730
|
+
if (file.rawData) {
|
|
2731
|
+
const data = file.rawData;
|
|
2732
|
+
let remain = file.size;
|
|
2733
|
+
let srcPos = 0;
|
|
2734
|
+
if (file.type === BinType.Tilemap) {
|
|
2735
|
+
remain -= 2;
|
|
2736
|
+
buf[pos++] = data[srcPos++];
|
|
2737
|
+
buf[pos++] = data[srcPos++];
|
|
2738
|
+
} else if (file.type === BinType.Meta17) {
|
|
2739
|
+
remain -= 4;
|
|
2740
|
+
for (let i = 0; i < 4; i++) buf[pos++] = data[srcPos++];
|
|
2741
|
+
} else if (file.type === BinType.Sound) {
|
|
2742
|
+
remain -= 2;
|
|
2743
|
+
buf[pos++] = remain & 255;
|
|
2744
|
+
buf[pos++] = remain >> 8 & 255;
|
|
2745
|
+
}
|
|
2746
|
+
if (file.compressed !== void 0) {
|
|
2747
|
+
remain -= 2;
|
|
2748
|
+
const inverse = 0 - remain & 65535;
|
|
2749
|
+
buf[pos++] = inverse & 255;
|
|
2750
|
+
buf[pos++] = inverse >> 8 & 255;
|
|
2751
|
+
}
|
|
2752
|
+
while (remain > 0) {
|
|
2753
|
+
buf[pos++] = data[srcPos++];
|
|
2754
|
+
remain--;
|
|
2755
|
+
}
|
|
2756
|
+
} else {
|
|
2757
|
+
if (file.parts && file.parts.length > 0) {
|
|
2758
|
+
if (file.parts[0].location !== file.location && (file.location || 0) !== 0) {
|
|
2759
|
+
throw new Error("Assembly was not based properly");
|
|
2760
|
+
}
|
|
2761
|
+
this.parseAssembly(file.parts, _chunkLookup, file.includeLookup);
|
|
2762
|
+
}
|
|
3209
2763
|
}
|
|
3210
|
-
|
|
3211
|
-
this.romState = new RomState();
|
|
2764
|
+
return pos - start;
|
|
3212
2765
|
}
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
if (!this.dbRoot || !this.romData) {
|
|
3218
|
-
throw new Error("ROM processor not initialized. Call initialize() first.");
|
|
3219
|
-
}
|
|
3220
|
-
const result = {
|
|
3221
|
-
romSize: this.romData.length,
|
|
3222
|
-
headerInfo: this.analyzeHeader(),
|
|
3223
|
-
entryPoints: this.dbRoot.entryPoints,
|
|
3224
|
-
fileCount: this.dbRoot.files.length,
|
|
3225
|
-
blockCount: this.dbRoot.blocks.length,
|
|
3226
|
-
compressionUsed: this.detectCompression(),
|
|
3227
|
-
spriteMapFound: RomState.spriteMap !== null,
|
|
3228
|
-
opcodeStats: this.analyzeOpcodes()
|
|
3229
|
-
};
|
|
3230
|
-
return result;
|
|
2766
|
+
// No address mapping required: layout assigns absolute file offsets
|
|
2767
|
+
writeAscii(text, pos) {
|
|
2768
|
+
const buf = this.outBuffer;
|
|
2769
|
+
for (let i = 0; i < text.length; i++) buf[pos + i] = text.charCodeAt(i) & 255;
|
|
3231
2770
|
}
|
|
3232
2771
|
/**
|
|
3233
|
-
*
|
|
2772
|
+
* Parse assembly blocks and write binary data to output buffer
|
|
2773
|
+
* Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs ParseAssembly method
|
|
3234
2774
|
*/
|
|
3235
|
-
|
|
3236
|
-
if (!
|
|
3237
|
-
throw new Error("
|
|
3238
|
-
}
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
2775
|
+
parseAssembly(blocks, chunkLookup, includeLookup) {
|
|
2776
|
+
if (!blocks) {
|
|
2777
|
+
throw new Error("Assembly has not been parsed");
|
|
2778
|
+
}
|
|
2779
|
+
const buf = this.outBuffer;
|
|
2780
|
+
for (const block of blocks) {
|
|
2781
|
+
let position = block.location;
|
|
2782
|
+
const objList = block.objList;
|
|
2783
|
+
let opos = 0;
|
|
2784
|
+
const processObject = (obj, parentOp) => {
|
|
2785
|
+
let currentObj = obj;
|
|
2786
|
+
while (true) {
|
|
2787
|
+
if (Array.isArray(currentObj)) {
|
|
2788
|
+
for (const obj2 of currentObj) {
|
|
2789
|
+
processObject(obj2, parentOp);
|
|
2790
|
+
}
|
|
2791
|
+
break;
|
|
2792
|
+
} else if (this.isTableEntry(currentObj)) {
|
|
2793
|
+
currentObj = currentObj.object;
|
|
2794
|
+
continue;
|
|
2795
|
+
} else if (currentObj instanceof Op) {
|
|
2796
|
+
const op = currentObj;
|
|
2797
|
+
buf[position++] = op.code & 255;
|
|
2798
|
+
opos += op.size;
|
|
2799
|
+
for (const operand of op.operands) {
|
|
2800
|
+
processObject(operand, op);
|
|
2801
|
+
}
|
|
2802
|
+
break;
|
|
2803
|
+
} else if (currentObj instanceof Uint8Array) {
|
|
2804
|
+
const arr = currentObj;
|
|
2805
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2806
|
+
buf[position + i] = arr[i];
|
|
2807
|
+
}
|
|
2808
|
+
position += arr.length;
|
|
2809
|
+
opos += arr.length;
|
|
2810
|
+
break;
|
|
2811
|
+
} else if (typeof currentObj === "string") {
|
|
2812
|
+
const str = currentObj;
|
|
2813
|
+
let label = str;
|
|
2814
|
+
let ix = 0;
|
|
2815
|
+
while (ix < label.length && RomProcessingConstants.ADDRESS_SPACE.includes(label[ix])) {
|
|
2816
|
+
ix++;
|
|
2817
|
+
}
|
|
2818
|
+
if (ix > 0) {
|
|
2819
|
+
label = label.substring(ix);
|
|
2820
|
+
}
|
|
2821
|
+
let loc;
|
|
2822
|
+
const isRelative = parentOp && (parentOp.mode === "PCRelative" || parentOp.mode === "PCRelativeLong");
|
|
2823
|
+
const operatorIdx = this.indexOfAny(label, RomProcessingConstants.OPERATORS);
|
|
2824
|
+
let offset = null;
|
|
2825
|
+
let useMarker = false;
|
|
2826
|
+
if (operatorIdx > 0) {
|
|
2827
|
+
if (label[operatorIdx + 1] === "M") {
|
|
2828
|
+
useMarker = true;
|
|
2829
|
+
} else {
|
|
2830
|
+
offset = parseInt(label.substring(operatorIdx + 1), 16);
|
|
2831
|
+
if (label[operatorIdx] === "-") {
|
|
2832
|
+
offset = -offset;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
label = label.substring(0, operatorIdx);
|
|
2836
|
+
}
|
|
2837
|
+
const labelUpper = label.toUpperCase();
|
|
2838
|
+
let target;
|
|
2839
|
+
if (includeLookup.has(labelUpper)) {
|
|
2840
|
+
target = includeLookup.get(labelUpper);
|
|
2841
|
+
loc = target.location;
|
|
2842
|
+
} else if (chunkLookup.has(labelUpper)) {
|
|
2843
|
+
loc = chunkLookup.get(labelUpper);
|
|
2844
|
+
} else {
|
|
2845
|
+
if (label.startsWith("#")) {
|
|
2846
|
+
label = label.substring(1);
|
|
2847
|
+
}
|
|
2848
|
+
if (label.startsWith("$")) {
|
|
2849
|
+
label = label.substring(1);
|
|
2850
|
+
}
|
|
2851
|
+
if (isRelative && label.length > 4) {
|
|
2852
|
+
const off = parseInt(label, 16) - (block.location + opos);
|
|
2853
|
+
currentObj = parentOp?.size === 2 ? off & 255 : off & 65535;
|
|
2854
|
+
continue;
|
|
2855
|
+
} else {
|
|
2856
|
+
switch (label.length) {
|
|
2857
|
+
case 1:
|
|
2858
|
+
case 2:
|
|
2859
|
+
currentObj = new Byte(parseInt(label, 16));
|
|
2860
|
+
continue;
|
|
2861
|
+
case 3:
|
|
2862
|
+
case 4:
|
|
2863
|
+
currentObj = new Word(parseInt(label, 16));
|
|
2864
|
+
continue;
|
|
2865
|
+
case 5:
|
|
2866
|
+
case 6:
|
|
2867
|
+
currentObj = new Long(parseInt(label, 16));
|
|
2868
|
+
continue;
|
|
2869
|
+
default:
|
|
2870
|
+
throw new Error(`Invalid operand '${label}'`);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
let type = Address.typeFromCode(str[0]);
|
|
2875
|
+
if (type === AddressType.Unknown) {
|
|
2876
|
+
type = parentOp?.size === 4 ? AddressType.Address : parentOp?.size === 2 ? AddressType.Unknown : AddressType.Offset;
|
|
2877
|
+
}
|
|
2878
|
+
if (isRelative) {
|
|
2879
|
+
loc -= block.location + opos;
|
|
2880
|
+
if (type === AddressType.Unknown && !(loc < 128 || loc >= 4194176)) {
|
|
2881
|
+
throw new Error("Relative out of range");
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
if (offset !== null) {
|
|
2885
|
+
loc += offset;
|
|
2886
|
+
} else if (useMarker && target) {
|
|
2887
|
+
let markerOffset = 0;
|
|
2888
|
+
for (const part of target.objList) {
|
|
2889
|
+
if (this.isStringMarker(part)) {
|
|
2890
|
+
loc += markerOffset;
|
|
2891
|
+
break;
|
|
2892
|
+
} else {
|
|
2893
|
+
markerOffset += RomProcessingConstants.getSize(part);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
switch (type) {
|
|
2898
|
+
case AddressType.Offset:
|
|
2899
|
+
currentObj = new Word(loc);
|
|
2900
|
+
continue;
|
|
2901
|
+
case AddressType.Bank:
|
|
2902
|
+
currentObj = new Byte(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
|
|
2903
|
+
continue;
|
|
2904
|
+
case AddressType.WBank:
|
|
2905
|
+
currentObj = new Word(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
|
|
2906
|
+
continue;
|
|
2907
|
+
case AddressType.Address:
|
|
2908
|
+
currentObj = new Long(loc | ((loc & 65535) >= 32768 ? 8388608 : 12582912));
|
|
2909
|
+
continue;
|
|
2910
|
+
default:
|
|
2911
|
+
currentObj = new Byte(loc);
|
|
2912
|
+
continue;
|
|
2913
|
+
}
|
|
2914
|
+
} else if (currentObj instanceof TypedNumber) {
|
|
2915
|
+
let value = currentObj.value;
|
|
2916
|
+
for (let i = 0; i < currentObj.size; i++) {
|
|
2917
|
+
buf[position++] = value & 255;
|
|
2918
|
+
value >>= 8;
|
|
2919
|
+
}
|
|
2920
|
+
break;
|
|
2921
|
+
} else if (typeof currentObj === "number") {
|
|
2922
|
+
const num = currentObj;
|
|
2923
|
+
const size = parentOp?.size ?? 0;
|
|
2924
|
+
if (num <= 255 && size <= 2) {
|
|
2925
|
+
buf[position] = num & 255;
|
|
2926
|
+
position++;
|
|
2927
|
+
} else if (num <= 65535 && size <= 3) {
|
|
2928
|
+
buf[position] = num & 255;
|
|
2929
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2930
|
+
position += 2;
|
|
2931
|
+
} else if (num <= 16777215 && size <= 4) {
|
|
2932
|
+
buf[position] = num & 255;
|
|
2933
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2934
|
+
buf[position + 2] = num >> 16 & 255;
|
|
2935
|
+
position += 3;
|
|
2936
|
+
} else {
|
|
2937
|
+
buf[position] = num & 255;
|
|
2938
|
+
buf[position + 1] = num >> 8 & 255;
|
|
2939
|
+
buf[position + 2] = num >> 16 & 255;
|
|
2940
|
+
buf[position + 3] = num >> 24 & 255;
|
|
2941
|
+
position += 4;
|
|
2942
|
+
}
|
|
2943
|
+
break;
|
|
2944
|
+
} else if (this.isStringMarker(currentObj)) {
|
|
2945
|
+
break;
|
|
2946
|
+
} else {
|
|
2947
|
+
throw new Error(`Unable to process '${currentObj}'`);
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
};
|
|
2951
|
+
for (const obj of objList) {
|
|
2952
|
+
processObject(obj);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
3247
2956
|
/**
|
|
3248
|
-
*
|
|
2957
|
+
* Helper method to find index of any character from an array in a string
|
|
3249
2958
|
*/
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
metaFile,
|
|
3258
|
-
sceneId
|
|
3259
|
-
);
|
|
3260
|
-
this.romState = state;
|
|
3261
|
-
return state;
|
|
2959
|
+
indexOfAny(str, chars) {
|
|
2960
|
+
for (let i = 0; i < str.length; i++) {
|
|
2961
|
+
if (chars.includes(str[i])) {
|
|
2962
|
+
return i;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return -1;
|
|
3262
2966
|
}
|
|
3263
2967
|
/**
|
|
3264
|
-
*
|
|
2968
|
+
* Helper method to check if an object is a StringMarker
|
|
3265
2969
|
*/
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
2970
|
+
isStringMarker(obj) {
|
|
2971
|
+
return typeof obj === "object" && obj !== null && "offset" in obj;
|
|
2972
|
+
}
|
|
2973
|
+
isTableEntry(obj) {
|
|
2974
|
+
return typeof obj === "object" && obj !== null && "location" in obj && "object" in obj;
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
var StringProcessor = class {
|
|
2978
|
+
constructor(context) {
|
|
2979
|
+
this.memBuffer = [];
|
|
2980
|
+
this.context = context;
|
|
2981
|
+
this.root = context.root;
|
|
2982
|
+
}
|
|
2983
|
+
dispose() {
|
|
2984
|
+
this.memBuffer.length = 0;
|
|
2985
|
+
}
|
|
2986
|
+
consumeString() {
|
|
2987
|
+
let str = null;
|
|
2988
|
+
const typeChar = this.context.lineBuffer[0];
|
|
2989
|
+
const endIx = this.context.lineBuffer.indexOf(typeChar, 1);
|
|
2990
|
+
if (endIx >= 0) {
|
|
2991
|
+
str = this.context.lineBuffer.substring(1, endIx);
|
|
2992
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
2993
|
+
} else {
|
|
2994
|
+
str = this.context.lineBuffer.substring(1);
|
|
2995
|
+
this.context.lineBuffer = "";
|
|
2996
|
+
}
|
|
2997
|
+
this.memBuffer.length = 0;
|
|
2998
|
+
const stringType = this.root.stringCharLookup[typeChar];
|
|
2999
|
+
this.processString(str, stringType);
|
|
3000
|
+
}
|
|
3001
|
+
flushBuffer(stringType, wrap = false) {
|
|
3002
|
+
const size = this.memBuffer.length;
|
|
3003
|
+
if (size > 0) {
|
|
3004
|
+
const buffer = new Uint8Array(this.memBuffer);
|
|
3005
|
+
this.context.currentBlock.objList.push(buffer);
|
|
3006
|
+
this.context.currentBlock.size += size;
|
|
3007
|
+
this.memBuffer.length = 0;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
processString(str, stringType) {
|
|
3011
|
+
const dict = stringType.commands;
|
|
3012
|
+
stringType.characterMap;
|
|
3013
|
+
this.getShiftUp(stringType.shiftType);
|
|
3014
|
+
let lastCmd = null;
|
|
3015
|
+
for (let x = 0; x < str.length; x++) {
|
|
3016
|
+
const c = str[x];
|
|
3017
|
+
if (c === "[") {
|
|
3018
|
+
const endIx = str.indexOf("]", x + 1);
|
|
3019
|
+
const splitChars = [":", ",", " "];
|
|
3020
|
+
const parts = str.substring(x + 1, endIx).split(new RegExp(`[${splitChars.join("")}]`)).filter((p) => p.length > 0);
|
|
3021
|
+
x = endIx;
|
|
3022
|
+
if (parts.length === 0) {
|
|
3023
|
+
this.flushBuffer(stringType, true);
|
|
3024
|
+
this.context.currentBlock.objList.push({
|
|
3025
|
+
offset: this.context.currentBlock.size
|
|
3026
|
+
});
|
|
3027
|
+
continue;
|
|
3028
|
+
}
|
|
3029
|
+
const cmd = Object.values(dict).find((x2) => x2.value === parts[0]);
|
|
3030
|
+
if (cmd) {
|
|
3031
|
+
lastCmd = cmd;
|
|
3032
|
+
this.memBuffer.push(cmd.key);
|
|
3033
|
+
this.processStringCommand(cmd, stringType, parts);
|
|
3034
|
+
continue;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
lastCmd = null;
|
|
3038
|
+
if (this.applyLayers(c, stringType)) {
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
if (lastCmd === null || !lastCmd.halt) {
|
|
3043
|
+
this.memBuffer.push(stringType.terminator);
|
|
3044
|
+
}
|
|
3045
|
+
this.flushBuffer(stringType, true);
|
|
3046
|
+
}
|
|
3047
|
+
applyLayers(c, stringType) {
|
|
3048
|
+
if (this.applyMap(c, stringType.characterMap, this.getShiftUp(stringType.shiftType))) {
|
|
3049
|
+
return true;
|
|
3050
|
+
}
|
|
3051
|
+
if (stringType.layers) {
|
|
3052
|
+
for (const layer of stringType.layers) {
|
|
3053
|
+
if (this.applyMap(c, layer.map, (x) => x + layer.base)) {
|
|
3054
|
+
return true;
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
return false;
|
|
3059
|
+
}
|
|
3060
|
+
applyMap(c, map, shift) {
|
|
3061
|
+
for (let i = 0, len = map.length; i < len; i++) {
|
|
3062
|
+
const v = map[i];
|
|
3063
|
+
if (v != null && c === v[0]) {
|
|
3064
|
+
this.memBuffer.push(shift(i));
|
|
3065
|
+
return true;
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
processStringCommand(cmd, stringType, parts) {
|
|
3071
|
+
const hasPointer = cmd.types.includes(MemberType.Address) || cmd.types.includes(MemberType.Offset);
|
|
3072
|
+
if (hasPointer) {
|
|
3073
|
+
this.flushBuffer(stringType, true);
|
|
3074
|
+
}
|
|
3075
|
+
for (let y = 0, pix = 1; y < cmd.types.length; y++, pix++) {
|
|
3076
|
+
switch (cmd.types[y]) {
|
|
3077
|
+
case MemberType.Byte:
|
|
3078
|
+
this.memBuffer.push(parseInt(parts[pix], 16));
|
|
3079
|
+
break;
|
|
3080
|
+
case MemberType.Word:
|
|
3081
|
+
const us = parseInt(parts[pix], 16);
|
|
3082
|
+
this.memBuffer.push(us & 255);
|
|
3083
|
+
this.memBuffer.push(us >> 8 & 255);
|
|
3084
|
+
break;
|
|
3085
|
+
case MemberType.Binary:
|
|
3086
|
+
while (pix < parts.length) {
|
|
3087
|
+
const ch = parseInt(parts[pix], 16);
|
|
3088
|
+
this.memBuffer.push(ch);
|
|
3089
|
+
pix++;
|
|
3090
|
+
}
|
|
3091
|
+
if (cmd.delimiter !== void 0) {
|
|
3092
|
+
this.memBuffer.push(cmd.delimiter);
|
|
3093
|
+
}
|
|
3094
|
+
break;
|
|
3095
|
+
case MemberType.Offset:
|
|
3096
|
+
case MemberType.Address:
|
|
3097
|
+
this.flushBuffer(stringType, false);
|
|
3098
|
+
this.context.currentBlock.objList.push(parts[pix]);
|
|
3099
|
+
this.context.currentBlock.size += cmd.types[y] === MemberType.Offset ? 2 : 3;
|
|
3100
|
+
break;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
if (hasPointer) {
|
|
3104
|
+
this.flushBuffer(stringType, false);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
getShiftUp(shiftType) {
|
|
3108
|
+
switch (shiftType) {
|
|
3109
|
+
case "h2":
|
|
3110
|
+
return (x) => (x & 112) << 1 | x & 15;
|
|
3111
|
+
case "wh2":
|
|
3112
|
+
return (x) => (x & 56) << 1 | x & 7;
|
|
3113
|
+
default:
|
|
3114
|
+
return (x) => x;
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
};
|
|
3118
|
+
|
|
3119
|
+
// src/rom/rebuild/sorted-map.ts
|
|
3120
|
+
var SortedMap = class {
|
|
3121
|
+
constructor() {
|
|
3122
|
+
this.map = /* @__PURE__ */ new Map();
|
|
3123
|
+
this._keys = [];
|
|
3124
|
+
}
|
|
3125
|
+
get size() {
|
|
3126
|
+
return this.map.size;
|
|
3127
|
+
}
|
|
3128
|
+
set(key, value) {
|
|
3129
|
+
if (!this.map.has(key)) {
|
|
3130
|
+
this._keys.push(key);
|
|
3131
|
+
this.sortKeys();
|
|
3132
|
+
}
|
|
3133
|
+
this.map.set(key, value);
|
|
3134
|
+
return this;
|
|
3135
|
+
}
|
|
3136
|
+
get(key) {
|
|
3137
|
+
return this.map.get(key);
|
|
3138
|
+
}
|
|
3139
|
+
has(key) {
|
|
3140
|
+
return this.map.has(key);
|
|
3141
|
+
}
|
|
3142
|
+
delete(key) {
|
|
3143
|
+
const result = this.map.delete(key);
|
|
3144
|
+
if (result) {
|
|
3145
|
+
const index = this._keys.indexOf(key);
|
|
3146
|
+
if (index >= 0) {
|
|
3147
|
+
this._keys.splice(index, 1);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3277
3150
|
return result;
|
|
3278
3151
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
getCompression() {
|
|
3283
|
-
return this.projectRoot.getCompression();
|
|
3152
|
+
clear() {
|
|
3153
|
+
this.map.clear();
|
|
3154
|
+
this._keys.length = 0;
|
|
3284
3155
|
}
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
*/
|
|
3288
|
-
getRomState() {
|
|
3289
|
-
return this.romState;
|
|
3156
|
+
keys() {
|
|
3157
|
+
return [...this._keys];
|
|
3290
3158
|
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
*/
|
|
3294
|
-
getDatabase() {
|
|
3295
|
-
return this.dbRoot;
|
|
3159
|
+
values() {
|
|
3160
|
+
return this._keys.map((key) => this.map.get(key));
|
|
3296
3161
|
}
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
*/
|
|
3300
|
-
getProject() {
|
|
3301
|
-
return this.projectRoot;
|
|
3302
|
-
}
|
|
3303
|
-
// Private helper methods
|
|
3304
|
-
analyzeHeader() {
|
|
3305
|
-
if (!this.romData) {
|
|
3306
|
-
throw new Error("ROM data not loaded");
|
|
3307
|
-
}
|
|
3308
|
-
const headerOffset = 32704;
|
|
3309
|
-
const title = new TextDecoder().decode(this.romData.slice(headerOffset, headerOffset + 21)).replace(/\0/g, "");
|
|
3310
|
-
const mapMode = this.romData[headerOffset + 21];
|
|
3311
|
-
const cartridgeType = this.romData[headerOffset + 22];
|
|
3312
|
-
const romSize = this.romData[headerOffset + 23];
|
|
3313
|
-
const ramSize = this.romData[headerOffset + 24];
|
|
3314
|
-
return {
|
|
3315
|
-
title,
|
|
3316
|
-
mapMode,
|
|
3317
|
-
cartridgeType,
|
|
3318
|
-
romSize,
|
|
3319
|
-
ramSize,
|
|
3320
|
-
isValid: title.length > 0 && mapMode !== 255
|
|
3321
|
-
};
|
|
3162
|
+
entries() {
|
|
3163
|
+
return this._keys.map((key) => [key, this.map.get(key)]);
|
|
3322
3164
|
}
|
|
3323
|
-
|
|
3324
|
-
|
|
3165
|
+
*[Symbol.iterator]() {
|
|
3166
|
+
for (const key of this._keys) {
|
|
3167
|
+
yield [key, this.map.get(key)];
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
sortKeys() {
|
|
3171
|
+
this._keys.sort((a, b) => {
|
|
3172
|
+
if (a.length !== b.length) {
|
|
3173
|
+
return b.length - a.length;
|
|
3174
|
+
}
|
|
3175
|
+
return a.localeCompare(b);
|
|
3176
|
+
});
|
|
3325
3177
|
}
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3178
|
+
};
|
|
3179
|
+
var AssemblerState = class _AssemblerState {
|
|
3180
|
+
constructor(context, structType = null, saveDelimiter = false) {
|
|
3181
|
+
this.context = context;
|
|
3182
|
+
this.root = context.root;
|
|
3183
|
+
this.dbStruct = structType === null ? null : Object.values(this.root.structs).find(
|
|
3184
|
+
(x) => x.name.toLowerCase() === structType.toLowerCase()
|
|
3185
|
+
) || null;
|
|
3186
|
+
this.parentStruct = this.dbStruct?.parent == null ? null : Object.values(this.root.structs).find(
|
|
3187
|
+
(x) => x.name.toLowerCase() === this.dbStruct.parent.toLowerCase()
|
|
3188
|
+
) || null;
|
|
3189
|
+
this.discriminator = this.parentStruct?.discriminator ?? null;
|
|
3190
|
+
this.delimiter = this.dbStruct?.delimiter || null;
|
|
3191
|
+
this.memberOffset = 0;
|
|
3192
|
+
this.dataOffset = 0;
|
|
3193
|
+
this.memberTypes = this.dbStruct?.types || null;
|
|
3194
|
+
this.currentType = this.memberTypes?.[this.memberOffset] || null;
|
|
3195
|
+
if (saveDelimiter) {
|
|
3196
|
+
this.context.lastDelimiter = this.dbStruct?.delimiter ?? this.parentStruct?.delimiter ?? null;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
checkDisc() {
|
|
3200
|
+
if (this.discriminator === this.dataOffset) {
|
|
3201
|
+
this.context.currentBlock.objList.push(this.dbStruct.discriminator);
|
|
3202
|
+
this.context.currentBlock.size += 1;
|
|
3203
|
+
this.dataOffset += 1;
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
advancePart() {
|
|
3207
|
+
if (this.currentType != null && this.discriminator != null) {
|
|
3208
|
+
this.dataOffset += RomProcessingConstants.getSize(this.currentType);
|
|
3209
|
+
}
|
|
3210
|
+
if (this.memberTypes != null && this.memberOffset + 1 < this.memberTypes.length) {
|
|
3211
|
+
this.currentType = this.memberTypes[++this.memberOffset];
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
processOrigin() {
|
|
3215
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(3).replace(/^[\s,\t]+/, "");
|
|
3216
|
+
if (this.context.lineBuffer.startsWith("$")) {
|
|
3217
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1);
|
|
3218
|
+
}
|
|
3219
|
+
let hex;
|
|
3220
|
+
const endIx = this.context.lineBuffer.search(/[\s,\t]/);
|
|
3221
|
+
if (endIx >= 0) {
|
|
3222
|
+
hex = this.context.lineBuffer.substring(0, endIx);
|
|
3223
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
3224
|
+
} else {
|
|
3225
|
+
hex = this.context.lineBuffer;
|
|
3226
|
+
this.context.lineBuffer = "";
|
|
3329
3227
|
}
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3228
|
+
const location = parseInt(hex, 16);
|
|
3229
|
+
this.context.blocks.push(this.context.currentBlock = new AsmBlock(location));
|
|
3230
|
+
this.context.blockIndex++;
|
|
3231
|
+
}
|
|
3232
|
+
static doMath(operand) {
|
|
3233
|
+
const ix = operand.search(/[-+]/);
|
|
3234
|
+
if (ix >= 0) {
|
|
3235
|
+
const op = operand[ix];
|
|
3236
|
+
const vix = operand.lastIndexOf("$", ix) + 1;
|
|
3237
|
+
const valueStr = operand.substring(vix, ix);
|
|
3238
|
+
if (valueStr.match(/^[a-fA-F0-9]+$/)) {
|
|
3239
|
+
const value = parseInt(valueStr, 16);
|
|
3240
|
+
let endIx = operand.substring(ix + 1).search(RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3241
|
+
if (endIx < 0) {
|
|
3242
|
+
endIx = operand.length;
|
|
3243
|
+
}
|
|
3244
|
+
const number = parseInt(operand.substring(ix + 1, endIx), 16);
|
|
3245
|
+
let result;
|
|
3246
|
+
if (op === "-") {
|
|
3247
|
+
result = value - number;
|
|
3248
|
+
} else {
|
|
3249
|
+
result = value + number;
|
|
3250
|
+
}
|
|
3251
|
+
const len = ix - vix <= 2 ? 2 : ix - vix <= 4 ? 4 : 6;
|
|
3252
|
+
operand = operand.substring(0, vix) + result.toString(16).toUpperCase().padStart(len, "0") + operand.substring(endIx);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
return operand;
|
|
3256
|
+
}
|
|
3257
|
+
tryCreateLabel(mnemonic, operand) {
|
|
3258
|
+
if (!operand || operand.length === 0) {
|
|
3259
|
+
return false;
|
|
3260
|
+
}
|
|
3261
|
+
const labelChar = operand[0];
|
|
3262
|
+
if (!RomProcessingConstants.LABEL_SPACE.includes(labelChar)) {
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3265
|
+
const newBlock = new AsmBlock(
|
|
3266
|
+
this.context.currentBlock.location + this.context.currentBlock.size,
|
|
3267
|
+
0,
|
|
3268
|
+
this.root.stringDelimiters.includes(operand[0]),
|
|
3269
|
+
mnemonic
|
|
3270
|
+
);
|
|
3271
|
+
this.context.blocks.push(newBlock);
|
|
3272
|
+
this.context.currentBlock = newBlock;
|
|
3273
|
+
this.context.blockIndex++;
|
|
3274
|
+
if (labelChar === ":") {
|
|
3275
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1);
|
|
3276
|
+
} else if (labelChar === "[" || labelChar === "{") {
|
|
3277
|
+
this.context.lineBuffer = operand.substring(1).replace(/^[\s,\t]+/, "");
|
|
3278
|
+
const state = new _AssemblerState(this.context, this.currentType);
|
|
3279
|
+
state.processText(labelChar);
|
|
3280
|
+
this.advancePart();
|
|
3281
|
+
}
|
|
3282
|
+
return true;
|
|
3283
|
+
}
|
|
3284
|
+
processText(openTag) {
|
|
3285
|
+
this.checkDisc();
|
|
3286
|
+
while (!this.context.eof) {
|
|
3287
|
+
if (!this.context.getLine()) {
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
let mnemonic = null;
|
|
3291
|
+
let operand = null;
|
|
3292
|
+
let operand2 = null;
|
|
3293
|
+
while (this.context.lineBuffer.length > 0) {
|
|
3294
|
+
const lineSymbol = this.context.lineBuffer[0];
|
|
3295
|
+
if (this.root.stringDelimiters.includes(lineSymbol)) {
|
|
3296
|
+
this.context.stringProcessor.consumeString();
|
|
3297
|
+
this.advancePart();
|
|
3298
|
+
continue;
|
|
3299
|
+
}
|
|
3300
|
+
if (RomProcessingConstants.ADDRESS_SPACE.includes(lineSymbol)) {
|
|
3301
|
+
this.context.processRawData();
|
|
3302
|
+
if (openTag === "[") {
|
|
3303
|
+
this.context.lastDelimiter = null;
|
|
3304
|
+
}
|
|
3305
|
+
this.advancePart();
|
|
3306
|
+
continue;
|
|
3307
|
+
}
|
|
3308
|
+
if (lineSymbol === ">") {
|
|
3309
|
+
if (openTag === "<") {
|
|
3310
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3311
|
+
this.checkDisc();
|
|
3312
|
+
}
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
if (lineSymbol === "]") {
|
|
3316
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3317
|
+
this.delimiter = this.delimiter ?? this.context.lastDelimiter;
|
|
3318
|
+
if (this.delimiter != null) {
|
|
3319
|
+
if (this.delimiter >= 256) {
|
|
3320
|
+
this.context.currentBlock.objList.push(this.delimiter);
|
|
3321
|
+
this.context.currentBlock.size += 2;
|
|
3322
|
+
} else {
|
|
3323
|
+
this.context.currentBlock.objList.push(this.delimiter);
|
|
3324
|
+
this.context.currentBlock.size += 1;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
if (lineSymbol === "}") {
|
|
3330
|
+
if (openTag === "{") {
|
|
3331
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3332
|
+
}
|
|
3333
|
+
return;
|
|
3334
|
+
}
|
|
3335
|
+
if (lineSymbol === "[") {
|
|
3336
|
+
this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3337
|
+
const state = new _AssemblerState(this.context, this.currentType);
|
|
3338
|
+
state.processText("[");
|
|
3339
|
+
this.advancePart();
|
|
3340
|
+
continue;
|
|
3341
|
+
}
|
|
3342
|
+
if (this.context.lineBuffer.startsWith("ORG")) {
|
|
3343
|
+
this.processOrigin();
|
|
3344
|
+
continue;
|
|
3345
|
+
}
|
|
3346
|
+
const symbolIndex = this.context.lineBuffer.search(RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3347
|
+
if (symbolIndex > 0) {
|
|
3348
|
+
mnemonic = this.context.lineBuffer.substring(0, symbolIndex);
|
|
3349
|
+
operand = this.context.lineBuffer.substring(symbolIndex).replace(/^[, \t]+/, "");
|
|
3350
|
+
if (operand && operand.startsWith("<")) {
|
|
3351
|
+
this.context.lineBuffer = operand.substring(1).replace(/^[, \t]+/, "");
|
|
3352
|
+
const state = new _AssemblerState(this.context, mnemonic, openTag === "[" && this.currentType == null);
|
|
3353
|
+
state.processText("<");
|
|
3354
|
+
mnemonic = null;
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
this.context.lineBuffer = operand;
|
|
3358
|
+
} else {
|
|
3359
|
+
mnemonic = this.context.lineBuffer;
|
|
3360
|
+
this.context.lineBuffer = "";
|
|
3361
|
+
}
|
|
3362
|
+
break;
|
|
3363
|
+
}
|
|
3364
|
+
if (mnemonic && mnemonic.length > 0) {
|
|
3365
|
+
const codes = this.root.opLookup[mnemonic.toUpperCase()];
|
|
3366
|
+
if (!codes || codes.length === 0) {
|
|
3367
|
+
if (this.tryCreateLabel(mnemonic, operand || void 0)) {
|
|
3368
|
+
continue;
|
|
3369
|
+
}
|
|
3370
|
+
throw new Error(`Unknown instruction line ${this.context.lineCount}: '${mnemonic}'`);
|
|
3371
|
+
}
|
|
3372
|
+
this.context.lineBuffer = "";
|
|
3373
|
+
if (!operand) {
|
|
3374
|
+
const opCode2 = codes.find((x) => this.root.addrLookup[x.mode].size === 1);
|
|
3375
|
+
this.context.currentBlock.objList.push(new Op(opCode2, 0, [], 1));
|
|
3376
|
+
this.context.currentBlock.size++;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
operand = _AssemblerState.doMath(operand);
|
|
3380
|
+
let opCode = codes[0];
|
|
3381
|
+
if (opCode?.mnem === "COP") {
|
|
3382
|
+
const parts = operand.split(/[\s\t,()[\]$#]/).filter((p) => p.length > 0);
|
|
3383
|
+
const cmd = parts[0];
|
|
3384
|
+
const cop = this.root.copLookup[cmd];
|
|
3385
|
+
if (!cop) {
|
|
3386
|
+
throw new Error(`Unknown COP command ${cmd}`);
|
|
3387
|
+
}
|
|
3388
|
+
this.context.currentBlock.objList.push(new Op(opCode, 0, [new Byte(cop.code), ...parts.slice(1)], cop.size + 2));
|
|
3389
|
+
this.context.currentBlock.size += cop.size + 2;
|
|
3390
|
+
continue;
|
|
3391
|
+
}
|
|
3392
|
+
opCode = null;
|
|
3393
|
+
for (const code of codes) {
|
|
3394
|
+
if (code.mode === "PCRelative" || code.mode === "PCRelativeLong") {
|
|
3395
|
+
opCode = code;
|
|
3396
|
+
break;
|
|
3397
|
+
}
|
|
3398
|
+
const addrMode2 = this.root.addrLookup[code.mode];
|
|
3399
|
+
const regex = addrMode2.parseRegex;
|
|
3400
|
+
if (regex) {
|
|
3401
|
+
const match = new RegExp(regex).exec(operand);
|
|
3402
|
+
if (match) {
|
|
3403
|
+
opCode = code;
|
|
3404
|
+
operand = match[1];
|
|
3405
|
+
if (match.length > 2) {
|
|
3406
|
+
operand2 = match[2];
|
|
3407
|
+
}
|
|
3408
|
+
break;
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
if (operand.startsWith("#")) {
|
|
3413
|
+
operand = operand.substring(1);
|
|
3414
|
+
}
|
|
3415
|
+
if (operand.startsWith("$")) {
|
|
3416
|
+
operand = operand.substring(1);
|
|
3417
|
+
}
|
|
3418
|
+
if (opCode == null) {
|
|
3419
|
+
const addrIx = operand.search(RomProcessingConstants.ADDRESS_SPACE_REGEX);
|
|
3420
|
+
if (addrIx && addrIx >= 0) {
|
|
3421
|
+
const eix = operand.search(/[\s\t,\])]/);
|
|
3422
|
+
if (eix && eix >= 0) {
|
|
3423
|
+
operand = operand.substring(addrIx, eix);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
opCode = codes.find((x) => x.mode === "Immediate") ?? codes.find((x) => x.mode === "AbsoluteLong") ?? codes.find((x) => x.mode === "Absolute") ?? null;
|
|
3427
|
+
if (opCode == null) {
|
|
3428
|
+
throw new Error(`Unable to determine mode/code line ${this.context.lineCount}: '${this.context.lineBuffer}'`);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
const opnd1 = this.context.parseOperand(operand);
|
|
3432
|
+
const addrMode = this.root.addrLookup[opCode.mode];
|
|
3433
|
+
let size = addrMode.size;
|
|
3434
|
+
if (size === AsmReader.VARIABLE_SIZE_INDICATOR || opCode.mode === "Immediate") {
|
|
3435
|
+
size = operand?.startsWith("^") || operand?.length === 2 ? 2 : 3;
|
|
3436
|
+
}
|
|
3437
|
+
const operands = operand2 != null ? [opnd1, this.context.parseOperand(operand2)] : [opnd1];
|
|
3438
|
+
this.context.currentBlock.objList.push(new Op(opCode, 0, operands, size));
|
|
3439
|
+
this.context.currentBlock.size += size;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
hexStringToBytes(hex) {
|
|
3444
|
+
const result = new Uint8Array(hex.length / 2);
|
|
3445
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
3446
|
+
result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
3447
|
+
}
|
|
3448
|
+
return result;
|
|
3338
3449
|
}
|
|
3339
3450
|
};
|
|
3340
3451
|
|
|
3341
|
-
// src/
|
|
3342
|
-
var
|
|
3343
|
-
constructor(
|
|
3344
|
-
this.
|
|
3345
|
-
this.
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3452
|
+
// src/rom/rebuild/assembler.ts
|
|
3453
|
+
var Assembler = class {
|
|
3454
|
+
constructor(dbRoot, textData) {
|
|
3455
|
+
this.currentLineIndex = 0;
|
|
3456
|
+
this.lineBuffer = "";
|
|
3457
|
+
this.includes = /* @__PURE__ */ new Set();
|
|
3458
|
+
this.blocks = [];
|
|
3459
|
+
this.tags = new SortedMap();
|
|
3460
|
+
this.currentBlock = null;
|
|
3461
|
+
this.lineCount = 0;
|
|
3462
|
+
this.blockIndex = 0;
|
|
3463
|
+
this.lastDelimiter = null;
|
|
3464
|
+
this.reqBank = null;
|
|
3465
|
+
this.eof = false;
|
|
3466
|
+
this.root = dbRoot;
|
|
3467
|
+
this.lines = textData.split(/\r?\n/);
|
|
3468
|
+
this.stringProcessor = new StringProcessor(this);
|
|
3469
|
+
}
|
|
3470
|
+
dispose() {
|
|
3471
|
+
this.stringProcessor.dispose();
|
|
3472
|
+
}
|
|
3473
|
+
parseAssembly() {
|
|
3474
|
+
this.blocks.push(this.currentBlock = new AsmBlock());
|
|
3475
|
+
const state = new AssemblerState(this);
|
|
3476
|
+
state.processText();
|
|
3477
|
+
return {
|
|
3478
|
+
blocks: this.blocks,
|
|
3479
|
+
includes: this.includes,
|
|
3480
|
+
reqBank: this.reqBank
|
|
3351
3481
|
};
|
|
3352
|
-
this.events = {};
|
|
3353
|
-
this.events = events || {};
|
|
3354
3482
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3483
|
+
getLine() {
|
|
3484
|
+
if (this.eof) {
|
|
3485
|
+
return false;
|
|
3486
|
+
}
|
|
3487
|
+
if (this.lineBuffer.length > 0) {
|
|
3488
|
+
return true;
|
|
3489
|
+
}
|
|
3490
|
+
let rawLine = null;
|
|
3491
|
+
while (true) {
|
|
3492
|
+
if (this.currentLineIndex >= this.lines.length) {
|
|
3493
|
+
if (this.lineBuffer.length === 0) {
|
|
3494
|
+
this.eof = true;
|
|
3495
|
+
return false;
|
|
3496
|
+
} else {
|
|
3497
|
+
rawLine = "";
|
|
3498
|
+
}
|
|
3499
|
+
} else {
|
|
3500
|
+
rawLine = this.lines[this.currentLineIndex++];
|
|
3501
|
+
this.lineBuffer += rawLine;
|
|
3502
|
+
}
|
|
3503
|
+
this.lineCount++;
|
|
3504
|
+
this.trimComments("--");
|
|
3505
|
+
this.trimComments(";");
|
|
3506
|
+
this.trimComments("//");
|
|
3507
|
+
this.lineBuffer = this.lineBuffer.replace(RomProcessingConstants.COMMA_SPACE_TRIM_REGEX, "");
|
|
3508
|
+
if (this.lineBuffer.length === 0) {
|
|
3509
|
+
continue;
|
|
3510
|
+
}
|
|
3511
|
+
if (this.lineBuffer.endsWith("\\")) {
|
|
3512
|
+
this.lineBuffer = this.lineBuffer.slice(0, -1);
|
|
3513
|
+
continue;
|
|
3514
|
+
}
|
|
3515
|
+
if (this.lineBuffer[0] === "?") {
|
|
3516
|
+
this.processDirectives();
|
|
3517
|
+
this.lineBuffer = "";
|
|
3518
|
+
continue;
|
|
3519
|
+
}
|
|
3520
|
+
if (this.lineBuffer[0] === "!") {
|
|
3521
|
+
this.processTags();
|
|
3522
|
+
this.lineBuffer = "";
|
|
3523
|
+
continue;
|
|
3524
|
+
}
|
|
3525
|
+
this.resolveTags();
|
|
3526
|
+
return true;
|
|
3527
|
+
}
|
|
3362
3528
|
}
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3529
|
+
trimComments(sequence) {
|
|
3530
|
+
const index = this.lineBuffer.indexOf(sequence);
|
|
3531
|
+
if (index >= 0) {
|
|
3532
|
+
const strDelimRegex = new RegExp(`[${this.root.stringDelimiters.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("")}]`);
|
|
3533
|
+
const strIndex = this.lineBuffer.search(strDelimRegex);
|
|
3534
|
+
if (strIndex < 0 || strIndex > index || this.lineBuffer.lastIndexOf(this.lineBuffer[strIndex] || "") < index) {
|
|
3535
|
+
this.lineBuffer = this.lineBuffer.substring(0, index);
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3370
3538
|
}
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
});
|
|
3388
|
-
this.events.onLoadComplete?.(this.processor);
|
|
3389
|
-
} catch (error) {
|
|
3390
|
-
const err = error;
|
|
3391
|
-
this.updateStatus({ lastError: err, currentTask: "Load failed", progress: 0 });
|
|
3392
|
-
this.events.onLoadError?.(err);
|
|
3393
|
-
throw err;
|
|
3539
|
+
processDirectives() {
|
|
3540
|
+
let endIx = this.lineBuffer.search(RomProcessingConstants.COMMA_SPACE_REGEX);
|
|
3541
|
+
if (endIx < 0) {
|
|
3542
|
+
endIx = this.lineBuffer.length;
|
|
3543
|
+
}
|
|
3544
|
+
const value = this.lineBuffer.substring(endIx).replace(/^[\s,\t]+/, "");
|
|
3545
|
+
switch (this.lineBuffer.substring(1, endIx).toUpperCase()) {
|
|
3546
|
+
// This is taken care of by the loader
|
|
3547
|
+
case "BANK":
|
|
3548
|
+
this.reqBank = parseInt(value, 16);
|
|
3549
|
+
break;
|
|
3550
|
+
case "INCLUDE":
|
|
3551
|
+
if (value.length > 0) {
|
|
3552
|
+
this.includes.add(value.toUpperCase().replace(/'/g, ""));
|
|
3553
|
+
}
|
|
3554
|
+
break;
|
|
3394
3555
|
}
|
|
3395
3556
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3557
|
+
processTags() {
|
|
3558
|
+
this.lineBuffer = this.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
|
|
3559
|
+
while (this.lineBuffer.length > 0) {
|
|
3560
|
+
let name = this.lineBuffer;
|
|
3561
|
+
let value = null;
|
|
3562
|
+
let endIx = this.lineBuffer.search(/[\s,\t]/);
|
|
3563
|
+
if (endIx >= 0) {
|
|
3564
|
+
name = this.lineBuffer.substring(0, endIx);
|
|
3565
|
+
value = this.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
|
|
3566
|
+
const nextIx = value.search(/[\s,\t]/);
|
|
3567
|
+
if (nextIx >= 0) {
|
|
3568
|
+
this.lineBuffer = value.substring(nextIx + 1).replace(/^[\s,\t]+/, "");
|
|
3569
|
+
value = value.substring(0, nextIx);
|
|
3570
|
+
} else {
|
|
3571
|
+
this.lineBuffer = "";
|
|
3572
|
+
}
|
|
3573
|
+
} else {
|
|
3574
|
+
this.lineBuffer = "";
|
|
3575
|
+
}
|
|
3576
|
+
this.tags.set(name, value);
|
|
3577
|
+
}
|
|
3402
3578
|
}
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3579
|
+
resolveTags() {
|
|
3580
|
+
for (const [key, value] of this.tags) {
|
|
3581
|
+
let ix;
|
|
3582
|
+
while ((ix = this.lineBuffer.toLowerCase().indexOf(key.toLowerCase())) >= 0) {
|
|
3583
|
+
this.lineBuffer = this.lineBuffer.substring(0, ix) + (value || "") + this.lineBuffer.substring(ix + key.length);
|
|
3584
|
+
}
|
|
3409
3585
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
const err = error;
|
|
3423
|
-
this.updateStatus({ lastError: err, currentTask: "Analysis failed" });
|
|
3424
|
-
throw err;
|
|
3586
|
+
}
|
|
3587
|
+
parseOperand(opnd) {
|
|
3588
|
+
if (!opnd) return null;
|
|
3589
|
+
if (opnd.match(/^[a-fA-F0-9]{2,6}$/)) {
|
|
3590
|
+
switch (opnd.length) {
|
|
3591
|
+
case 2:
|
|
3592
|
+
return new Byte(parseInt(opnd, 16));
|
|
3593
|
+
case 4:
|
|
3594
|
+
return new Word(parseInt(opnd, 16));
|
|
3595
|
+
case 6:
|
|
3596
|
+
return new Long(parseInt(opnd, 16));
|
|
3597
|
+
}
|
|
3425
3598
|
}
|
|
3599
|
+
return opnd;
|
|
3426
3600
|
}
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
if (!this.processor) {
|
|
3432
|
-
throw new Error("Project not loaded. Call loadProject() first.");
|
|
3601
|
+
processRawData() {
|
|
3602
|
+
let reverse = false;
|
|
3603
|
+
if (this.lineBuffer[0] === "#") {
|
|
3604
|
+
this.lineBuffer = this.lineBuffer.substring(1);
|
|
3433
3605
|
}
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
this.
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
this.
|
|
3445
|
-
|
|
3446
|
-
}
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3606
|
+
if (this.lineBuffer[0] === "$") {
|
|
3607
|
+
reverse = true;
|
|
3608
|
+
this.lineBuffer = this.lineBuffer.substring(1);
|
|
3609
|
+
}
|
|
3610
|
+
let hex;
|
|
3611
|
+
const symbolIx = this.lineBuffer.search(RomProcessingConstants.SYMBOL_SPACE_REGEX);
|
|
3612
|
+
if (symbolIx >= 0) {
|
|
3613
|
+
hex = this.lineBuffer.substring(0, symbolIx);
|
|
3614
|
+
this.lineBuffer = this.lineBuffer.substring(symbolIx).replace(/^[, \t]+/, "");
|
|
3615
|
+
} else {
|
|
3616
|
+
hex = this.lineBuffer;
|
|
3617
|
+
this.lineBuffer = "";
|
|
3618
|
+
}
|
|
3619
|
+
if (hex.length > 0) {
|
|
3620
|
+
if (RomProcessingConstants.ADDRESS_SPACE.includes(hex[0])) {
|
|
3621
|
+
this.currentBlock.objList.push(hex);
|
|
3622
|
+
this.currentBlock.size += RomProcessingConstants.getSize(hex);
|
|
3623
|
+
} else {
|
|
3624
|
+
const data = this.hexStringToBytes(hex);
|
|
3625
|
+
if (data.length === 1) {
|
|
3626
|
+
this.currentBlock.objList.push(new Byte(data[0]));
|
|
3627
|
+
this.currentBlock.size++;
|
|
3628
|
+
} else {
|
|
3629
|
+
if (reverse) {
|
|
3630
|
+
data.reverse();
|
|
3631
|
+
}
|
|
3632
|
+
if (data.length === 2) {
|
|
3633
|
+
this.currentBlock.objList.push(new Word(data[0] | data[1] << 8));
|
|
3634
|
+
this.currentBlock.size += 2;
|
|
3635
|
+
} else {
|
|
3636
|
+
this.currentBlock.objList.push(data);
|
|
3637
|
+
this.currentBlock.size += data.length;
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3450
3641
|
}
|
|
3451
3642
|
}
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
if (!this.processor) {
|
|
3457
|
-
throw new Error("Project not loaded. Call loadProject() first.");
|
|
3643
|
+
hexStringToBytes(hex) {
|
|
3644
|
+
const result = new Uint8Array(hex.length / 2);
|
|
3645
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
3646
|
+
result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
3458
3647
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3648
|
+
return result;
|
|
3649
|
+
}
|
|
3650
|
+
};
|
|
3651
|
+
var RomGenerator = class {
|
|
3652
|
+
constructor(modules, sourceData) {
|
|
3653
|
+
this.moduleLookup = /* @__PURE__ */ new Map();
|
|
3654
|
+
this.moduleList = ["jp-viper", "title-enhanced"];
|
|
3655
|
+
this.patchFiles = [];
|
|
3656
|
+
this.chunkFiles = [];
|
|
3657
|
+
this.asmFiles = [];
|
|
3658
|
+
this.dbRoot = {};
|
|
3659
|
+
this.sourceData = sourceData;
|
|
3660
|
+
this.moduleList = modules;
|
|
3661
|
+
const crc = crc32_buffer(this.sourceData);
|
|
3662
|
+
if (crc !== 473450688) {
|
|
3663
|
+
throw new Error("CRC mismatch, please provide the correct ROM");
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
async generateProject(projectName) {
|
|
3667
|
+
await this.initializeDatabase(projectName);
|
|
3668
|
+
const reader = this.initializeChunks();
|
|
3669
|
+
this.writeScriptTexts(reader);
|
|
3670
|
+
this.applyBaseRom();
|
|
3671
|
+
this.applyProjectInit();
|
|
3672
|
+
this.applySelectedModules();
|
|
3673
|
+
this.assembleCodeFromText();
|
|
3674
|
+
this.applyCodePatches();
|
|
3675
|
+
this.calculateSizes();
|
|
3676
|
+
this.performLayout();
|
|
3677
|
+
this.rebaseAssemblies();
|
|
3678
|
+
this.generateAsmIncludeLookups();
|
|
3679
|
+
const blockLookup = this.generateBlockLookup();
|
|
3680
|
+
const outRom = await this.writeRom(blockLookup);
|
|
3681
|
+
return outRom;
|
|
3682
|
+
}
|
|
3683
|
+
//Grab structures from supabase
|
|
3684
|
+
async initializeDatabase(projectName) {
|
|
3685
|
+
this.dbRoot = await DbRootUtils.fromSupabaseProject({ projectName });
|
|
3686
|
+
}
|
|
3687
|
+
//Read graph from rom
|
|
3688
|
+
initializeChunks() {
|
|
3689
|
+
const reader = new BlockReader(this.sourceData, this.dbRoot);
|
|
3690
|
+
this.chunkFiles = reader.analyzeAndResolve();
|
|
3691
|
+
this.asmFiles = this.chunkFiles.filter((b) => b.type === BinType.Assembly);
|
|
3692
|
+
return reader;
|
|
3693
|
+
}
|
|
3694
|
+
//Convert blocks to text
|
|
3695
|
+
writeScriptTexts(reader) {
|
|
3696
|
+
const writer = new BlockWriter(reader);
|
|
3697
|
+
for (const block of this.asmFiles) {
|
|
3698
|
+
block.textData = writer.generateAsm(block);
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
//Apply base rom files
|
|
3702
|
+
applyBaseRom() {
|
|
3703
|
+
for (const chunkFile of this.dbRoot.baseRomFiles) {
|
|
3704
|
+
this.applyPatchFile(chunkFile);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
applyProjectInit() {
|
|
3708
|
+
for (const chunkFile of this.dbRoot.projectFiles) {
|
|
3709
|
+
console.log(`Processing patch: ${chunkFile.name}`);
|
|
3710
|
+
if (chunkFile.group) {
|
|
3711
|
+
let modArray;
|
|
3712
|
+
if (!this.moduleLookup.has(chunkFile.group)) {
|
|
3713
|
+
this.moduleLookup.set(chunkFile.group, modArray = []);
|
|
3714
|
+
} else {
|
|
3715
|
+
modArray = this.moduleLookup.get(chunkFile.group);
|
|
3716
|
+
}
|
|
3717
|
+
modArray.push(chunkFile);
|
|
3718
|
+
} else {
|
|
3719
|
+
this.applyPatchFile(chunkFile);
|
|
3720
|
+
}
|
|
3461
3721
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
this.
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
});
|
|
3470
|
-
this.events.onBuildComplete?.(results);
|
|
3471
|
-
return results;
|
|
3472
|
-
} catch (error) {
|
|
3473
|
-
const err = error;
|
|
3474
|
-
this.updateStatus({ lastError: err, currentTask: "Build failed" });
|
|
3475
|
-
throw err;
|
|
3722
|
+
}
|
|
3723
|
+
//Apply selected project modules
|
|
3724
|
+
applySelectedModules() {
|
|
3725
|
+
for (const module of this.moduleList) {
|
|
3726
|
+
for (const file of this.moduleLookup.get(module)) {
|
|
3727
|
+
this.applyPatchFile(file);
|
|
3728
|
+
}
|
|
3476
3729
|
}
|
|
3477
3730
|
}
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3731
|
+
//Assemble code from script text
|
|
3732
|
+
assembleCodeFromText() {
|
|
3733
|
+
for (const block of this.asmFiles) {
|
|
3734
|
+
const assembler = new Assembler(this.dbRoot, block.textData);
|
|
3735
|
+
const { blocks, includes, reqBank } = assembler.parseAssembly();
|
|
3736
|
+
block.parts = blocks;
|
|
3737
|
+
block.includes = includes;
|
|
3738
|
+
block.bank = reqBank ?? void 0;
|
|
3484
3739
|
}
|
|
3485
|
-
return await this.processor.processScene(sceneId, metaFile);
|
|
3486
3740
|
}
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
getStatus() {
|
|
3491
|
-
return { ...this.status };
|
|
3741
|
+
//Apply patches
|
|
3742
|
+
applyCodePatches() {
|
|
3743
|
+
RomProcessor.applyPatches(this.asmFiles, this.patchFiles);
|
|
3492
3744
|
}
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3745
|
+
//Calculate sizes
|
|
3746
|
+
calculateSizes() {
|
|
3747
|
+
for (const asm of this.chunkFiles) {
|
|
3748
|
+
ChunkFileUtils.calculateSize(asm);
|
|
3749
|
+
}
|
|
3498
3750
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
return this.processor?.getProject().config || null;
|
|
3751
|
+
// Assign locations
|
|
3752
|
+
performLayout() {
|
|
3753
|
+
const layout = new RomLayout(this.chunkFiles);
|
|
3754
|
+
layout.organize();
|
|
3504
3755
|
}
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3756
|
+
// Rebase assemblies
|
|
3757
|
+
rebaseAssemblies() {
|
|
3758
|
+
for (const file of this.asmFiles) {
|
|
3759
|
+
ChunkFileUtils.rebase(file);
|
|
3760
|
+
}
|
|
3510
3761
|
}
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3762
|
+
// Build include lookup map per asm file
|
|
3763
|
+
generateAsmIncludeLookups() {
|
|
3764
|
+
for (const f of this.asmFiles) {
|
|
3765
|
+
const includeBlocks = this.asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
|
|
3766
|
+
f.includeLookup = /* @__PURE__ */ new Map();
|
|
3767
|
+
for (const b of includeBlocks) {
|
|
3768
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
3769
|
+
}
|
|
3770
|
+
for (const b of (f.parts || []).filter((x) => !!x.label)) {
|
|
3771
|
+
if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
applyPatchFile(chunkFile) {
|
|
3776
|
+
console.log(`Processing patch: ${chunkFile.name}`);
|
|
3777
|
+
const existing = this.chunkFiles.find((x) => x.name === chunkFile.name);
|
|
3778
|
+
if (chunkFile.type === BinType.Patch || chunkFile.type === BinType.Assembly) {
|
|
3779
|
+
if (existing) {
|
|
3780
|
+
existing.textData = chunkFile.textData;
|
|
3781
|
+
} else {
|
|
3782
|
+
if (chunkFile.type === BinType.Patch) {
|
|
3783
|
+
this.patchFiles.push(chunkFile);
|
|
3784
|
+
}
|
|
3785
|
+
this.asmFiles.push(chunkFile);
|
|
3786
|
+
this.chunkFiles.push(chunkFile);
|
|
3787
|
+
}
|
|
3788
|
+
} else {
|
|
3789
|
+
if (existing) {
|
|
3790
|
+
existing.rawData = chunkFile.rawData;
|
|
3791
|
+
existing.size = chunkFile.size;
|
|
3792
|
+
} else {
|
|
3793
|
+
this.chunkFiles.push(chunkFile);
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
// Create block lookup for resolving labels to locations
|
|
3798
|
+
generateBlockLookup() {
|
|
3799
|
+
const blockLookup = /* @__PURE__ */ new Map();
|
|
3800
|
+
for (const f of this.chunkFiles) {
|
|
3801
|
+
blockLookup.set(f.name.toUpperCase(), f.location);
|
|
3802
|
+
}
|
|
3803
|
+
return blockLookup;
|
|
3804
|
+
}
|
|
3805
|
+
async writeRom(blockLookup) {
|
|
3806
|
+
const romWriter = new RomWriter(this.dbRoot.entryPoints, "GAIALABS", "01JG ");
|
|
3807
|
+
for (const file of this.chunkFiles) {
|
|
3808
|
+
await romWriter.writeFile(file, blockLookup);
|
|
3809
|
+
}
|
|
3810
|
+
romWriter.writeHeader();
|
|
3811
|
+
romWriter.writeEntryPoints(this.asmFiles);
|
|
3812
|
+
romWriter.writeChecksum();
|
|
3813
|
+
return romWriter.outBuffer;
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
|
|
3817
|
+
// src/sprites/SpriteFrame.ts
|
|
3818
|
+
var SpriteFrame = class {
|
|
3819
|
+
constructor(duration = 0, groupIndex = 0, groupOffset = 0) {
|
|
3820
|
+
this.duration = duration;
|
|
3821
|
+
this.groupIndex = groupIndex;
|
|
3822
|
+
this.groupOffset = groupOffset;
|
|
3823
|
+
}
|
|
3824
|
+
};
|
|
3825
|
+
|
|
3826
|
+
// src/sprites/SpritePart.ts
|
|
3827
|
+
var SpritePart = class {
|
|
3828
|
+
constructor() {
|
|
3829
|
+
this.isLarge = false;
|
|
3830
|
+
this.xOffset = 0;
|
|
3831
|
+
this.xOffsetMirror = 0;
|
|
3832
|
+
this.yOffset = 0;
|
|
3833
|
+
this.yOffsetMirror = 0;
|
|
3834
|
+
this.vMirror = false;
|
|
3835
|
+
this.hMirror = false;
|
|
3836
|
+
this.someOffset = 0;
|
|
3837
|
+
this.paletteIndex = 0;
|
|
3838
|
+
this.tileIndex = 0;
|
|
3839
|
+
}
|
|
3840
|
+
};
|
|
3841
|
+
|
|
3842
|
+
// src/sprites/SpriteGroup.ts
|
|
3843
|
+
var SpriteGroup = class {
|
|
3844
|
+
constructor() {
|
|
3845
|
+
this.xOffset = 0;
|
|
3846
|
+
this.xOffsetMirror = 0;
|
|
3847
|
+
this.yOffset = 0;
|
|
3848
|
+
this.yOffsetMirror = 0;
|
|
3849
|
+
this.xRecoilHitboxOffset = 0;
|
|
3850
|
+
this.yRecoilHitboxOffset = 0;
|
|
3851
|
+
this.xRecoilHitboxTilesize = 0;
|
|
3852
|
+
this.yRecoilHitboxTilesize = 0;
|
|
3853
|
+
this.xHostileHitboxOffset = 0;
|
|
3854
|
+
this.xHostileHitboxSize = 0;
|
|
3855
|
+
this.yHostileHitboxOffset = 0;
|
|
3856
|
+
this.yHostileHitboxSize = 0;
|
|
3857
|
+
this.parts = [];
|
|
3858
|
+
}
|
|
3859
|
+
};
|
|
3860
|
+
|
|
3861
|
+
// src/sprites/SpriteMap.ts
|
|
3862
|
+
var SpriteMap = class _SpriteMap {
|
|
3863
|
+
constructor() {
|
|
3864
|
+
this.frameSets = [];
|
|
3865
|
+
this.groups = [];
|
|
3516
3866
|
}
|
|
3517
3867
|
/**
|
|
3518
|
-
*
|
|
3868
|
+
* Create a SpriteMap from binary data
|
|
3869
|
+
* @param data Binary data buffer
|
|
3870
|
+
* @returns SpriteMap instance
|
|
3519
3871
|
*/
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
isExtracted: false,
|
|
3526
|
-
canBuild: false,
|
|
3527
|
-
progress: 0
|
|
3872
|
+
static fromBytes(data) {
|
|
3873
|
+
let position = 0;
|
|
3874
|
+
const getByte = () => {
|
|
3875
|
+
if (position >= data.length) return 0;
|
|
3876
|
+
return data[position++];
|
|
3528
3877
|
};
|
|
3878
|
+
const getUshort = () => {
|
|
3879
|
+
if (position + 1 >= data.length) return 0;
|
|
3880
|
+
const low = data[position++];
|
|
3881
|
+
const high = data[position++];
|
|
3882
|
+
return low | high << 8;
|
|
3883
|
+
};
|
|
3884
|
+
const spriteMap = new _SpriteMap();
|
|
3885
|
+
const setOffsets = /* @__PURE__ */ new Set();
|
|
3886
|
+
const groupOffsets = /* @__PURE__ */ new Set();
|
|
3887
|
+
position = 0;
|
|
3888
|
+
while (!setOffsets.has(position)) {
|
|
3889
|
+
const offset = getUshort() - 16384;
|
|
3890
|
+
setOffsets.add(offset);
|
|
3891
|
+
}
|
|
3892
|
+
for (const offStart of setOffsets) {
|
|
3893
|
+
position = offStart;
|
|
3894
|
+
const frameList = [];
|
|
3895
|
+
while (true) {
|
|
3896
|
+
const duration = getUshort();
|
|
3897
|
+
if (duration === 65535) {
|
|
3898
|
+
break;
|
|
3899
|
+
}
|
|
3900
|
+
const groupOffset = getUshort() - 16384;
|
|
3901
|
+
frameList.push(new SpriteFrame(duration, 0, groupOffset));
|
|
3902
|
+
groupOffsets.add(groupOffset);
|
|
3903
|
+
}
|
|
3904
|
+
spriteMap.frameSets.push(frameList);
|
|
3905
|
+
}
|
|
3906
|
+
let grpIx = 0;
|
|
3907
|
+
const sortedGroupOffsets = Array.from(groupOffsets).sort((a, b) => a - b);
|
|
3908
|
+
for (const offStart of sortedGroupOffsets) {
|
|
3909
|
+
position = offStart;
|
|
3910
|
+
for (const set of spriteMap.frameSets) {
|
|
3911
|
+
for (const frame of set) {
|
|
3912
|
+
if (frame.groupOffset === offStart) {
|
|
3913
|
+
frame.groupIndex = grpIx;
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
const grp = new SpriteGroup();
|
|
3918
|
+
grp.xOffset = getByte();
|
|
3919
|
+
grp.xOffsetMirror = getByte();
|
|
3920
|
+
grp.yOffset = getByte();
|
|
3921
|
+
grp.yOffsetMirror = getByte();
|
|
3922
|
+
grp.xRecoilHitboxOffset = getByte();
|
|
3923
|
+
grp.yRecoilHitboxOffset = getByte();
|
|
3924
|
+
grp.xRecoilHitboxTilesize = getByte();
|
|
3925
|
+
grp.yRecoilHitboxTilesize = getByte();
|
|
3926
|
+
grp.xHostileHitboxOffset = getByte();
|
|
3927
|
+
grp.xHostileHitboxSize = getByte();
|
|
3928
|
+
grp.yHostileHitboxOffset = getByte();
|
|
3929
|
+
grp.yHostileHitboxSize = getByte();
|
|
3930
|
+
let numParts = getByte();
|
|
3931
|
+
while (numParts-- > 0) {
|
|
3932
|
+
const part = new SpritePart();
|
|
3933
|
+
part.isLarge = getByte() !== 0;
|
|
3934
|
+
part.xOffset = getByte();
|
|
3935
|
+
part.xOffsetMirror = getByte();
|
|
3936
|
+
part.yOffset = getByte();
|
|
3937
|
+
part.yOffsetMirror = getByte();
|
|
3938
|
+
const props = getUshort();
|
|
3939
|
+
part.vMirror = (props & 32768) !== 0;
|
|
3940
|
+
part.hMirror = (props & 16384) !== 0;
|
|
3941
|
+
part.someOffset = props >> 12 & 3;
|
|
3942
|
+
part.paletteIndex = props >> 9 & 7;
|
|
3943
|
+
part.tileIndex = props & 511;
|
|
3944
|
+
grp.parts.push(part);
|
|
3945
|
+
}
|
|
3946
|
+
spriteMap.groups.push(grp);
|
|
3947
|
+
grpIx++;
|
|
3948
|
+
}
|
|
3949
|
+
return spriteMap;
|
|
3529
3950
|
}
|
|
3530
3951
|
/**
|
|
3531
|
-
*
|
|
3952
|
+
* Convert this SpriteMap to binary data
|
|
3953
|
+
* @returns Binary data buffer
|
|
3532
3954
|
*/
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
const
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3955
|
+
toBytes() {
|
|
3956
|
+
let size = this.frameSets.length * 2;
|
|
3957
|
+
for (const set of this.frameSets) {
|
|
3958
|
+
size += set.length * 4 + 2;
|
|
3959
|
+
}
|
|
3960
|
+
for (const grp of this.groups) {
|
|
3961
|
+
size += grp.parts.length * 7 + 13;
|
|
3962
|
+
}
|
|
3963
|
+
const buffer = new Uint8Array(size);
|
|
3964
|
+
let position = 0;
|
|
3965
|
+
const writeShort = (val) => {
|
|
3966
|
+
buffer[position++] = val & 255;
|
|
3967
|
+
buffer[position++] = val >> 8 & 255;
|
|
3542
3968
|
};
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3969
|
+
const writeLoc = (val) => writeShort(val + 16384);
|
|
3970
|
+
let pos = this.frameSets.length << 1;
|
|
3971
|
+
const groupPos = new Array(this.groups.length);
|
|
3972
|
+
for (const set of this.frameSets) {
|
|
3973
|
+
writeLoc(pos);
|
|
3974
|
+
pos += (set.length << 2) + 2;
|
|
3975
|
+
}
|
|
3976
|
+
for (let i = 0; i < this.groups.length; i++) {
|
|
3977
|
+
groupPos[i] = pos;
|
|
3978
|
+
pos += this.groups[i].parts.length * 7 + 13;
|
|
3979
|
+
}
|
|
3980
|
+
for (const set of this.frameSets) {
|
|
3981
|
+
for (const frm of set) {
|
|
3982
|
+
writeShort(frm.duration);
|
|
3983
|
+
writeLoc(groupPos[frm.groupIndex]);
|
|
3984
|
+
}
|
|
3985
|
+
writeShort(65535);
|
|
3986
|
+
}
|
|
3987
|
+
for (const grp of this.groups) {
|
|
3988
|
+
buffer[position++] = grp.xOffset;
|
|
3989
|
+
buffer[position++] = grp.xOffsetMirror;
|
|
3990
|
+
buffer[position++] = grp.yOffset;
|
|
3991
|
+
buffer[position++] = grp.yOffsetMirror;
|
|
3992
|
+
buffer[position++] = grp.xRecoilHitboxOffset;
|
|
3993
|
+
buffer[position++] = grp.yRecoilHitboxOffset;
|
|
3994
|
+
buffer[position++] = grp.xRecoilHitboxTilesize;
|
|
3995
|
+
buffer[position++] = grp.yRecoilHitboxTilesize;
|
|
3996
|
+
buffer[position++] = grp.xHostileHitboxOffset;
|
|
3997
|
+
buffer[position++] = grp.xHostileHitboxSize;
|
|
3998
|
+
buffer[position++] = grp.yHostileHitboxOffset;
|
|
3999
|
+
buffer[position++] = grp.yHostileHitboxSize;
|
|
4000
|
+
buffer[position++] = grp.parts.length;
|
|
4001
|
+
for (const prt of grp.parts) {
|
|
4002
|
+
buffer[position++] = prt.isLarge ? 1 : 0;
|
|
4003
|
+
buffer[position++] = prt.xOffset;
|
|
4004
|
+
buffer[position++] = prt.xOffsetMirror;
|
|
4005
|
+
buffer[position++] = prt.yOffset;
|
|
4006
|
+
buffer[position++] = prt.yOffsetMirror;
|
|
4007
|
+
const accum = (prt.vMirror ? 32768 : 0) | (prt.hMirror ? 16384 : 0) | (prt.someOffset & 3) << 12 | (prt.paletteIndex & 7) << 9 | prt.tileIndex;
|
|
4008
|
+
writeShort(accum);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
return buffer.slice(0, position);
|
|
3547
4012
|
}
|
|
3548
4013
|
};
|
|
3549
4014
|
|
|
3550
|
-
// src/rom/rebuild/index.ts
|
|
3551
|
-
var ROM_REBUILD_MODULE = "gaia-core/rom/rebuild";
|
|
3552
|
-
|
|
3553
4015
|
// src/index.ts
|
|
3554
4016
|
var GAIA_CORE_VERSION = "0.1.0";
|
|
3555
4017
|
var isPlatformBrowser = typeof window !== "undefined";
|
|
3556
4018
|
var isPlatformNode = typeof process !== "undefined" && process.versions?.node;
|
|
3557
4019
|
var isPlatformWebWorker = typeof importScripts !== "undefined";
|
|
3558
4020
|
|
|
3559
|
-
export {
|
|
4021
|
+
export { AddressingModeHandler, AsmReader, Assembler, AssemblerState, BlockReader, BlockWriter, CopCommandProcessor, GAIA_CORE_VERSION, ObjectType, OperationContext, PostProcessor, ProcessorStateManager, ProjectRoot, QuintetLZ, ReferenceManager, Registers, RomDataReader, RomGenerator, RomLayout, RomProcessor, RomState, RomStateUtils, RomWriter, SortedMap, SpriteFrame, SpriteGroup, SpriteMap, SpritePart, Stack, StackOperations, StringProcessor, StringReader, TransformProcessor, TypeParser, isPlatformBrowser, isPlatformNode, isPlatformWebWorker, registerCompressionProviders };
|
|
3560
4022
|
//# sourceMappingURL=index.mjs.map
|
|
3561
4023
|
//# sourceMappingURL=index.mjs.map
|