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