@gaialabs/core 0.2.12 → 0.2.14

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++];
@@ -1096,7 +1098,7 @@ var RomProcessingConstants = class RomProcessingConstants {
1096
1098
  * BlockReader specific constants
1097
1099
  */
1098
1100
  var BlockReaderConstants = class {
1099
- static REF_SEARCH_MAX_RANGE = 416;
1101
+ static REF_SEARCH_MAX_RANGE = 896;
1100
1102
  static BANK_MASK_CHECK = 64;
1101
1103
  static BYTE_DELIMITER_THRESHOLD = 256;
1102
1104
  static BANK_HIGH_MEMORY_1 = 126;
@@ -1670,31 +1672,63 @@ var ChunkFile = class {
1670
1672
  mnemonics;
1671
1673
  group;
1672
1674
  scene;
1673
- constructor(name, size, location, type) {
1675
+ base;
1676
+ referenceManager;
1677
+ constructor(type, name, size = 0, location = 0) {
1678
+ this.type = type;
1674
1679
  this.name = name;
1675
1680
  this.size = size;
1676
1681
  this.location = location;
1677
1682
  this.mnemonics = {};
1678
1683
  this.compressed = type.compressed;
1679
- 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
+ }
1680
1730
  }
1681
1731
  };
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
1732
  function combineHeader(data, position, length, header, type) {
1699
1733
  let totalLength = length + (header ? header.length : 0);
1700
1734
  if (type === BinType.Palette && length < 512) totalLength += 512 - length;
@@ -1710,48 +1744,6 @@ function combineHeader(data, position, length, header, type) {
1710
1744
  return result;
1711
1745
  }
1712
1746
  /**
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
1747
  * Chunk file utilities
1756
1748
  */
1757
1749
  var ChunkFileUtils = class {
@@ -1773,16 +1765,11 @@ var ChunkFileUtils = class {
1773
1765
  * Calculate the total size of all blocks
1774
1766
  */
1775
1767
  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
1768
  let size = 0;
1785
- 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++) {
1786
1773
  const block = chunkFile.parts[x];
1787
1774
  if (x > 0 && !block.label) break;
1788
1775
  size += block.size || 0;
@@ -1801,7 +1788,7 @@ var ChunkFileUtils = class {
1801
1788
  block,
1802
1789
  part
1803
1790
  ];
1804
- for (const otherBlock of root) if (otherBlock !== block) {
1791
+ for (const otherBlock of root) if (otherBlock !== block && !otherBlock.type.struct) {
1805
1792
  const [otherIsInside, otherPart] = this.isInsideWithPart(otherBlock, location);
1806
1793
  if (otherIsInside) return [
1807
1794
  true,
@@ -1938,6 +1925,7 @@ var ReferenceManager = class {
1938
1925
  structTable = /* @__PURE__ */ new Map();
1939
1926
  markerTable = /* @__PURE__ */ new Map();
1940
1927
  nameTable = /* @__PURE__ */ new Map();
1928
+ fileTable = /* @__PURE__ */ new Map();
1941
1929
  root;
1942
1930
  constructor(root) {
1943
1931
  if (!root) throw new Error("root cannot be null");
@@ -2007,7 +1995,7 @@ var ReferenceManager = class {
2007
1995
  }
2008
1996
  resolveName(location, type, isBranch) {
2009
1997
  const prefix = Address.codeFromType(type);
2010
- let name;
1998
+ let name = null;
2011
1999
  let label = null;
2012
2000
  let resolvedLocation = location;
2013
2001
  const rewrite = this.root.rewrites[location];
@@ -2016,9 +2004,8 @@ var ReferenceManager = class {
2016
2004
  resolvedLocation = result.location;
2017
2005
  label = result.label;
2018
2006
  }
2019
- const existingName = this.nameTable.get(resolvedLocation);
2020
- if (existingName) name = existingName;
2021
- else name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
2007
+ name = this.nameTable.get(resolvedLocation) || null;
2008
+ if (!name) name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
2022
2009
  return `${prefix || ""}${name}${label || ""}`;
2023
2010
  }
2024
2011
  findClosestReference(location) {
@@ -2120,6 +2107,10 @@ var StackOperations = class {
2120
2107
  case "XBA":
2121
2108
  this.handleExchangeBytes();
2122
2109
  break;
2110
+ case "RTL":
2111
+ case "RTS":
2112
+ this._blockReader._referenceManager.tryAddStruct(this._blockReader._romDataReader.position, "Code");
2113
+ break;
2123
2114
  }
2124
2115
  }
2125
2116
  handleAccumulatorPush() {
@@ -2169,7 +2160,16 @@ var CopCommandProcessor = class {
2169
2160
  * Parses a COP command based on its definition
2170
2161
  */
2171
2162
  parseCopCommand(copDef, operands) {
2172
- for (let partStr of copDef.parts) {
2163
+ let parts = copDef.parts;
2164
+ if (copDef.conditions) for (const condition of copDef.conditions) {
2165
+ let value = this._romDataReader.romData[this._romDataReader.position + condition.offset];
2166
+ if (condition.value >= 256) value |= this._romDataReader.romData[this._romDataReader.position + condition.offset + 1] << 8;
2167
+ if (value === condition.value) {
2168
+ parts = condition.parts;
2169
+ break;
2170
+ }
2171
+ }
2172
+ for (let partStr of parts) {
2173
2173
  let bank = null;
2174
2174
  const bankHintIx = partStr.indexOf("$");
2175
2175
  if (bankHintIx > 0) {
@@ -2352,6 +2352,7 @@ var AddressingModeHandler = class {
2352
2352
  if (address.isROM) {
2353
2353
  const wrapper = new LocationWrapper(address.toLocation(), AddressType.Address);
2354
2354
  if (this.isJumpInstruction(mnemonic)) this._blockReader.noteType(wrapper.location, "Code", false, reg);
2355
+ if (mnemonic === "JML") this._blockReader._referenceManager.tryAddStruct(this._dataReader.position, "Code");
2355
2356
  operands.push(wrapper);
2356
2357
  } else operands.push(address);
2357
2358
  }
@@ -2390,12 +2391,13 @@ var AddressingModeHandler = class {
2390
2391
  const isPush = this.isPushInstruction(mnemonic);
2391
2392
  if (isPush) refLoc++;
2392
2393
  const isJump = isPush || isIndexedIndirect || this.isJumpInstruction(mnemonic);
2393
- const addr = new Address(xBank1 ?? (isJump ? Address.resolveBank(this._dataReader.position, registers.mode) : dataBank) ?? 129, refLoc, registers.mode);
2394
+ const addr = new Address(xBank1 ?? (isJump ? Address.resolveBank(this._dataReader.position, registers.mode) : dataBank) ?? this._blockReader._root.config.defaultBank ?? 129, refLoc, registers.mode);
2394
2395
  if (addr.isROM) {
2395
2396
  const wrapper = new LocationWrapper(addr.toLocation(), AddressType.Offset);
2396
2397
  if (isJump) {
2397
2398
  const type = isIndexedIndirect ? "&Code" : "Code";
2398
2399
  const name = this._blockReader.noteType(wrapper.location, type, isPush, registers);
2400
+ if (mnemonic === "JMP") this._blockReader._referenceManager.tryAddStruct(this._dataReader.position, "Code");
2399
2401
  if (isPush) {
2400
2402
  operands.push(`&${name}-1`);
2401
2403
  return;
@@ -3030,10 +3032,11 @@ var StringReader = class StringReader {
3030
3032
  }
3031
3033
  parseString(stringType, fixedSize) {
3032
3034
  const commands = stringType.commandLookup;
3033
- stringType.dictionaries;
3035
+ const dictionaries = stringType.dictionaries;
3034
3036
  const builder = [];
3035
3037
  const strLoc = this._romDataReader.position;
3036
3038
  const terminator = stringType.terminator;
3039
+ const modifiers = stringType.modifiers;
3037
3040
  let currentLayer = stringType.layers[0];
3038
3041
  do {
3039
3042
  const c = this._romDataReader.readByte();
@@ -3041,23 +3044,35 @@ var StringReader = class StringReader {
3041
3044
  if (stringType.greedyTerminator) while (this._romDataReader.peekByte() === terminator && this._blockReader.partCanContinue()) this._romDataReader.position++;
3042
3045
  break;
3043
3046
  }
3044
- const cmd = commands[c];
3045
- if (cmd) {
3047
+ let cmd = void 0;
3048
+ const mod = modifiers ? modifiers[c] : void 0;
3049
+ if (mod) {
3050
+ const next = mod[builder[builder.length - 1]];
3051
+ if (next) builder[builder.length - 1] = next;
3052
+ else builder.push(currentLayer.map[c - (currentLayer.base ?? 0)]);
3053
+ } else if (cmd = commands[c]) {
3046
3054
  this.resolveCommand(cmd, builder);
3047
3055
  if (cmd.halt) break;
3048
3056
  } else {
3049
3057
  let found = false;
3050
- for (const layer of stringType.layers) if (layer.on !== void 0) {
3051
- if (c === layer.on) {
3052
- currentLayer = layer;
3053
- builder.push("[--]");
3058
+ for (const dictionary of Object.values(dictionaries)) if (dictionary.base !== void 0 && c >= dictionary.base && c <= dictionary.base + dictionary.entries.length) {
3059
+ builder.push(dictionary.entries[c - dictionary.base]);
3060
+ found = true;
3061
+ break;
3062
+ }
3063
+ if (!found) {
3064
+ for (const layer of stringType.layers) if (layer.on !== void 0) {
3065
+ if (c === layer.on) {
3066
+ currentLayer = layer;
3067
+ builder.push("[--]");
3068
+ found = true;
3069
+ break;
3070
+ }
3071
+ } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3072
+ builder.push(layer.map[c - layer.base]);
3054
3073
  found = true;
3055
3074
  break;
3056
3075
  }
3057
- } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3058
- builder.push(layer.map[c - layer.base]);
3059
- found = true;
3060
- break;
3061
3076
  }
3062
3077
  if (!found) {
3063
3078
  let index = c - (currentLayer.base ?? 0);
@@ -3092,7 +3107,7 @@ var StringReader = class StringReader {
3092
3107
  if (isNaN(sloc)) break;
3093
3108
  if (new Address(sloc >> 16 & 255, sloc & 65535, this._blockReader._root.config.memoryMode).isROM) {
3094
3109
  this._blockReader.resolveInclude(sloc, false);
3095
- const name = this._blockReader.resolveName(sloc, AddressType.Unknown, false);
3110
+ const name = this._blockReader._referenceManager.resolveName(sloc, AddressType.Unknown, false);
3096
3111
  const opix = indexOfAny(name, RomProcessingConstants.OPERATORS);
3097
3112
  if (opix > 0) {
3098
3113
  const offsetStr = name.substring(opix + 1);
@@ -3163,29 +3178,44 @@ var TypeParser = class {
3163
3178
  if (!parentType) throw new Error(`Unknown type: ${fixedTypeName}`);
3164
3179
  const delimiter = parentType.delimiter;
3165
3180
  const discOffset = parentType.discriminator;
3181
+ const discLogic = parentType.discriminatorLogic;
3182
+ const nullValue = parentType.null;
3166
3183
  const objects = [];
3184
+ if (delimiter === void 0 && this._blockReader.delimiterReached(nullValue)) {
3185
+ objects.push({
3186
+ name: parentType.name,
3187
+ parts: ["null"]
3188
+ });
3189
+ return objects;
3190
+ }
3167
3191
  let delReached;
3168
3192
  while (!(delReached = this._blockReader.delimiterReached(delimiter))) {
3169
3193
  const startPosition = this._romDataReader.position;
3170
3194
  let targetType = parentType;
3171
- if (discOffset !== void 0) {
3172
- const discPosition = this._romDataReader.position + discOffset;
3173
- const desc = this._romDataReader.romData[discPosition];
3174
- if (discOffset === 0) this._romDataReader.position++;
3175
- targetType = Object.values(this._blockReader._root.structs).find((x) => x.parent === fixedTypeName && x.discriminator === desc) || parentType;
3176
- }
3177
- const types = targetType.types;
3178
- if (types && types.length > 0) {
3179
- const memberCount = types.length;
3180
- const prevPosition = this._romDataReader.position;
3181
- const parts = new Array(memberCount);
3182
- const def = {
3183
- name: targetType.name,
3184
- parts
3185
- };
3186
- for (let i = 0; i < memberCount; i++) parts[i] = this.parseType(types[i], reg, depth + 1, bank);
3187
- if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) this._romDataReader.position++;
3188
- objects.push(def);
3195
+ if (this._blockReader.delimiterReached(nullValue)) objects.push({
3196
+ name: parentType.name,
3197
+ parts: ["null"]
3198
+ });
3199
+ else {
3200
+ if (discOffset !== void 0) {
3201
+ const discPosition = this._romDataReader.position + discOffset;
3202
+ const desc = this._romDataReader.romData[discPosition];
3203
+ targetType = Object.values(this._blockReader._root.structs).find((x) => x.parent === fixedTypeName && (discLogic === "&" ? ((x.discriminator ?? 0) & desc) !== 0 : x.discriminator === desc)) || parentType;
3204
+ if (discOffset === 0 && parentType != targetType && discLogic === "=") this._romDataReader.position++;
3205
+ }
3206
+ const types = targetType.types;
3207
+ if (types && types.length > 0) {
3208
+ const memberCount = types.length;
3209
+ const prevPosition = this._romDataReader.position;
3210
+ const parts = new Array(memberCount);
3211
+ const def = {
3212
+ name: targetType.name,
3213
+ parts
3214
+ };
3215
+ for (let i = 0; i < memberCount; i++) parts[i] = this.parseType(types[i], reg, depth + 1, bank);
3216
+ if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) this._romDataReader.position++;
3217
+ objects.push(def);
3218
+ } else this._romDataReader.position++;
3189
3219
  }
3190
3220
  let checkPosition = startPosition;
3191
3221
  while (++checkPosition < this._romDataReader.position) {
@@ -3196,6 +3226,7 @@ var TypeParser = class {
3196
3226
  }
3197
3227
  }
3198
3228
  if (!this._blockReader.partCanContinue()) break;
3229
+ if (nullValue !== void 0) break;
3199
3230
  }
3200
3231
  if (delReached && depth === 0) this._referenceManager.tryAddStruct(this._romDataReader.position, typeName);
3201
3232
  return objects;
@@ -3221,15 +3252,16 @@ var TypeParser = class {
3221
3252
  }
3222
3253
  parseLocation(offset, bank, typeName, addrType) {
3223
3254
  if ((bank === void 0 || bank === null) && offset === 0) return new Word(offset);
3255
+ offset -= this._romDataReader.offset;
3224
3256
  let adrs;
3225
3257
  let loc;
3226
- if (addrType === AddressType.Location) loc = offset | bank << 16;
3258
+ if (addrType === AddressType.Location || this._romDataReader.offset) loc = offset | bank << 16;
3227
3259
  else {
3228
3260
  adrs = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), offset, this._blockReader._root.config.memoryMode);
3229
3261
  if (!adrs.isROM) return adrs;
3230
3262
  loc = adrs.toLocation();
3231
3263
  }
3232
- if (typeName && !this._blockReader._root.rewrites[loc]) {
3264
+ if (typeName && (this._romDataReader.offset || !this._blockReader._root.rewrites[loc])) {
3233
3265
  const oldStruct = this._referenceManager.structTable.get(loc);
3234
3266
  if (!oldStruct || oldStruct === "Branch") this._referenceManager.structTable.set(loc, typeName);
3235
3267
  const referenceName = `${typeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
@@ -3420,8 +3452,6 @@ var BlockReader = class {
3420
3452
  this._stringReader = new StringReader(this);
3421
3453
  this._asmReader = new AsmReader(this);
3422
3454
  this._typeParser = new TypeParser(this);
3423
- this.initializeOverrides();
3424
- this.initializeFileReferences();
3425
3455
  }
3426
3456
  /**
3427
3457
  * Processes predefined overrides for registers and bank notes
@@ -3449,9 +3479,6 @@ var BlockReader = class {
3449
3479
  /**
3450
3480
  * Processes predefined file references
3451
3481
  */
3452
- initializeFileReferences() {
3453
- for (const file of this._root.files) this._referenceManager.tryAddName(file.start, file.name);
3454
- }
3455
3482
  /**
3456
3483
  * Resolves mnemonic for a given address
3457
3484
  */
@@ -3471,9 +3498,6 @@ var BlockReader = class {
3471
3498
  /**
3472
3499
  * Resolves name for a location (delegated to ReferenceManager)
3473
3500
  */
3474
- resolveName(location, type, isBranch) {
3475
- return this._referenceManager.resolveName(location, type, isBranch);
3476
- }
3477
3501
  /**
3478
3502
  * Resolves include for a location
3479
3503
  */
@@ -3534,6 +3558,7 @@ var BlockReader = class {
3534
3558
  }
3535
3559
  analyzeAndResolve() {
3536
3560
  console.log("analyzeAndResolve started");
3561
+ this.initializeOverrides();
3537
3562
  this.createChunkFilesFromDatabase();
3538
3563
  this.initializeBlocksAndParts();
3539
3564
  this.analyzeChunkFiles();
@@ -3549,9 +3574,12 @@ var BlockReader = class {
3549
3574
  */
3550
3575
  initializeBlocksAndParts() {
3551
3576
  console.log("initializeBlocksAndParts");
3552
- for (const block of this._enrichedChunks) for (const part of block.parts || []) {
3553
- if (part.structName) this._referenceManager.tryAddStruct(part.location, part.structName);
3554
- if (part.label) this._referenceManager.tryAddName(part.location, part.label);
3577
+ for (const block of this._enrichedChunks) {
3578
+ for (const part of block.parts || []) {
3579
+ if (part.structName) this._referenceManager.tryAddStruct(part.location, part.structName);
3580
+ if (part.label) this._referenceManager.tryAddName(part.location, part.label);
3581
+ }
3582
+ this._referenceManager.tryAddName(block.location, block.name);
3555
3583
  }
3556
3584
  }
3557
3585
  /**
@@ -3648,7 +3676,7 @@ var BlockReader = class {
3648
3676
  const size = getSize();
3649
3677
  const startPos = pos + offset;
3650
3678
  const data = getBytes(size);
3651
- const chunk = new ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, fileType);
3679
+ const chunk = new ChunkFile(fileType, `sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos);
3652
3680
  chunk.rawData = data;
3653
3681
  chunk.group = "sfx";
3654
3682
  this._enrichedChunks.push(chunk);
@@ -3659,7 +3687,9 @@ var BlockReader = class {
3659
3687
  */
3660
3688
  createChunkFilesFromDbFiles() {
3661
3689
  for (const dbFile of this._root.files) {
3662
- const chunkFile = createChunkFileFromDbFile(this._romDataReader.romData, this._root.compression, dbFile, this._root.fileTypes[dbFile.type]);
3690
+ const fileType = this._root.fileTypes[dbFile.type];
3691
+ const chunkFile = new ChunkFile(fileType, dbFile.name);
3692
+ chunkFile.enrichWithRawDataFromDbFile(dbFile, this._romDataReader.romData, this._root.compression);
3663
3693
  this._enrichedChunks.push(chunkFile);
3664
3694
  }
3665
3695
  }
@@ -3669,7 +3699,8 @@ var BlockReader = class {
3669
3699
  createChunkFilesFromDbBlocks() {
3670
3700
  const fileType = Object.values(this._root.fileTypes).find((x) => x.isBlock);
3671
3701
  for (const block of this._root.blocks) {
3672
- const chunkFile = createChunkFileFromDbBlock(block, fileType, this._root.config.memoryMode);
3702
+ const chunkFile = new ChunkFile(fileType, block.name);
3703
+ chunkFile.enrichWithAsmBlocksFromDbBlock(block, this._root.config.memoryMode);
3673
3704
  this._enrichedChunks.push(chunkFile);
3674
3705
  }
3675
3706
  }
@@ -3678,14 +3709,32 @@ var BlockReader = class {
3678
3709
  */
3679
3710
  analyzeChunkFiles() {
3680
3711
  console.log("analyzeChunkFiles");
3681
- const assemblyChunks = this._enrichedChunks.filter((chunk) => chunk.type.type === "Assembly");
3682
- for (const chunkFile of assemblyChunks) {
3712
+ for (const chunkFile of this._enrichedChunks) {
3713
+ if (!chunkFile.type.isBlock) continue;
3714
+ chunkFile.referenceManager = this._referenceManager;
3683
3715
  this._currentChunk = chunkFile;
3684
3716
  for (const asmBlock of chunkFile.parts || []) {
3685
3717
  this._currentAsmBlock = asmBlock;
3686
3718
  this.processPart(asmBlock);
3687
3719
  }
3688
3720
  }
3721
+ const oldState = this._referenceManager;
3722
+ const oldReader = this._romDataReader;
3723
+ const oldTypeParser = this._typeParser;
3724
+ for (const chunkFile of this._enrichedChunks) {
3725
+ if (!chunkFile.type.struct) continue;
3726
+ this._currentChunk = chunkFile;
3727
+ const asmBlock = new AsmBlock(0, chunkFile.size, false, chunkFile.name, chunkFile.type.struct);
3728
+ chunkFile.parts = [asmBlock];
3729
+ this._currentAsmBlock = asmBlock;
3730
+ this._referenceManager = chunkFile.referenceManager = new ReferenceManager(this._root);
3731
+ this._romDataReader = new RomDataReader(chunkFile.rawData, chunkFile.base ?? 0);
3732
+ this._typeParser = new TypeParser(this);
3733
+ this.processPart(asmBlock);
3734
+ }
3735
+ this._referenceManager = oldState;
3736
+ this._romDataReader = oldReader;
3737
+ this._typeParser = oldTypeParser;
3689
3738
  }
3690
3739
  /**
3691
3740
  * Resolves references in assembly ChunkFiles only
@@ -3693,8 +3742,9 @@ var BlockReader = class {
3693
3742
  resolveReferences() {
3694
3743
  console.log("resolveReferences");
3695
3744
  for (const chunkFile of this._enrichedChunks) {
3745
+ if (!chunkFile.type.isBlock) continue;
3696
3746
  this._currentChunk = chunkFile;
3697
- for (const asmBlock of chunkFile.parts || []) {
3747
+ for (const asmBlock of chunkFile.parts) {
3698
3748
  this._currentAsmBlock = asmBlock;
3699
3749
  this.resolveObject(asmBlock.objList, false);
3700
3750
  }
@@ -3884,6 +3934,7 @@ var BlockWriter = class {
3884
3934
  generateAsm(block) {
3885
3935
  if (!block.parts) throw new Error("Invalid block structure for generateAsm");
3886
3936
  const lines = [];
3937
+ this._referenceManager = block.referenceManager;
3887
3938
  if (block.bank !== void 0) lines.push(`?BANK ${block.bank.toString(16).toUpperCase().padStart(2, "0")}`);
3888
3939
  const includes = ChunkFileUtils.getIncludes(block);
3889
3940
  if (includes && includes.length > 0) {
@@ -3928,11 +3979,11 @@ var BlockWriter = class {
3928
3979
  }
3929
3980
  if (typeof obj === "number") {
3930
3981
  if (op.mode === "Immediate") return obj;
3931
- return this._blockReader.resolveName(obj, AddressType.Address, isBranch);
3982
+ return this._referenceManager.resolveName(obj, AddressType.Address, isBranch);
3932
3983
  }
3933
3984
  if (this.getObjectType(obj) === ObjectType.LocationWrapper) {
3934
3985
  const lw = obj;
3935
- return this._blockReader.resolveName(lw.location, lw.type, isBranch);
3986
+ return this._referenceManager.resolveName(lw.location, lw.type, isBranch);
3936
3987
  }
3937
3988
  if (this.getObjectType(obj) === ObjectType.Address) {
3938
3989
  const addr = obj;
@@ -3984,10 +4035,10 @@ var BlockWriter = class {
3984
4035
  objLines = this.writeOpArray(obj, depth);
3985
4036
  break;
3986
4037
  case ObjectType.LocationWrapper:
3987
- objLines = [this._blockReader.resolveName(obj.location, obj.type, isBranch)];
4038
+ objLines = [this._referenceManager.resolveName(obj.location, obj.type, isBranch)];
3988
4039
  break;
3989
4040
  case ObjectType.Address:
3990
- objLines = isArray ? [`$#${obj.offset.toString(16).toUpperCase().padStart(4, "0")}`] : [`$${obj.toString()}`];
4041
+ objLines = isArray ? [`$#${obj.toString()}`] : [`$${obj.toString()}`];
3991
4042
  break;
3992
4043
  case ObjectType.ByteArray:
3993
4044
  objLines = [`#${Array.from(obj).map((b) => b.toString(16).toUpperCase().padStart(2, "0")).join("")}`];
@@ -4047,14 +4098,14 @@ var BlockWriter = class {
4047
4098
  this._isInline = true;
4048
4099
  let first = true;
4049
4100
  for (const op of opList) {
4050
- if (first) first = false;
4051
- else {
4101
+ if (!first || depth > 0) {
4052
4102
  const labelResult = this._referenceManager.tryGetName(op.location);
4053
4103
  if (labelResult.found) {
4054
4104
  lines.push("");
4055
4105
  lines.push(` ${labelResult.referenceName}:`);
4056
4106
  }
4057
4107
  }
4108
+ first = false;
4058
4109
  let opLine = ` ${op.mnem} `;
4059
4110
  if (op.copDef) {
4060
4111
  opLine += `[${op.copDef.name}]`;
@@ -4102,7 +4153,7 @@ var BlockWriter = class {
4102
4153
  const adrs = new Address(rawAddr >> 16 & 255, rawAddr & 65535, this._blockReader._root.config.memoryMode);
4103
4154
  const addressType = char === "^" ? AddressType.Offset : AddressType.Address;
4104
4155
  let name = "";
4105
- if (adrs.isROM) name = this._blockReader.resolveName(adrs.toLocation(), addressType, false);
4156
+ if (adrs.isROM) name = this._referenceManager.resolveName(adrs.toLocation(), addressType, false);
4106
4157
  else if (addressType === AddressType.Offset) name = adrs.offset.toString(16).toUpperCase().padStart(4, "0");
4107
4158
  else name = adrs.toString();
4108
4159
  str = str.replace(str.substring(ix, ix + 7), name);
@@ -4244,8 +4295,8 @@ var RomLayout = class RomLayout {
4244
4295
  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);
4245
4296
  }) : [];
4246
4297
  this.unmatchedFiles = files.filter((x) => (x.size || 0) > 0).filter((x) => this.sfxPackType === "Individual" || x.type.type !== "Sound").sort((a, b) => {
4247
- const aAsm = a.parts ? 0 : 1;
4248
- const bAsm = b.parts ? 0 : 1;
4298
+ const aAsm = a.rawData ? 1 : 0;
4299
+ const bAsm = b.rawData ? 1 : 0;
4249
4300
  if (aAsm !== bAsm) return aAsm - bAsm;
4250
4301
  if (b.size !== a.size) return b.size - a.size;
4251
4302
  if (a.location !== b.location) return a.location - b.location;
@@ -4295,7 +4346,7 @@ var RomLayout = class RomLayout {
4295
4346
  start += RomProcessingConstants.PAGE_SIZE;
4296
4347
  page++;
4297
4348
  if (offset) {
4298
- const newFile = new ChunkFile(file.name + "_2", file.size, file.location, Object.values(this.root.fileTypes).find((x) => x.type === BinType.Binary));
4349
+ const newFile = new ChunkFile(Object.values(this.root.fileTypes).find((x) => x.type === BinType.Binary), file.name + "_2", file.size, file.location);
4299
4350
  const end = file.rawData.length - offset;
4300
4351
  newFile.rawData = file.rawData.slice(end);
4301
4352
  newFile.size = newFile.rawData.length;
@@ -4385,107 +4436,13 @@ var RomLayout = class RomLayout {
4385
4436
  }
4386
4437
  };
4387
4438
 
4388
- //#endregion
4389
- //#region src/rom/rebuild/processor.ts
4390
- /**
4391
- * ROM rebuild processor
4392
- * Converted from ext/GaiaLib/Rom/Rebuild/RomProcessor.cs
4393
- */
4394
- var RomProcessor = class RomProcessor {
4395
- writer;
4396
- constructor(writer) {
4397
- this.writer = writer;
4398
- }
4399
- async repack(allFiles, modules) {
4400
- const patches = [];
4401
- const asmFiles = [];
4402
- const compression = this.writer.root.compression;
4403
- const canCompress = !!compression;
4404
- const conditionFiles = [];
4405
- if (modules) conditionFiles.push(...modules);
4406
- for (const file of allFiles) {
4407
- if (file.type.type === "Patch") patches.push(file);
4408
- else if (file.type.type !== "Assembly") {
4409
- if (file.compressed === true && canCompress) if (this.writer.root.config.uncompress) file.compressed = false;
4410
- else {
4411
- let newData = compression.compact(file.rawData, file.type.header);
4412
- if (file.type.header) newData = new Uint8Array([...file.rawData.slice(0, file.type.header), ...newData]);
4413
- file.rawData = newData;
4414
- file.size = newData.length;
4415
- }
4416
- continue;
4417
- }
4418
- asmFiles.push(file);
4419
- conditionFiles.push(file.name);
4420
- }
4421
- for (const file of asmFiles) {
4422
- const { blocks, includes, reqBank } = new Assembler(this.writer.root, file.textData, conditionFiles).parseAssembly();
4423
- file.parts = blocks;
4424
- file.includes = includes;
4425
- file.bank = reqBank ?? void 0;
4426
- }
4427
- RomProcessor.applyPatches(asmFiles, patches);
4428
- for (const file of allFiles) ChunkFileUtils.calculateSize(file);
4429
- const pages = new RomLayout(allFiles, this.writer.root).organize();
4430
- for (const file of asmFiles) ChunkFileUtils.rebase(file);
4431
- const masterLookup = /* @__PURE__ */ new Map();
4432
- for (const f of asmFiles) {
4433
- const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
4434
- f.includeLookup = /* @__PURE__ */ new Map();
4435
- for (const b of includeBlocks) if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
4436
- for (const b of f.parts) if (b.label) {
4437
- const nameUpper = b.label.toUpperCase();
4438
- masterLookup.set(nameUpper, b.location);
4439
- f.includeLookup.set(nameUpper, b);
4440
- }
4441
- }
4442
- const fileLookup = /* @__PURE__ */ new Map();
4443
- for (const f of allFiles) fileLookup.set(f.name.toUpperCase(), f.location);
4444
- this.writer.allocate(pages);
4445
- for (const file of allFiles) await this.writer.writeFile(file, fileLookup);
4446
- return masterLookup;
4447
- }
4448
- static applyPatches(asmFiles, patches) {
4449
- for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
4450
- let file = null;
4451
- let dstIx = -1;
4452
- const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
4453
- for (let ix = 0; patch.parts && ix < patch.parts.length;) {
4454
- const block = patch.parts[ix];
4455
- let match = null;
4456
- if (block.label) for (const i of inc) {
4457
- if (!i.parts) continue;
4458
- for (let y = 0; y < i.parts.length; y++) {
4459
- const check = i.parts[y];
4460
- if (check.label === block.label) {
4461
- file = i;
4462
- dstIx = y;
4463
- match = check;
4464
- break;
4465
- }
4466
- }
4467
- }
4468
- if (match) file.parts[dstIx++] = block;
4469
- else if (dstIx >= 0) file.parts.splice(dstIx++, 0, block);
4470
- else {
4471
- ix++;
4472
- continue;
4473
- }
4474
- file.includes = file.includes || /* @__PURE__ */ new Set();
4475
- file.includes.add(patch.name.toUpperCase());
4476
- patch.parts.splice(ix, 1);
4477
- }
4478
- }
4479
- }
4480
- };
4481
-
4482
4439
  //#endregion
4483
4440
  //#region src/rom/rebuild/writer.ts
4484
4441
  /**
4485
4442
  * ROM writer (binary)
4486
4443
  * Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs (subset: binary write only)
4487
4444
  */
4488
- var RomWriter = class {
4445
+ var RomWriter = class RomWriter {
4489
4446
  bpsPath;
4490
4447
  outBuffer;
4491
4448
  romSize;
@@ -4640,7 +4597,7 @@ var RomWriter = class {
4640
4597
  }
4641
4598
  } else if (file.parts && file.parts.length > 0) {
4642
4599
  if (file.parts[0].location !== file.location && (file.location || 0) !== 0) throw new Error("Assembly was not based properly");
4643
- this.parseAssembly(file.parts, fileLookup, file.includeLookup);
4600
+ RomWriter.parseAssembly(this.root, file.parts, fileLookup, file.includeLookup, this.outBuffer, void 0);
4644
4601
  }
4645
4602
  return pos - start;
4646
4603
  }
@@ -4652,12 +4609,10 @@ var RomWriter = class {
4652
4609
  * Parse assembly blocks and write binary data to output buffer
4653
4610
  * Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs ParseAssembly method
4654
4611
  */
4655
- parseAssembly(blocks, fileLookup, includeLookup) {
4612
+ static parseAssembly(root, blocks, fileLookup, includeLookup, outBuffer, addrOffset) {
4656
4613
  if (!blocks) throw new Error("Assembly has not been parsed");
4657
- const buf = this.outBuffer;
4658
4614
  let bix = 0;
4659
4615
  for (const block of blocks) {
4660
- let oldPos = null;
4661
4616
  let position = block.location;
4662
4617
  const objList = block.objList;
4663
4618
  let oix = 0;
@@ -4672,13 +4627,13 @@ var RomWriter = class {
4672
4627
  continue;
4673
4628
  } else if (currentObj instanceof Op) {
4674
4629
  const op = currentObj;
4675
- buf[position++] = op.code & 255;
4630
+ outBuffer[position++] = op.code & 255;
4676
4631
  opos += op.size;
4677
4632
  for (const operand of op.operands) processObject(operand, op);
4678
4633
  break;
4679
4634
  } else if (currentObj instanceof Uint8Array) {
4680
4635
  const arr = currentObj;
4681
- for (let i = 0; i < arr.length; i++) buf[position + i] = arr[i];
4636
+ for (let i = 0; i < arr.length; i++) outBuffer[position + i] = arr[i];
4682
4637
  position += arr.length;
4683
4638
  opos += arr.length;
4684
4639
  break;
@@ -4690,7 +4645,7 @@ var RomWriter = class {
4690
4645
  if (ix > 0) label = label.substring(ix);
4691
4646
  let loc;
4692
4647
  const isRelative = parentOp && (parentOp.mode === "PCRelative" || parentOp.mode === "PCRelativeLong");
4693
- const operatorIdx = this.indexOfAny(label, RomProcessingConstants.OPERATORS);
4648
+ const operatorIdx = RomWriter.indexOfAny(label, RomProcessingConstants.OPERATORS);
4694
4649
  let offset = null;
4695
4650
  let useMarker = false;
4696
4651
  if (operatorIdx > 0) {
@@ -4739,12 +4694,13 @@ var RomWriter = class {
4739
4694
  if (offset !== null) loc += offset;
4740
4695
  else if (useMarker && target instanceof AsmBlock) {
4741
4696
  let markerOffset = 0;
4742
- for (const part of target.objList) if (this.isStringMarker(part)) {
4697
+ for (const part of target.objList) if (RomWriter.isStringMarker(part)) {
4743
4698
  loc += markerOffset;
4744
4699
  break;
4745
4700
  } else markerOffset += RomProcessingConstants.getSize(part);
4746
4701
  }
4747
- if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, this.root.config.memoryMode, this.root.config.cpuMode).toInt();
4702
+ if (addrOffset !== void 0) loc += addrOffset;
4703
+ else if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, root.config.memoryMode, root.config.cpuMode).toInt();
4748
4704
  switch (type) {
4749
4705
  case AddressType.Offset:
4750
4706
  case AddressType.WRelative:
@@ -4769,7 +4725,7 @@ var RomWriter = class {
4769
4725
  } else if (currentObj instanceof TypedNumber) {
4770
4726
  let value = currentObj.value;
4771
4727
  for (let i = 0; i < currentObj.size; i++) {
4772
- buf[position++] = value & 255;
4728
+ outBuffer[position++] = value & 255;
4773
4729
  value >>= 8;
4774
4730
  }
4775
4731
  break;
@@ -4777,26 +4733,26 @@ var RomWriter = class {
4777
4733
  const num = currentObj;
4778
4734
  const size = parentOp?.size ?? 0;
4779
4735
  if (num <= 255 && size <= 2) {
4780
- buf[position] = num & 255;
4736
+ outBuffer[position] = num & 255;
4781
4737
  position++;
4782
4738
  } else if (num <= 65535 && size <= 3) {
4783
- buf[position] = num & 255;
4784
- buf[position + 1] = num >> 8 & 255;
4739
+ outBuffer[position] = num & 255;
4740
+ outBuffer[position + 1] = num >> 8 & 255;
4785
4741
  position += 2;
4786
4742
  } else if (num <= 16777215 && size <= 4) {
4787
- buf[position] = num & 255;
4788
- buf[position + 1] = num >> 8 & 255;
4789
- buf[position + 2] = num >> 16 & 255;
4743
+ outBuffer[position] = num & 255;
4744
+ outBuffer[position + 1] = num >> 8 & 255;
4745
+ outBuffer[position + 2] = num >> 16 & 255;
4790
4746
  position += 3;
4791
4747
  } else {
4792
- buf[position] = num & 255;
4793
- buf[position + 1] = num >> 8 & 255;
4794
- buf[position + 2] = num >> 16 & 255;
4795
- buf[position + 3] = num >> 24 & 255;
4748
+ outBuffer[position] = num & 255;
4749
+ outBuffer[position + 1] = num >> 8 & 255;
4750
+ outBuffer[position + 2] = num >> 16 & 255;
4751
+ outBuffer[position + 3] = num >> 24 & 255;
4796
4752
  position += 4;
4797
4753
  }
4798
4754
  break;
4799
- } else if (this.isStringMarker(currentObj)) break;
4755
+ } else if (RomWriter.isStringMarker(currentObj)) break;
4800
4756
  else throw new Error(`Unable to process '${currentObj}'`);
4801
4757
  };
4802
4758
  for (const obj of objList) {
@@ -4809,21 +4765,121 @@ var RomWriter = class {
4809
4765
  /**
4810
4766
  * Helper method to find index of any character from an array in a string
4811
4767
  */
4812
- indexOfAny(str, chars) {
4768
+ static indexOfAny(str, chars) {
4813
4769
  for (let i = 0; i < str.length; i++) if (chars.includes(str[i])) return i;
4814
4770
  return -1;
4815
4771
  }
4816
4772
  /**
4817
4773
  * Helper method to check if an object is a StringMarker
4818
4774
  */
4819
- isStringMarker(obj) {
4775
+ static isStringMarker(obj) {
4820
4776
  return typeof obj === "object" && obj !== null && "offset" in obj;
4821
4777
  }
4822
- isTableEntry(obj) {
4778
+ static isTableEntry(obj) {
4823
4779
  return typeof obj === "object" && obj !== null && "location" in obj && "object" in obj;
4824
4780
  }
4825
4781
  };
4826
4782
 
4783
+ //#endregion
4784
+ //#region src/rom/rebuild/processor.ts
4785
+ /**
4786
+ * ROM rebuild processor
4787
+ * Converted from ext/GaiaLib/Rom/Rebuild/RomProcessor.cs
4788
+ */
4789
+ var RomProcessor = class RomProcessor {
4790
+ writer;
4791
+ constructor(writer) {
4792
+ this.writer = writer;
4793
+ }
4794
+ async repack(allFiles, modules) {
4795
+ const patches = [];
4796
+ const asmFiles = [];
4797
+ const compression = this.writer.root.compression;
4798
+ const canCompress = !!compression;
4799
+ const conditionFiles = [];
4800
+ if (modules) conditionFiles.push(...modules);
4801
+ const dummyMap = /* @__PURE__ */ new Map();
4802
+ for (const file of allFiles) {
4803
+ conditionFiles.push(file.name);
4804
+ if (file.type.isPatch) {
4805
+ patches.push(file);
4806
+ asmFiles.push(file);
4807
+ } else if (file.type.isBlock) asmFiles.push(file);
4808
+ else if (!file.type.struct) continue;
4809
+ const { blocks, includes, reqBank } = new Assembler(this.writer.root, file.textData, conditionFiles).parseAssembly();
4810
+ file.parts = blocks;
4811
+ file.includes = includes;
4812
+ file.bank = reqBank ?? void 0;
4813
+ if (file.type.struct) {
4814
+ file.includeLookup = /* @__PURE__ */ new Map();
4815
+ for (const b of file.parts) if (b.label) file.includeLookup.set(b.label.toUpperCase(), b);
4816
+ file.rawData = void 0;
4817
+ file.rawData = new Uint8Array(ChunkFileUtils.calculateSize(file));
4818
+ RomWriter.parseAssembly(this.writer.root, file.parts, dummyMap, file.includeLookup, file.rawData, file.type.base ?? 0);
4819
+ }
4820
+ }
4821
+ for (const file of allFiles) if (file.compressed === true && canCompress) if (this.writer.root.config.uncompress) file.compressed = false;
4822
+ else {
4823
+ let newData = compression.compact(file.rawData, file.type.header);
4824
+ if (file.type.header) newData = new Uint8Array([...file.rawData.slice(0, file.type.header), ...newData]);
4825
+ file.rawData = newData;
4826
+ file.size = newData.length;
4827
+ }
4828
+ RomProcessor.applyPatches(asmFiles, patches);
4829
+ for (const file of allFiles) ChunkFileUtils.calculateSize(file);
4830
+ const pages = new RomLayout(allFiles, this.writer.root).organize();
4831
+ for (const file of asmFiles) ChunkFileUtils.rebase(file);
4832
+ const masterLookup = /* @__PURE__ */ new Map();
4833
+ for (const f of asmFiles) {
4834
+ const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
4835
+ f.includeLookup = /* @__PURE__ */ new Map();
4836
+ for (const b of includeBlocks) if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
4837
+ for (const b of f.parts) if (b.label) {
4838
+ const nameUpper = b.label.toUpperCase();
4839
+ masterLookup.set(nameUpper, b.location);
4840
+ f.includeLookup.set(nameUpper, b);
4841
+ }
4842
+ }
4843
+ const fileLookup = /* @__PURE__ */ new Map();
4844
+ for (const f of allFiles) fileLookup.set(f.name.toUpperCase(), f.location);
4845
+ this.writer.allocate(pages);
4846
+ for (const file of allFiles) await this.writer.writeFile(file, fileLookup);
4847
+ return masterLookup;
4848
+ }
4849
+ static applyPatches(asmFiles, patches) {
4850
+ for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
4851
+ let file = null;
4852
+ let dstIx = -1;
4853
+ const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
4854
+ for (let ix = 0; patch.parts && ix < patch.parts.length;) {
4855
+ const block = patch.parts[ix];
4856
+ let match = null;
4857
+ if (block.label) for (const i of inc) {
4858
+ if (!i.parts) continue;
4859
+ for (let y = 0; y < i.parts.length; y++) {
4860
+ const check = i.parts[y];
4861
+ if (check.label === block.label) {
4862
+ file = i;
4863
+ dstIx = y;
4864
+ match = check;
4865
+ break;
4866
+ }
4867
+ }
4868
+ }
4869
+ if (match) file.parts[dstIx++] = block;
4870
+ else if (dstIx >= 0) file.parts.splice(dstIx++, 0, block);
4871
+ else {
4872
+ ix++;
4873
+ continue;
4874
+ }
4875
+ if (!file.includes) file.includes = /* @__PURE__ */ new Set();
4876
+ file.includes.add(patch.name.toUpperCase());
4877
+ patch.parts.splice(ix, 1);
4878
+ }
4879
+ }
4880
+ }
4881
+ };
4882
+
4827
4883
  //#endregion
4828
4884
  //#region src/rom/rebuild/string-processor.ts
4829
4885
  /**
@@ -5516,10 +5572,7 @@ var DbBlock = class {
5516
5572
  this.group = data.group ?? void 0;
5517
5573
  this.scene = data.scene ?? void 0;
5518
5574
  this.parts = (data.parts ?? []).sort((a, b) => {
5519
- const orderA = a.order ?? 0;
5520
- const orderB = b.order ?? 0;
5521
- if (orderA !== orderB) return orderA - orderB;
5522
- return a.start - b.start;
5575
+ return (a.order ?? a.start ?? 0) - (b.order ?? b.start ?? 0);
5523
5576
  });
5524
5577
  this.transforms = data.transforms ?? [];
5525
5578
  this.postProcess = data.postProcess ?? void 0;
@@ -5539,6 +5592,7 @@ var DbFile = class {
5539
5592
  upper;
5540
5593
  group;
5541
5594
  scene;
5595
+ base;
5542
5596
  constructor(data) {
5543
5597
  if (!data.name) throw new Error("Name is required");
5544
5598
  if (!data.type) throw new Error("Type is required");
@@ -5552,6 +5606,7 @@ var DbFile = class {
5552
5606
  this.upper = data.upper ?? void 0;
5553
5607
  this.group = data.group || void 0;
5554
5608
  this.scene = data.scene || void 0;
5609
+ this.base = data.base ?? void 0;
5555
5610
  }
5556
5611
  };
5557
5612
  var DbFileType = class {
@@ -5562,17 +5617,21 @@ var DbFileType = class {
5562
5617
  isBlock;
5563
5618
  header;
5564
5619
  compressed;
5620
+ struct;
5621
+ base;
5565
5622
  constructor(data) {
5566
- this.name = data.name ?? "";
5567
- this.extension = data.extension ?? "";
5568
- this.type = data.type ?? "";
5623
+ if (!data.name) throw new Error("Name is required");
5624
+ if (!data.extension) throw new Error("Extension is required");
5625
+ if (!data.type) throw new Error("Type is required");
5626
+ this.name = data.name;
5627
+ this.extension = data.extension;
5628
+ this.type = data.type;
5569
5629
  this.isPatch = data.isPatch ?? false;
5570
5630
  this.isBlock = data.isBlock ?? false;
5571
5631
  this.header = data.header ?? 0;
5572
5632
  this.compressed = data.compressed ?? void 0;
5573
- if (!this.name) throw new Error("Name is required");
5574
- if (!this.extension) throw new Error("Extension is required");
5575
- if (!this.type) throw new Error("Type is required");
5633
+ this.struct = data.struct ?? void 0;
5634
+ this.base = data.base ?? void 0;
5576
5635
  }
5577
5636
  };
5578
5637
 
@@ -5588,12 +5647,16 @@ var DbStruct = class {
5588
5647
  parent;
5589
5648
  delimiter;
5590
5649
  discriminator;
5650
+ discriminatorLogic;
5651
+ null;
5591
5652
  constructor(data) {
5592
5653
  this.name = data.name ?? "";
5593
5654
  this.types = data.types ?? void 0;
5594
5655
  this.parent = data.parent ?? void 0;
5595
5656
  this.delimiter = data.delimiter ?? void 0;
5596
5657
  this.discriminator = data.discriminator ?? void 0;
5658
+ this.discriminatorLogic = data.discriminatorLogic ?? (this.discriminator !== void 0 ? "=" : void 0);
5659
+ this.null = data.null ?? void 0;
5597
5660
  if (!this.name) throw new Error("Name is required");
5598
5661
  }
5599
5662
  };
@@ -5648,14 +5711,16 @@ var DbStringCommand = class {
5648
5711
  }
5649
5712
  };
5650
5713
  var DbStringDictionary = class {
5714
+ base;
5651
5715
  command;
5652
5716
  commandName;
5653
5717
  name;
5654
5718
  entries;
5655
5719
  constructor(data) {
5656
- if (typeof data.command !== "number") throw new Error("Command is required");
5720
+ if (typeof data.command !== "number" && typeof data.base !== "number") throw new Error("Command or base is required");
5657
5721
  if (!data.name) throw new Error("Name is required");
5658
5722
  if (!data.entries || data.entries.length === 0) throw new Error("Entries is required");
5723
+ this.base = data.base ?? void 0;
5659
5724
  this.command = data.command ?? void 0;
5660
5725
  this.commandName = data.commandName ?? void 0;
5661
5726
  this.name = data.name;
@@ -5676,6 +5741,7 @@ var DbStringType = class {
5676
5741
  greedyTerminator;
5677
5742
  dictionaries;
5678
5743
  dictionaryLookup;
5744
+ modifiers;
5679
5745
  constructor(data) {
5680
5746
  if (!data.name) throw new Error("Name is required");
5681
5747
  if (!data.delimiter) throw new Error("Delimiter is required");
@@ -5696,6 +5762,7 @@ var DbStringType = class {
5696
5762
  text: z,
5697
5763
  id: (y.command ?? 0) << 8 | ix
5698
5764
  }))).sort((a, b) => b.text.length - a.text.length);
5765
+ this.modifiers = data.modifiers;
5699
5766
  }
5700
5767
  };
5701
5768
 
@@ -5711,6 +5778,7 @@ var CopDef = class {
5711
5778
  size;
5712
5779
  parts;
5713
5780
  halt;
5781
+ conditions;
5714
5782
  constructor(data) {
5715
5783
  if (!data.name) throw new Error("Name is required");
5716
5784
  if (typeof data.id !== "number") throw new Error("ID is required");
@@ -5719,6 +5787,7 @@ var CopDef = class {
5719
5787
  this.parts = data.parts ?? [];
5720
5788
  this.halt = data.halt ?? false;
5721
5789
  this.size = data.size ?? 0;
5790
+ this.conditions = data.conditions ?? [];
5722
5791
  }
5723
5792
  };
5724
5793
 
@@ -6101,7 +6170,7 @@ var DbRootUtils = class {
6101
6170
  const cfg = module.config;
6102
6171
  const compression = cfg.compression ? CompressionAlgorithms[cfg.compression]() : void 0;
6103
6172
  const baseRomChunks = module.baseRomFiles ?? module.supaBaseRomFiles?.map((file) => {
6104
- const chunkFile = new ChunkFile(file.name, 0, 0, fileTypeLookup[file.type]);
6173
+ const chunkFile = new ChunkFile(fileTypeLookup[file.type], file.name);
6105
6174
  if (file.isText) {
6106
6175
  chunkFile.textData = file.text ?? void 0;
6107
6176
  chunkFile.size = file.text?.length ?? 0;
@@ -6112,7 +6181,7 @@ var DbRootUtils = class {
6112
6181
  return chunkFile;
6113
6182
  });
6114
6183
  const projectChunks = module.projectFiles ?? module.supaProjectFiles?.map((file) => {
6115
- const chunkFile = new ChunkFile(file.name, 0, 0, fileTypeLookup[file.type]);
6184
+ const chunkFile = new ChunkFile(fileTypeLookup[file.type], file.name);
6116
6185
  chunkFile.group = file.module ?? void 0;
6117
6186
  if (file.isText) {
6118
6187
  chunkFile.textData = file.text ?? void 0;
@@ -6164,9 +6233,9 @@ var DbRootUtils = class {
6164
6233
  const type = root.fileExtLookup[entry.extension];
6165
6234
  if (!type) continue;
6166
6235
  const existing = chunkFiles.find((x) => x.name === entry.name && x.type.type === type.type);
6167
- const chunkFile = existing ?? new ChunkFile(entry.name, 0, 0, type);
6236
+ const chunkFile = existing ?? new ChunkFile(type, entry.name);
6168
6237
  if (group) chunkFile.group = group;
6169
- if (chunkFile.type.type === "Assembly" || chunkFile.type.type === "Patch") {
6238
+ if (chunkFile.type.isBlock || chunkFile.type.isPatch || chunkFile.type.struct) {
6170
6239
  const chunkData = await readFileAsText(entry.path);
6171
6240
  chunkFile.textData = chunkData;
6172
6241
  chunkFile.size = chunkData.length;
@@ -6195,7 +6264,7 @@ var DbRootUtils = class {
6195
6264
  const ext = block.type.extension;
6196
6265
  const filePath = `${outPath}/${block.group ? block.group + "/" : ""}${block.scene ? block.scene + "/" : ""}${block.name}${ext ? "." + ext : ""}`;
6197
6266
  if (block.parts?.length) {
6198
- if (!block.textData) block.textData = writer.generateAsm(block);
6267
+ block.textData = writer.generateAsm(block);
6199
6268
  await saveFileAsText(filePath, block.textData);
6200
6269
  } else {
6201
6270
  if (!block.rawData) continue;
@@ -7113,6 +7182,7 @@ async function summaryFromSupabaseByProject(projectName) {
7113
7182
  notes,
7114
7183
  gameRomId,
7115
7184
  platformBranchId,
7185
+ fileTypes,
7116
7186
  createdAt,
7117
7187
  updatedAt,
7118
7188
  gameRom:GameRom!inner(
@@ -7223,10 +7293,10 @@ var RomGenerator = class {
7223
7293
  if (!this.sourceData) throw new Error("Source data not initialized");
7224
7294
  const reader = new BlockReader(this.sourceData, this.dbRoot);
7225
7295
  const chunkFiles = reader.analyzeAndResolve();
7226
- const asmFiles = chunkFiles.filter((b) => b.type.type === "Assembly");
7296
+ const asmFiles = chunkFiles.filter((b) => b.type.isBlock);
7227
7297
  const patchFiles = [];
7228
7298
  const writer = new BlockWriter(reader);
7229
- for (const block of asmFiles) block.textData = writer.generateAsm(block);
7299
+ for (const block of chunkFiles) if (block.parts?.length) block.textData = writer.generateAsm(block);
7230
7300
  for (const chunkFile of this.dbRoot.baseRomFiles) this.applyPatchFile(chunkFile, chunkFiles, asmFiles, patchFiles);
7231
7301
  const moduleLookup = this.applyProjectInit(chunkFiles, asmFiles, patchFiles);
7232
7302
  if (unshiftManualFiles) for (const file of manualFiles ?? []) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
@@ -7251,7 +7321,7 @@ var RomGenerator = class {
7251
7321
  const existing = chunkFiles.find((x) => x.name === chunkFile.name);
7252
7322
  if (chunkFile.textData) if (existing) existing.textData = chunkFile.textData;
7253
7323
  else {
7254
- if (chunkFile.type.type === "Patch") patchFiles.push(chunkFile);
7324
+ if (chunkFile.type.isPatch) patchFiles.push(chunkFile);
7255
7325
  asmFiles.push(chunkFile);
7256
7326
  chunkFiles.push(chunkFile);
7257
7327
  }
@@ -7502,5 +7572,5 @@ const snes = {
7502
7572
  const isPlatformNode = typeof process !== "undefined" && process.versions?.node;
7503
7573
 
7504
7574
  //#endregion
7505
- 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 };
7575
+ 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 };
7506
7576
  //# sourceMappingURL=index.mjs.map