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