@gaialabs/core 0.2.13 → 0.2.15

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
@@ -937,7 +937,7 @@ var RomDataReader = class {
937
937
  romData;
938
938
  position;
939
939
  offset;
940
- constructor(romData, offset = 0) {
940
+ constructor(romData, offset) {
941
941
  if (!romData) throw new Error("romData cannot be null");
942
942
  this.romData = romData;
943
943
  this.position = 0;
@@ -1099,7 +1099,7 @@ var RomProcessingConstants = class RomProcessingConstants {
1099
1099
  * BlockReader specific constants
1100
1100
  */
1101
1101
  var BlockReaderConstants = class {
1102
- static REF_SEARCH_MAX_RANGE = 416;
1102
+ static REF_SEARCH_MAX_RANGE = 896;
1103
1103
  static BANK_MASK_CHECK = 64;
1104
1104
  static BYTE_DELIMITER_THRESHOLD = 256;
1105
1105
  static BANK_HIGH_MEMORY_1 = 126;
@@ -1131,6 +1131,7 @@ let AddressType = /* @__PURE__ */ function(AddressType) {
1131
1131
  AddressType["Relative"] = "Relative";
1132
1132
  AddressType["WRelative"] = "WRelative";
1133
1133
  AddressType["Location"] = "Location";
1134
+ AddressType["OddLocation"] = "OddLocation";
1134
1135
  return AddressType;
1135
1136
  }({});
1136
1137
  /**
@@ -1221,6 +1222,7 @@ var Address = class Address {
1221
1222
  case "@": return AddressType.Address;
1222
1223
  case "*": return AddressType.WBank;
1223
1224
  case "%": return AddressType.Location;
1225
+ case "!": return AddressType.OddLocation;
1224
1226
  default: return AddressType.Unknown;
1225
1227
  }
1226
1228
  }
@@ -1231,6 +1233,7 @@ var Address = class Address {
1231
1233
  case AddressType.Address: return "@";
1232
1234
  case AddressType.WBank: return "*";
1233
1235
  case AddressType.Location: return "%";
1236
+ case AddressType.OddLocation: return "!";
1234
1237
  default: return null;
1235
1238
  }
1236
1239
  }
@@ -1675,6 +1678,7 @@ var ChunkFile = class {
1675
1678
  scene;
1676
1679
  base;
1677
1680
  referenceManager;
1681
+ isFile;
1678
1682
  constructor(type, name, size = 0, location = 0) {
1679
1683
  this.type = type;
1680
1684
  this.name = name;
@@ -1682,6 +1686,7 @@ var ChunkFile = class {
1682
1686
  this.location = location;
1683
1687
  this.mnemonics = {};
1684
1688
  this.compressed = type.compressed;
1689
+ this.isFile = !type.isBlock;
1685
1690
  }
1686
1691
  enrichWithRawDataFromDbFile(file, rom, compression) {
1687
1692
  this.upper = file.upper ?? this.upper;
@@ -1690,7 +1695,8 @@ var ChunkFile = class {
1690
1695
  this.base = file.base ?? this.type.base ?? this.base;
1691
1696
  this.group = file.group ?? this.group;
1692
1697
  this.scene = file.scene ?? this.scene;
1693
- this.compressed = file.compressed;
1698
+ this.compressed = file.compressed ?? this.type.compressed ?? this.compressed;
1699
+ this.isFile = true;
1694
1700
  let start = this.location;
1695
1701
  let header = null;
1696
1702
  const fileType = this.type;
@@ -1716,6 +1722,8 @@ var ChunkFile = class {
1716
1722
  enrichWithAsmBlocksFromDbBlock(block, memoryMode) {
1717
1723
  this.group = block.group;
1718
1724
  this.scene = block.scene;
1725
+ this.base = block.base;
1726
+ this.isFile = false;
1719
1727
  this.parts = [];
1720
1728
  this.size = 0;
1721
1729
  if (!block.parts?.length) throw new Error(`Block ${block.name} has no parts`);
@@ -1926,20 +1934,30 @@ var ReferenceManager = class {
1926
1934
  structTable = /* @__PURE__ */ new Map();
1927
1935
  markerTable = /* @__PURE__ */ new Map();
1928
1936
  nameTable = /* @__PURE__ */ new Map();
1937
+ fileTable = /* @__PURE__ */ new Map();
1929
1938
  root;
1930
1939
  constructor(root) {
1931
1940
  if (!root) throw new Error("root cannot be null");
1932
1941
  this.root = root;
1933
1942
  }
1934
1943
  tryGetStruct(location) {
1935
- const chunkType = this.structTable.get(location);
1944
+ let chunkType = this.structTable.get(location);
1945
+ const found = chunkType !== void 0;
1946
+ let isSoft;
1947
+ if (chunkType) if (chunkType[0] === "~") {
1948
+ isSoft = true;
1949
+ chunkType = chunkType.substring(1);
1950
+ } else isSoft = false;
1936
1951
  return {
1937
- found: chunkType !== void 0,
1938
- chunkType
1952
+ found,
1953
+ chunkType,
1954
+ isSoft
1939
1955
  };
1940
1956
  }
1941
1957
  tryAddStruct(location, chunkType) {
1942
- if (this.structTable.has(location)) return false;
1958
+ const existingStruct = this.tryGetStruct(location);
1959
+ const isSoft = chunkType[0] === "~";
1960
+ if (existingStruct.isSoft === false || existingStruct.found && isSoft) return false;
1943
1961
  this.structTable.set(location, chunkType);
1944
1962
  return true;
1945
1963
  }
@@ -1947,14 +1965,23 @@ var ReferenceManager = class {
1947
1965
  return this.structTable.has(location);
1948
1966
  }
1949
1967
  tryGetName(location) {
1950
- const referenceName = this.nameTable.get(location);
1968
+ let referenceName = this.nameTable.get(location);
1969
+ const found = referenceName !== void 0;
1970
+ let isSoft;
1971
+ if (referenceName) if (referenceName[0] === "~") {
1972
+ isSoft = true;
1973
+ referenceName = referenceName.substring(1);
1974
+ } else isSoft = false;
1951
1975
  return {
1952
- found: referenceName !== void 0,
1953
- referenceName
1976
+ found,
1977
+ referenceName,
1978
+ isSoft
1954
1979
  };
1955
1980
  }
1956
1981
  tryAddName(location, referenceName) {
1957
- if (this.nameTable.has(location)) return false;
1982
+ const existingName = this.tryGetName(location);
1983
+ const isSoft = referenceName[0] === "~";
1984
+ if (existingName.isSoft === false || existingName.found && isSoft) return false;
1958
1985
  this.nameTable.set(location, referenceName);
1959
1986
  return true;
1960
1987
  }
@@ -1975,6 +2002,7 @@ var ReferenceManager = class {
1975
2002
  }
1976
2003
  createTypeName(type, location) {
1977
2004
  let name = type.toLowerCase();
2005
+ if (type[0] === "~") name = name.substring(1);
1978
2006
  if (name === "branch") name = "loc";
1979
2007
  while (name.length > 0 && BlockReaderConstants.POINTER_CHARACTERS.includes(name[0])) name = name.substring(1) + "_list";
1980
2008
  return `${name}_${location.toString(16).toUpperCase().padStart(6, "0")}`;
@@ -1996,7 +2024,7 @@ var ReferenceManager = class {
1996
2024
  resolveName(location, type, isBranch) {
1997
2025
  const prefix = Address.codeFromType(type);
1998
2026
  let name;
1999
- let label = null;
2027
+ let label;
2000
2028
  let resolvedLocation = location;
2001
2029
  const rewrite = this.root.rewrites[location];
2002
2030
  if (rewrite !== void 0) {
@@ -2004,9 +2032,8 @@ var ReferenceManager = class {
2004
2032
  resolvedLocation = result.location;
2005
2033
  label = result.label;
2006
2034
  }
2007
- const existingName = this.nameTable.get(resolvedLocation);
2008
- if (existingName) name = existingName;
2009
- else name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
2035
+ name = this.tryGetName(resolvedLocation).referenceName;
2036
+ if (!name) name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
2010
2037
  return `${prefix || ""}${name}${label || ""}`;
2011
2038
  }
2012
2039
  findClosestReference(location) {
@@ -2028,8 +2055,8 @@ var ReferenceManager = class {
2028
2055
  const offset = location - rewrite;
2029
2056
  const cmd = offset < 0 ? "-" : "+";
2030
2057
  const absOffset = Math.abs(offset);
2031
- let label = null;
2032
- if (this.structTable.get(rewrite) === BlockReaderConstants.WIDE_STRING_TYPE) {
2058
+ let label;
2059
+ if (this.tryGetStruct(rewrite).chunkType === BlockReaderConstants.WIDE_STRING_TYPE) {
2033
2060
  this.markerTable.set(rewrite, absOffset);
2034
2061
  this.markerTable.set(location, absOffset);
2035
2062
  label = cmd === "-" ? BlockReaderConstants.NEGATIVE_MARKER_FORMAT : BlockReaderConstants.MARKER_FORMAT;
@@ -2042,7 +2069,7 @@ var ReferenceManager = class {
2042
2069
  processClosestMatch(location, closestName, closestLocation, closestDistance) {
2043
2070
  if (!closestName || closestLocation === null) return null;
2044
2071
  let result = closestName;
2045
- if (this.structTable.get(closestLocation) === BlockReaderConstants.WIDE_STRING_TYPE) {
2072
+ if (this.tryGetStruct(closestLocation).chunkType === BlockReaderConstants.WIDE_STRING_TYPE) {
2046
2073
  this.markerTable.set(closestLocation, closestDistance);
2047
2074
  this.markerTable.set(location, closestDistance);
2048
2075
  result += BlockReaderConstants.MARKER_FORMAT;
@@ -2108,6 +2135,10 @@ var StackOperations = class {
2108
2135
  case "XBA":
2109
2136
  this.handleExchangeBytes();
2110
2137
  break;
2138
+ case "RTL":
2139
+ case "RTS":
2140
+ this._blockReader._referenceManager.tryAddStruct(this._blockReader._romDataReader.position, "~Code");
2141
+ break;
2111
2142
  }
2112
2143
  }
2113
2144
  handleAccumulatorPush() {
@@ -2157,7 +2188,16 @@ var CopCommandProcessor = class {
2157
2188
  * Parses a COP command based on its definition
2158
2189
  */
2159
2190
  parseCopCommand(copDef, operands) {
2160
- for (let partStr of copDef.parts) {
2191
+ let parts = copDef.parts;
2192
+ if (copDef.conditions) for (const condition of copDef.conditions) {
2193
+ let value = this._romDataReader.romData[this._romDataReader.position + condition.offset];
2194
+ if (condition.value >= 256) value |= this._romDataReader.romData[this._romDataReader.position + condition.offset + 1] << 8;
2195
+ if (value === condition.value) {
2196
+ parts = condition.parts;
2197
+ break;
2198
+ }
2199
+ }
2200
+ for (let partStr of parts) {
2161
2201
  let bank = null;
2162
2202
  const bankHintIx = partStr.indexOf("$");
2163
2203
  if (bankHintIx > 0) {
@@ -2340,6 +2380,7 @@ var AddressingModeHandler = class {
2340
2380
  if (address.isROM) {
2341
2381
  const wrapper = new LocationWrapper(address.toLocation(), AddressType.Address);
2342
2382
  if (this.isJumpInstruction(mnemonic)) this._blockReader.noteType(wrapper.location, "Code", false, reg);
2383
+ if (mnemonic === "JML") this._blockReader._referenceManager.tryAddStruct(this._dataReader.position, "Code");
2343
2384
  operands.push(wrapper);
2344
2385
  } else operands.push(address);
2345
2386
  }
@@ -2353,7 +2394,7 @@ var AddressingModeHandler = class {
2353
2394
  }
2354
2395
  handlePCRelativeMode(operands, nextAddress, reg, isLong) {
2355
2396
  const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
2356
- this._blockReader._referenceManager.tryAddStruct(relative, "Branch");
2397
+ this._blockReader._referenceManager.tryAddStruct(relative, "~Branch");
2357
2398
  this._blockReader.updateRegisterState(relative, reg);
2358
2399
  operands.push(new LocationWrapper(relative, isLong ? AddressType.WRelative : AddressType.Relative));
2359
2400
  }
@@ -2378,12 +2419,13 @@ var AddressingModeHandler = class {
2378
2419
  const isPush = this.isPushInstruction(mnemonic);
2379
2420
  if (isPush) refLoc++;
2380
2421
  const isJump = isPush || isIndexedIndirect || this.isJumpInstruction(mnemonic);
2381
- const addr = new Address(xBank1 ?? (isJump ? Address.resolveBank(this._dataReader.position, registers.mode) : dataBank) ?? 129, refLoc, registers.mode);
2422
+ const addr = new Address(xBank1 ?? (isJump ? Address.resolveBank(this._dataReader.position, registers.mode) : dataBank) ?? this._blockReader._root.config.defaultBank ?? 129, refLoc, registers.mode);
2382
2423
  if (addr.isROM) {
2383
2424
  const wrapper = new LocationWrapper(addr.toLocation(), AddressType.Offset);
2384
2425
  if (isJump) {
2385
2426
  const type = isIndexedIndirect ? "&Code" : "Code";
2386
2427
  const name = this._blockReader.noteType(wrapper.location, type, isPush, registers);
2428
+ if (mnemonic === "JMP") this._blockReader._referenceManager.tryAddStruct(this._dataReader.position, "~Code");
2387
2429
  if (isPush) {
2388
2430
  operands.push(`&${name}-1`);
2389
2431
  return;
@@ -3018,10 +3060,11 @@ var StringReader = class StringReader {
3018
3060
  }
3019
3061
  parseString(stringType, fixedSize) {
3020
3062
  const commands = stringType.commandLookup;
3021
- stringType.dictionaries;
3063
+ const dictionaries = stringType.dictionaries;
3022
3064
  const builder = [];
3023
3065
  const strLoc = this._romDataReader.position;
3024
3066
  const terminator = stringType.terminator;
3067
+ const modifiers = stringType.modifiers;
3025
3068
  let currentLayer = stringType.layers[0];
3026
3069
  do {
3027
3070
  const c = this._romDataReader.readByte();
@@ -3029,23 +3072,35 @@ var StringReader = class StringReader {
3029
3072
  if (stringType.greedyTerminator) while (this._romDataReader.peekByte() === terminator && this._blockReader.partCanContinue()) this._romDataReader.position++;
3030
3073
  break;
3031
3074
  }
3032
- const cmd = commands[c];
3033
- if (cmd) {
3075
+ let cmd = void 0;
3076
+ const mod = modifiers ? modifiers[c] : void 0;
3077
+ if (mod) {
3078
+ const next = mod[builder[builder.length - 1]];
3079
+ if (next) builder[builder.length - 1] = next;
3080
+ else builder.push(currentLayer.map[c - (currentLayer.base ?? 0)]);
3081
+ } else if (cmd = commands[c]) {
3034
3082
  this.resolveCommand(cmd, builder);
3035
3083
  if (cmd.halt) break;
3036
3084
  } else {
3037
3085
  let found = false;
3038
- for (const layer of stringType.layers) if (layer.on !== void 0) {
3039
- if (c === layer.on) {
3040
- currentLayer = layer;
3041
- builder.push("[--]");
3086
+ for (const dictionary of Object.values(dictionaries)) if (dictionary.base !== void 0 && c >= dictionary.base && c <= dictionary.base + dictionary.entries.length) {
3087
+ builder.push(dictionary.entries[c - dictionary.base]);
3088
+ found = true;
3089
+ break;
3090
+ }
3091
+ if (!found) {
3092
+ for (const layer of stringType.layers) if (layer.on !== void 0) {
3093
+ if (c === layer.on) {
3094
+ currentLayer = layer;
3095
+ builder.push("[--]");
3096
+ found = true;
3097
+ break;
3098
+ }
3099
+ } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3100
+ builder.push(layer.map[c - layer.base]);
3042
3101
  found = true;
3043
3102
  break;
3044
3103
  }
3045
- } else if (layer.base && c >= layer.base && c < layer.base + layer.map.length) {
3046
- builder.push(layer.map[c - layer.base]);
3047
- found = true;
3048
- break;
3049
3104
  }
3050
3105
  if (!found) {
3051
3106
  let index = c - (currentLayer.base ?? 0);
@@ -3121,27 +3176,33 @@ var TypeParser = class {
3121
3176
  this._stringReader = blockReader._stringReader;
3122
3177
  this._stringTypes = blockReader._root.stringTypes;
3123
3178
  }
3124
- parseType(typeName, reg, depth, bank) {
3125
- if (typeName[0] === "&") return this.parseLocation(this._romDataReader.readUShort(), bank, typeName.substring(1), AddressType.Offset);
3126
- if (typeName[0] === "@") return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1), AddressType.Address);
3127
- if (typeName[0] === "%") return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1), AddressType.Location);
3128
- const isFixed = typeName[typeName.length - 1] === ")";
3129
- let fixedTypeName = typeName;
3130
- let fixedSize = 0;
3131
- if (isFixed) {
3132
- const startIx = typeName.indexOf("(");
3133
- fixedTypeName = typeName.substring(0, startIx);
3134
- fixedSize = parseInt(typeName.substring(startIx + 1, typeName.length - 1), 10);
3135
- }
3179
+ parseType(typeName, reg, depth, bank, single) {
3180
+ const objects = [];
3181
+ const arrayIx = typeName.indexOf("[");
3182
+ const arrayCount = arrayIx !== -1 ? parseInt(typeName.substring(arrayIx + 1, typeName.length - 1), 16) : 0;
3183
+ const arrayTypeName = arrayIx !== -1 ? typeName.substring(0, arrayIx) : typeName;
3184
+ if (arrayCount > 0) {
3185
+ for (let i = 0; i < arrayCount; i++) objects.push(this.parseType(arrayTypeName, reg, depth + 1, bank, true));
3186
+ return objects;
3187
+ }
3188
+ const isSoft = arrayTypeName[0] === "~";
3189
+ const realTypeName = isSoft ? arrayTypeName.substring(1) : arrayTypeName;
3190
+ const fixedIx = realTypeName.indexOf("(");
3191
+ const fixedSize = fixedIx !== -1 ? parseInt(realTypeName.substring(fixedIx + 1, realTypeName.length - 1), 16) : 0;
3192
+ const fixedTypeName = fixedIx !== -1 ? realTypeName.substring(0, fixedIx) : realTypeName;
3136
3193
  const stringType = this._stringTypes[fixedTypeName];
3137
3194
  if (stringType) return this._stringReader.parseString(stringType, fixedSize);
3195
+ if (fixedTypeName[0] === "&") return this.parseLocation(this._romDataReader.readUShort(), bank, fixedTypeName.substring(1), AddressType.Offset, isSoft);
3196
+ if (fixedTypeName[0] === "@") return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), fixedTypeName.substring(1), AddressType.Address, isSoft);
3197
+ if (fixedTypeName[0] === "%") return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), fixedTypeName.substring(1), AddressType.Location, isSoft);
3198
+ if (fixedTypeName[0] === "!") return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), fixedTypeName.substring(1), AddressType.OddLocation, isSoft);
3138
3199
  const mType = this.tryParseMemberType(fixedTypeName);
3139
3200
  if (mType !== null) switch (mType) {
3140
3201
  case MemberType.Byte: return new Byte(this._romDataReader.readByte());
3141
3202
  case MemberType.Word: return new Word(this.parseWordSafe());
3142
- case MemberType.Offset: return this.parseLocation(this._romDataReader.readUShort(), bank, null, AddressType.Offset);
3143
- case MemberType.Address: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Address);
3144
- case MemberType.Location: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Location);
3203
+ case MemberType.Offset: return this.parseLocation(this._romDataReader.readUShort(), bank, null, AddressType.Offset, isSoft);
3204
+ case MemberType.Address: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Address, isSoft);
3205
+ case MemberType.Location: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Location, isSoft);
3145
3206
  case MemberType.Binary: return this.parseBinary(fixedSize);
3146
3207
  case MemberType.Code:
3147
3208
  case MemberType.Branch: return this.parseCode(reg);
@@ -3151,29 +3212,52 @@ var TypeParser = class {
3151
3212
  if (!parentType) throw new Error(`Unknown type: ${fixedTypeName}`);
3152
3213
  const delimiter = parentType.delimiter;
3153
3214
  const discOffset = parentType.discriminator;
3154
- const objects = [];
3215
+ const discLogic = parentType.discriminatorLogic;
3216
+ const discSize = parentType.discriminatorSize ?? 1;
3217
+ const nullValue = parentType.null;
3218
+ if (delimiter === void 0 && this._blockReader.delimiterReached(nullValue)) {
3219
+ objects.push({
3220
+ name: parentType.name,
3221
+ parts: ["null"]
3222
+ });
3223
+ return objects;
3224
+ }
3155
3225
  let delReached;
3156
3226
  while (!(delReached = this._blockReader.delimiterReached(delimiter))) {
3157
3227
  const startPosition = this._romDataReader.position;
3158
3228
  let targetType = parentType;
3159
- if (discOffset !== void 0) {
3160
- const discPosition = this._romDataReader.position + discOffset;
3161
- const desc = this._romDataReader.romData[discPosition];
3162
- if (discOffset === 0) this._romDataReader.position++;
3163
- targetType = Object.values(this._blockReader._root.structs).find((x) => x.parent === fixedTypeName && x.discriminator === desc) || parentType;
3164
- }
3165
- const types = targetType.types;
3166
- if (types && types.length > 0) {
3167
- const memberCount = types.length;
3168
- const prevPosition = this._romDataReader.position;
3169
- const parts = new Array(memberCount);
3170
- const def = {
3171
- name: targetType.name,
3172
- parts
3173
- };
3174
- for (let i = 0; i < memberCount; i++) parts[i] = this.parseType(types[i], reg, depth + 1, bank);
3175
- if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) this._romDataReader.position++;
3176
- objects.push(def);
3229
+ if (this._blockReader.delimiterReached(nullValue)) objects.push({
3230
+ name: parentType.name,
3231
+ parts: ["null"]
3232
+ });
3233
+ else {
3234
+ if (discOffset !== void 0) {
3235
+ const discPosition = this._romDataReader.position + discOffset;
3236
+ const disc = this._romDataReader.romData[discPosition];
3237
+ const disc16 = this._romDataReader.romData[discPosition + 1] << 8 | disc;
3238
+ const matchedStruct = Object.values(this._blockReader._root.structs).find((x) => x.parent === fixedTypeName && (discLogic === "&" ? ((x.discriminator ?? 0) & disc) !== 0 : (x.discriminator ?? 0) >= 256 ? x.discriminator === disc16 : x.discriminator === disc));
3239
+ targetType = matchedStruct || parentType;
3240
+ if (discOffset === 0 && parentType != targetType && discLogic === "=") {
3241
+ this._romDataReader.position++;
3242
+ if (discSize > 1 || (matchedStruct?.discriminator ?? 0) >= 256) this._romDataReader.position++;
3243
+ }
3244
+ }
3245
+ const types = targetType.types;
3246
+ if (types) {
3247
+ const memberCount = types.length;
3248
+ const prevPosition = this._romDataReader.position;
3249
+ const parts = new Array(memberCount);
3250
+ const def = {
3251
+ name: targetType.name,
3252
+ parts
3253
+ };
3254
+ for (let i = 0; i < memberCount; i++) parts[i] = this.parseType(types[i], reg, depth + 1, bank);
3255
+ if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition && types?.length > 0) {
3256
+ this._romDataReader.position++;
3257
+ if (discSize > 1) this._romDataReader.position++;
3258
+ }
3259
+ objects.push(def);
3260
+ } else this._romDataReader.position++;
3177
3261
  }
3178
3262
  let checkPosition = startPosition;
3179
3263
  while (++checkPosition < this._romDataReader.position) {
@@ -3184,9 +3268,12 @@ var TypeParser = class {
3184
3268
  }
3185
3269
  }
3186
3270
  if (!this._blockReader.partCanContinue()) break;
3271
+ if (nullValue !== void 0) break;
3272
+ if (single) break;
3187
3273
  }
3188
3274
  if (delReached && depth === 0) this._referenceManager.tryAddStruct(this._romDataReader.position, typeName);
3189
- return objects;
3275
+ if (single) return objects[0];
3276
+ else return objects;
3190
3277
  }
3191
3278
  tryParseMemberType(memberTypeName) {
3192
3279
  const upperName = memberTypeName.toUpperCase();
@@ -3207,22 +3294,41 @@ var TypeParser = class {
3207
3294
  for (let i = 0; i < len; i++) outBuffer[i] = this._romDataReader.romData[startPosition + i];
3208
3295
  return outBuffer;
3209
3296
  }
3210
- parseLocation(offset, bank, typeName, addrType) {
3211
- if ((bank === void 0 || bank === null) && offset === 0) return new Word(offset);
3212
- offset -= this._romDataReader.offset;
3297
+ parseLocation(offset, bank, typeName, addrType, isSoft = false) {
3298
+ if ((bank === void 0 || bank === null) && (offset === 0 || offset === 65535)) return new Word(offset);
3299
+ const hasOffset = this._romDataReader.offset !== void 0;
3300
+ if (hasOffset) offset -= this._romDataReader.offset;
3213
3301
  let adrs;
3214
3302
  let loc;
3215
- if (addrType === AddressType.Location || this._romDataReader.offset) loc = offset | bank << 16;
3216
- else {
3303
+ if (addrType === AddressType.Location || hasOffset) {
3304
+ loc = offset | bank << 16;
3305
+ if (hasOffset && !this._blockReader._currentChunk.isFile) loc += this._blockReader._currentChunk.location;
3306
+ } else {
3307
+ if (addrType === AddressType.OddLocation) {
3308
+ const bankFlag = this._blockReader._root.config.cpuMode === CpuMode.Fast ? 128 : 0;
3309
+ const bankReal = this._romDataReader.position >> 16;
3310
+ const isVeryOdd = !this._blockReader._root.config.oddLocationBase || bankReal < this._blockReader._root.config.oddLocationBase;
3311
+ const bankBase = (isVeryOdd ? 64 : 0) + bankReal | bankFlag;
3312
+ const bankMax = this._romDataReader.romData.length >> 16 | bankFlag;
3313
+ const bankSpan = isVeryOdd ? 0 : (64 | bankFlag) - bankMax;
3314
+ const hiOffset = offset >> 8 & 127;
3315
+ const hiAdjust = (hiOffset ^ (offset >> 8 | bank << 8)) << 1 | 128 | hiOffset;
3316
+ const hiBank = (hiAdjust >> 8) + bankBase;
3317
+ offset = (hiAdjust & 255) << 8 | offset & 255;
3318
+ bank = hiBank;
3319
+ if (hiBank >= bankMax) {
3320
+ bank += bankSpan;
3321
+ offset &= 32767;
3322
+ }
3323
+ }
3217
3324
  adrs = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), offset, this._blockReader._root.config.memoryMode);
3218
3325
  if (!adrs.isROM) return adrs;
3219
3326
  loc = adrs.toLocation();
3220
3327
  }
3221
- if (typeName && (this._romDataReader.offset || !this._blockReader._root.rewrites[loc])) {
3222
- const oldStruct = this._referenceManager.structTable.get(loc);
3223
- if (!oldStruct || oldStruct === "Branch") this._referenceManager.structTable.set(loc, typeName);
3328
+ if (typeName && (hasOffset || !this._blockReader._root.rewrites[loc])) {
3329
+ this._referenceManager.tryAddStruct(loc, isSoft ? "~" + typeName : typeName);
3224
3330
  const referenceName = `${typeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
3225
- this._referenceManager.tryAddName(loc, referenceName);
3331
+ this._referenceManager.tryAddName(loc, isSoft ? "~" + referenceName : referenceName);
3226
3332
  }
3227
3333
  return new LocationWrapper(loc, addrType);
3228
3334
  }
@@ -3232,8 +3338,8 @@ var TypeParser = class {
3232
3338
  while (this._romDataReader.position < this._blockReader._partEnd) {
3233
3339
  if (first) first = false;
3234
3340
  else {
3235
- const struct = this._referenceManager.structTable.get(this._romDataReader.position);
3236
- if (struct && struct !== "Branch") break;
3341
+ const struct = this._referenceManager.tryGetStruct(this._romDataReader.position);
3342
+ if (struct.found && struct.chunkType !== "Branch") break;
3237
3343
  }
3238
3344
  if (reg) this._blockReader.hydrateRegisters(reg);
3239
3345
  const op = this._blockReader._asmReader.parseAsm(reg);
@@ -3436,9 +3542,6 @@ var BlockReader = class {
3436
3542
  /**
3437
3543
  * Processes predefined file references
3438
3544
  */
3439
- initializeFileReferences() {
3440
- for (const file of this._root.files) this._referenceManager.tryAddName(file.start, file.name);
3441
- }
3442
3545
  /**
3443
3546
  * Resolves mnemonic for a given address
3444
3547
  */
@@ -3476,13 +3579,13 @@ var BlockReader = class {
3476
3579
  * Notes a type at a location and manages chunk references
3477
3580
  */
3478
3581
  noteType(loc, type, silent = false, reg) {
3479
- const oldStruct = this._referenceManager.structTable.get(loc);
3480
- if (!oldStruct || oldStruct === "Branch") this._referenceManager.structTable.set(loc, type);
3582
+ const isSoft = type[0] === "~";
3583
+ this._referenceManager.tryAddStruct(loc, type);
3481
3584
  const nameResult = this._referenceManager.tryGetName(loc);
3482
3585
  let name;
3483
3586
  if (!nameResult.found) {
3484
3587
  name = this._referenceManager.createTypeName(type, loc);
3485
- this._referenceManager.tryAddName(loc, name);
3588
+ this._referenceManager.tryAddName(loc, isSoft ? "~" + name : name);
3486
3589
  } else name = nameResult.referenceName;
3487
3590
  if (!silent && type === BlockReaderConstants.CODE_TYPE && reg) this.updateRegisterState(loc, reg);
3488
3591
  return name;
@@ -3519,7 +3622,6 @@ var BlockReader = class {
3519
3622
  analyzeAndResolve() {
3520
3623
  console.log("analyzeAndResolve started");
3521
3624
  this.initializeOverrides();
3522
- this.initializeFileReferences();
3523
3625
  this.createChunkFilesFromDatabase();
3524
3626
  this.initializeBlocksAndParts();
3525
3627
  this.analyzeChunkFiles();
@@ -3535,9 +3637,12 @@ var BlockReader = class {
3535
3637
  */
3536
3638
  initializeBlocksAndParts() {
3537
3639
  console.log("initializeBlocksAndParts");
3538
- for (const block of this._enrichedChunks) for (const part of block.parts || []) {
3539
- if (part.structName) this._referenceManager.tryAddStruct(part.location, part.structName);
3540
- if (part.label) this._referenceManager.tryAddName(part.location, part.label);
3640
+ for (const block of this._enrichedChunks) {
3641
+ for (const part of block.parts || []) {
3642
+ if (part.structName) this._referenceManager.tryAddStruct(part.location, part.structName);
3643
+ if (part.label) this._referenceManager.tryAddName(part.location, part.label);
3644
+ }
3645
+ this._referenceManager.tryAddName(block.location, block.name);
3541
3646
  }
3542
3647
  }
3543
3648
  /**
@@ -3582,7 +3687,7 @@ var BlockReader = class {
3582
3687
  */
3583
3688
  processNewEntry(current, reg, bank, last) {
3584
3689
  let res = this._typeParser.parseType(current, reg, 0, bank);
3585
- if (BlockReaderConstants.POINTER_CHARACTERS.includes(current[0]) && !Array.isArray(res)) res = [res];
3690
+ if (current.indexOf("[") !== -1 || BlockReaderConstants.POINTER_CHARACTERS.includes(current[0]) && !Array.isArray(res)) res = [res];
3586
3691
  last.object = res;
3587
3692
  }
3588
3693
  /**
@@ -3671,6 +3776,7 @@ var BlockReader = class {
3671
3776
  if (!chunkFile.type.isBlock) continue;
3672
3777
  chunkFile.referenceManager = this._referenceManager;
3673
3778
  this._currentChunk = chunkFile;
3779
+ this._romDataReader.offset = chunkFile.base;
3674
3780
  for (const asmBlock of chunkFile.parts || []) {
3675
3781
  this._currentAsmBlock = asmBlock;
3676
3782
  this.processPart(asmBlock);
@@ -3686,7 +3792,7 @@ var BlockReader = class {
3686
3792
  chunkFile.parts = [asmBlock];
3687
3793
  this._currentAsmBlock = asmBlock;
3688
3794
  this._referenceManager = chunkFile.referenceManager = new ReferenceManager(this._root);
3689
- this._romDataReader = new RomDataReader(chunkFile.rawData, chunkFile.base ?? 0);
3795
+ this._romDataReader = new RomDataReader(chunkFile.rawData, chunkFile.base);
3690
3796
  this._typeParser = new TypeParser(this);
3691
3797
  this.processPart(asmBlock);
3692
3798
  }
@@ -3996,7 +4102,7 @@ var BlockWriter = class {
3996
4102
  objLines = [this._referenceManager.resolveName(obj.location, obj.type, isBranch)];
3997
4103
  break;
3998
4104
  case ObjectType.Address:
3999
- objLines = isArray ? [`$#${obj.offset.toString(16).toUpperCase().padStart(4, "0")}`] : [`$${obj.toString()}`];
4105
+ objLines = isArray ? [`$#${obj.toString()}`] : [`$${obj.toString()}`];
4000
4106
  break;
4001
4107
  case ObjectType.ByteArray:
4002
4108
  objLines = [`#${Array.from(obj).map((b) => b.toString(16).toUpperCase().padStart(2, "0")).join("")}`];
@@ -4056,14 +4162,14 @@ var BlockWriter = class {
4056
4162
  this._isInline = true;
4057
4163
  let first = true;
4058
4164
  for (const op of opList) {
4059
- if (first) first = false;
4060
- else {
4165
+ if (!first || depth > 0) {
4061
4166
  const labelResult = this._referenceManager.tryGetName(op.location);
4062
4167
  if (labelResult.found) {
4063
4168
  lines.push("");
4064
4169
  lines.push(` ${labelResult.referenceName}:`);
4065
4170
  }
4066
4171
  }
4172
+ first = false;
4067
4173
  let opLine = ` ${op.mnem} `;
4068
4174
  if (op.copDef) {
4069
4175
  opLine += `[${op.copDef.name}]`;
@@ -4555,7 +4661,7 @@ var RomWriter = class RomWriter {
4555
4661
  }
4556
4662
  } else if (file.parts && file.parts.length > 0) {
4557
4663
  if (file.parts[0].location !== file.location && (file.location || 0) !== 0) throw new Error("Assembly was not based properly");
4558
- RomWriter.parseAssembly(this.root, file.parts, fileLookup, file.includeLookup, this.outBuffer, void 0);
4664
+ RomWriter.parseAssembly(this.root, file.parts, fileLookup, file.includeLookup, this.outBuffer, file.base);
4559
4665
  }
4560
4666
  return pos - start;
4561
4667
  }
@@ -4570,6 +4676,7 @@ var RomWriter = class RomWriter {
4570
4676
  static parseAssembly(root, blocks, fileLookup, includeLookup, outBuffer, addrOffset) {
4571
4677
  if (!blocks) throw new Error("Assembly has not been parsed");
4572
4678
  let bix = 0;
4679
+ const rootLoc = blocks[0].location;
4573
4680
  for (const block of blocks) {
4574
4681
  let position = block.location;
4575
4682
  const objList = block.objList;
@@ -4657,8 +4764,10 @@ var RomWriter = class RomWriter {
4657
4764
  break;
4658
4765
  } else markerOffset += RomProcessingConstants.getSize(part);
4659
4766
  }
4660
- if (addrOffset !== void 0) loc += addrOffset;
4661
- else if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, root.config.memoryMode, root.config.cpuMode).toInt();
4767
+ if (addrOffset !== void 0) {
4768
+ loc += addrOffset;
4769
+ if (fileLookup?.size) loc -= rootLoc;
4770
+ } else if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, root.config.memoryMode, root.config.cpuMode).toInt();
4662
4771
  switch (type) {
4663
4772
  case AddressType.Offset:
4664
4773
  case AddressType.WRelative:
@@ -4773,7 +4882,7 @@ var RomProcessor = class RomProcessor {
4773
4882
  for (const b of file.parts) if (b.label) file.includeLookup.set(b.label.toUpperCase(), b);
4774
4883
  file.rawData = void 0;
4775
4884
  file.rawData = new Uint8Array(ChunkFileUtils.calculateSize(file));
4776
- RomWriter.parseAssembly(this.writer.root, file.parts, dummyMap, file.includeLookup, file.rawData, file.type.base ?? 0);
4885
+ RomWriter.parseAssembly(this.writer.root, file.parts, dummyMap, file.includeLookup, file.rawData, file.type.base);
4777
4886
  }
4778
4887
  }
4779
4888
  for (const file of allFiles) if (file.compressed === true && canCompress) if (this.writer.root.config.uncompress) file.compressed = false;
@@ -4830,7 +4939,7 @@ var RomProcessor = class RomProcessor {
4830
4939
  ix++;
4831
4940
  continue;
4832
4941
  }
4833
- file.includes = file.includes || /* @__PURE__ */ new Set();
4942
+ if (!file.includes) file.includes = /* @__PURE__ */ new Set();
4834
4943
  file.includes.add(patch.name.toUpperCase());
4835
4944
  patch.parts.splice(ix, 1);
4836
4945
  }
@@ -5524,20 +5633,19 @@ var DbBlock = class {
5524
5633
  transforms;
5525
5634
  postProcess;
5526
5635
  order;
5636
+ base;
5527
5637
  constructor(data) {
5528
5638
  this.name = data.name ?? "";
5529
5639
  this.movable = data.movable ?? false;
5530
5640
  this.group = data.group ?? void 0;
5531
5641
  this.scene = data.scene ?? void 0;
5532
5642
  this.parts = (data.parts ?? []).sort((a, b) => {
5533
- const orderA = a.order ?? 0;
5534
- const orderB = b.order ?? 0;
5535
- if (orderA !== orderB) return orderA - orderB;
5536
- return a.start - b.start;
5643
+ return (a.order ?? a.start ?? 0) - (b.order ?? b.start ?? 0);
5537
5644
  });
5538
5645
  this.transforms = data.transforms ?? [];
5539
5646
  this.postProcess = data.postProcess ?? void 0;
5540
5647
  this.order = data.order ?? void 0;
5648
+ this.base = data.base ?? void 0;
5541
5649
  if (!this.name) throw new Error("Name is required");
5542
5650
  }
5543
5651
  };
@@ -5608,12 +5716,18 @@ var DbStruct = class {
5608
5716
  parent;
5609
5717
  delimiter;
5610
5718
  discriminator;
5719
+ discriminatorLogic;
5720
+ discriminatorSize;
5721
+ null;
5611
5722
  constructor(data) {
5612
5723
  this.name = data.name ?? "";
5613
5724
  this.types = data.types ?? void 0;
5614
5725
  this.parent = data.parent ?? void 0;
5615
5726
  this.delimiter = data.delimiter ?? void 0;
5616
5727
  this.discriminator = data.discriminator ?? void 0;
5728
+ this.discriminatorLogic = data.discriminatorLogic ?? (this.discriminator !== void 0 ? "=" : void 0);
5729
+ this.discriminatorSize = data.discriminatorSize ?? void 0;
5730
+ this.null = data.null ?? void 0;
5617
5731
  if (!this.name) throw new Error("Name is required");
5618
5732
  }
5619
5733
  };
@@ -5668,14 +5782,16 @@ var DbStringCommand = class {
5668
5782
  }
5669
5783
  };
5670
5784
  var DbStringDictionary = class {
5785
+ base;
5671
5786
  command;
5672
5787
  commandName;
5673
5788
  name;
5674
5789
  entries;
5675
5790
  constructor(data) {
5676
- if (typeof data.command !== "number") throw new Error("Command is required");
5791
+ if (typeof data.command !== "number" && typeof data.base !== "number") throw new Error("Command or base is required");
5677
5792
  if (!data.name) throw new Error("Name is required");
5678
5793
  if (!data.entries || data.entries.length === 0) throw new Error("Entries is required");
5794
+ this.base = data.base ?? void 0;
5679
5795
  this.command = data.command ?? void 0;
5680
5796
  this.commandName = data.commandName ?? void 0;
5681
5797
  this.name = data.name;
@@ -5696,6 +5812,7 @@ var DbStringType = class {
5696
5812
  greedyTerminator;
5697
5813
  dictionaries;
5698
5814
  dictionaryLookup;
5815
+ modifiers;
5699
5816
  constructor(data) {
5700
5817
  if (!data.name) throw new Error("Name is required");
5701
5818
  if (!data.delimiter) throw new Error("Delimiter is required");
@@ -5716,6 +5833,7 @@ var DbStringType = class {
5716
5833
  text: z,
5717
5834
  id: (y.command ?? 0) << 8 | ix
5718
5835
  }))).sort((a, b) => b.text.length - a.text.length);
5836
+ this.modifiers = data.modifiers;
5719
5837
  }
5720
5838
  };
5721
5839
 
@@ -5731,6 +5849,7 @@ var CopDef = class {
5731
5849
  size;
5732
5850
  parts;
5733
5851
  halt;
5852
+ conditions;
5734
5853
  constructor(data) {
5735
5854
  if (!data.name) throw new Error("Name is required");
5736
5855
  if (typeof data.id !== "number") throw new Error("ID is required");
@@ -5739,6 +5858,7 @@ var CopDef = class {
5739
5858
  this.parts = data.parts ?? [];
5740
5859
  this.halt = data.halt ?? false;
5741
5860
  this.size = data.size ?? 0;
5861
+ this.conditions = data.conditions ?? [];
5742
5862
  }
5743
5863
  };
5744
5864