@gaialabs/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,3561 @@
1
+ import { BitStream, RomProcessingConstants, BinType, AddressingMode, RegisterType, Address, DbBlockUtils, BlockReaderConstants, createTableEntry, LocationWrapper, createWord, createByte, StatusFlags, AddressSpace, AddressType, MemberType, readFileAsBinary, DbRootUtils, readJsonFile, getDirectory } from 'gaia-shared';
2
+ import { promises } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ // src/api/RomProcessor.ts
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
+ };
307
+ var QuintetLZ = class _QuintetLZ {
308
+ static {
309
+ this.DICTIONARY_SIZE = 256;
310
+ }
311
+ static {
312
+ this.DICTIONARY_INIT = 32;
313
+ }
314
+ static {
315
+ this.DICTIONARY_OFFSET = 239;
316
+ }
317
+ static {
318
+ this.DEFAULT_PAGE_SIZE = 32768;
319
+ }
320
+ /**
321
+ * Expand (decompress) data using QuintetLZ algorithm
322
+ * @param srcData Source data buffer
323
+ * @param srcPosition Starting position in source data
324
+ * @param srcLen Length of source data to process
325
+ * @returns Expanded data
326
+ */
327
+ expand(srcData, srcPosition = 0, srcLen = _QuintetLZ.DEFAULT_PAGE_SIZE) {
328
+ const bitStream = new BitStream(srcData, srcPosition);
329
+ const srcStop = srcPosition + srcLen;
330
+ const dictionary = new Uint8Array(_QuintetLZ.DICTIONARY_SIZE);
331
+ dictionary.fill(_QuintetLZ.DICTIONARY_INIT);
332
+ let dictPosition = _QuintetLZ.DICTIONARY_OFFSET;
333
+ let outPosition = 0;
334
+ const dstLen = bitStream.readShort();
335
+ const outBuffer = new Uint8Array(dstLen);
336
+ while (bitStream.currentPosition < srcStop && outPosition < dstLen) {
337
+ if (bitStream.readBit()) {
338
+ const sample = bitStream.readByte();
339
+ if (outPosition < dstLen) {
340
+ outBuffer[outPosition++] = sample;
341
+ }
342
+ dictionary[dictPosition] = sample;
343
+ dictPosition = dictPosition + 1 & 255;
344
+ } else {
345
+ let wordIndex = bitStream.readByte();
346
+ let wordLength = bitStream.readNibble() + 2;
347
+ while (wordLength-- > 0) {
348
+ const sample = dictionary[wordIndex];
349
+ wordIndex = wordIndex + 1 & 255;
350
+ if (outPosition < dstLen) {
351
+ outBuffer[outPosition++] = sample;
352
+ }
353
+ dictionary[dictPosition] = sample;
354
+ dictPosition = dictPosition + 1 & 255;
355
+ }
356
+ }
357
+ }
358
+ if (outPosition < dstLen) {
359
+ return outBuffer.slice(0, outPosition);
360
+ }
361
+ return outBuffer;
362
+ }
363
+ /**
364
+ * Compact (compress) data using QuintetLZ algorithm
365
+ * @param srcData Source data to compress
366
+ * @returns Compressed data
367
+ */
368
+ compact(srcData) {
369
+ const dictionary = new Uint8Array(_QuintetLZ.DICTIONARY_SIZE);
370
+ dictionary.fill(_QuintetLZ.DICTIONARY_INIT);
371
+ let offset = _QuintetLZ.DICTIONARY_OFFSET;
372
+ let srcIx = 0;
373
+ let dstIx = 0;
374
+ const srcLen = srcData.length;
375
+ const outputBuffer = new Uint8Array(srcLen * 2);
376
+ outputBuffer[dstIx++] = srcLen & 255;
377
+ outputBuffer[dstIx++] = srcLen >> 8 & 255;
378
+ const bitStream = new BitStream(outputBuffer, 2);
379
+ const getCommand = () => {
380
+ const maxLen = Math.min(srcLen - srcIx, 17);
381
+ if (maxLen < 2) {
382
+ return [0, 0];
383
+ }
384
+ let startByte = 0;
385
+ let bestLen = 0;
386
+ let bx = offset;
387
+ for (let i = 0; i < 256; i++, bx = bx + 1 & 255) {
388
+ let size = 0;
389
+ let bix = bx;
390
+ while (size < maxLen && dictionary[bix] === srcData[srcIx + size]) {
391
+ bix = bix + 1 & 255;
392
+ if (bix === offset) {
393
+ bix = bx;
394
+ }
395
+ size++;
396
+ }
397
+ if (size > bestLen) {
398
+ startByte = bx;
399
+ bestLen = size;
400
+ if (bestLen >= maxLen) {
401
+ break;
402
+ }
403
+ }
404
+ }
405
+ return [startByte, bestLen];
406
+ };
407
+ while (srcIx < srcLen) {
408
+ const [cmdStart, cmdLen] = getCommand();
409
+ if (cmdLen >= 2) {
410
+ bitStream.writeBit(false);
411
+ bitStream.writeByte(cmdStart);
412
+ bitStream.writeNibble(cmdLen - 2);
413
+ for (let i = 0; i < cmdLen; i++) {
414
+ dictionary[offset] = srcData[srcIx++];
415
+ offset = offset + 1 & 255;
416
+ }
417
+ } else {
418
+ bitStream.writeBit(true);
419
+ const val = srcData[srcIx++];
420
+ bitStream.writeByte(val);
421
+ dictionary[offset] = val;
422
+ offset = offset + 1 & 255;
423
+ }
424
+ }
425
+ bitStream.flush();
426
+ const finalSize = Math.max(dstIx, bitStream.currentPosition);
427
+ return outputBuffer.slice(0, finalSize);
428
+ }
429
+ };
430
+
431
+ // src/rom/extraction/processor.ts
432
+ var ProcessorStateManager = class {
433
+ constructor() {
434
+ this.accumulatorFlags = /* @__PURE__ */ new Map();
435
+ this.indexFlags = /* @__PURE__ */ new Map();
436
+ this.bankNotes = /* @__PURE__ */ new Map();
437
+ this.stackPositions = /* @__PURE__ */ new Map();
438
+ }
439
+ /**
440
+ * Hydrate processor registers with stored state
441
+ * Uses Registers from gaia-core/assembly for processor state management
442
+ */
443
+ hydrateRegisters(position, reg) {
444
+ const acc = this.accumulatorFlags.get(position);
445
+ if (acc !== void 0) {
446
+ reg.accumulatorFlag = acc;
447
+ }
448
+ const ind = this.indexFlags.get(position);
449
+ if (ind !== void 0) {
450
+ reg.indexFlag = ind;
451
+ }
452
+ const bnk = this.bankNotes.get(position);
453
+ if (bnk !== void 0) {
454
+ reg.dataBank = bnk;
455
+ }
456
+ const stack = this.stackPositions.get(position);
457
+ if (stack !== void 0) {
458
+ reg.stack.location = stack;
459
+ }
460
+ }
461
+ // Accumulator flag methods
462
+ getAccumulatorFlag(location) {
463
+ return this.accumulatorFlags.get(location);
464
+ }
465
+ setAccumulatorFlag(location, value) {
466
+ this.accumulatorFlags.set(location, value);
467
+ }
468
+ tryAddAccumulatorFlag(location, value) {
469
+ if (this.accumulatorFlags.has(location)) {
470
+ return false;
471
+ }
472
+ this.accumulatorFlags.set(location, value);
473
+ return true;
474
+ }
475
+ // Index flag methods
476
+ getIndexFlag(location) {
477
+ return this.indexFlags.get(location);
478
+ }
479
+ setIndexFlag(location, value) {
480
+ this.indexFlags.set(location, value);
481
+ }
482
+ tryAddIndexFlag(location, value) {
483
+ if (this.indexFlags.has(location)) {
484
+ return false;
485
+ }
486
+ this.indexFlags.set(location, value);
487
+ return true;
488
+ }
489
+ // Bank note methods
490
+ getBankNote(location) {
491
+ return this.bankNotes.get(location);
492
+ }
493
+ setBankNote(location, value) {
494
+ this.bankNotes.set(location, value);
495
+ }
496
+ // Stack position methods
497
+ getStackPosition(location) {
498
+ return this.stackPositions.get(location);
499
+ }
500
+ setStackPosition(location, value) {
501
+ this.stackPositions.set(location, value);
502
+ }
503
+ tryAddStackPosition(location, value) {
504
+ if (this.stackPositions.has(location)) {
505
+ return false;
506
+ }
507
+ this.stackPositions.set(location, value);
508
+ return true;
509
+ }
510
+ };
511
+
512
+ // src/rom/extraction/reader.ts
513
+ var RomDataReader = class {
514
+ constructor(romData) {
515
+ if (!romData) {
516
+ throw new Error("romData cannot be null");
517
+ }
518
+ this.romData = romData;
519
+ this.position = 0;
520
+ }
521
+ readByte() {
522
+ return this.romData[this.position++];
523
+ }
524
+ readSByte() {
525
+ const value = this.readByte();
526
+ return value > 127 ? value - 256 : value;
527
+ }
528
+ readUShort() {
529
+ return this.readByte() | this.readByte() << 8;
530
+ }
531
+ readShort() {
532
+ const value = this.readUShort();
533
+ return value > 32767 ? value - 65536 : value;
534
+ }
535
+ readAddress() {
536
+ return this.readByte() | this.readByte() << 8 | this.readByte() << 16;
537
+ }
538
+ readInt() {
539
+ return this.readByte() | this.readByte() << 8 | this.readByte() << 16 | this.readByte() << 24;
540
+ }
541
+ peekByte() {
542
+ return this.romData[this.position];
543
+ }
544
+ peekShort() {
545
+ return this.romData[this.position] | this.romData[this.position + 1] << 8;
546
+ }
547
+ peekAddress() {
548
+ return this.romData[this.position] | this.romData[this.position + 1] << 8 | this.romData[this.position + 2] << 16;
549
+ }
550
+ };
551
+ var ReferenceManager = class {
552
+ constructor(root) {
553
+ this.structTable = /* @__PURE__ */ new Map();
554
+ this.markerTable = /* @__PURE__ */ new Map();
555
+ this.nameTable = /* @__PURE__ */ new Map();
556
+ if (!root) {
557
+ throw new Error("root cannot be null");
558
+ }
559
+ this.root = root;
560
+ }
561
+ // Struct management
562
+ tryGetStruct(location) {
563
+ const chunkType = this.structTable.get(location);
564
+ return { found: chunkType !== void 0, chunkType };
565
+ }
566
+ tryAddStruct(location, chunkType) {
567
+ if (this.structTable.has(location)) {
568
+ return false;
569
+ }
570
+ this.structTable.set(location, chunkType);
571
+ return true;
572
+ }
573
+ containsStruct(location) {
574
+ return this.structTable.has(location);
575
+ }
576
+ // Name management
577
+ tryGetName(location) {
578
+ const referenceName = this.nameTable.get(location);
579
+ return { found: referenceName !== void 0, referenceName };
580
+ }
581
+ tryAddName(location, referenceName) {
582
+ if (this.nameTable.has(location)) {
583
+ return false;
584
+ }
585
+ this.nameTable.set(location, referenceName);
586
+ return true;
587
+ }
588
+ // Marker management
589
+ tryGetMarker(location) {
590
+ const offset = this.markerTable.get(location);
591
+ return { found: offset !== void 0, offset };
592
+ }
593
+ setMarker(location, offset) {
594
+ this.markerTable.set(location, offset);
595
+ }
596
+ // Label creation
597
+ createBranchLabel(location) {
598
+ const name = `loc_${location.toString(16).toUpperCase().padStart(6, "0")}`;
599
+ this.nameTable.set(location, name);
600
+ return name;
601
+ }
602
+ createTypeName(type, location) {
603
+ let name = type.toLowerCase();
604
+ while (name.length > 0 && BlockReaderConstants.POINTER_CHARACTERS.includes(name[0])) {
605
+ name = name.substring(1) + "_list";
606
+ }
607
+ return `${name}_${location.toString(16).toUpperCase().padStart(6, "0")}`;
608
+ }
609
+ createFallbackName(location) {
610
+ const fileMatch = this.root.files.find(
611
+ (x) => x.start <= location && x.end > location
612
+ );
613
+ if (fileMatch) {
614
+ const offset = location - fileMatch.start;
615
+ return fileMatch.name + (offset !== 0 ? `+${offset.toString(16).toUpperCase()}` : "");
616
+ }
617
+ return location.toString(16).toUpperCase().padStart(6, "0");
618
+ }
619
+ /**
620
+ * Finds a reference location by its assigned name.
621
+ */
622
+ findLocationByName(name) {
623
+ for (const [loc, refName] of this.nameTable) {
624
+ if (refName === name) {
625
+ return loc;
626
+ }
627
+ }
628
+ return void 0;
629
+ }
630
+ resolveName(location, type, isBranch) {
631
+ const prefix = Address.codeFromType(type);
632
+ let name;
633
+ let label = null;
634
+ let resolvedLocation = location;
635
+ const rewrite = this.root.rewrites[location];
636
+ if (rewrite !== void 0) {
637
+ const result = this.processRewrite(location, rewrite);
638
+ resolvedLocation = result.location;
639
+ label = result.label;
640
+ }
641
+ const existingName = this.nameTable.get(resolvedLocation);
642
+ if (existingName) {
643
+ name = existingName;
644
+ } else {
645
+ name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
646
+ }
647
+ return `${prefix || ""}${name}${label || ""}`;
648
+ }
649
+ findClosestReference(location) {
650
+ let closestDistance = BlockReaderConstants.REF_SEARCH_MAX_RANGE;
651
+ let closestName = null;
652
+ let closestLocation = null;
653
+ for (const [entryKey, entryValue] of this.nameTable) {
654
+ if (entryKey > location) {
655
+ continue;
656
+ }
657
+ const distance = location - entryKey;
658
+ if (distance >= closestDistance) {
659
+ continue;
660
+ }
661
+ closestDistance = distance;
662
+ closestName = entryValue;
663
+ closestLocation = entryKey;
664
+ if (closestDistance === 1) {
665
+ break;
666
+ }
667
+ }
668
+ return this.processClosestMatch(location, closestName, closestLocation, closestDistance);
669
+ }
670
+ processRewrite(location, rewrite) {
671
+ const offset = location - rewrite;
672
+ const cmd = offset < 0 ? "-" : "+";
673
+ const absOffset = Math.abs(offset);
674
+ let label = null;
675
+ const structType = this.structTable.get(rewrite);
676
+ if (structType === BlockReaderConstants.WIDE_STRING_TYPE) {
677
+ this.markerTable.set(rewrite, absOffset);
678
+ this.markerTable.set(location, absOffset);
679
+ label = cmd === "-" ? BlockReaderConstants.NEGATIVE_MARKER_FORMAT : BlockReaderConstants.MARKER_FORMAT;
680
+ } else {
681
+ const formatString = cmd === "-" ? BlockReaderConstants.NEGATIVE_OFFSET_FORMAT : BlockReaderConstants.OFFSET_FORMAT;
682
+ label = formatString.replace("{0:X}", absOffset.toString(16).toUpperCase());
683
+ }
684
+ return { location: rewrite, label };
685
+ }
686
+ processClosestMatch(location, closestName, closestLocation, closestDistance) {
687
+ if (!closestName || closestLocation === null) {
688
+ return null;
689
+ }
690
+ let result = closestName;
691
+ const structType = this.structTable.get(closestLocation);
692
+ if (structType === BlockReaderConstants.WIDE_STRING_TYPE) {
693
+ this.markerTable.set(closestLocation, closestDistance);
694
+ this.markerTable.set(location, closestDistance);
695
+ result += BlockReaderConstants.MARKER_FORMAT;
696
+ } else {
697
+ result += `+${closestDistance.toString(16).toUpperCase()}`;
698
+ }
699
+ return result;
700
+ }
701
+ };
702
+
703
+ // src/rom/extraction/stack.ts
704
+ var StackOperations = class {
705
+ constructor(registers, blockReader) {
706
+ this._registers = registers;
707
+ this._blockReader = blockReader;
708
+ }
709
+ handleStackOperation(mnemonic) {
710
+ switch (mnemonic) {
711
+ case "PHD":
712
+ this._registers.stack.push(this._registers.direct ?? 0);
713
+ break;
714
+ case "PLD":
715
+ this._registers.direct = this._registers.stack.popUInt16();
716
+ break;
717
+ case "PHK":
718
+ this._registers.stack.push(this._blockReader._romDataReader.position >> 16 | 128);
719
+ break;
720
+ case "PHB":
721
+ this._registers.stack.push(this._registers.dataBank ?? 129);
722
+ break;
723
+ case "PLB":
724
+ this._registers.dataBank = this._registers.stack.popByte();
725
+ break;
726
+ case "PHP":
727
+ this._registers.stack.push(this._registers.statusFlags);
728
+ break;
729
+ case "PLP":
730
+ this._registers.statusFlags = this._registers.stack.popByte();
731
+ break;
732
+ case "PHA":
733
+ this.handleAccumulatorPush();
734
+ break;
735
+ case "PLA":
736
+ this.handleAccumulatorPull();
737
+ break;
738
+ case "PHX":
739
+ this.handleXIndexPush();
740
+ break;
741
+ case "PLX":
742
+ this.handleXIndexPull();
743
+ break;
744
+ case "PHY":
745
+ this.handleYIndexPush();
746
+ break;
747
+ case "PLY":
748
+ this.handleYIndexPull();
749
+ break;
750
+ case "XBA":
751
+ this.handleExchangeBytes();
752
+ break;
753
+ }
754
+ }
755
+ handleAccumulatorPush() {
756
+ if (this._registers.accumulatorFlag === true) {
757
+ this._registers.stack.push((this._registers.accumulator ?? 0) & 255);
758
+ } else {
759
+ this._registers.stack.pushUInt16(this._registers.accumulator ?? 0);
760
+ }
761
+ }
762
+ handleAccumulatorPull() {
763
+ if (this._registers.accumulatorFlag === true) {
764
+ this._registers.accumulator = (this._registers.accumulator ?? 0) & 65280 | this._registers.stack.popByte();
765
+ } else {
766
+ this._registers.accumulator = this._registers.stack.popUInt16();
767
+ }
768
+ }
769
+ handleXIndexPush() {
770
+ if (this._registers.indexFlag === true) {
771
+ this._registers.stack.push((this._registers.xIndex ?? 0) & 255);
772
+ } else {
773
+ this._registers.stack.pushUInt16(this._registers.xIndex ?? 0);
774
+ }
775
+ }
776
+ handleXIndexPull() {
777
+ if (this._registers.indexFlag === true) {
778
+ this._registers.xIndex = (this._registers.xIndex ?? 0) & 65280 | this._registers.stack.popByte();
779
+ } else {
780
+ this._registers.xIndex = this._registers.stack.popUInt16();
781
+ }
782
+ }
783
+ handleYIndexPush() {
784
+ if (this._registers.indexFlag === true) {
785
+ this._registers.stack.push((this._registers.yIndex ?? 0) & 255);
786
+ } else {
787
+ this._registers.stack.pushUInt16(this._registers.yIndex ?? 0);
788
+ }
789
+ }
790
+ handleYIndexPull() {
791
+ if (this._registers.indexFlag === true) {
792
+ this._registers.yIndex = (this._registers.yIndex ?? 0) & 65280 | this._registers.stack.popByte();
793
+ } else {
794
+ this._registers.yIndex = this._registers.stack.popUInt16();
795
+ }
796
+ }
797
+ handleExchangeBytes() {
798
+ const acc = this._registers.accumulator ?? 0;
799
+ this._registers.accumulator = (acc >> 8 | acc << 8) & 65535;
800
+ }
801
+ };
802
+ var CopCommandProcessor = class {
803
+ constructor(blockReader) {
804
+ this._blockReader = blockReader;
805
+ this._romDataReader = blockReader._romDataReader;
806
+ }
807
+ /**
808
+ * Parses a COP command based on its definition
809
+ */
810
+ parseCopCommand(copDef, operands) {
811
+ for (const partStr of copDef.parts) {
812
+ const addrType = Address.typeFromCode(partStr[0]);
813
+ const isPtr = addrType !== AddressType.Unknown;
814
+ const referenceType = isPtr ? partStr.substring(1) : this._blockReader._currentPart?.struct ?? "Binary";
815
+ const memberTypeName = isPtr ? addrType.toString() : partStr;
816
+ const memberType = this.tryParseMemberType(memberTypeName);
817
+ if (memberType === null) {
818
+ throw new Error("Cannot use structs in cop def");
819
+ }
820
+ const label = this._blockReader._root.labels[this._romDataReader.position];
821
+ if (label) {
822
+ this._romDataReader.position += this.getMemberTypeSize(memberType);
823
+ operands.push(label);
824
+ } else {
825
+ operands.push(this.readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType));
826
+ }
827
+ }
828
+ }
829
+ tryParseMemberType(memberTypeName) {
830
+ const upperName = memberTypeName.toUpperCase();
831
+ for (const [key, value] of Object.entries(MemberType)) {
832
+ if (key.toUpperCase() === upperName) {
833
+ return value;
834
+ }
835
+ }
836
+ return null;
837
+ }
838
+ getMemberTypeSize(memberType) {
839
+ switch (memberType) {
840
+ case MemberType.Byte:
841
+ return 1;
842
+ case MemberType.Word:
843
+ case MemberType.Offset:
844
+ return 2;
845
+ case MemberType.Address:
846
+ return 3;
847
+ default:
848
+ throw new Error("Unsupported COP member type");
849
+ }
850
+ }
851
+ readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType) {
852
+ switch (memberType) {
853
+ case MemberType.Byte:
854
+ return createByte(this._romDataReader.readByte());
855
+ case MemberType.Word:
856
+ return createWord(this._romDataReader.readUShort());
857
+ case MemberType.Offset:
858
+ return this.createCopLocation(this._romDataReader.readUShort(), null, partStr, isPtr, referenceType, addrType);
859
+ case MemberType.Address:
860
+ return this.createCopLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), partStr, isPtr, referenceType, addrType);
861
+ default:
862
+ throw new Error("Unsupported COP member type");
863
+ }
864
+ }
865
+ createCopLocation(offset, bank, partStr, isPtr, otherStr, type) {
866
+ if (bank === null && offset === 0) {
867
+ return createWord(offset);
868
+ }
869
+ const addr = new Address(bank ?? this._romDataReader.position >> 16, offset);
870
+ if (addr.space === AddressSpace.ROM) {
871
+ const location = addr.toInt();
872
+ if (partStr !== "Address" && isPtr && !this._blockReader._root.rewrites[location]) {
873
+ this._blockReader.noteType(location, otherStr, true);
874
+ }
875
+ if (type === AddressType.Unknown) {
876
+ type = this.tryParseAddressType(partStr) ?? AddressType.Unknown;
877
+ }
878
+ return new LocationWrapper(location, type);
879
+ }
880
+ return addr;
881
+ }
882
+ tryParseAddressType(addressTypeName) {
883
+ const upperName = addressTypeName.toUpperCase();
884
+ for (const [key, value] of Object.entries(AddressType)) {
885
+ if (key.toUpperCase() === upperName) {
886
+ return value;
887
+ }
888
+ }
889
+ return null;
890
+ }
891
+ };
892
+
893
+ // src/rom/extraction/addressing.ts
894
+ var OperationContext = class {
895
+ constructor() {
896
+ this.size = 0;
897
+ this.nextAddress = 0;
898
+ this.xForm1 = null;
899
+ this.xForm2 = null;
900
+ this.copDef = null;
901
+ }
902
+ };
903
+ var AddressingModeHandler = class {
904
+ constructor(blockReader, transformProcessor) {
905
+ this._blockReader = blockReader;
906
+ this._transformProcessor = transformProcessor;
907
+ this._copProcessor = new CopCommandProcessor(blockReader);
908
+ this._dataReader = blockReader._romDataReader;
909
+ }
910
+ processAddressingMode(code, context, reg) {
911
+ const operands = [];
912
+ switch (code.mode) {
913
+ case AddressingMode.Stack:
914
+ case AddressingMode.Implied:
915
+ this.handleStackOrImpliedMode(code.mnem, reg);
916
+ break;
917
+ case AddressingMode.Immediate:
918
+ this.handleImmediateMode(code.mnem, context.size, operands, reg);
919
+ break;
920
+ case AddressingMode.AbsoluteIndirect:
921
+ case AddressingMode.AbsoluteIndirectLong:
922
+ case AddressingMode.AbsoluteIndexedIndirect:
923
+ case AddressingMode.Absolute:
924
+ case AddressingMode.AbsoluteIndexedX:
925
+ case AddressingMode.AbsoluteIndexedY:
926
+ this.handleAbsoluteMode(code.mnem, void 0, context.nextAddress, reg.dataBank, operands, reg);
927
+ break;
928
+ case AddressingMode.AbsoluteLong:
929
+ case AddressingMode.AbsoluteLongIndexedX:
930
+ this.handleAbsoluteLongMode(code.mnem, operands, reg);
931
+ break;
932
+ case AddressingMode.BlockMove:
933
+ this.handleBlockMoveMode(operands, context);
934
+ break;
935
+ case AddressingMode.DirectPage:
936
+ case AddressingMode.DirectPageIndexedIndirectX:
937
+ case AddressingMode.DirectPageIndexedX:
938
+ case AddressingMode.DirectPageIndexedY:
939
+ case AddressingMode.DirectPageIndirect:
940
+ case AddressingMode.DirectPageIndirectIndexedY:
941
+ case AddressingMode.DirectPageIndirectLong:
942
+ case AddressingMode.DirectPageIndirectLongIndexedY:
943
+ this.handleDirectPageMode(operands);
944
+ break;
945
+ case AddressingMode.PCRelative:
946
+ this.handlePCRelativeMode(operands, context.nextAddress, reg, false);
947
+ break;
948
+ case AddressingMode.PCRelativeLong:
949
+ this.handlePCRelativeMode(operands, context.nextAddress, reg, true);
950
+ break;
951
+ case AddressingMode.StackRelative:
952
+ case AddressingMode.StackRelativeIndirectIndexedY:
953
+ this.handleStackRelativeMode(operands);
954
+ break;
955
+ case AddressingMode.StackInterrupt:
956
+ this.handleStackInterruptMode(code.mnem, operands, context);
957
+ break;
958
+ }
959
+ return operands;
960
+ }
961
+ handleImmediateMode(mnemonic, size, operands, reg) {
962
+ const operand = this.readImmediateOperand(size);
963
+ operands.push(operand);
964
+ this.updateRegisterForImmediateInstruction(mnemonic, operand, reg);
965
+ }
966
+ readImmediateOperand(size) {
967
+ return size === 3 ? createWord(this._dataReader.readUShort()) : createByte(this._dataReader.readByte());
968
+ }
969
+ updateRegisterForImmediateInstruction(mnemonic, operand, reg) {
970
+ const value = typeof operand === "number" ? operand : operand.value;
971
+ switch (mnemonic) {
972
+ case "LDA":
973
+ reg.accumulator = this.calculateRegisterValue(reg.accumulator, value);
974
+ break;
975
+ case "LDX":
976
+ reg.xIndex = this.calculateRegisterValue(reg.xIndex, value);
977
+ break;
978
+ case "LDY":
979
+ reg.yIndex = this.calculateRegisterValue(reg.yIndex, value);
980
+ break;
981
+ case "SEP":
982
+ case "REP":
983
+ this.updateStatusFlags(mnemonic, value);
984
+ break;
985
+ }
986
+ }
987
+ calculateRegisterValue(currentValue, operand) {
988
+ if (operand > 255) {
989
+ return operand;
990
+ } else {
991
+ return (currentValue ?? 0) & 65280 | operand;
992
+ }
993
+ }
994
+ updateStatusFlags(mnemonic, flagValue) {
995
+ const flag = flagValue;
996
+ const isSep = mnemonic === "SEP";
997
+ if (flag & StatusFlags.AccumulatorMode) {
998
+ this._blockReader.AccumulatorFlags.set(this._dataReader.position, isSep);
999
+ }
1000
+ if (flag & StatusFlags.IndexMode) {
1001
+ this._blockReader.IndexFlags.set(this._dataReader.position, isSep);
1002
+ }
1003
+ }
1004
+ handleAbsoluteLongMode(mnemonic, operands, reg) {
1005
+ const refLoc = this._dataReader.readUShort();
1006
+ const bank = this._dataReader.readByte();
1007
+ const address = new Address(bank, refLoc);
1008
+ if (address.space === AddressSpace.ROM) {
1009
+ const wrapper = new LocationWrapper(address.toInt(), AddressType.Address);
1010
+ if (this.isJumpInstruction(mnemonic)) {
1011
+ this._blockReader.noteType(wrapper.location, "Code", false, reg);
1012
+ }
1013
+ operands.push(wrapper);
1014
+ } else {
1015
+ operands.push(address);
1016
+ }
1017
+ }
1018
+ handleBlockMoveMode(operands, context) {
1019
+ operands.push(createByte(this._dataReader.readByte()));
1020
+ context.xForm2 = this._transformProcessor.getTransform();
1021
+ operands.push(createByte(this._dataReader.readByte()));
1022
+ }
1023
+ handleDirectPageMode(operands) {
1024
+ operands.push(createByte(this._dataReader.readByte()));
1025
+ }
1026
+ handlePCRelativeMode(operands, nextAddress, reg, isLong) {
1027
+ const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
1028
+ this._blockReader.noteType(relative, "Code", void 0, reg);
1029
+ operands.push(new LocationWrapper(relative, AddressType.Relative));
1030
+ }
1031
+ handleStackRelativeMode(operands) {
1032
+ operands.push(createByte(this._dataReader.readByte()));
1033
+ }
1034
+ handleStackInterruptMode(mnemonic, operands, context) {
1035
+ const cmd = this._dataReader.readByte();
1036
+ operands.push(cmd);
1037
+ if (mnemonic === "COP") {
1038
+ const copDef = this._blockReader._root.copDef[cmd];
1039
+ if (!copDef) {
1040
+ throw new Error("Unknown COP command");
1041
+ }
1042
+ context.copDef = copDef;
1043
+ this._copProcessor.parseCopCommand(copDef, operands);
1044
+ }
1045
+ }
1046
+ handleStackOrImpliedMode(mnemonic, reg) {
1047
+ const stackOperations = new StackOperations(reg, this._blockReader);
1048
+ stackOperations.handleStackOperation(mnemonic);
1049
+ }
1050
+ handleAbsoluteMode(mnemonic, xBank1, next, dataBank, operands, registers) {
1051
+ let refLoc = this._dataReader.readUShort();
1052
+ const isPush = this.isPushInstruction(mnemonic);
1053
+ if (isPush) {
1054
+ refLoc++;
1055
+ }
1056
+ const isJump = isPush || this.isJumpInstruction(mnemonic);
1057
+ const bank = xBank1 ?? (isJump ? this._dataReader.position >> 16 : dataBank ?? 129);
1058
+ const addr = new Address(bank, refLoc);
1059
+ if (addr.isROM) {
1060
+ const wrapper = new LocationWrapper(addr.toInt(), AddressType.Offset);
1061
+ if (isJump) {
1062
+ const name = this._blockReader.noteType(wrapper.location, "Code", isPush, registers);
1063
+ if (isPush) {
1064
+ operands.push(`&${name}-1`);
1065
+ return;
1066
+ }
1067
+ }
1068
+ operands.push(wrapper);
1069
+ } else {
1070
+ operands.push(addr);
1071
+ }
1072
+ }
1073
+ isJumpInstruction(mnemonic) {
1074
+ return mnemonic[0] === "J";
1075
+ }
1076
+ isPushInstruction(mnemonic) {
1077
+ return mnemonic[0] === "P";
1078
+ }
1079
+ };
1080
+ var TransformProcessor = class _TransformProcessor {
1081
+ static {
1082
+ // Location regex pattern: _([A-Fa-f0-9]{6})
1083
+ this.LOCATION_REGEX = /_([A-Fa-f0-9]{6})/;
1084
+ }
1085
+ constructor(romReader) {
1086
+ this._blockReader = romReader;
1087
+ this._romDataReader = romReader._romDataReader;
1088
+ this._referenceManager = romReader._referenceManager;
1089
+ this._labelLookup = romReader._root.labels;
1090
+ }
1091
+ /**
1092
+ * Retrieves transform information for the current ROM position
1093
+ */
1094
+ getTransform() {
1095
+ const transform = this._labelLookup[this._romDataReader.position];
1096
+ if (transform === "") {
1097
+ return transform;
1098
+ } else if (!transform) {
1099
+ return null;
1100
+ }
1101
+ const transformName = this.cleanTransformName(transform);
1102
+ const referenceLocation = this.resolveTransformReference(transformName);
1103
+ if (referenceLocation !== null) {
1104
+ this._blockReader.resolveInclude(referenceLocation, false);
1105
+ }
1106
+ return transform;
1107
+ }
1108
+ /**
1109
+ * Applies transforms to operands
1110
+ */
1111
+ applyTransforms(op1Label, op2Label, operands) {
1112
+ this.applyTransform(op1Label, 0, operands);
1113
+ this.applyTransform(op2Label, 1, operands);
1114
+ }
1115
+ applyTransform(transform, operandIndex, operands) {
1116
+ if (transform === null || transform === void 0 || operandIndex >= operands.length) {
1117
+ return;
1118
+ }
1119
+ if (transform === "") {
1120
+ this.applyDefaultTransform(operandIndex, operands);
1121
+ } else {
1122
+ operands[operandIndex] = transform;
1123
+ }
1124
+ }
1125
+ applyDefaultTransform(operandIndex, operands) {
1126
+ let value = this._romDataReader.position & 16711680;
1127
+ const opnd = operands[operandIndex];
1128
+ if (opnd && "value" in opnd) {
1129
+ value |= opnd["value"];
1130
+ } else {
1131
+ value |= opnd;
1132
+ }
1133
+ const nameResult = this._referenceManager.tryGetName(value);
1134
+ let referenceName;
1135
+ if (!nameResult.found) {
1136
+ referenceName = `loc_${value.toString(16).toUpperCase().padStart(6, "0")}`;
1137
+ this._referenceManager.tryAddName(value, referenceName);
1138
+ } else {
1139
+ referenceName = nameResult.referenceName;
1140
+ }
1141
+ this._blockReader.resolveInclude(value, false);
1142
+ operands[operandIndex] = `&${referenceName}`;
1143
+ }
1144
+ cleanTransformName(transform) {
1145
+ let name = transform;
1146
+ while (name.length > 0 && RomProcessingConstants.ADDRESS_SPACE.includes(name[0])) {
1147
+ name = name.substring(1);
1148
+ }
1149
+ let mathIndex = -1;
1150
+ for (let i = 0; i < name.length; i++) {
1151
+ if (RomProcessingConstants.OPERATORS.includes(name[i])) {
1152
+ mathIndex = i;
1153
+ break;
1154
+ }
1155
+ }
1156
+ if (mathIndex > 0) {
1157
+ name = name.substring(0, mathIndex);
1158
+ }
1159
+ return name;
1160
+ }
1161
+ resolveTransformReference(transformName) {
1162
+ const location = this._referenceManager.findLocationByName(transformName);
1163
+ if (location !== void 0) {
1164
+ return location;
1165
+ }
1166
+ const match = _TransformProcessor.LOCATION_REGEX.exec(transformName);
1167
+ return match ? parseInt(match[1], 16) : null;
1168
+ }
1169
+ };
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
+
1334
+ // src/utils/index.ts
1335
+ function indexOfAny(str, chars, startIndex = 0) {
1336
+ for (let i = startIndex; i < str.length; i++) {
1337
+ if (chars.includes(str[i])) {
1338
+ return i;
1339
+ }
1340
+ }
1341
+ return -1;
1342
+ }
1343
+
1344
+ // src/rom/extraction/strings.ts
1345
+ var StringReader = class _StringReader {
1346
+ static {
1347
+ this.STRING_REFERENCE_CHARACTERS = ["~", "^"];
1348
+ }
1349
+ constructor(blockReader) {
1350
+ this._blockReader = blockReader;
1351
+ this._romDataReader = blockReader._romDataReader;
1352
+ }
1353
+ resolveCommand(cmd, builder) {
1354
+ if (cmd.types && cmd.types.length > 0) {
1355
+ builder.push(`[${cmd.value}`);
1356
+ let first = true;
1357
+ for (const t of cmd.types) {
1358
+ if (first) {
1359
+ builder.push(":");
1360
+ first = false;
1361
+ } else {
1362
+ builder.push(",");
1363
+ }
1364
+ switch (t) {
1365
+ case MemberType.Byte:
1366
+ builder.push(this._romDataReader.readByte().toString(16).toUpperCase());
1367
+ break;
1368
+ case MemberType.Word:
1369
+ builder.push(this._romDataReader.readUShort().toString(16).toUpperCase());
1370
+ break;
1371
+ case MemberType.Offset:
1372
+ const loc = this._romDataReader.readUShort() | this._romDataReader.position & 4128768;
1373
+ builder.push(`^${loc.toString(16).toUpperCase().padStart(6, "0")}`);
1374
+ break;
1375
+ case MemberType.Address:
1376
+ builder.push(`~${this._romDataReader.readAddress().toString(16).toUpperCase().padStart(6, "0")}`);
1377
+ break;
1378
+ case MemberType.Binary:
1379
+ let sfirst = true;
1380
+ do {
1381
+ const r = this._romDataReader.readByte();
1382
+ if (cmd.delimiter !== void 0 && r === cmd.delimiter) {
1383
+ break;
1384
+ }
1385
+ if (sfirst) {
1386
+ sfirst = false;
1387
+ } else {
1388
+ builder.push(",");
1389
+ }
1390
+ builder.push(r.toString(16).toUpperCase());
1391
+ } while (this._blockReader.partCanContinue());
1392
+ break;
1393
+ default:
1394
+ throw new Error("Unsupported member type");
1395
+ }
1396
+ }
1397
+ builder.push("]");
1398
+ } else {
1399
+ builder.push(`[${cmd.value}]`);
1400
+ }
1401
+ }
1402
+ parseString(stringType) {
1403
+ const dict = stringType.commands;
1404
+ const builder = [];
1405
+ const strLoc = this._romDataReader.position;
1406
+ const map = stringType.characterMap;
1407
+ const terminator = stringType.terminator;
1408
+ do {
1409
+ const c = this._romDataReader.readByte();
1410
+ if (c === terminator) {
1411
+ if (stringType.greedyTerminator) {
1412
+ while (this._romDataReader.peekByte() === terminator && this._blockReader.partCanContinue()) {
1413
+ this._romDataReader.position++;
1414
+ }
1415
+ }
1416
+ break;
1417
+ }
1418
+ const cmd = dict[c];
1419
+ if (cmd) {
1420
+ this.resolveCommand(cmd, builder);
1421
+ if (cmd.halt) {
1422
+ break;
1423
+ }
1424
+ } else {
1425
+ const index = this.shiftDown(c, stringType.shiftType);
1426
+ if (index >= 0 && index < map.length) {
1427
+ builder.push(map[index]);
1428
+ } else {
1429
+ builder.push(`[${c.toString(16).toUpperCase()}]`);
1430
+ }
1431
+ }
1432
+ } while (this._blockReader.partCanContinue());
1433
+ return {
1434
+ string: builder.join(""),
1435
+ type: stringType,
1436
+ marker: 0,
1437
+ location: strLoc
1438
+ };
1439
+ }
1440
+ /**
1441
+ * Handles character shifting based on string type
1442
+ * This is a simplified implementation of the shift logic
1443
+ */
1444
+ shiftDown(c, shiftType) {
1445
+ if (!shiftType) {
1446
+ return c;
1447
+ }
1448
+ switch (shiftType) {
1449
+ case "wh2":
1450
+ return (c & 112) >> 1 | c & 7;
1451
+ case "h2":
1452
+ return (c & 224) >> 1 | c & 15;
1453
+ default:
1454
+ return c;
1455
+ }
1456
+ }
1457
+ resolveString(sw, isBranch) {
1458
+ let str = sw.string;
1459
+ let ix = indexOfAny(str, _StringReader.STRING_REFERENCE_CHARACTERS);
1460
+ while (ix >= 0) {
1461
+ if (ix + 6 < str.length) {
1462
+ const hexStr = str.substring(ix + 1, ix + 7);
1463
+ const sloc = parseInt(hexStr, 16);
1464
+ if (!isNaN(sloc)) {
1465
+ const addrs = new Address(sloc >> 16, sloc & 65535);
1466
+ if (addrs.space === AddressSpace.ROM) {
1467
+ this._blockReader.resolveInclude(sloc, false);
1468
+ const name = this._blockReader.resolveName(sloc, AddressType.Unknown, false);
1469
+ const opix = indexOfAny(name, RomProcessingConstants.OPERATORS);
1470
+ if (opix > 0) {
1471
+ const offsetStr = name.substring(opix + 1);
1472
+ let offset;
1473
+ if (offsetStr === "M") {
1474
+ offset = this._blockReader._referenceManager.markerTable.get(sloc) || 0;
1475
+ } else {
1476
+ offset = parseInt(offsetStr, 16) || 0;
1477
+ }
1478
+ if (name[opix] === "-") {
1479
+ offset = -offset;
1480
+ }
1481
+ name.substring(0, opix);
1482
+ const target = sloc - offset;
1483
+ const [isOutside, prt] = DbBlockUtils.isOutsideWithPart(this._blockReader._currentBlock, sloc);
1484
+ if (prt != null) {
1485
+ const root = prt.objectRoot;
1486
+ const entry = root?.find((x) => x.Location === target);
1487
+ if (entry && entry.Object) {
1488
+ entry.Object.marker = offset;
1489
+ }
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+ }
1495
+ ix = indexOfAny(str, _StringReader.STRING_REFERENCE_CHARACTERS, ix + 7);
1496
+ }
1497
+ }
1498
+ };
1499
+ var TypeParser = class {
1500
+ constructor(blockReader) {
1501
+ this._blockReader = blockReader;
1502
+ this._referenceManager = blockReader._referenceManager;
1503
+ this._romDataReader = blockReader._romDataReader;
1504
+ this._stringReader = blockReader._stringReader;
1505
+ this._stringTypes = blockReader._root.stringTypes;
1506
+ }
1507
+ parseType(typeName, reg, depth, bank) {
1508
+ if (typeName[0] === "&") {
1509
+ return this.parseLocation(this._romDataReader.readUShort(), bank, typeName.substring(1), AddressType.Offset);
1510
+ }
1511
+ if (typeName[0] === "@") {
1512
+ return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1), AddressType.Address);
1513
+ }
1514
+ const stringType = this._stringTypes[typeName];
1515
+ if (stringType) {
1516
+ return this._stringReader.parseString(stringType);
1517
+ }
1518
+ const mType = this.tryParseMemberType(typeName);
1519
+ if (mType !== null) {
1520
+ switch (mType) {
1521
+ case MemberType.Byte:
1522
+ return createByte(this._romDataReader.readByte());
1523
+ case MemberType.Word:
1524
+ return createWord(this.parseWordSafe());
1525
+ case MemberType.Offset:
1526
+ return this.parseLocation(this._romDataReader.readUShort(), bank, null, AddressType.Offset);
1527
+ case MemberType.Address:
1528
+ return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Address);
1529
+ case MemberType.Binary:
1530
+ return this.parseBinary();
1531
+ case MemberType.Code:
1532
+ return this.parseCode(reg);
1533
+ default:
1534
+ throw new Error("Invalid member type");
1535
+ }
1536
+ }
1537
+ const parentType = this._blockReader._root.structs[typeName];
1538
+ if (!parentType) {
1539
+ throw new Error(`Unknown type: ${typeName}`);
1540
+ }
1541
+ const delimiter = parentType.delimiter;
1542
+ const discOffset = parentType.discriminator;
1543
+ const objects = [];
1544
+ let delReached;
1545
+ while (!(delReached = this._blockReader.delimiterReached(delimiter))) {
1546
+ const startPosition = this._romDataReader.position;
1547
+ let targetType = parentType;
1548
+ if (discOffset !== void 0) {
1549
+ const discPosition = this._romDataReader.position + discOffset;
1550
+ const desc = this._romDataReader.romData[discPosition];
1551
+ if (discOffset === 0) {
1552
+ this._romDataReader.position++;
1553
+ }
1554
+ const matchedStruct = Object.values(this._blockReader._root.structs).find(
1555
+ (x) => x.parent === typeName && x.discriminator === desc
1556
+ );
1557
+ targetType = matchedStruct || parentType;
1558
+ }
1559
+ const types = targetType.types;
1560
+ if (types && types.length > 0) {
1561
+ const memberCount = types.length;
1562
+ const prevPosition = this._romDataReader.position;
1563
+ const parts = new Array(memberCount);
1564
+ const def = { name: targetType.name, parts };
1565
+ for (let i = 0; i < memberCount; i++) {
1566
+ parts[i] = this.parseType(types[i], null, depth + 1);
1567
+ }
1568
+ if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) {
1569
+ this._romDataReader.position++;
1570
+ }
1571
+ objects.push(def);
1572
+ }
1573
+ let checkPosition = startPosition;
1574
+ while (++checkPosition < this._romDataReader.position) {
1575
+ if (this._referenceManager.containsStruct(checkPosition)) {
1576
+ this._romDataReader.position = checkPosition;
1577
+ break;
1578
+ }
1579
+ }
1580
+ if (!this._blockReader.partCanContinue()) {
1581
+ break;
1582
+ }
1583
+ }
1584
+ if (delReached && depth === 0) {
1585
+ this._referenceManager.tryAddStruct(this._romDataReader.position, typeName);
1586
+ }
1587
+ return objects;
1588
+ }
1589
+ tryParseMemberType(memberTypeName) {
1590
+ const upperName = memberTypeName.toUpperCase();
1591
+ for (const [key, value] of Object.entries(MemberType)) {
1592
+ if (key.toUpperCase() === upperName) {
1593
+ return value;
1594
+ }
1595
+ }
1596
+ return null;
1597
+ }
1598
+ parseWordSafe() {
1599
+ return this._referenceManager.containsStruct(this._romDataReader.position + 1) ? this._romDataReader.readByte() : this._romDataReader.readUShort();
1600
+ }
1601
+ parseBinary() {
1602
+ const startPosition = this._romDataReader.position;
1603
+ do {
1604
+ this._romDataReader.position++;
1605
+ } while (this._blockReader.partCanContinue());
1606
+ const len = this._romDataReader.position - startPosition;
1607
+ const outBuffer = new Uint8Array(len);
1608
+ for (let i = 0; i < len; i++) {
1609
+ outBuffer[i] = this._romDataReader.romData[startPosition + i];
1610
+ }
1611
+ return outBuffer;
1612
+ }
1613
+ parseLocation(offset, bank, typeName, addrType) {
1614
+ if (bank === void 0 && offset === 0) {
1615
+ return createWord(offset);
1616
+ }
1617
+ const resolvedBank = bank ?? this._romDataReader.position >> 16;
1618
+ const adrs = new Address(resolvedBank, offset);
1619
+ if (adrs.space !== AddressSpace.ROM) {
1620
+ return adrs;
1621
+ }
1622
+ const loc = adrs.toInt();
1623
+ if (this._blockReader._currentBlock && DbBlockUtils.isInside(this._blockReader._currentBlock, loc) && !this._blockReader._root.rewrites[loc]) {
1624
+ const resolvedTypeName = typeName ?? this._blockReader._currentPart?.struct ?? "Binary";
1625
+ this._referenceManager.tryAddStruct(loc, resolvedTypeName);
1626
+ const referenceName = `${resolvedTypeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
1627
+ this._referenceManager.tryAddName(loc, referenceName);
1628
+ }
1629
+ return new LocationWrapper(loc, addrType);
1630
+ }
1631
+ parseCode(reg) {
1632
+ const opList = [];
1633
+ let first = true;
1634
+ while (this._romDataReader.position < this._blockReader._partEnd) {
1635
+ if (first) {
1636
+ first = false;
1637
+ } else if (this._referenceManager.containsStruct(this._romDataReader.position)) {
1638
+ break;
1639
+ }
1640
+ if (reg) {
1641
+ this._blockReader.HydrateRegisters(reg);
1642
+ }
1643
+ const op = this._blockReader._asmReader.parseAsm(reg);
1644
+ opList.push(op);
1645
+ }
1646
+ return opList;
1647
+ }
1648
+ };
1649
+
1650
+ // src/assembly/Stack.ts
1651
+ var Stack = class {
1652
+ constructor() {
1653
+ this.bytes = new Uint8Array(70);
1654
+ this.location = 10;
1655
+ }
1656
+ /**
1657
+ * Push a byte onto the stack
1658
+ */
1659
+ push(value) {
1660
+ this.bytes[this.location++] = value & 255;
1661
+ }
1662
+ /**
1663
+ * Push a 16-bit value onto the stack (little-endian)
1664
+ */
1665
+ pushUInt16(value) {
1666
+ this.bytes[this.location++] = value & 255;
1667
+ this.bytes[this.location++] = value >> 8 & 255;
1668
+ }
1669
+ /**
1670
+ * Pop a byte from the stack
1671
+ */
1672
+ popByte() {
1673
+ return this.bytes[--this.location];
1674
+ }
1675
+ /**
1676
+ * Pop a 16-bit value from the stack (little-endian)
1677
+ */
1678
+ popUInt16() {
1679
+ const high = this.bytes[--this.location];
1680
+ const low = this.bytes[--this.location];
1681
+ return high << 8 | low;
1682
+ }
1683
+ /**
1684
+ * Reset the stack to initial state
1685
+ */
1686
+ reset() {
1687
+ this.bytes.fill(0);
1688
+ this.location = 10;
1689
+ }
1690
+ };
1691
+ var Registers = class {
1692
+ constructor() {
1693
+ this.stack = new Stack();
1694
+ }
1695
+ /**
1696
+ * Get the current status flags
1697
+ */
1698
+ get statusFlags() {
1699
+ let flags = 0;
1700
+ if (this.accumulatorFlag ?? false) {
1701
+ flags |= StatusFlags.AccumulatorMode;
1702
+ }
1703
+ if (this.indexFlag ?? false) {
1704
+ flags |= StatusFlags.IndexMode;
1705
+ }
1706
+ return flags;
1707
+ }
1708
+ /**
1709
+ * Set the status flags
1710
+ */
1711
+ set statusFlags(value) {
1712
+ this.accumulatorFlag = (value & StatusFlags.AccumulatorMode) !== 0;
1713
+ this.indexFlag = (value & StatusFlags.IndexMode) !== 0;
1714
+ }
1715
+ /**
1716
+ * Reset all registers to initial state
1717
+ */
1718
+ reset() {
1719
+ this.accumulatorFlag = void 0;
1720
+ this.indexFlag = void 0;
1721
+ this.direct = void 0;
1722
+ this.dataBank = void 0;
1723
+ this.accumulator = void 0;
1724
+ this.xIndex = void 0;
1725
+ this.yIndex = void 0;
1726
+ this.stack.reset();
1727
+ }
1728
+ };
1729
+
1730
+ // src/assembly/Op.ts
1731
+ var Op = class {
1732
+ constructor(code, location = 0, operands = [], size = 1) {
1733
+ this.code = code;
1734
+ this.location = location;
1735
+ this.operands = operands;
1736
+ this.size = size;
1737
+ }
1738
+ /**
1739
+ * Get the formatted string representation of this operation
1740
+ */
1741
+ toString() {
1742
+ const mnem = this.code.mnem;
1743
+ const op = this.operands;
1744
+ if (!op || op.length === 0) {
1745
+ return mnem;
1746
+ }
1747
+ const operandStrings = op.map((operand) => String(operand));
1748
+ return `${mnem} ${operandStrings.join(", ")}`;
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);
2100
+ }
2101
+ /**
2102
+ * Get opcodes by mnemonic
2103
+ */
2104
+ static getByMnemonic(mnemonic) {
2105
+ return GROUPED_OPCODES[mnemonic] || [];
2106
+ }
2107
+ /**
2108
+ * Find opcode by hex value
2109
+ */
2110
+ static findByCode(code) {
2111
+ return ALL_OPCODES[code];
2112
+ }
2113
+ };
2114
+
2115
+ // src/rom/extraction/asm.ts
2116
+ var AsmReader = class _AsmReader {
2117
+ static {
2118
+ // Constants
2119
+ this.ACCUMULATOR_OP_MASK = 15;
2120
+ }
2121
+ static {
2122
+ this.ACCUMULATOR_OP_VALUE = 9;
2123
+ }
2124
+ static {
2125
+ this.VARIABLE_SIZE_INDICATOR = -2;
2126
+ }
2127
+ static {
2128
+ this.TWO_BYTES_SIZE = 2;
2129
+ }
2130
+ static {
2131
+ this.THREE_BYTES_SIZE = 3;
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})/;
2233
+ }
2234
+ // Backward Compatibility Properties
2235
+ get AccumulatorFlags() {
2236
+ return this._stateManager.accumulatorFlags;
2237
+ }
2238
+ get IndexFlags() {
2239
+ return this._stateManager.indexFlags;
2240
+ }
2241
+ get BankNotes() {
2242
+ return this._stateManager.bankNotes;
2243
+ }
2244
+ get StackPosition() {
2245
+ return this._stateManager.stackPositions;
2246
+ }
2247
+ // Direct access to reference management collections
2248
+ get _structTable() {
2249
+ return this._referenceManager.structTable;
2250
+ }
2251
+ get _markerTable() {
2252
+ return this._referenceManager.markerTable;
2253
+ }
2254
+ get _nameTable() {
2255
+ return this._referenceManager.nameTable;
2256
+ }
2257
+ /**
2258
+ * Processes predefined overrides for registers and bank notes
2259
+ */
2260
+ initializeOverrides() {
2261
+ for (const over of Object.values(this._root.overrides)) {
2262
+ switch (over.register) {
2263
+ case RegisterType.M:
2264
+ this._stateManager.setAccumulatorFlag(over.location, over.value === 1);
2265
+ break;
2266
+ case RegisterType.X:
2267
+ this._stateManager.setIndexFlag(over.location, over.value === 1);
2268
+ break;
2269
+ case RegisterType.B:
2270
+ this._stateManager.setBankNote(over.location, over.value);
2271
+ break;
2272
+ }
2273
+ }
2274
+ }
2275
+ /**
2276
+ * Processes predefined file references
2277
+ */
2278
+ initializeFileReferences() {
2279
+ for (const file of this._root.files) {
2280
+ this._referenceManager.tryAddName(file.start, file.name);
2281
+ }
2282
+ }
2283
+ /**
2284
+ * Resolves mnemonic for a given address
2285
+ */
2286
+ resolveMnemonic(addr) {
2287
+ if ((addr.bank & Address.DATA_BANK_FLAG) !== 0) {
2288
+ return;
2289
+ }
2290
+ let offset = addr.offset;
2291
+ if (offset === 1750) {
2292
+ console.log(this._root.mnemonics[offset]);
2293
+ }
2294
+ const label = this._root.mnemonics[offset];
2295
+ if (!label) {
2296
+ return;
2297
+ }
2298
+ const ix = indexOfAny(label, RomProcessingConstants.OPERATORS);
2299
+ if (ix >= 0) {
2300
+ let opnd = parseInt(label.substring(ix + 1), 16);
2301
+ const op = label[ix];
2302
+ if (op === "-")
2303
+ opnd = -opnd;
2304
+ offset -= opnd;
2305
+ }
2306
+ if (this._currentBlock.mnemonics) {
2307
+ this._currentBlock.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
2308
+ }
2309
+ }
2310
+ /**
2311
+ * Resolves name for a location (delegated to ReferenceManager)
2312
+ */
2313
+ resolveName(location, type, isBranch) {
2314
+ return this._referenceManager.resolveName(location, type, isBranch);
2315
+ }
2316
+ /**
2317
+ * Resolves include for a location
2318
+ */
2319
+ resolveInclude(loc, isBranch) {
2320
+ if (DbBlockUtils.isOutside(this._currentBlock, loc) && this._currentPart) {
2321
+ let foundPart = null;
2322
+ for (const block of this._root.blocks) {
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
+ }
2335
+ } else if (isBranch && !this._referenceManager.tryGetName(loc).found) {
2336
+ const name = `loc_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
2337
+ this._referenceManager.tryAddName(loc, name);
2338
+ }
2339
+ }
2340
+ /**
2341
+ * Notes a type at a location and manages chunk references
2342
+ */
2343
+ noteType(loc, type, silent = false, reg) {
2344
+ this._referenceManager.tryAddStruct(loc, type);
2345
+ const nameResult = this._referenceManager.tryGetName(loc);
2346
+ let name;
2347
+ if (!nameResult.found) {
2348
+ name = this._referenceManager.createTypeName(type, loc);
2349
+ this._referenceManager.tryAddName(loc, name);
2350
+ } else {
2351
+ name = nameResult.referenceName;
2352
+ }
2353
+ if (!silent && type === BlockReaderConstants.CODE_TYPE && reg) {
2354
+ this.updateRegisterState(loc, reg);
2355
+ }
2356
+ return name;
2357
+ }
2358
+ updateRegisterState(loc, reg) {
2359
+ if (reg.accumulatorFlag !== void 0) {
2360
+ this._stateManager.tryAddAccumulatorFlag(loc, reg.accumulatorFlag);
2361
+ }
2362
+ if (reg.indexFlag !== void 0) {
2363
+ this._stateManager.tryAddIndexFlag(loc, reg.indexFlag);
2364
+ }
2365
+ if (reg.stack.location > 0) {
2366
+ this._stateManager.tryAddStackPosition(loc, reg.stack.location);
2367
+ }
2368
+ }
2369
+ /**
2370
+ * Checks if a delimiter has been reached
2371
+ */
2372
+ delimiterReached(delimiter) {
2373
+ if (delimiter === void 0) {
2374
+ return false;
2375
+ }
2376
+ if (delimiter >= BlockReaderConstants.BYTE_DELIMITER_THRESHOLD) {
2377
+ if (this._romDataReader.peekShort() === delimiter) {
2378
+ this._romDataReader.position += 2;
2379
+ return true;
2380
+ }
2381
+ } else if (this._romDataReader.peekByte() === delimiter) {
2382
+ this._romDataReader.position++;
2383
+ return true;
2384
+ }
2385
+ return false;
2386
+ }
2387
+ /**
2388
+ * Checks if processing of the current part can continue
2389
+ */
2390
+ partCanContinue() {
2391
+ return this._romDataReader.position < this._partEnd && !this._referenceManager.containsStruct(this._romDataReader.position);
2392
+ }
2393
+ /**
2394
+ * Main analysis entry point
2395
+ */
2396
+ analyzeAndResolve() {
2397
+ this.analyzeBlocks();
2398
+ this.resolveReferences();
2399
+ }
2400
+ /**
2401
+ * Analyzes all blocks in the ROM
2402
+ */
2403
+ analyzeBlocks() {
2404
+ this.initializeBlocksAndParts();
2405
+ for (const block of this._root.blocks) {
2406
+ this._currentBlock = block;
2407
+ for (const part of block.parts) {
2408
+ this.processPart(part);
2409
+ }
2410
+ }
2411
+ }
2412
+ /**
2413
+ * Initializes blocks and parts with base references
2414
+ */
2415
+ initializeBlocksAndParts() {
2416
+ for (const block of this._root.blocks) {
2417
+ for (const part of block.parts) {
2418
+ part.includes = /* @__PURE__ */ new Set();
2419
+ this._referenceManager.tryAddStruct(part.start, part.struct);
2420
+ this._referenceManager.tryAddName(part.start, part.name);
2421
+ }
2422
+ }
2423
+ }
2424
+ /**
2425
+ * Processes a single part
2426
+ */
2427
+ processPart(part) {
2428
+ this._currentPart = part;
2429
+ this._romDataReader.position = part.start;
2430
+ this._partEnd = part.end;
2431
+ let current = part.struct || BlockReaderConstants.BINARY_TYPE;
2432
+ const chunks = [];
2433
+ const reg = new Registers();
2434
+ const bank = part.bank;
2435
+ let last = null;
2436
+ while (this._romDataReader.position < this._partEnd) {
2437
+ const structResult = this._referenceManager.tryGetStruct(this._romDataReader.position);
2438
+ if (structResult.found) {
2439
+ current = structResult.chunkType;
2440
+ } else if (last !== null) {
2441
+ this.processContinuousEntry(current, reg, bank, last);
2442
+ continue;
2443
+ }
2444
+ last = createTableEntry(this._romDataReader.position);
2445
+ chunks.push(last);
2446
+ this.processNewEntry(current, reg, bank, last);
2447
+ }
2448
+ part.objectRoot = chunks;
2449
+ }
2450
+ /**
2451
+ * Processes a continuous entry (same type as previous)
2452
+ */
2453
+ processContinuousEntry(current, reg, bank, last) {
2454
+ const obj = this._typeParser.parseType(current, reg, 0, bank);
2455
+ if (!Array.isArray(last.object)) {
2456
+ last.object = [last.object];
2457
+ }
2458
+ last.object.push(obj);
2459
+ }
2460
+ /**
2461
+ * Processes a new entry
2462
+ */
2463
+ processNewEntry(current, reg, bank, last) {
2464
+ let res = this._typeParser.parseType(current, reg, 0, bank);
2465
+ if (BlockReaderConstants.POINTER_CHARACTERS.includes(current[0]) && !Array.isArray(res)) {
2466
+ res = [res];
2467
+ }
2468
+ last.object = res;
2469
+ }
2470
+ /**
2471
+ * Resolves all references after analysis
2472
+ */
2473
+ resolveReferences() {
2474
+ for (const block of this._root.blocks) {
2475
+ this._currentBlock = block;
2476
+ for (const part of block.parts) {
2477
+ this._currentPart = part;
2478
+ this.resolveObject(part.objectRoot, false);
2479
+ }
2480
+ }
2481
+ }
2482
+ /**
2483
+ * Resolves a single object and its references
2484
+ */
2485
+ resolveObject(obj, isBranch) {
2486
+ if (typeof obj === "string") {
2487
+ return;
2488
+ }
2489
+ if (Array.isArray(obj)) {
2490
+ for (const o of obj) {
2491
+ this.resolveObject(o, isBranch);
2492
+ }
2493
+ return;
2494
+ }
2495
+ if (obj instanceof Address) {
2496
+ this.resolveMnemonic(obj);
2497
+ return;
2498
+ }
2499
+ if (typeof obj === "number") {
2500
+ this.resolveInclude(obj, isBranch);
2501
+ return;
2502
+ }
2503
+ if (obj instanceof LocationWrapper) {
2504
+ this.resolveInclude(obj.location, isBranch);
2505
+ return;
2506
+ }
2507
+ if (obj && typeof obj === "object") {
2508
+ if ("string" in obj && "type" in obj) {
2509
+ this._stringReader.resolveString(obj, isBranch);
2510
+ return;
2511
+ }
2512
+ if ("name" in obj && "parts" in obj) {
2513
+ this.resolveObject(obj.parts, isBranch);
2514
+ return;
2515
+ }
2516
+ if ("location" in obj && "object" in obj) {
2517
+ this.resolveObject(obj.object, isBranch);
2518
+ return;
2519
+ }
2520
+ if ("code" in obj && "operands" in obj) {
2521
+ this.resolveOperationObject(obj);
2522
+ return;
2523
+ }
2524
+ }
2525
+ }
2526
+ /**
2527
+ * Resolves references in an operation object
2528
+ */
2529
+ resolveOperationObject(op) {
2530
+ const branch = this.isBranchOperation(op);
2531
+ for (const opnd of op.operands) {
2532
+ this.resolveObject(opnd, branch);
2533
+ }
2534
+ }
2535
+ /**
2536
+ * Checks if an operation is a branch operation
2537
+ */
2538
+ isBranchOperation(op) {
2539
+ return op.code.mode === AddressingMode.PCRelative || op.code.mode === AddressingMode.PCRelativeLong || op.code.mnem[0] === "J";
2540
+ }
2541
+ /**
2542
+ * Hydrates registers with stored state
2543
+ */
2544
+ hydrateRegisters(reg) {
2545
+ this._stateManager.hydrateRegisters(this._romDataReader.position, reg);
2546
+ }
2547
+ /**
2548
+ * PascalCase wrapper for hydrateRegisters (for C# compatibility)
2549
+ */
2550
+ HydrateRegisters(reg) {
2551
+ this.hydrateRegisters(reg);
2552
+ }
2553
+ };
2554
+ var PostProcessor = class {
2555
+ constructor(reader) {
2556
+ this._referenceManager = reader._referenceManager;
2557
+ }
2558
+ /**
2559
+ * Execute post process directive on a block if present.
2560
+ */
2561
+ process(block) {
2562
+ if (!block.postProcess || block.postProcess.trim() === "") {
2563
+ return;
2564
+ }
2565
+ let signature = block.postProcess;
2566
+ const parts = [];
2567
+ const index = signature.indexOf("(");
2568
+ if (index > 0) {
2569
+ const endIx = signature.indexOf(")", index);
2570
+ const params = signature.substring(
2571
+ index + 1,
2572
+ endIx >= 0 ? endIx : signature.length
2573
+ );
2574
+ if (params.length > 0) {
2575
+ for (const p of params.split(",")) {
2576
+ parts.push(p.trim());
2577
+ }
2578
+ }
2579
+ signature = signature.substring(0, index);
2580
+ }
2581
+ const fn = this[signature];
2582
+ if (typeof fn !== "function") {
2583
+ throw new Error(`Unable to locate postprocess function ${signature}`);
2584
+ }
2585
+ fn.apply(this, [block, ...parts]);
2586
+ }
2587
+ /**
2588
+ * Builds a lookup table from struct entries.
2589
+ * Equivalent to PostProcessor.Lookup in C# implementation.
2590
+ */
2591
+ Lookup(block, keyIx, valueIx) {
2592
+ const kix = parseInt(keyIx.trim());
2593
+ const vix = parseInt(valueIx.trim());
2594
+ const table = block.parts[0].objectRoot;
2595
+ const tableEntry = table && table[0];
2596
+ const entries = tableEntry?.object;
2597
+ if (!tableEntry || !entries) {
2598
+ throw new Error("Invalid table structure for Lookup post process");
2599
+ }
2600
+ const newParts = [];
2601
+ const newList = [];
2602
+ newParts.push({ location: tableEntry.location, object: newList });
2603
+ let eIx = 1;
2604
+ for (const entry of entries) {
2605
+ if (!entry || !Array.isArray(entry.parts)) {
2606
+ continue;
2607
+ }
2608
+ const struct = entry;
2609
+ let cIx = 0;
2610
+ let key = null;
2611
+ let value = null;
2612
+ for (const obj of struct.parts) {
2613
+ if (cIx === kix) {
2614
+ if (obj && typeof obj === "object" && "value" in obj) {
2615
+ key = obj.value;
2616
+ } else {
2617
+ key = obj;
2618
+ }
2619
+ } else if (cIx === vix) {
2620
+ value = obj;
2621
+ }
2622
+ cIx++;
2623
+ }
2624
+ if (key === null || value === null) {
2625
+ throw new Error("Could not locate key or value for transform");
2626
+ }
2627
+ const name = `entry_${key.toString(16).toUpperCase().padStart(2, "0")}`;
2628
+ const loc = tableEntry.location + eIx;
2629
+ newParts.push({ location: loc, object: value });
2630
+ this._referenceManager.nameTable.set(loc, name);
2631
+ while (newList.length <= key) {
2632
+ newList.push(createWord(0));
2633
+ }
2634
+ newList[key] = `&${name}`;
2635
+ eIx++;
2636
+ }
2637
+ block.parts[0].objectRoot = newParts;
2638
+ }
2639
+ };
2640
+
2641
+ // src/rom/extraction/writer.ts
2642
+ var ObjectType = /* @__PURE__ */ ((ObjectType2) => {
2643
+ ObjectType2["TableEntryArray"] = "TableEntryArray";
2644
+ ObjectType2["StructDef"] = "StructDef";
2645
+ ObjectType2["OpArray"] = "OpArray";
2646
+ ObjectType2["LocationWrapper"] = "LocationWrapper";
2647
+ ObjectType2["Address"] = "Address";
2648
+ ObjectType2["StringWrapper"] = "StringWrapper";
2649
+ ObjectType2["ByteArray"] = "ByteArray";
2650
+ ObjectType2["Array"] = "Array";
2651
+ ObjectType2["String"] = "String";
2652
+ ObjectType2["Number"] = "Number";
2653
+ ObjectType2["TypedNumber"] = "TypedNumber";
2654
+ return ObjectType2;
2655
+ })(ObjectType || {});
2656
+ var BlockWriter = class {
2657
+ constructor(reader) {
2658
+ this._isInline = false;
2659
+ this._currentPart = null;
2660
+ this._blockReader = reader;
2661
+ this._root = reader._root;
2662
+ this._referenceManager = reader._referenceManager;
2663
+ this._postProcessor = new PostProcessor(reader);
2664
+ }
2665
+ async writeBlocks(outPath) {
2666
+ const res = DbRootUtils.getPath(this._root, BinType.Assembly);
2667
+ const folderPath = join(outPath, res.folder);
2668
+ for (const block of this._root.blocks) {
2669
+ const groupedFolderPath = block.group ? join(folderPath, block.group) : folderPath;
2670
+ await promises.mkdir(groupedFolderPath, { recursive: true });
2671
+ const outFile = join(groupedFolderPath, `${block.name}.${res.extension}`);
2672
+ try {
2673
+ await promises.access(outFile);
2674
+ continue;
2675
+ } catch {
2676
+ }
2677
+ const content = this.generateAsm(block);
2678
+ await promises.writeFile(outFile, content);
2679
+ }
2680
+ }
2681
+ generateAsm(block) {
2682
+ const lines = [];
2683
+ if (!block.movable && block.parts.length > 0) {
2684
+ lines.push(`?BANK ${(block.parts[0].start >> 16).toString(16).toUpperCase().padStart(2, "0")}`);
2685
+ }
2686
+ const includes = DbBlockUtils.getIncludes(block);
2687
+ if (includes && includes.length > 0) {
2688
+ lines.push("");
2689
+ for (const inc of includes) {
2690
+ lines.push(`?INCLUDE '${inc.name}'`);
2691
+ }
2692
+ }
2693
+ const mnemonics = this.getMnemonicsForBlock(block);
2694
+ if (mnemonics && mnemonics.length > 0) {
2695
+ lines.push("");
2696
+ for (const [name, address] of mnemonics) {
2697
+ const paddedName = name.padEnd(30, " ");
2698
+ lines.push(`!${paddedName} ${address.toString(16).toUpperCase().padStart(4, "0")}`);
2699
+ }
2700
+ }
2701
+ this._postProcessor.process(block);
2702
+ for (const part of block.parts) {
2703
+ this._currentPart = part;
2704
+ this._isInline = true;
2705
+ lines.push("");
2706
+ lines.push("---------------------------------------------");
2707
+ const objectLines = this.writeObject(part.objectRoot, -1);
2708
+ lines.push(...objectLines);
2709
+ }
2710
+ let content = lines.join("\r\n");
2711
+ if (block.transforms) {
2712
+ for (const x of block.transforms) {
2713
+ if (x.key && x.value) {
2714
+ const regex = new RegExp(x.key, "g");
2715
+ content = content.replace(regex, x.value);
2716
+ }
2717
+ }
2718
+ }
2719
+ return content;
2720
+ }
2721
+ getMnemonicsForBlock(block) {
2722
+ if (!block.mnemonics) {
2723
+ return [];
2724
+ }
2725
+ return Object.entries(block.mnemonics).map(([k, v]) => [v, parseInt(k, 10)]).sort((a, b) => a[1] - b[1]);
2726
+ }
2727
+ resolveOperand(op, obj, isBranch = false) {
2728
+ this.getObjectType(obj);
2729
+ if (typeof obj === "number") {
2730
+ if (op.size === 3) ;
2731
+ if (op.code.mode === AddressingMode.Immediate) {
2732
+ return obj;
2733
+ }
2734
+ return this._blockReader.resolveName(obj, AddressType.Address, isBranch);
2735
+ }
2736
+ if (this.getObjectType(obj) === "LocationWrapper" /* LocationWrapper */) {
2737
+ const lw = obj;
2738
+ return this._blockReader.resolveName(lw.location, lw.type, isBranch);
2739
+ }
2740
+ if (this.getObjectType(obj) === "Address" /* Address */) {
2741
+ const addr = obj;
2742
+ if (op.size === 4) {
2743
+ return addr;
2744
+ }
2745
+ if (addr.isCodeBank && addr.offset < Address.UPPER_BANK) {
2746
+ const label = this._root.mnemonics[addr.offset];
2747
+ if (label) {
2748
+ return label;
2749
+ }
2750
+ }
2751
+ return addr.offset;
2752
+ }
2753
+ if (obj && typeof obj === "object" && obj._tag && "value" in 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
+ }
2762
+ return obj;
2763
+ }
2764
+ return obj;
2765
+ }
2766
+ getObjectType(obj) {
2767
+ if (obj === null || obj === void 0) {
2768
+ return "String" /* String */;
2769
+ }
2770
+ if (obj._tag) {
2771
+ if (obj._tag === "Byte" || obj._tag === "Word") {
2772
+ return "TypedNumber" /* TypedNumber */;
2773
+ }
2774
+ return obj._tag;
2775
+ }
2776
+ if (Array.isArray(obj)) {
2777
+ if (obj.length > 0) {
2778
+ if (obj[0] && typeof obj[0] === "object" && "location" in obj[0] && "object" in obj[0]) {
2779
+ return "TableEntryArray" /* TableEntryArray */;
2780
+ }
2781
+ if (obj[0] instanceof Op) {
2782
+ return "OpArray" /* OpArray */;
2783
+ }
2784
+ }
2785
+ return "Array" /* Array */;
2786
+ }
2787
+ if (obj instanceof Op) {
2788
+ return "OpArray" /* OpArray */;
2789
+ }
2790
+ if (obj instanceof LocationWrapper) {
2791
+ return "LocationWrapper" /* LocationWrapper */;
2792
+ }
2793
+ if (obj instanceof Address) {
2794
+ return "Address" /* Address */;
2795
+ }
2796
+ if (obj instanceof Uint8Array) {
2797
+ return "ByteArray" /* ByteArray */;
2798
+ }
2799
+ if (obj && typeof obj === "object") {
2800
+ if ("string" in obj && "type" in obj && "marker" in obj && "location" in obj) {
2801
+ return "StringWrapper" /* StringWrapper */;
2802
+ }
2803
+ if ("name" in obj && "parts" in obj) {
2804
+ return "StructDef" /* StructDef */;
2805
+ }
2806
+ }
2807
+ if (typeof obj === "string") {
2808
+ return "String" /* String */;
2809
+ }
2810
+ if (typeof obj === "number") {
2811
+ return "Number" /* Number */;
2812
+ }
2813
+ return "String" /* String */;
2814
+ }
2815
+ writeObject(obj, depth, isBranch = false) {
2816
+ const lines = [];
2817
+ const objType = this.getObjectType(obj);
2818
+ let objLines;
2819
+ switch (objType) {
2820
+ case "TableEntryArray" /* TableEntryArray */:
2821
+ objLines = this.writeTableEntryArray(obj, depth);
2822
+ break;
2823
+ case "StructDef" /* StructDef */:
2824
+ objLines = this.writeStructDef(obj, depth);
2825
+ break;
2826
+ case "OpArray" /* OpArray */:
2827
+ objLines = this.writeOpArray(obj, depth);
2828
+ break;
2829
+ case "LocationWrapper" /* LocationWrapper */:
2830
+ objLines = [
2831
+ this._blockReader.resolveName(
2832
+ obj.location,
2833
+ obj.type,
2834
+ isBranch
2835
+ )
2836
+ ];
2837
+ break;
2838
+ case "Address" /* Address */:
2839
+ objLines = [`$${obj.toString()}`];
2840
+ break;
2841
+ case "ByteArray" /* ByteArray */:
2842
+ objLines = [
2843
+ `#${Array.from(obj).map((b) => b.toString(16).toUpperCase().padStart(2, "0")).join("")}`
2844
+ ];
2845
+ break;
2846
+ case "StringWrapper" /* StringWrapper */:
2847
+ objLines = this.writeStringWrapper(obj);
2848
+ break;
2849
+ case "Array" /* Array */:
2850
+ objLines = this.writeArray(obj, depth);
2851
+ break;
2852
+ case "Number" /* Number */:
2853
+ objLines = this.writeNumber(obj);
2854
+ break;
2855
+ case "TypedNumber" /* TypedNumber */:
2856
+ objLines = this.writeTypedNumber(obj);
2857
+ break;
2858
+ case "String" /* String */:
2859
+ objLines = [String(obj)];
2860
+ break;
2861
+ default:
2862
+ objLines = [String(obj)];
2863
+ break;
2864
+ }
2865
+ lines.push(...objLines);
2866
+ return lines;
2867
+ }
2868
+ writeTableEntryArray(tGroup, depth) {
2869
+ const lines = [];
2870
+ const isInline = this._isInline;
2871
+ for (const t of tGroup) {
2872
+ const nameResult = this._referenceManager.tryGetName(t.location);
2873
+ const name = nameResult.found ? nameResult.referenceName : `loc_${t.location.toString(16).toUpperCase().padStart(6, "0")}`;
2874
+ const objectLines = this.writeObject(t.object, depth + 1);
2875
+ lines.push("");
2876
+ lines.push(`${name} ${objectLines[0]}`);
2877
+ if (objectLines.length > 1) {
2878
+ lines.push(...objectLines.slice(1));
2879
+ }
2880
+ }
2881
+ this._isInline = isInline;
2882
+ return lines;
2883
+ }
2884
+ writeStructDef(structObj, depth) {
2885
+ const parts = [];
2886
+ const isInline = this._isInline;
2887
+ this._isInline = true;
2888
+ for (const part of structObj.parts) {
2889
+ const partLines = this.writeObject(part, depth);
2890
+ parts.push(partLines.join("\r\n"));
2891
+ }
2892
+ const line = `${structObj.name} < ${parts.join(", ")} >`;
2893
+ this._isInline = isInline;
2894
+ return [line];
2895
+ }
2896
+ writeOpArray(opList, depth) {
2897
+ const lines = [];
2898
+ lines.push("{");
2899
+ const isInline = this._isInline;
2900
+ this._isInline = true;
2901
+ let first = true;
2902
+ for (const op of opList) {
2903
+ if (first) {
2904
+ first = false;
2905
+ } else {
2906
+ const labelResult = this._referenceManager.tryGetName(op.location);
2907
+ if (labelResult.found) {
2908
+ lines.push("");
2909
+ lines.push(` ${labelResult.referenceName}:`);
2910
+ }
2911
+ }
2912
+ let opLine = ` ${op.code.mnem} `;
2913
+ if (op.copDef) {
2914
+ opLine += `[${op.copDef.mnem}]`;
2915
+ if (op.operands && op.operands.length > 1) {
2916
+ const operandStrings = [];
2917
+ for (let i = 1; i < op.operands.length; i++) {
2918
+ const operandLines = this.writeObject(op.operands[i], depth + 1, false);
2919
+ operandStrings.push(operandLines[0]);
2920
+ }
2921
+ opLine += ` ( ${operandStrings.join(", ")} )`;
2922
+ }
2923
+ } else if (op.operands && op.operands.length > 0) {
2924
+ if (op.code.mnem === "COP") {
2925
+ const operand = op.operands[0];
2926
+ if (typeof operand === "number") {
2927
+ opLine += `[${operand.toString(16).toUpperCase().padStart(2, "0")}]`;
2928
+ } else {
2929
+ opLine += `[${operand}]`;
2930
+ }
2931
+ } else {
2932
+ const isBr = op.code.mnem[0] === "J" || op.code.mode === AddressingMode.PCRelative || op.code.mode === AddressingMode.PCRelativeLong;
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
+ }
2944
+ }
2945
+ }
2946
+ lines.push(opLine);
2947
+ }
2948
+ lines.push("}");
2949
+ this._isInline = isInline;
2950
+ return lines;
2951
+ }
2952
+ formatDefaultOperand(operand, size) {
2953
+ if (typeof operand === "string") {
2954
+ return operand;
2955
+ }
2956
+ if (operand && typeof operand === "object" && operand._tag && "value" in operand) {
2957
+ return this.formatTypedNumber(operand);
2958
+ }
2959
+ if (typeof operand === "number") {
2960
+ const hexSize = (size - 1) * 2;
2961
+ const hex = operand.toString(16).toUpperCase().padStart(hexSize, "0");
2962
+ return `$${hex}`;
2963
+ }
2964
+ return String(operand);
2965
+ }
2966
+ writeStringWrapper(stringObj) {
2967
+ let str = stringObj.string;
2968
+ const stringReferenceChars = ["~", "^"];
2969
+ for (const char of stringReferenceChars) {
2970
+ let ix = str.indexOf(char);
2971
+ while (ix >= 0) {
2972
+ const hexStr = str.substring(ix + 1, ix + 7);
2973
+ const rawAddr = parseInt(hexStr, 16);
2974
+ const adrs = new Address(rawAddr >> 16 & 255, rawAddr & 65535);
2975
+ if (adrs.space === AddressSpace.ROM) {
2976
+ const addressType = char === "^" ? AddressType.Offset : AddressType.Address;
2977
+ const location = adrs.toInt();
2978
+ const name = this._blockReader.resolveName(location, addressType, false);
2979
+ str = str.replace(str.substring(ix, ix + 7), name);
2980
+ } else {
2981
+ throw new Error("Unsupported address space");
2982
+ }
2983
+ ix = str.indexOf(char, ix + 7);
2984
+ }
2985
+ }
2986
+ const refChar = stringObj.type.delimiter;
2987
+ let marker = stringObj.marker;
2988
+ if (marker <= 0) {
2989
+ const markerResult = this._referenceManager.tryGetMarker(stringObj.location);
2990
+ marker = markerResult.found ? markerResult.offset || 0 : 0;
2991
+ }
2992
+ if (marker > 0) {
2993
+ let six = 0;
2994
+ let mix = 0;
2995
+ while (mix < marker) {
2996
+ if (str[six] === "[") {
2997
+ const eix = str.indexOf("]", ++six);
2998
+ const parts = str.substring(six, eix).split(/[,: ]/);
2999
+ const cmd = Object.values(stringObj.type.commands).find((x) => x.value === parts[0]);
3000
+ if (cmd && cmd.types) {
3001
+ for (const t of cmd.types) {
3002
+ switch (t) {
3003
+ case MemberType.Byte:
3004
+ mix += 1;
3005
+ break;
3006
+ case MemberType.Word:
3007
+ case MemberType.Offset:
3008
+ mix += 2;
3009
+ break;
3010
+ case MemberType.Address:
3011
+ mix += 3;
3012
+ break;
3013
+ case MemberType.Binary:
3014
+ mix += parts.length - 1;
3015
+ break;
3016
+ default:
3017
+ throw new Error("Unsupported member type");
3018
+ }
3019
+ }
3020
+ }
3021
+ six = eix + 1;
3022
+ mix++;
3023
+ } else {
3024
+ six++;
3025
+ mix++;
3026
+ }
3027
+ }
3028
+ str = str.substring(0, six) + "[::]" + str.substring(six);
3029
+ }
3030
+ return [`${refChar}${str}${refChar}`];
3031
+ }
3032
+ writeArray(arr, depth) {
3033
+ const lines = [];
3034
+ const indent = " ".repeat(depth);
3035
+ const isInline = this._isInline;
3036
+ lines.push("[");
3037
+ this._isInline = false;
3038
+ for (let i = 0; i < arr.length; i++) {
3039
+ const objLines = this.writeObject(arr[i], depth + 1);
3040
+ for (const line of objLines) {
3041
+ lines.push(indent + " " + line);
3042
+ }
3043
+ lines[lines.length - 1] += ` ;${i.toString(16).toUpperCase().padStart(2, "0")}`;
3044
+ }
3045
+ lines.push(indent + "]");
3046
+ this._isInline = isInline;
3047
+ return lines;
3048
+ }
3049
+ writeNumber(num) {
3050
+ if (num <= 255) {
3051
+ return [`#${num.toString(16).toUpperCase().padStart(2, "0")}`];
3052
+ }
3053
+ if (num <= 65535) {
3054
+ return [`#$${num.toString(16).toUpperCase().padStart(4, "0")}`];
3055
+ }
3056
+ return [`#$${num.toString(16).toUpperCase().padStart(6, "0")}`];
3057
+ }
3058
+ 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
+ if (typeof num.value === "number") {
3066
+ const width = size * 2;
3067
+ const hex = num.value.toString(16).toUpperCase().padStart(width, "0");
3068
+ return size === 1 ? `#${hex}` : `#$${hex}`;
3069
+ }
3070
+ return `#${num.value}`;
3071
+ }
3072
+ writeTypedNumber(num) {
3073
+ return [this.formatTypedNumber(num)];
3074
+ }
3075
+ formatOperand(format, operands) {
3076
+ return format.replace(/\{(\d+)(?::([^}]+))?\}/g, (match, index, formatSpec) => {
3077
+ const operand = operands[parseInt(index)];
3078
+ if (operand && typeof operand === "object" && operand._tag && "value" in operand) {
3079
+ const value = operand.value;
3080
+ if (formatSpec && typeof value === "number" && formatSpec.startsWith("X")) {
3081
+ const width = parseInt(formatSpec.substring(1)) || 2;
3082
+ return value.toString(16).toUpperCase().padStart(width, "0");
3083
+ }
3084
+ return String(value);
3085
+ }
3086
+ if (formatSpec && typeof operand === "number") {
3087
+ if (formatSpec.startsWith("X")) {
3088
+ const width = parseInt(formatSpec.substring(1)) || 2;
3089
+ return operand.toString(16).toUpperCase().padStart(width, "0");
3090
+ }
3091
+ }
3092
+ return String(operand);
3093
+ });
3094
+ }
3095
+ };
3096
+
3097
+ // src/rom/project.ts
3098
+ var ProjectRoot = class _ProjectRoot {
3099
+ constructor(config) {
3100
+ this.config = config;
3101
+ this.name = config.name;
3102
+ this.romPath = config.romPath;
3103
+ this.baseDir = config.baseDir;
3104
+ this.system = config.system;
3105
+ this.database = config.database;
3106
+ this.flipsPath = config.flipsPath;
3107
+ this.resources = config.resources;
3108
+ this.databasePath = config.databasePath;
3109
+ this.systemPath = config.systemPath;
3110
+ this.compression = config.compression;
3111
+ }
3112
+ /**
3113
+ * Get compression provider
3114
+ */
3115
+ getCompression() {
3116
+ const compressionType = this.compression || "QuintetLZ";
3117
+ switch (compressionType) {
3118
+ case "QuintetLZ":
3119
+ default:
3120
+ return new QuintetLZ();
3121
+ }
3122
+ }
3123
+ /**
3124
+ * Load project from file or directory
3125
+ */
3126
+ static async load(path) {
3127
+ path = path || "./project.json";
3128
+ try {
3129
+ const config = await readJsonFile(path);
3130
+ if (!config.baseDir) {
3131
+ config.baseDir = getDirectory(path);
3132
+ }
3133
+ return new _ProjectRoot(config);
3134
+ } catch (error) {
3135
+ console.warn(`Failed to load project file: ${error}. Using directory-based configuration.`);
3136
+ }
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
+ }
3172
+ };
3173
+
3174
+ // src/api/RomProcessor.ts
3175
+ var RomProcessor = class _RomProcessor {
3176
+ constructor(projectConfig) {
3177
+ this.dbRoot = null;
3178
+ this.romData = null;
3179
+ this.romState = null;
3180
+ this.projectRoot = new ProjectRoot(projectConfig);
3181
+ }
3182
+ /**
3183
+ * Create ROM processor from project file or directory
3184
+ */
3185
+ static async fromProject(path) {
3186
+ const projectRoot = await ProjectRoot.load(path);
3187
+ return new _RomProcessor(projectRoot.config);
3188
+ }
3189
+ /**
3190
+ * Load ROM data from file or URL
3191
+ */
3192
+ async loadRom(romPath) {
3193
+ const path = romPath || this.projectRoot.romPath;
3194
+ if (!path) {
3195
+ throw new Error("ROM path not specified");
3196
+ }
3197
+ try {
3198
+ this.romData = await readFileAsBinary(path);
3199
+ } catch (error) {
3200
+ throw new Error(`Failed to load ROM from ${path}: ${error}`);
3201
+ }
3202
+ }
3203
+ /**
3204
+ * Initialize database and load ROM metadata
3205
+ */
3206
+ async initialize() {
3207
+ if (!this.romData) {
3208
+ throw new Error("ROM data not loaded. Call loadRom() first.");
3209
+ }
3210
+ this.dbRoot = await this.projectRoot.dumpDatabase();
3211
+ this.romState = new RomState();
3212
+ }
3213
+ /**
3214
+ * Analyze ROM structure and extract metadata
3215
+ */
3216
+ async analyze() {
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;
3231
+ }
3232
+ /**
3233
+ * Extract files from ROM data
3234
+ */
3235
+ async extract(outputDir) {
3236
+ if (!this.dbRoot || !this.romData) {
3237
+ throw new Error("ROM processor not initialized. Call initialize() first.");
3238
+ }
3239
+ const result = {
3240
+ extractedFiles: [],
3241
+ errors: [],
3242
+ totalSize: 0
3243
+ };
3244
+ console.warn("File extraction not yet implemented - awaiting FileReader conversion");
3245
+ return result;
3246
+ }
3247
+ /**
3248
+ * Process scene data for a specific scene ID
3249
+ */
3250
+ async processScene(sceneId, metaFile) {
3251
+ if (!this.dbRoot) {
3252
+ throw new Error("ROM processor not initialized. Call initialize() first.");
3253
+ }
3254
+ const state = await RomState.fromScene(
3255
+ this.projectRoot.baseDir,
3256
+ this.dbRoot,
3257
+ metaFile,
3258
+ sceneId
3259
+ );
3260
+ this.romState = state;
3261
+ return state;
3262
+ }
3263
+ /**
3264
+ * Build ROM from extracted files
3265
+ */
3266
+ async build(outputPath) {
3267
+ if (!this.dbRoot) {
3268
+ throw new Error("ROM processor not initialized. Call initialize() first.");
3269
+ }
3270
+ const result = {
3271
+ success: false,
3272
+ outputPath,
3273
+ romSize: 0,
3274
+ errors: []
3275
+ };
3276
+ console.warn("ROM building not yet implemented - awaiting Assembler conversion");
3277
+ return result;
3278
+ }
3279
+ /**
3280
+ * Get compression provider for the project
3281
+ */
3282
+ getCompression() {
3283
+ return this.projectRoot.getCompression();
3284
+ }
3285
+ /**
3286
+ * Get current ROM state
3287
+ */
3288
+ getRomState() {
3289
+ return this.romState;
3290
+ }
3291
+ /**
3292
+ * Get database root
3293
+ */
3294
+ getDatabase() {
3295
+ return this.dbRoot;
3296
+ }
3297
+ /**
3298
+ * Get project configuration
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
+ };
3322
+ }
3323
+ detectCompression() {
3324
+ return this.projectRoot.getCompression() instanceof QuintetLZ;
3325
+ }
3326
+ analyzeOpcodes() {
3327
+ if (!this.dbRoot) {
3328
+ return { totalOpcodes: 0, uniqueOpcodes: 0, coverage: 0 };
3329
+ }
3330
+ const totalOpcodes = Object.keys(this.dbRoot.opCodes).length;
3331
+ const uniqueOpcodes = new Set(Object.values(this.dbRoot.opCodes)).size;
3332
+ const coverage = totalOpcodes > 0 ? uniqueOpcodes / OpCodeUtils.getAllOpcodes().length * 100 : 0;
3333
+ return {
3334
+ totalOpcodes,
3335
+ uniqueOpcodes,
3336
+ coverage
3337
+ };
3338
+ }
3339
+ };
3340
+
3341
+ // src/api/ProjectManager.ts
3342
+ var ProjectManager = class _ProjectManager {
3343
+ constructor(events) {
3344
+ this.processor = null;
3345
+ this.status = {
3346
+ isLoaded: false,
3347
+ isAnalyzed: false,
3348
+ isExtracted: false,
3349
+ canBuild: false,
3350
+ progress: 0
3351
+ };
3352
+ this.events = {};
3353
+ this.events = events || {};
3354
+ }
3355
+ /**
3356
+ * Create project manager from existing project configuration
3357
+ */
3358
+ static async fromConfig(config, events) {
3359
+ const manager = new _ProjectManager(events);
3360
+ await manager.loadProject(config);
3361
+ return manager;
3362
+ }
3363
+ /**
3364
+ * Create project manager from project file or directory
3365
+ */
3366
+ static async fromPath(path, events) {
3367
+ const manager = new _ProjectManager(events);
3368
+ await manager.loadProjectFromPath(path);
3369
+ return manager;
3370
+ }
3371
+ /**
3372
+ * Load project from configuration
3373
+ */
3374
+ async loadProject(config) {
3375
+ try {
3376
+ this.updateStatus({ currentTask: "Loading project configuration", progress: 10 });
3377
+ this.events.onLoadStart?.();
3378
+ this.processor = new RomProcessor(config);
3379
+ this.updateStatus({ currentTask: "Loading ROM data", progress: 30 });
3380
+ await this.processor.loadRom();
3381
+ this.updateStatus({ currentTask: "Initializing database", progress: 60 });
3382
+ await this.processor.initialize();
3383
+ this.updateStatus({
3384
+ isLoaded: true,
3385
+ currentTask: "Project loaded successfully",
3386
+ progress: 100
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;
3394
+ }
3395
+ }
3396
+ /**
3397
+ * Load project from file or directory path
3398
+ */
3399
+ async loadProjectFromPath(path) {
3400
+ const processor = await RomProcessor.fromProject(path);
3401
+ await this.loadProject(processor.getProject().config);
3402
+ }
3403
+ /**
3404
+ * Analyze ROM structure and extract metadata
3405
+ */
3406
+ async analyzeRom() {
3407
+ if (!this.processor) {
3408
+ throw new Error("Project not loaded. Call loadProject() first.");
3409
+ }
3410
+ try {
3411
+ this.updateStatus({ currentTask: "Analyzing ROM structure", progress: 20 });
3412
+ this.events.onAnalysisStart?.();
3413
+ const results = await this.processor.analyze();
3414
+ this.updateStatus({
3415
+ isAnalyzed: true,
3416
+ currentTask: "ROM analysis complete",
3417
+ progress: 100
3418
+ });
3419
+ this.events.onAnalysisComplete?.(results);
3420
+ return results;
3421
+ } catch (error) {
3422
+ const err = error;
3423
+ this.updateStatus({ lastError: err, currentTask: "Analysis failed" });
3424
+ throw err;
3425
+ }
3426
+ }
3427
+ /**
3428
+ * Extract files from ROM
3429
+ */
3430
+ async extractFiles(outputDir) {
3431
+ if (!this.processor) {
3432
+ throw new Error("Project not loaded. Call loadProject() first.");
3433
+ }
3434
+ try {
3435
+ this.updateStatus({ currentTask: "Extracting files from ROM", progress: 30 });
3436
+ this.events.onExtractionStart?.();
3437
+ const results = await this.processor.extract(outputDir);
3438
+ this.updateStatus({
3439
+ isExtracted: true,
3440
+ canBuild: true,
3441
+ currentTask: "File extraction complete",
3442
+ progress: 100
3443
+ });
3444
+ this.events.onExtractionComplete?.(results);
3445
+ return results;
3446
+ } catch (error) {
3447
+ const err = error;
3448
+ this.updateStatus({ lastError: err, currentTask: "Extraction failed" });
3449
+ throw err;
3450
+ }
3451
+ }
3452
+ /**
3453
+ * Build ROM from extracted files
3454
+ */
3455
+ async buildRom(outputPath) {
3456
+ if (!this.processor) {
3457
+ throw new Error("Project not loaded. Call loadProject() first.");
3458
+ }
3459
+ if (!this.status.canBuild) {
3460
+ throw new Error("Cannot build ROM. Extract files first.");
3461
+ }
3462
+ try {
3463
+ this.updateStatus({ currentTask: "Building ROM", progress: 40 });
3464
+ this.events.onBuildStart?.();
3465
+ const results = await this.processor.build(outputPath);
3466
+ this.updateStatus({
3467
+ currentTask: "ROM build complete",
3468
+ progress: 100
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;
3476
+ }
3477
+ }
3478
+ /**
3479
+ * Process specific scene
3480
+ */
3481
+ async processScene(sceneId, metaFile) {
3482
+ if (!this.processor) {
3483
+ throw new Error("Project not loaded. Call loadProject() first.");
3484
+ }
3485
+ return await this.processor.processScene(sceneId, metaFile);
3486
+ }
3487
+ /**
3488
+ * Get current project status
3489
+ */
3490
+ getStatus() {
3491
+ return { ...this.status };
3492
+ }
3493
+ /**
3494
+ * Get the ROM processor instance
3495
+ */
3496
+ getProcessor() {
3497
+ return this.processor;
3498
+ }
3499
+ /**
3500
+ * Get project configuration
3501
+ */
3502
+ getProjectConfig() {
3503
+ return this.processor?.getProject().config || null;
3504
+ }
3505
+ /**
3506
+ * Get database root
3507
+ */
3508
+ getDatabase() {
3509
+ return this.processor?.getDatabase() || null;
3510
+ }
3511
+ /**
3512
+ * Get ROM state
3513
+ */
3514
+ getRomState() {
3515
+ return this.processor?.getRomState() || null;
3516
+ }
3517
+ /**
3518
+ * Reset project to initial state
3519
+ */
3520
+ reset() {
3521
+ this.processor = null;
3522
+ this.status = {
3523
+ isLoaded: false,
3524
+ isAnalyzed: false,
3525
+ isExtracted: false,
3526
+ canBuild: false,
3527
+ progress: 0
3528
+ };
3529
+ }
3530
+ /**
3531
+ * Complete workflow: load, analyze, extract, and build
3532
+ */
3533
+ async completeWorkflow(projectPath, outputDir, outputRomPath) {
3534
+ await this.loadProjectFromPath(projectPath);
3535
+ const analysisResults = await this.analyzeRom();
3536
+ const extractionResults = await this.extractFiles(outputDir);
3537
+ const buildResults = await this.buildRom(outputRomPath);
3538
+ return {
3539
+ analysis: analysisResults,
3540
+ extraction: extractionResults,
3541
+ build: buildResults
3542
+ };
3543
+ }
3544
+ // Private helper methods
3545
+ updateStatus(update) {
3546
+ this.status = { ...this.status, ...update };
3547
+ }
3548
+ };
3549
+
3550
+ // src/rom/rebuild/index.ts
3551
+ var ROM_REBUILD_MODULE = "gaia-core/rom/rebuild";
3552
+
3553
+ // src/index.ts
3554
+ var GAIA_CORE_VERSION = "0.1.0";
3555
+ var isPlatformBrowser = typeof window !== "undefined";
3556
+ var isPlatformNode = typeof process !== "undefined" && process.versions?.node;
3557
+ var isPlatformWebWorker = typeof importScripts !== "undefined";
3558
+
3559
+ export { ADDRESSING_REGEX, ALL_OPCODES, AddressingModeHandler, AsmReader, BlockReader, BlockWriter, CopCommandProcessor, FileReader, GAIA_CORE_VERSION, GROUPED_OPCODES, HEX_REGEX, ObjectType, Op, OpCode, OpCodeUtils, OperationContext, PostProcessor, ProcessorStateManager, ProjectManager, ProjectRoot, QuintetLZ, ROM_REBUILD_MODULE, ReferenceManager, Registers, RomDataReader, RomProcessor, RomState, RomStateUtils, SfxReader, SpriteFrame, SpriteGroup, SpriteMap, SpritePart, Stack, StackOperations, StringReader, TransformProcessor, TypeParser, isPlatformBrowser, isPlatformNode, isPlatformWebWorker };
3560
+ //# sourceMappingURL=index.mjs.map
3561
+ //# sourceMappingURL=index.mjs.map