@gaialabs/core 0.1.0 → 0.1.2

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