@gaialabs/core 0.2.11 → 0.2.13

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.cjs CHANGED
@@ -936,10 +936,12 @@ var ProcessorStateManager = class {
936
936
  var RomDataReader = class {
937
937
  romData;
938
938
  position;
939
- constructor(romData) {
939
+ offset;
940
+ constructor(romData, offset = 0) {
940
941
  if (!romData) throw new Error("romData cannot be null");
941
942
  this.romData = romData;
942
943
  this.position = 0;
944
+ this.offset = offset;
943
945
  }
944
946
  readByte() {
945
947
  return this.romData[this.position++];
@@ -1247,6 +1249,7 @@ let MemberType = /* @__PURE__ */ function(MemberType) {
1247
1249
  MemberType["Address"] = "Address";
1248
1250
  MemberType["Binary"] = "Binary";
1249
1251
  MemberType["Code"] = "Code";
1252
+ MemberType["Branch"] = "Branch";
1250
1253
  MemberType["Location"] = "Location";
1251
1254
  return MemberType;
1252
1255
  }({});
@@ -1670,31 +1673,63 @@ var ChunkFile = class {
1670
1673
  mnemonics;
1671
1674
  group;
1672
1675
  scene;
1673
- constructor(name, size, location, type) {
1676
+ base;
1677
+ referenceManager;
1678
+ constructor(type, name, size = 0, location = 0) {
1679
+ this.type = type;
1674
1680
  this.name = name;
1675
1681
  this.size = size;
1676
1682
  this.location = location;
1677
1683
  this.mnemonics = {};
1678
1684
  this.compressed = type.compressed;
1679
- this.type = type;
1685
+ }
1686
+ enrichWithRawDataFromDbFile(file, rom, compression) {
1687
+ this.upper = file.upper ?? this.upper;
1688
+ this.size = file.end - file.start;
1689
+ this.location = file.start;
1690
+ this.base = file.base ?? this.type.base ?? this.base;
1691
+ this.group = file.group ?? this.group;
1692
+ this.scene = file.scene ?? this.scene;
1693
+ this.compressed = file.compressed;
1694
+ let start = this.location;
1695
+ let header = null;
1696
+ const fileType = this.type;
1697
+ if (fileType.header) {
1698
+ header = new Uint8Array(rom.slice(start, start + fileType.header));
1699
+ start += fileType.header;
1700
+ }
1701
+ let length = this.size;
1702
+ let fileData;
1703
+ if (this.compressed === true && compression) {
1704
+ const expanded = compression.expand(rom, start, length);
1705
+ fileData = combineHeader(expanded, 0, expanded.length, header, fileType.type);
1706
+ } else {
1707
+ if (this.compressed !== void 0) {
1708
+ start += 2;
1709
+ length -= 2;
1710
+ }
1711
+ fileData = combineHeader(rom, start, length, header, fileType.type);
1712
+ }
1713
+ this.rawData = fileData;
1714
+ this.size = fileData.length;
1715
+ }
1716
+ enrichWithAsmBlocksFromDbBlock(block, memoryMode) {
1717
+ this.group = block.group;
1718
+ this.scene = block.scene;
1719
+ this.parts = [];
1720
+ this.size = 0;
1721
+ if (!block.parts?.length) throw new Error(`Block ${block.name} has no parts`);
1722
+ this.location = block.parts[0].start;
1723
+ this.bank = block.movable ? void 0 : memoryMode === MemoryMapMode.Lo ? this.location >> 15 : this.location >> 16;
1724
+ this.transforms = block.transforms;
1725
+ this.postProcess = block.postProcess;
1726
+ for (const part of block.parts) {
1727
+ const asmBlock = new AsmBlock(part.start, part.end - part.start, false, part.name, part.type || void 0, part.bank);
1728
+ this.size += asmBlock.size;
1729
+ this.parts.push(asmBlock);
1730
+ }
1680
1731
  }
1681
1732
  };
1682
- function createChunkFileFromDbFile(rom, compression, dbFile, fileType) {
1683
- const chunkFile = new ChunkFile(dbFile.name, dbFile.end - dbFile.start, dbFile.start, fileType);
1684
- chunkFile.compressed = dbFile.compressed;
1685
- chunkFile.upper = dbFile.upper;
1686
- chunkFile.group = dbFile.group;
1687
- chunkFile.scene = dbFile.scene;
1688
- enrichWithRawDataFromDbFile(rom, chunkFile, compression, dbFile, fileType);
1689
- return chunkFile;
1690
- }
1691
- function createChunkFileFromDbBlock(block, fileType, memoryMode) {
1692
- const chunkFile = new ChunkFile(block.name, 0, 0, fileType);
1693
- chunkFile.group = block.group;
1694
- chunkFile.scene = block.scene;
1695
- enrichWithPartsFromDbBlock(chunkFile, block, memoryMode);
1696
- return chunkFile;
1697
- }
1698
1733
  function combineHeader(data, position, length, header, type) {
1699
1734
  let totalLength = length + (header ? header.length : 0);
1700
1735
  if (type === BinType.Palette && length < 512) totalLength += 512 - length;
@@ -1710,48 +1745,6 @@ function combineHeader(data, position, length, header, type) {
1710
1745
  return result;
1711
1746
  }
1712
1747
  /**
1713
- * Enriches ChunkFile with raw binary data from DbFile
1714
- */
1715
- function enrichWithRawDataFromDbFile(rom, chunkFile, compression, dbFile, fileType) {
1716
- let start = dbFile.start;
1717
- let header = null;
1718
- if (fileType.header) {
1719
- header = new Uint8Array(rom.slice(start, start + fileType.header));
1720
- start += fileType.header;
1721
- }
1722
- let length = dbFile.end - start;
1723
- let fileData;
1724
- if (dbFile.compressed === true && compression) {
1725
- const expanded = compression.expand(rom, start, length);
1726
- fileData = combineHeader(expanded, 0, expanded.length, header, fileType.type);
1727
- } else {
1728
- if (dbFile.compressed !== void 0) {
1729
- start += 2;
1730
- length -= 2;
1731
- }
1732
- fileData = combineHeader(rom, start, length, header, fileType.type);
1733
- }
1734
- chunkFile.rawData = fileData;
1735
- chunkFile.size = fileData.length;
1736
- }
1737
- /**
1738
- * Enriches ChunkFile with AsmBlock parts from DbBlock
1739
- */
1740
- function enrichWithPartsFromDbBlock(chunkFile, block, memoryMode) {
1741
- chunkFile.parts = [];
1742
- chunkFile.size = 0;
1743
- if (!block.parts?.length) throw new Error(`Block ${block.name} has no parts`);
1744
- chunkFile.location = block.parts[0].start;
1745
- chunkFile.bank = block.movable ? void 0 : memoryMode === MemoryMapMode.Lo ? chunkFile.location >> 15 : chunkFile.location >> 16;
1746
- chunkFile.transforms = block.transforms;
1747
- chunkFile.postProcess = block.postProcess;
1748
- for (const part of block.parts) {
1749
- const asmBlock = new AsmBlock(part.start, part.end - part.start, false, part.name, part.type || void 0, part.bank);
1750
- chunkFile.size += asmBlock.size;
1751
- chunkFile.parts.push(asmBlock);
1752
- }
1753
- }
1754
- /**
1755
1748
  * Chunk file utilities
1756
1749
  */
1757
1750
  var ChunkFileUtils = class {
@@ -1773,16 +1766,11 @@ var ChunkFileUtils = class {
1773
1766
  * Calculate the total size of all blocks
1774
1767
  */
1775
1768
  static calculateSize(chunkFile) {
1776
- if (!chunkFile.parts) {
1777
- let size = chunkFile.rawData?.length ?? chunkFile.size;
1778
- if (chunkFile.type.header === -2 || chunkFile.compressed === false) {
1779
- size += 2;
1780
- chunkFile.size = size;
1781
- }
1782
- return size;
1783
- }
1784
1769
  let size = 0;
1785
- for (let x = 0; x < chunkFile.parts.length; x++) {
1770
+ if (chunkFile.rawData) {
1771
+ size = chunkFile.rawData.length;
1772
+ if (chunkFile.type.header === -2 || chunkFile.compressed === false) size += 2;
1773
+ } else if (chunkFile.parts) for (let x = 0; x < chunkFile.parts.length; x++) {
1786
1774
  const block = chunkFile.parts[x];
1787
1775
  if (x > 0 && !block.label) break;
1788
1776
  size += block.size || 0;
@@ -1801,7 +1789,7 @@ var ChunkFileUtils = class {
1801
1789
  block,
1802
1790
  part
1803
1791
  ];
1804
- for (const otherBlock of root) if (otherBlock !== block) {
1792
+ for (const otherBlock of root) if (otherBlock !== block && !otherBlock.type.struct) {
1805
1793
  const [otherIsInside, otherPart] = this.isInsideWithPart(otherBlock, location);
1806
1794
  if (otherIsInside) return [
1807
1795
  true,
@@ -1987,6 +1975,7 @@ var ReferenceManager = class {
1987
1975
  }
1988
1976
  createTypeName(type, location) {
1989
1977
  let name = type.toLowerCase();
1978
+ if (name === "branch") name = "loc";
1990
1979
  while (name.length > 0 && BlockReaderConstants.POINTER_CHARACTERS.includes(name[0])) name = name.substring(1) + "_list";
1991
1980
  return `${name}_${location.toString(16).toUpperCase().padStart(6, "0")}`;
1992
1981
  }
@@ -2364,6 +2353,7 @@ var AddressingModeHandler = class {
2364
2353
  }
2365
2354
  handlePCRelativeMode(operands, nextAddress, reg, isLong) {
2366
2355
  const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
2356
+ this._blockReader._referenceManager.tryAddStruct(relative, "Branch");
2367
2357
  this._blockReader.updateRegisterState(relative, reg);
2368
2358
  operands.push(new LocationWrapper(relative, isLong ? AddressType.WRelative : AddressType.Relative));
2369
2359
  }
@@ -2987,7 +2977,7 @@ var StringReader = class StringReader {
2987
2977
  this._romDataReader = blockReader._romDataReader;
2988
2978
  }
2989
2979
  resolveCommand(cmd, builder) {
2990
- if (cmd.dictionary) builder.push(cmd.dictionary.entries[this._romDataReader.readByte() - (cmd.dictionary.base ?? 0)]);
2980
+ if (cmd.dictionary) builder.push(`[${cmd.dictionary.commandName ?? cmd.dictionary.command?.toString(16).padEnd(2, "0").toUpperCase()}:${this._romDataReader.readByte().toString(16).padEnd(2, "0").toUpperCase()}]`);
2991
2981
  else if (cmd.types && cmd.types.length > 0) {
2992
2982
  builder.push(`[${cmd.name}`);
2993
2983
  let first = true;
@@ -3028,7 +3018,7 @@ var StringReader = class StringReader {
3028
3018
  }
3029
3019
  parseString(stringType, fixedSize) {
3030
3020
  const commands = stringType.commandLookup;
3031
- const dictionaries = stringType.dictionaries;
3021
+ stringType.dictionaries;
3032
3022
  const builder = [];
3033
3023
  const strLoc = this._romDataReader.position;
3034
3024
  const terminator = stringType.terminator;
@@ -3045,23 +3035,17 @@ var StringReader = class StringReader {
3045
3035
  if (cmd.halt) break;
3046
3036
  } else {
3047
3037
  let found = false;
3048
- for (const dictionary of Object.values(dictionaries)) if (dictionary.base !== void 0 && c >= dictionary.base && c <= dictionary.range) {
3049
- builder.push(dictionary.entries[c - dictionary.base]);
3050
- found = true;
3051
- break;
3052
- }
3053
- if (!found) {
3054
- for (const layer of stringType.layers) if (layer.on !== void 0) {
3055
- if (c === layer.on) {
3056
- currentLayer = layer;
3057
- found = true;
3058
- break;
3059
- }
3060
- } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3061
- builder.push(layer.map[c - layer.base]);
3038
+ for (const layer of stringType.layers) if (layer.on !== void 0) {
3039
+ if (c === layer.on) {
3040
+ currentLayer = layer;
3041
+ builder.push("[--]");
3062
3042
  found = true;
3063
3043
  break;
3064
3044
  }
3045
+ } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3046
+ builder.push(layer.map[c - layer.base]);
3047
+ found = true;
3048
+ break;
3065
3049
  }
3066
3050
  if (!found) {
3067
3051
  let index = c - (currentLayer.base ?? 0);
@@ -3090,28 +3074,26 @@ var StringReader = class StringReader {
3090
3074
  let str = sw.string;
3091
3075
  let ix = indexOfAny(str, StringReader.STRING_REFERENCE_CHARACTERS);
3092
3076
  while (ix >= 0) {
3093
- if (ix + 6 < str.length) {
3094
- const hexStr = str.substring(ix + 1, ix + 7);
3095
- const sloc = parseInt(hexStr, 16);
3096
- if (!isNaN(sloc)) {
3097
- if (new Address(sloc >> 16 & 255, sloc & 65535, this._blockReader._root.config.memoryMode).isROM) {
3098
- this._blockReader.resolveInclude(sloc, false);
3099
- const name = this._blockReader.resolveName(sloc, AddressType.Unknown, false);
3100
- const opix = indexOfAny(name, RomProcessingConstants.OPERATORS);
3101
- if (opix > 0) {
3102
- const offsetStr = name.substring(opix + 1);
3103
- let offset;
3104
- if (offsetStr === "M") offset = this._blockReader._referenceManager.markerTable.get(sloc) || 0;
3105
- else offset = parseInt(offsetStr, 16) || 0;
3106
- if (name[opix] === "-") offset = -offset;
3107
- name.substring(0, opix);
3108
- const target = sloc - offset;
3109
- const [isOutside, block, part] = ChunkFileUtils.isOutsideWithPart(this._blockReader._enrichedChunks, this._blockReader._currentChunk, sloc);
3110
- if (part != null) {
3111
- const entry = part.objList?.find((x) => typeof x === "object" && x !== null && "location" in x && "object" in x && x.location === target);
3112
- if (entry && entry.object) entry.object.marker = offset;
3113
- }
3114
- }
3077
+ if (ix + 6 >= str.length) break;
3078
+ const hexStr = str.substring(ix + 1, ix + 7);
3079
+ const sloc = parseInt(hexStr, 16);
3080
+ if (isNaN(sloc)) break;
3081
+ if (new Address(sloc >> 16 & 255, sloc & 65535, this._blockReader._root.config.memoryMode).isROM) {
3082
+ this._blockReader.resolveInclude(sloc, false);
3083
+ const name = this._blockReader._referenceManager.resolveName(sloc, AddressType.Unknown, false);
3084
+ const opix = indexOfAny(name, RomProcessingConstants.OPERATORS);
3085
+ if (opix > 0) {
3086
+ const offsetStr = name.substring(opix + 1);
3087
+ let offset;
3088
+ if (offsetStr === "M") offset = this._blockReader._referenceManager.markerTable.get(sloc) || 0;
3089
+ else offset = parseInt(offsetStr, 16) || 0;
3090
+ if (name[opix] === "-") offset = -offset;
3091
+ name.substring(0, opix);
3092
+ const target = sloc - offset;
3093
+ const [isOutside, block, part] = ChunkFileUtils.isOutsideWithPart(this._blockReader._enrichedChunks, this._blockReader._currentChunk, sloc);
3094
+ if (part != null) {
3095
+ const entry = part.objList?.find((x) => typeof x === "object" && x !== null && "location" in x && "object" in x && x.location === target);
3096
+ if (entry && entry.object) entry.object.marker = offset;
3115
3097
  }
3116
3098
  }
3117
3099
  }
@@ -3161,7 +3143,8 @@ var TypeParser = class {
3161
3143
  case MemberType.Address: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Address);
3162
3144
  case MemberType.Location: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Location);
3163
3145
  case MemberType.Binary: return this.parseBinary(fixedSize);
3164
- case MemberType.Code: return this.parseCode(reg);
3146
+ case MemberType.Code:
3147
+ case MemberType.Branch: return this.parseCode(reg);
3165
3148
  default: throw new Error("Invalid member type");
3166
3149
  }
3167
3150
  const parentType = this._blockReader._root.structs[fixedTypeName];
@@ -3195,7 +3178,7 @@ var TypeParser = class {
3195
3178
  let checkPosition = startPosition;
3196
3179
  while (++checkPosition < this._romDataReader.position) {
3197
3180
  const struct = this._referenceManager.tryGetStruct(checkPosition);
3198
- if (struct.found && struct.chunkType !== "Code") {
3181
+ if (struct.found && struct.chunkType !== "Code" && struct.chunkType !== "Branch") {
3199
3182
  this._romDataReader.position = checkPosition;
3200
3183
  break;
3201
3184
  }
@@ -3226,16 +3209,18 @@ var TypeParser = class {
3226
3209
  }
3227
3210
  parseLocation(offset, bank, typeName, addrType) {
3228
3211
  if ((bank === void 0 || bank === null) && offset === 0) return new Word(offset);
3212
+ offset -= this._romDataReader.offset;
3229
3213
  let adrs;
3230
3214
  let loc;
3231
- if (addrType === AddressType.Location) loc = offset | bank << 16;
3215
+ if (addrType === AddressType.Location || this._romDataReader.offset) loc = offset | bank << 16;
3232
3216
  else {
3233
3217
  adrs = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), offset, this._blockReader._root.config.memoryMode);
3234
3218
  if (!adrs.isROM) return adrs;
3235
3219
  loc = adrs.toLocation();
3236
3220
  }
3237
- if (typeName && !this._blockReader._root.rewrites[loc]) {
3238
- this._referenceManager.tryAddStruct(loc, typeName);
3221
+ if (typeName && (this._romDataReader.offset || !this._blockReader._root.rewrites[loc])) {
3222
+ const oldStruct = this._referenceManager.structTable.get(loc);
3223
+ if (!oldStruct || oldStruct === "Branch") this._referenceManager.structTable.set(loc, typeName);
3239
3224
  const referenceName = `${typeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
3240
3225
  this._referenceManager.tryAddName(loc, referenceName);
3241
3226
  }
@@ -3246,7 +3231,10 @@ var TypeParser = class {
3246
3231
  let first = true;
3247
3232
  while (this._romDataReader.position < this._blockReader._partEnd) {
3248
3233
  if (first) first = false;
3249
- else if (this._referenceManager.containsStruct(this._romDataReader.position)) break;
3234
+ else {
3235
+ const struct = this._referenceManager.structTable.get(this._romDataReader.position);
3236
+ if (struct && struct !== "Branch") break;
3237
+ }
3250
3238
  if (reg) this._blockReader.hydrateRegisters(reg);
3251
3239
  const op = this._blockReader._asmReader.parseAsm(reg);
3252
3240
  opList.push(op);
@@ -3421,8 +3409,6 @@ var BlockReader = class {
3421
3409
  this._stringReader = new StringReader(this);
3422
3410
  this._asmReader = new AsmReader(this);
3423
3411
  this._typeParser = new TypeParser(this);
3424
- this.initializeOverrides();
3425
- this.initializeFileReferences();
3426
3412
  }
3427
3413
  /**
3428
3414
  * Processes predefined overrides for registers and bank notes
@@ -3472,9 +3458,6 @@ var BlockReader = class {
3472
3458
  /**
3473
3459
  * Resolves name for a location (delegated to ReferenceManager)
3474
3460
  */
3475
- resolveName(location, type, isBranch) {
3476
- return this._referenceManager.resolveName(location, type, isBranch);
3477
- }
3478
3461
  /**
3479
3462
  * Resolves include for a location
3480
3463
  */
@@ -3493,7 +3476,8 @@ var BlockReader = class {
3493
3476
  * Notes a type at a location and manages chunk references
3494
3477
  */
3495
3478
  noteType(loc, type, silent = false, reg) {
3496
- this._referenceManager.tryAddStruct(loc, type);
3479
+ const oldStruct = this._referenceManager.structTable.get(loc);
3480
+ if (!oldStruct || oldStruct === "Branch") this._referenceManager.structTable.set(loc, type);
3497
3481
  const nameResult = this._referenceManager.tryGetName(loc);
3498
3482
  let name;
3499
3483
  if (!nameResult.found) {
@@ -3533,10 +3517,14 @@ var BlockReader = class {
3533
3517
  return this._romDataReader.position < this._partEnd && !this._referenceManager.containsStruct(this._romDataReader.position);
3534
3518
  }
3535
3519
  analyzeAndResolve() {
3520
+ console.log("analyzeAndResolve started");
3521
+ this.initializeOverrides();
3522
+ this.initializeFileReferences();
3536
3523
  this.createChunkFilesFromDatabase();
3537
3524
  this.initializeBlocksAndParts();
3538
3525
  this.analyzeChunkFiles();
3539
3526
  this.resolveReferences();
3527
+ console.log("analyzeAndResolve complete");
3540
3528
  return this._enrichedChunks;
3541
3529
  }
3542
3530
  /**
@@ -3546,6 +3534,7 @@ var BlockReader = class {
3546
3534
  * Initializes blocks and parts with base references
3547
3535
  */
3548
3536
  initializeBlocksAndParts() {
3537
+ console.log("initializeBlocksAndParts");
3549
3538
  for (const block of this._enrichedChunks) for (const part of block.parts || []) {
3550
3539
  if (part.structName) this._referenceManager.tryAddStruct(part.location, part.structName);
3551
3540
  if (part.label) this._referenceManager.tryAddName(part.location, part.label);
@@ -3600,6 +3589,7 @@ var BlockReader = class {
3600
3589
  * Creates ChunkFile objects from database structure
3601
3590
  */
3602
3591
  createChunkFilesFromDatabase() {
3592
+ console.log("createChunkFilesFromDatabase");
3603
3593
  this._enrichedChunks = [];
3604
3594
  this.createChunkFilesFromSfx();
3605
3595
  this.createChunkFilesFromDbFiles();
@@ -3644,7 +3634,7 @@ var BlockReader = class {
3644
3634
  const size = getSize();
3645
3635
  const startPos = pos + offset;
3646
3636
  const data = getBytes(size);
3647
- const chunk = new ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, fileType);
3637
+ const chunk = new ChunkFile(fileType, `sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos);
3648
3638
  chunk.rawData = data;
3649
3639
  chunk.group = "sfx";
3650
3640
  this._enrichedChunks.push(chunk);
@@ -3655,7 +3645,9 @@ var BlockReader = class {
3655
3645
  */
3656
3646
  createChunkFilesFromDbFiles() {
3657
3647
  for (const dbFile of this._root.files) {
3658
- const chunkFile = createChunkFileFromDbFile(this._romDataReader.romData, this._root.compression, dbFile, this._root.fileTypes[dbFile.type]);
3648
+ const fileType = this._root.fileTypes[dbFile.type];
3649
+ const chunkFile = new ChunkFile(fileType, dbFile.name);
3650
+ chunkFile.enrichWithRawDataFromDbFile(dbFile, this._romDataReader.romData, this._root.compression);
3659
3651
  this._enrichedChunks.push(chunkFile);
3660
3652
  }
3661
3653
  }
@@ -3665,7 +3657,8 @@ var BlockReader = class {
3665
3657
  createChunkFilesFromDbBlocks() {
3666
3658
  const fileType = Object.values(this._root.fileTypes).find((x) => x.isBlock);
3667
3659
  for (const block of this._root.blocks) {
3668
- const chunkFile = createChunkFileFromDbBlock(block, fileType, this._root.config.memoryMode);
3660
+ const chunkFile = new ChunkFile(fileType, block.name);
3661
+ chunkFile.enrichWithAsmBlocksFromDbBlock(block, this._root.config.memoryMode);
3669
3662
  this._enrichedChunks.push(chunkFile);
3670
3663
  }
3671
3664
  }
@@ -3673,22 +3666,43 @@ var BlockReader = class {
3673
3666
  * Analyzes only assembly ChunkFiles (those with parts from DbBlocks)
3674
3667
  */
3675
3668
  analyzeChunkFiles() {
3676
- const assemblyChunks = this._enrichedChunks.filter((chunk) => chunk.type.type === "Assembly");
3677
- for (const chunkFile of assemblyChunks) {
3669
+ console.log("analyzeChunkFiles");
3670
+ for (const chunkFile of this._enrichedChunks) {
3671
+ if (!chunkFile.type.isBlock) continue;
3672
+ chunkFile.referenceManager = this._referenceManager;
3678
3673
  this._currentChunk = chunkFile;
3679
3674
  for (const asmBlock of chunkFile.parts || []) {
3680
3675
  this._currentAsmBlock = asmBlock;
3681
3676
  this.processPart(asmBlock);
3682
3677
  }
3683
3678
  }
3679
+ const oldState = this._referenceManager;
3680
+ const oldReader = this._romDataReader;
3681
+ const oldTypeParser = this._typeParser;
3682
+ for (const chunkFile of this._enrichedChunks) {
3683
+ if (!chunkFile.type.struct) continue;
3684
+ this._currentChunk = chunkFile;
3685
+ const asmBlock = new AsmBlock(0, chunkFile.size, false, chunkFile.name, chunkFile.type.struct);
3686
+ chunkFile.parts = [asmBlock];
3687
+ this._currentAsmBlock = asmBlock;
3688
+ this._referenceManager = chunkFile.referenceManager = new ReferenceManager(this._root);
3689
+ this._romDataReader = new RomDataReader(chunkFile.rawData, chunkFile.base ?? 0);
3690
+ this._typeParser = new TypeParser(this);
3691
+ this.processPart(asmBlock);
3692
+ }
3693
+ this._referenceManager = oldState;
3694
+ this._romDataReader = oldReader;
3695
+ this._typeParser = oldTypeParser;
3684
3696
  }
3685
3697
  /**
3686
3698
  * Resolves references in assembly ChunkFiles only
3687
3699
  */
3688
3700
  resolveReferences() {
3701
+ console.log("resolveReferences");
3689
3702
  for (const chunkFile of this._enrichedChunks) {
3703
+ if (!chunkFile.type.isBlock) continue;
3690
3704
  this._currentChunk = chunkFile;
3691
- for (const asmBlock of chunkFile.parts || []) {
3705
+ for (const asmBlock of chunkFile.parts) {
3692
3706
  this._currentAsmBlock = asmBlock;
3693
3707
  this.resolveObject(asmBlock.objList, false);
3694
3708
  }
@@ -3878,6 +3892,7 @@ var BlockWriter = class {
3878
3892
  generateAsm(block) {
3879
3893
  if (!block.parts) throw new Error("Invalid block structure for generateAsm");
3880
3894
  const lines = [];
3895
+ this._referenceManager = block.referenceManager;
3881
3896
  if (block.bank !== void 0) lines.push(`?BANK ${block.bank.toString(16).toUpperCase().padStart(2, "0")}`);
3882
3897
  const includes = ChunkFileUtils.getIncludes(block);
3883
3898
  if (includes && includes.length > 0) {
@@ -3922,11 +3937,11 @@ var BlockWriter = class {
3922
3937
  }
3923
3938
  if (typeof obj === "number") {
3924
3939
  if (op.mode === "Immediate") return obj;
3925
- return this._blockReader.resolveName(obj, AddressType.Address, isBranch);
3940
+ return this._referenceManager.resolveName(obj, AddressType.Address, isBranch);
3926
3941
  }
3927
3942
  if (this.getObjectType(obj) === ObjectType.LocationWrapper) {
3928
3943
  const lw = obj;
3929
- return this._blockReader.resolveName(lw.location, lw.type, isBranch);
3944
+ return this._referenceManager.resolveName(lw.location, lw.type, isBranch);
3930
3945
  }
3931
3946
  if (this.getObjectType(obj) === ObjectType.Address) {
3932
3947
  const addr = obj;
@@ -3978,7 +3993,7 @@ var BlockWriter = class {
3978
3993
  objLines = this.writeOpArray(obj, depth);
3979
3994
  break;
3980
3995
  case ObjectType.LocationWrapper:
3981
- objLines = [this._blockReader.resolveName(obj.location, obj.type, isBranch)];
3996
+ objLines = [this._referenceManager.resolveName(obj.location, obj.type, isBranch)];
3982
3997
  break;
3983
3998
  case ObjectType.Address:
3984
3999
  objLines = isArray ? [`$#${obj.offset.toString(16).toUpperCase().padStart(4, "0")}`] : [`$${obj.toString()}`];
@@ -4096,7 +4111,7 @@ var BlockWriter = class {
4096
4111
  const adrs = new Address(rawAddr >> 16 & 255, rawAddr & 65535, this._blockReader._root.config.memoryMode);
4097
4112
  const addressType = char === "^" ? AddressType.Offset : AddressType.Address;
4098
4113
  let name = "";
4099
- if (adrs.isROM) name = this._blockReader.resolveName(adrs.toLocation(), addressType, false);
4114
+ if (adrs.isROM) name = this._referenceManager.resolveName(adrs.toLocation(), addressType, false);
4100
4115
  else if (addressType === AddressType.Offset) name = adrs.offset.toString(16).toUpperCase().padStart(4, "0");
4101
4116
  else name = adrs.toString();
4102
4117
  str = str.replace(str.substring(ix, ix + 7), name);
@@ -4113,10 +4128,10 @@ var BlockWriter = class {
4113
4128
  let six = 0;
4114
4129
  let mix = 0;
4115
4130
  while (mix < marker) if (str[six] === "[") {
4116
- const eix = str.indexOf("]", ++six);
4117
- const parts = str.substring(six, eix).split(/[,: ]/);
4131
+ const eix = str.indexOf("]", six);
4132
+ const parts = str.substring(six + 1, eix).split(/[,: ]/);
4118
4133
  const cmd = stringObj.type.commands[parts[0]];
4119
- if (cmd && cmd.types) for (const t of cmd.types) switch (t) {
4134
+ if (cmd) for (const t of cmd.types) switch (t) {
4120
4135
  case MemberType.Byte:
4121
4136
  mix += 1;
4122
4137
  break;
@@ -4133,6 +4148,7 @@ var BlockWriter = class {
4133
4148
  break;
4134
4149
  default: throw new Error("Unsupported member type");
4135
4150
  }
4151
+ else mix += parts.length - 1;
4136
4152
  six = eix + 1;
4137
4153
  mix++;
4138
4154
  } else {
@@ -4141,6 +4157,18 @@ var BlockWriter = class {
4141
4157
  }
4142
4158
  str = str.substring(0, six) + "[::]" + str.substring(six);
4143
4159
  }
4160
+ const dictionaries = stringObj.type.dictionaries;
4161
+ for (const dictionary of Object.values(dictionaries)) {
4162
+ const commandName = `[${dictionary.commandName ?? dictionary.command?.toString(16).padEnd(2, "0").toUpperCase()}:`;
4163
+ let cmdIx;
4164
+ while ((cmdIx = str.indexOf(commandName)) >= 0) {
4165
+ const endIx = str.indexOf("]", cmdIx);
4166
+ const strIx = parseInt(str.substring(cmdIx + commandName.length, endIx), 16);
4167
+ const newText = dictionary.entries[strIx];
4168
+ str = str.substring(0, cmdIx) + newText + str.substring(endIx + 1);
4169
+ }
4170
+ }
4171
+ str = str.replace(/\[--\]/g, "");
4144
4172
  const sizeParam = stringObj.fixedSize ? `(${stringObj.fixedSize})` : "";
4145
4173
  return [`${refChar}${str}${refChar}${sizeParam}`];
4146
4174
  }
@@ -4225,8 +4253,8 @@ var RomLayout = class RomLayout {
4225
4253
  return parseInt(a.name.substring(a.name.length - 2, a.name.length), 16) - parseInt(b.name.substring(b.name.length - 2, b.name.length), 16);
4226
4254
  }) : [];
4227
4255
  this.unmatchedFiles = files.filter((x) => (x.size || 0) > 0).filter((x) => this.sfxPackType === "Individual" || x.type.type !== "Sound").sort((a, b) => {
4228
- const aAsm = a.parts ? 0 : 1;
4229
- const bAsm = b.parts ? 0 : 1;
4256
+ const aAsm = a.rawData ? 1 : 0;
4257
+ const bAsm = b.rawData ? 1 : 0;
4230
4258
  if (aAsm !== bAsm) return aAsm - bAsm;
4231
4259
  if (b.size !== a.size) return b.size - a.size;
4232
4260
  if (a.location !== b.location) return a.location - b.location;
@@ -4276,7 +4304,7 @@ var RomLayout = class RomLayout {
4276
4304
  start += RomProcessingConstants.PAGE_SIZE;
4277
4305
  page++;
4278
4306
  if (offset) {
4279
- const newFile = new ChunkFile(file.name + "_2", file.size, file.location, Object.values(this.root.fileTypes).find((x) => x.type === BinType.Binary));
4307
+ const newFile = new ChunkFile(Object.values(this.root.fileTypes).find((x) => x.type === BinType.Binary), file.name + "_2", file.size, file.location);
4280
4308
  const end = file.rawData.length - offset;
4281
4309
  newFile.rawData = file.rawData.slice(end);
4282
4310
  newFile.size = newFile.rawData.length;
@@ -4366,106 +4394,13 @@ var RomLayout = class RomLayout {
4366
4394
  }
4367
4395
  };
4368
4396
 
4369
- //#endregion
4370
- //#region src/rom/rebuild/processor.ts
4371
- /**
4372
- * ROM rebuild processor
4373
- * Converted from ext/GaiaLib/Rom/Rebuild/RomProcessor.cs
4374
- */
4375
- var RomProcessor = class RomProcessor {
4376
- writer;
4377
- constructor(writer) {
4378
- this.writer = writer;
4379
- }
4380
- async repack(allFiles) {
4381
- const patches = [];
4382
- const asmFiles = [];
4383
- const compression = this.writer.root.compression;
4384
- const canCompress = !!compression;
4385
- const conditionFiles = [];
4386
- for (const file of allFiles) {
4387
- if (file.type.type === "Patch") patches.push(file);
4388
- else if (file.type.type !== "Assembly") {
4389
- if (file.compressed === true && canCompress) if (this.writer.root.config.uncompress) file.compressed = false;
4390
- else {
4391
- let newData = compression.compact(file.rawData, file.type.header);
4392
- if (file.type.header) newData = new Uint8Array([...file.rawData.slice(0, file.type.header), ...newData]);
4393
- file.rawData = newData;
4394
- file.size = newData.length;
4395
- }
4396
- continue;
4397
- }
4398
- asmFiles.push(file);
4399
- conditionFiles.push(file.name);
4400
- }
4401
- for (const file of asmFiles) {
4402
- const { blocks, includes, reqBank } = new Assembler(this.writer.root, file.textData, conditionFiles).parseAssembly();
4403
- file.parts = blocks;
4404
- file.includes = includes;
4405
- file.bank = reqBank ?? void 0;
4406
- }
4407
- RomProcessor.applyPatches(asmFiles, patches);
4408
- for (const file of allFiles) ChunkFileUtils.calculateSize(file);
4409
- const pages = new RomLayout(allFiles, this.writer.root).organize();
4410
- for (const file of asmFiles) ChunkFileUtils.rebase(file);
4411
- const masterLookup = /* @__PURE__ */ new Map();
4412
- for (const f of asmFiles) {
4413
- const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
4414
- f.includeLookup = /* @__PURE__ */ new Map();
4415
- for (const b of includeBlocks) if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
4416
- for (const b of f.parts) if (b.label) {
4417
- const nameUpper = b.label.toUpperCase();
4418
- masterLookup.set(nameUpper, b.location);
4419
- f.includeLookup.set(nameUpper, b);
4420
- }
4421
- }
4422
- const fileLookup = /* @__PURE__ */ new Map();
4423
- for (const f of allFiles) fileLookup.set(f.name.toUpperCase(), f.location);
4424
- this.writer.allocate(pages);
4425
- for (const file of allFiles) await this.writer.writeFile(file, fileLookup);
4426
- return masterLookup;
4427
- }
4428
- static applyPatches(asmFiles, patches) {
4429
- for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
4430
- let file = null;
4431
- let dstIx = -1;
4432
- const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
4433
- for (let ix = 0; patch.parts && ix < patch.parts.length;) {
4434
- const block = patch.parts[ix];
4435
- let match = null;
4436
- if (block.label) for (const i of inc) {
4437
- if (!i.parts) continue;
4438
- for (let y = 0; y < i.parts.length; y++) {
4439
- const check = i.parts[y];
4440
- if (check.label === block.label) {
4441
- file = i;
4442
- dstIx = y;
4443
- match = check;
4444
- break;
4445
- }
4446
- }
4447
- }
4448
- if (match) file.parts[dstIx++] = block;
4449
- else if (dstIx >= 0) file.parts.splice(dstIx++, 0, block);
4450
- else {
4451
- ix++;
4452
- continue;
4453
- }
4454
- file.includes = file.includes || /* @__PURE__ */ new Set();
4455
- file.includes.add(patch.name.toUpperCase());
4456
- patch.parts.splice(ix, 1);
4457
- }
4458
- }
4459
- }
4460
- };
4461
-
4462
4397
  //#endregion
4463
4398
  //#region src/rom/rebuild/writer.ts
4464
4399
  /**
4465
4400
  * ROM writer (binary)
4466
4401
  * Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs (subset: binary write only)
4467
4402
  */
4468
- var RomWriter = class {
4403
+ var RomWriter = class RomWriter {
4469
4404
  bpsPath;
4470
4405
  outBuffer;
4471
4406
  romSize;
@@ -4473,8 +4408,8 @@ var RomWriter = class {
4473
4408
  constructor(root) {
4474
4409
  this.root = root;
4475
4410
  }
4476
- async repack(files) {
4477
- const masterLookup = await new RomProcessor(this).repack(files);
4411
+ async repack(files, modules) {
4412
+ const masterLookup = await new RomProcessor(this).repack(files, modules);
4478
4413
  this.writeHeaders(masterLookup);
4479
4414
  return this.outBuffer;
4480
4415
  }
@@ -4620,7 +4555,7 @@ var RomWriter = class {
4620
4555
  }
4621
4556
  } else if (file.parts && file.parts.length > 0) {
4622
4557
  if (file.parts[0].location !== file.location && (file.location || 0) !== 0) throw new Error("Assembly was not based properly");
4623
- this.parseAssembly(file.parts, fileLookup, file.includeLookup);
4558
+ RomWriter.parseAssembly(this.root, file.parts, fileLookup, file.includeLookup, this.outBuffer, void 0);
4624
4559
  }
4625
4560
  return pos - start;
4626
4561
  }
@@ -4632,12 +4567,10 @@ var RomWriter = class {
4632
4567
  * Parse assembly blocks and write binary data to output buffer
4633
4568
  * Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs ParseAssembly method
4634
4569
  */
4635
- parseAssembly(blocks, fileLookup, includeLookup) {
4570
+ static parseAssembly(root, blocks, fileLookup, includeLookup, outBuffer, addrOffset) {
4636
4571
  if (!blocks) throw new Error("Assembly has not been parsed");
4637
- const buf = this.outBuffer;
4638
4572
  let bix = 0;
4639
4573
  for (const block of blocks) {
4640
- let oldPos = null;
4641
4574
  let position = block.location;
4642
4575
  const objList = block.objList;
4643
4576
  let oix = 0;
@@ -4652,13 +4585,13 @@ var RomWriter = class {
4652
4585
  continue;
4653
4586
  } else if (currentObj instanceof Op) {
4654
4587
  const op = currentObj;
4655
- buf[position++] = op.code & 255;
4588
+ outBuffer[position++] = op.code & 255;
4656
4589
  opos += op.size;
4657
4590
  for (const operand of op.operands) processObject(operand, op);
4658
4591
  break;
4659
4592
  } else if (currentObj instanceof Uint8Array) {
4660
4593
  const arr = currentObj;
4661
- for (let i = 0; i < arr.length; i++) buf[position + i] = arr[i];
4594
+ for (let i = 0; i < arr.length; i++) outBuffer[position + i] = arr[i];
4662
4595
  position += arr.length;
4663
4596
  opos += arr.length;
4664
4597
  break;
@@ -4670,7 +4603,7 @@ var RomWriter = class {
4670
4603
  if (ix > 0) label = label.substring(ix);
4671
4604
  let loc;
4672
4605
  const isRelative = parentOp && (parentOp.mode === "PCRelative" || parentOp.mode === "PCRelativeLong");
4673
- const operatorIdx = this.indexOfAny(label, RomProcessingConstants.OPERATORS);
4606
+ const operatorIdx = RomWriter.indexOfAny(label, RomProcessingConstants.OPERATORS);
4674
4607
  let offset = null;
4675
4608
  let useMarker = false;
4676
4609
  if (operatorIdx > 0) {
@@ -4719,12 +4652,13 @@ var RomWriter = class {
4719
4652
  if (offset !== null) loc += offset;
4720
4653
  else if (useMarker && target instanceof AsmBlock) {
4721
4654
  let markerOffset = 0;
4722
- for (const part of target.objList) if (this.isStringMarker(part)) {
4655
+ for (const part of target.objList) if (RomWriter.isStringMarker(part)) {
4723
4656
  loc += markerOffset;
4724
4657
  break;
4725
4658
  } else markerOffset += RomProcessingConstants.getSize(part);
4726
4659
  }
4727
- if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, this.root.config.memoryMode, this.root.config.cpuMode).toInt();
4660
+ if (addrOffset !== void 0) loc += addrOffset;
4661
+ else if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, root.config.memoryMode, root.config.cpuMode).toInt();
4728
4662
  switch (type) {
4729
4663
  case AddressType.Offset:
4730
4664
  case AddressType.WRelative:
@@ -4749,7 +4683,7 @@ var RomWriter = class {
4749
4683
  } else if (currentObj instanceof TypedNumber) {
4750
4684
  let value = currentObj.value;
4751
4685
  for (let i = 0; i < currentObj.size; i++) {
4752
- buf[position++] = value & 255;
4686
+ outBuffer[position++] = value & 255;
4753
4687
  value >>= 8;
4754
4688
  }
4755
4689
  break;
@@ -4757,26 +4691,26 @@ var RomWriter = class {
4757
4691
  const num = currentObj;
4758
4692
  const size = parentOp?.size ?? 0;
4759
4693
  if (num <= 255 && size <= 2) {
4760
- buf[position] = num & 255;
4694
+ outBuffer[position] = num & 255;
4761
4695
  position++;
4762
4696
  } else if (num <= 65535 && size <= 3) {
4763
- buf[position] = num & 255;
4764
- buf[position + 1] = num >> 8 & 255;
4697
+ outBuffer[position] = num & 255;
4698
+ outBuffer[position + 1] = num >> 8 & 255;
4765
4699
  position += 2;
4766
4700
  } else if (num <= 16777215 && size <= 4) {
4767
- buf[position] = num & 255;
4768
- buf[position + 1] = num >> 8 & 255;
4769
- buf[position + 2] = num >> 16 & 255;
4701
+ outBuffer[position] = num & 255;
4702
+ outBuffer[position + 1] = num >> 8 & 255;
4703
+ outBuffer[position + 2] = num >> 16 & 255;
4770
4704
  position += 3;
4771
4705
  } else {
4772
- buf[position] = num & 255;
4773
- buf[position + 1] = num >> 8 & 255;
4774
- buf[position + 2] = num >> 16 & 255;
4775
- buf[position + 3] = num >> 24 & 255;
4706
+ outBuffer[position] = num & 255;
4707
+ outBuffer[position + 1] = num >> 8 & 255;
4708
+ outBuffer[position + 2] = num >> 16 & 255;
4709
+ outBuffer[position + 3] = num >> 24 & 255;
4776
4710
  position += 4;
4777
4711
  }
4778
4712
  break;
4779
- } else if (this.isStringMarker(currentObj)) break;
4713
+ } else if (RomWriter.isStringMarker(currentObj)) break;
4780
4714
  else throw new Error(`Unable to process '${currentObj}'`);
4781
4715
  };
4782
4716
  for (const obj of objList) {
@@ -4789,21 +4723,121 @@ var RomWriter = class {
4789
4723
  /**
4790
4724
  * Helper method to find index of any character from an array in a string
4791
4725
  */
4792
- indexOfAny(str, chars) {
4726
+ static indexOfAny(str, chars) {
4793
4727
  for (let i = 0; i < str.length; i++) if (chars.includes(str[i])) return i;
4794
4728
  return -1;
4795
4729
  }
4796
4730
  /**
4797
4731
  * Helper method to check if an object is a StringMarker
4798
4732
  */
4799
- isStringMarker(obj) {
4733
+ static isStringMarker(obj) {
4800
4734
  return typeof obj === "object" && obj !== null && "offset" in obj;
4801
4735
  }
4802
- isTableEntry(obj) {
4736
+ static isTableEntry(obj) {
4803
4737
  return typeof obj === "object" && obj !== null && "location" in obj && "object" in obj;
4804
4738
  }
4805
4739
  };
4806
4740
 
4741
+ //#endregion
4742
+ //#region src/rom/rebuild/processor.ts
4743
+ /**
4744
+ * ROM rebuild processor
4745
+ * Converted from ext/GaiaLib/Rom/Rebuild/RomProcessor.cs
4746
+ */
4747
+ var RomProcessor = class RomProcessor {
4748
+ writer;
4749
+ constructor(writer) {
4750
+ this.writer = writer;
4751
+ }
4752
+ async repack(allFiles, modules) {
4753
+ const patches = [];
4754
+ const asmFiles = [];
4755
+ const compression = this.writer.root.compression;
4756
+ const canCompress = !!compression;
4757
+ const conditionFiles = [];
4758
+ if (modules) conditionFiles.push(...modules);
4759
+ const dummyMap = /* @__PURE__ */ new Map();
4760
+ for (const file of allFiles) {
4761
+ conditionFiles.push(file.name);
4762
+ if (file.type.isPatch) {
4763
+ patches.push(file);
4764
+ asmFiles.push(file);
4765
+ } else if (file.type.isBlock) asmFiles.push(file);
4766
+ else if (!file.type.struct) continue;
4767
+ const { blocks, includes, reqBank } = new Assembler(this.writer.root, file.textData, conditionFiles).parseAssembly();
4768
+ file.parts = blocks;
4769
+ file.includes = includes;
4770
+ file.bank = reqBank ?? void 0;
4771
+ if (file.type.struct) {
4772
+ file.includeLookup = /* @__PURE__ */ new Map();
4773
+ for (const b of file.parts) if (b.label) file.includeLookup.set(b.label.toUpperCase(), b);
4774
+ file.rawData = void 0;
4775
+ file.rawData = new Uint8Array(ChunkFileUtils.calculateSize(file));
4776
+ RomWriter.parseAssembly(this.writer.root, file.parts, dummyMap, file.includeLookup, file.rawData, file.type.base ?? 0);
4777
+ }
4778
+ }
4779
+ for (const file of allFiles) if (file.compressed === true && canCompress) if (this.writer.root.config.uncompress) file.compressed = false;
4780
+ else {
4781
+ let newData = compression.compact(file.rawData, file.type.header);
4782
+ if (file.type.header) newData = new Uint8Array([...file.rawData.slice(0, file.type.header), ...newData]);
4783
+ file.rawData = newData;
4784
+ file.size = newData.length;
4785
+ }
4786
+ RomProcessor.applyPatches(asmFiles, patches);
4787
+ for (const file of allFiles) ChunkFileUtils.calculateSize(file);
4788
+ const pages = new RomLayout(allFiles, this.writer.root).organize();
4789
+ for (const file of asmFiles) ChunkFileUtils.rebase(file);
4790
+ const masterLookup = /* @__PURE__ */ new Map();
4791
+ for (const f of asmFiles) {
4792
+ const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
4793
+ f.includeLookup = /* @__PURE__ */ new Map();
4794
+ for (const b of includeBlocks) if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
4795
+ for (const b of f.parts) if (b.label) {
4796
+ const nameUpper = b.label.toUpperCase();
4797
+ masterLookup.set(nameUpper, b.location);
4798
+ f.includeLookup.set(nameUpper, b);
4799
+ }
4800
+ }
4801
+ const fileLookup = /* @__PURE__ */ new Map();
4802
+ for (const f of allFiles) fileLookup.set(f.name.toUpperCase(), f.location);
4803
+ this.writer.allocate(pages);
4804
+ for (const file of allFiles) await this.writer.writeFile(file, fileLookup);
4805
+ return masterLookup;
4806
+ }
4807
+ static applyPatches(asmFiles, patches) {
4808
+ for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
4809
+ let file = null;
4810
+ let dstIx = -1;
4811
+ const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
4812
+ for (let ix = 0; patch.parts && ix < patch.parts.length;) {
4813
+ const block = patch.parts[ix];
4814
+ let match = null;
4815
+ if (block.label) for (const i of inc) {
4816
+ if (!i.parts) continue;
4817
+ for (let y = 0; y < i.parts.length; y++) {
4818
+ const check = i.parts[y];
4819
+ if (check.label === block.label) {
4820
+ file = i;
4821
+ dstIx = y;
4822
+ match = check;
4823
+ break;
4824
+ }
4825
+ }
4826
+ }
4827
+ if (match) file.parts[dstIx++] = block;
4828
+ else if (dstIx >= 0) file.parts.splice(dstIx++, 0, block);
4829
+ else {
4830
+ ix++;
4831
+ continue;
4832
+ }
4833
+ file.includes = file.includes || /* @__PURE__ */ new Set();
4834
+ file.includes.add(patch.name.toUpperCase());
4835
+ patch.parts.splice(ix, 1);
4836
+ }
4837
+ }
4838
+ }
4839
+ };
4840
+
4807
4841
  //#endregion
4808
4842
  //#region src/rom/rebuild/string-processor.ts
4809
4843
  /**
@@ -4962,7 +4996,6 @@ var StringProcessor = class {
4962
4996
  break;
4963
4997
  case MemberType.Offset:
4964
4998
  case MemberType.Address:
4965
- this.flushBuffer(stringType, false);
4966
4999
  this.context.currentBlock.objList.push(parts[pix]);
4967
5000
  this.context.currentBlock.size += cmd.types[y] === MemberType.Offset ? 2 : 3;
4968
5001
  break;
@@ -5520,6 +5553,7 @@ var DbFile = class {
5520
5553
  upper;
5521
5554
  group;
5522
5555
  scene;
5556
+ base;
5523
5557
  constructor(data) {
5524
5558
  if (!data.name) throw new Error("Name is required");
5525
5559
  if (!data.type) throw new Error("Type is required");
@@ -5533,6 +5567,7 @@ var DbFile = class {
5533
5567
  this.upper = data.upper ?? void 0;
5534
5568
  this.group = data.group || void 0;
5535
5569
  this.scene = data.scene || void 0;
5570
+ this.base = data.base ?? void 0;
5536
5571
  }
5537
5572
  };
5538
5573
  var DbFileType = class {
@@ -5543,17 +5578,21 @@ var DbFileType = class {
5543
5578
  isBlock;
5544
5579
  header;
5545
5580
  compressed;
5581
+ struct;
5582
+ base;
5546
5583
  constructor(data) {
5547
- this.name = data.name ?? "";
5548
- this.extension = data.extension ?? "";
5549
- this.type = data.type ?? "";
5584
+ if (!data.name) throw new Error("Name is required");
5585
+ if (!data.extension) throw new Error("Extension is required");
5586
+ if (!data.type) throw new Error("Type is required");
5587
+ this.name = data.name;
5588
+ this.extension = data.extension;
5589
+ this.type = data.type;
5550
5590
  this.isPatch = data.isPatch ?? false;
5551
5591
  this.isBlock = data.isBlock ?? false;
5552
5592
  this.header = data.header ?? 0;
5553
5593
  this.compressed = data.compressed ?? void 0;
5554
- if (!this.name) throw new Error("Name is required");
5555
- if (!this.extension) throw new Error("Extension is required");
5556
- if (!this.type) throw new Error("Type is required");
5594
+ this.struct = data.struct ?? void 0;
5595
+ this.base = data.base ?? void 0;
5557
5596
  }
5558
5597
  };
5559
5598
 
@@ -5629,21 +5668,18 @@ var DbStringCommand = class {
5629
5668
  }
5630
5669
  };
5631
5670
  var DbStringDictionary = class {
5632
- base;
5633
- range;
5634
5671
  command;
5672
+ commandName;
5635
5673
  name;
5636
5674
  entries;
5637
5675
  constructor(data) {
5638
- if (typeof data.base !== "number" && typeof data.command !== "number") throw new Error("Base or command is required");
5676
+ if (typeof data.command !== "number") throw new Error("Command is required");
5639
5677
  if (!data.name) throw new Error("Name is required");
5640
5678
  if (!data.entries || data.entries.length === 0) throw new Error("Entries is required");
5641
- this.base = data.base ?? void 0;
5642
- this.range = data.range ?? 0;
5643
5679
  this.command = data.command ?? void 0;
5680
+ this.commandName = data.commandName ?? void 0;
5644
5681
  this.name = data.name;
5645
5682
  this.entries = data.entries;
5646
- if (this.base !== void 0) this.range = this.base + this.entries.length;
5647
5683
  }
5648
5684
  };
5649
5685
  /**
@@ -5678,7 +5714,7 @@ var DbStringType = class {
5678
5714
  }, {});
5679
5715
  this.dictionaryLookup = Object.values(this.dictionaries).flatMap((y) => y.entries.map((z, ix) => ({
5680
5716
  text: z,
5681
- id: (y.command ?? 0) << 8 | (y.base ?? 0 + ix)
5717
+ id: (y.command ?? 0) << 8 | ix
5682
5718
  }))).sort((a, b) => b.text.length - a.text.length);
5683
5719
  }
5684
5720
  };
@@ -6085,7 +6121,7 @@ var DbRootUtils = class {
6085
6121
  const cfg = module.config;
6086
6122
  const compression = cfg.compression ? CompressionAlgorithms[cfg.compression]() : void 0;
6087
6123
  const baseRomChunks = module.baseRomFiles ?? module.supaBaseRomFiles?.map((file) => {
6088
- const chunkFile = new ChunkFile(file.name, 0, 0, fileTypeLookup[file.type]);
6124
+ const chunkFile = new ChunkFile(fileTypeLookup[file.type], file.name);
6089
6125
  if (file.isText) {
6090
6126
  chunkFile.textData = file.text ?? void 0;
6091
6127
  chunkFile.size = file.text?.length ?? 0;
@@ -6096,7 +6132,7 @@ var DbRootUtils = class {
6096
6132
  return chunkFile;
6097
6133
  });
6098
6134
  const projectChunks = module.projectFiles ?? module.supaProjectFiles?.map((file) => {
6099
- const chunkFile = new ChunkFile(file.name, 0, 0, fileTypeLookup[file.type]);
6135
+ const chunkFile = new ChunkFile(fileTypeLookup[file.type], file.name);
6100
6136
  chunkFile.group = file.module ?? void 0;
6101
6137
  if (file.isText) {
6102
6138
  chunkFile.textData = file.text ?? void 0;
@@ -6140,7 +6176,7 @@ var DbRootUtils = class {
6140
6176
  projectFiles: projectChunks
6141
6177
  };
6142
6178
  }
6143
- static async applyFolder(root, folderPath, sourceFiles = []) {
6179
+ static async applyFolder(root, folderPath, sourceFiles = [], group) {
6144
6180
  const chunkFiles = sourceFiles;
6145
6181
  const folderEntries = await listDirectory(folderPath, { recursive: true });
6146
6182
  for (const entry of folderEntries) {
@@ -6148,8 +6184,9 @@ var DbRootUtils = class {
6148
6184
  const type = root.fileExtLookup[entry.extension];
6149
6185
  if (!type) continue;
6150
6186
  const existing = chunkFiles.find((x) => x.name === entry.name && x.type.type === type.type);
6151
- const chunkFile = existing ?? new ChunkFile(entry.name, 0, 0, type);
6152
- if (chunkFile.type.type === "Assembly" || chunkFile.type.type === "Patch") {
6187
+ const chunkFile = existing ?? new ChunkFile(type, entry.name);
6188
+ if (group) chunkFile.group = group;
6189
+ if (chunkFile.type.isBlock || chunkFile.type.isPatch || chunkFile.type.struct) {
6153
6190
  const chunkData = await readFileAsText(entry.path);
6154
6191
  chunkFile.textData = chunkData;
6155
6192
  chunkFile.size = chunkData.length;
@@ -6178,7 +6215,7 @@ var DbRootUtils = class {
6178
6215
  const ext = block.type.extension;
6179
6216
  const filePath = `${outPath}/${block.group ? block.group + "/" : ""}${block.scene ? block.scene + "/" : ""}${block.name}${ext ? "." + ext : ""}`;
6180
6217
  if (block.parts?.length) {
6181
- if (!block.textData) block.textData = writer.generateAsm(block);
6218
+ block.textData = writer.generateAsm(block);
6182
6219
  await saveFileAsText(filePath, block.textData);
6183
6220
  } else {
6184
6221
  if (!block.rawData) continue;
@@ -7096,6 +7133,7 @@ async function summaryFromSupabaseByProject(projectName) {
7096
7133
  notes,
7097
7134
  gameRomId,
7098
7135
  platformBranchId,
7136
+ fileTypes,
7099
7137
  createdAt,
7100
7138
  updatedAt,
7101
7139
  gameRom:GameRom!inner(
@@ -7179,6 +7217,20 @@ var RomGenerator = class {
7179
7217
  this.projectName = projectName;
7180
7218
  this.crc = crc;
7181
7219
  }
7220
+ async validateAndLoad(sourceData, gameModule, baseRomPath, modulePath) {
7221
+ const calc = crc32_buffer(sourceData);
7222
+ if (this.crc !== calc) return false;
7223
+ this.dbRoot = DbRootUtils.fromGameModule(gameModule);
7224
+ let moduleFiles = [];
7225
+ for (const entry of await listDirectory(modulePath)) {
7226
+ if (!entry.isDirectory) continue;
7227
+ moduleFiles.push(...await DbRootUtils.applyFolder(this.dbRoot, entry.path, [], entry.name));
7228
+ }
7229
+ this.dbRoot.baseRomFiles = await DbRootUtils.applyFolder(this.dbRoot, baseRomPath);
7230
+ this.dbRoot.projectFiles = moduleFiles;
7231
+ this.sourceData = sourceData;
7232
+ return true;
7233
+ }
7182
7234
  async validateAndDownload(sourceData) {
7183
7235
  const calc = crc32_buffer(sourceData);
7184
7236
  if (this.crc !== calc) return false;
@@ -7192,16 +7244,16 @@ var RomGenerator = class {
7192
7244
  if (!this.sourceData) throw new Error("Source data not initialized");
7193
7245
  const reader = new BlockReader(this.sourceData, this.dbRoot);
7194
7246
  const chunkFiles = reader.analyzeAndResolve();
7195
- const asmFiles = chunkFiles.filter((b) => b.type.type === "Assembly");
7247
+ const asmFiles = chunkFiles.filter((b) => b.type.isBlock);
7196
7248
  const patchFiles = [];
7197
7249
  const writer = new BlockWriter(reader);
7198
- for (const block of asmFiles) block.textData = writer.generateAsm(block);
7250
+ for (const block of chunkFiles) if (block.parts?.length) block.textData = writer.generateAsm(block);
7199
7251
  for (const chunkFile of this.dbRoot.baseRomFiles) this.applyPatchFile(chunkFile, chunkFiles, asmFiles, patchFiles);
7200
7252
  const moduleLookup = this.applyProjectInit(chunkFiles, asmFiles, patchFiles);
7201
7253
  if (unshiftManualFiles) for (const file of manualFiles ?? []) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
7202
7254
  for (const module of modules) for (const file of moduleLookup.get(module)) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
7203
7255
  if (!unshiftManualFiles) for (const file of manualFiles ?? []) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
7204
- return await new RomWriter(this.dbRoot).repack(chunkFiles);
7256
+ return await new RomWriter(this.dbRoot).repack(chunkFiles, modules);
7205
7257
  }
7206
7258
  applyProjectInit(chunkFiles, asmFiles, patchFiles) {
7207
7259
  const moduleLookup = /* @__PURE__ */ new Map();
@@ -7220,7 +7272,7 @@ var RomGenerator = class {
7220
7272
  const existing = chunkFiles.find((x) => x.name === chunkFile.name);
7221
7273
  if (chunkFile.textData) if (existing) existing.textData = chunkFile.textData;
7222
7274
  else {
7223
- if (chunkFile.type.type === "Patch") patchFiles.push(chunkFile);
7275
+ if (chunkFile.type.isPatch) patchFiles.push(chunkFile);
7224
7276
  asmFiles.push(chunkFile);
7225
7277
  chunkFiles.push(chunkFile);
7226
7278
  }
@@ -7555,11 +7607,10 @@ exports.base64ToUint8Array = base64ToUint8Array;
7555
7607
  exports.binaryToUtf8String = binaryToUtf8String;
7556
7608
  exports.bytesToHex = bytesToHex;
7557
7609
  exports.clamp = clamp;
7610
+ exports.combineHeader = combineHeader;
7558
7611
  exports.crc32_buffer = crc32_buffer;
7559
7612
  exports.crc32_text_utf16 = crc32_text_utf16;
7560
7613
  exports.crc32_text_utf8 = crc32_text_utf8;
7561
- exports.createChunkFileFromDbBlock = createChunkFileFromDbBlock;
7562
- exports.createChunkFileFromDbFile = createChunkFileFromDbFile;
7563
7614
  exports.createDbPath = createDbPath;
7564
7615
  exports.createSupabaseClient = createSupabaseClient;
7565
7616
  exports.createTempDirectory = createTempDirectory;