@gaialabs/core 0.1.0 → 0.1.1

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