@gaialabs/core 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -131,6 +131,148 @@ var RomDataReader = class {
131
131
  }
132
132
  };
133
133
 
134
+ //#endregion
135
+ //#region src/types/constants.ts
136
+ /**
137
+ * ROM processing constants
138
+ * Converted from GaiaLib/Types/RomProcessingConstants.cs
139
+ */
140
+ var RomProcessingConstants = class RomProcessingConstants {
141
+ static PAGE_SIZE = 32768;
142
+ static SNES_HEADER_SIZE = 80;
143
+ static DICTIONARIES = ["dictionary_01EBA8", "dictionary_01F54D"];
144
+ static DICT_COMMANDS = [214, 215];
145
+ static END_CHARS = [
146
+ 192,
147
+ 202,
148
+ 209
149
+ ];
150
+ static WHITESPACE = [" ", " "];
151
+ static OPERATORS = ["-", "+"];
152
+ static COMMA_SPACE = [
153
+ ",",
154
+ " ",
155
+ " "
156
+ ];
157
+ static ADDRESS_SPACE = [
158
+ "@",
159
+ "&",
160
+ "^",
161
+ "#",
162
+ "$",
163
+ "%",
164
+ "*"
165
+ ];
166
+ static SYMBOL_SPACE = [
167
+ ",",
168
+ " ",
169
+ " ",
170
+ "<",
171
+ ">",
172
+ "(",
173
+ ")",
174
+ ":",
175
+ "[",
176
+ "]",
177
+ "{",
178
+ "}",
179
+ "`",
180
+ "~",
181
+ "|"
182
+ ];
183
+ static LABEL_SPACE = [
184
+ "[",
185
+ "{",
186
+ "#",
187
+ "`",
188
+ "~",
189
+ "|",
190
+ ":"
191
+ ];
192
+ static OBJECT_SPACE = ["<", "["];
193
+ static COP_SPLIT_CHARS = [
194
+ " ",
195
+ " ",
196
+ ",",
197
+ "(",
198
+ ")",
199
+ "[",
200
+ "]",
201
+ "$",
202
+ "#"
203
+ ];
204
+ static WHITESPACE_REGEX = /[ \t]/;
205
+ static COMMA_SPACE_REGEX = /[, \t]/;
206
+ static SYMBOL_SPACE_REGEX = /[, \t<>()\:\[\]{}`~|]/;
207
+ static LABEL_SPACE_REGEX = /[\[{#`~|:]/;
208
+ static OBJECT_SPACE_REGEX = /[<\[]/;
209
+ static ADDRESS_SPACE_REGEX = /[@&^#$%*]/;
210
+ static COP_SPLIT_REGEX = /[ \t,()[\]$#]/;
211
+ static COMMA_SPACE_TRIM_REGEX = /^[, \t]+|[, \t]+$/g;
212
+ /**
213
+ * Gets the size of an object for processing purposes
214
+ * @param obj The object to get the size for
215
+ * @returns Size in bytes
216
+ * @throws Error when unable to determine size
217
+ */
218
+ static getSize(obj) {
219
+ if (obj === void 0 || obj === null) return 0;
220
+ switch (typeof obj) {
221
+ case "object":
222
+ if (Array.isArray(obj)) return obj.reduce((acc, x) => acc + RomProcessingConstants.getSize(x), 0);
223
+ if ("size" in obj) return obj.size;
224
+ if ("length" in obj) return obj.length;
225
+ if ("_tag" in obj) switch (obj._tag) {
226
+ case "Byte": return 1;
227
+ case "Word": return 2;
228
+ }
229
+ break;
230
+ case "number":
231
+ if (obj <= 255) return 1;
232
+ if (obj <= 65535) return 2;
233
+ if (obj <= 16777215) return 3;
234
+ return 4;
235
+ case "string":
236
+ if (!obj.length) return 0;
237
+ switch (obj[0]) {
238
+ case "@":
239
+ case "%": return 3;
240
+ case "*":
241
+ case "&": return 2;
242
+ case "^": return 1;
243
+ }
244
+ switch (obj) {
245
+ case "Byte": return 1;
246
+ case "Word": return 2;
247
+ case "Offset": return 2;
248
+ case "Address": return 3;
249
+ }
250
+ return obj.length;
251
+ }
252
+ return 0;
253
+ }
254
+ };
255
+ /**
256
+ * BlockReader specific constants
257
+ */
258
+ var BlockReaderConstants = class {
259
+ static REF_SEARCH_MAX_RANGE = 416;
260
+ static BANK_MASK_CHECK = 64;
261
+ static BYTE_DELIMITER_THRESHOLD = 256;
262
+ static BANK_HIGH_MEMORY_1 = 126;
263
+ static BANK_HIGH_MEMORY_2 = 127;
264
+ static POINTER_CHARACTERS = ["&", "@"];
265
+ static WIDE_STRING_TYPE = "WideString";
266
+ static BINARY_TYPE = "Binary";
267
+ static CODE_TYPE = "Code";
268
+ static LOCATION_FORMAT = "loc_{0:X6}";
269
+ static TYPE_NAME_FORMAT = "{0}_{1:X6}";
270
+ static OFFSET_FORMAT = "+{0:X}";
271
+ static MARKER_FORMAT = "+M";
272
+ static NEGATIVE_OFFSET_FORMAT = "-{0:X}";
273
+ static NEGATIVE_MARKER_FORMAT = "-M";
274
+ };
275
+
134
276
  //#endregion
135
277
  //#region src/types/addressing.ts
136
278
  /**
@@ -144,6 +286,7 @@ let AddressType = /* @__PURE__ */ function(AddressType) {
144
286
  AddressType["Address"] = "Address";
145
287
  AddressType["WBank"] = "WBank";
146
288
  AddressType["Relative"] = "Relative";
289
+ AddressType["WRelative"] = "WRelative";
147
290
  AddressType["Location"] = "Location";
148
291
  return AddressType;
149
292
  }({});
@@ -165,6 +308,11 @@ let MemoryMapMode = /* @__PURE__ */ function(MemoryMapMode) {
165
308
  MemoryMapMode["ExHi"] = "ExHi";
166
309
  return MemoryMapMode;
167
310
  }({});
311
+ let CpuMode = /* @__PURE__ */ function(CpuMode) {
312
+ CpuMode["Slow"] = "Slow";
313
+ CpuMode["Fast"] = "Fast";
314
+ return CpuMode;
315
+ }({});
168
316
  /**
169
317
  * SNES address structure
170
318
  * Converted from GaiaLib/Types/Address.cs
@@ -193,14 +341,19 @@ var Address = class Address {
193
341
  get isCodeBank() {
194
342
  return (this.bank & Address.DATA_BANK_FLAG) === 0;
195
343
  }
196
- toInt() {
344
+ toLocation() {
197
345
  if (this.mode === MemoryMapMode.Lo) return (this.bank & 63) << 15 | this.offset & 32767;
198
346
  if (this.mode === MemoryMapMode.Hi || this.bank & Address.FAST_BANK_FLAG) return (this.bank & 63) << 16 | this.offset & 65535;
199
347
  return this.bank === 62 ? this.offset + 20774912 : this.bank === 63 ? this.offset + 20840448 : ((this.bank & 63) << 16 | this.offset & 65535) + 16777216;
200
348
  }
201
- static fromInt(value, mode) {
202
- if (mode === MemoryMapMode.Lo) return new Address(value >> 15 & 255, value & 32767 | 32768, mode);
203
- if (mode === MemoryMapMode.Hi || value & Address.FAST_BANK_FLAG) return new Address(value >> 16 & 255, value & 65535, mode);
349
+ static fromLocation(value, mode, cpuMode) {
350
+ let bankFlag = cpuMode === CpuMode.Fast ? Address.FAST_BANK_FLAG : 0;
351
+ if (mode === MemoryMapMode.Lo) return new Address(value >> 15 & 127 | bankFlag, value & 32767 | 32768, mode);
352
+ if (mode === MemoryMapMode.Hi) {
353
+ if ((value & RomProcessingConstants.PAGE_SIZE) === 0) bankFlag = 192;
354
+ return new Address(value >> 16 & 127 | bankFlag, value & 65535, mode);
355
+ }
356
+ if (value & Address.FAST_BANK_FLAG) return new Address(value >> 16 & 255, value & 65535, mode);
204
357
  if (value >= 20774912 && value < 20840448) return new Address(62, value - 20774912, mode);
205
358
  if (value >= 20840448 && value < 20905984) return new Address(63, value - 20840448, mode);
206
359
  return new Address(value >> 16 & 127, value & 65535, mode);
@@ -212,8 +365,11 @@ var Address = class Address {
212
365
  if (value >= 20840448 && value < 20905984) return 63;
213
366
  return value >> 16 & 127;
214
367
  }
368
+ toInt() {
369
+ return this.bank << 16 | this.offset;
370
+ }
215
371
  toString() {
216
- return (this.bank << 16 | this.offset).toString(16).toUpperCase().padStart(6, "0");
372
+ return this.toInt().toString(16).toUpperCase().padStart(6, "0");
217
373
  }
218
374
  static typeFromCode(code) {
219
375
  switch (code) {
@@ -346,12 +502,12 @@ let BinType = /* @__PURE__ */ function(BinType) {
346
502
  BinType["Palette"] = "Palette";
347
503
  BinType["Sound"] = "Sound";
348
504
  BinType["Music"] = "Music";
349
- BinType["Unknown"] = "Unknown";
350
505
  BinType["Meta17"] = "Meta17";
351
506
  BinType["Spritemap"] = "Spritemap";
352
507
  BinType["Assembly"] = "Assembly";
353
508
  BinType["Patch"] = "Patch";
354
509
  BinType["Transform"] = "Transform";
510
+ BinType["Binary"] = "Binary";
355
511
  return BinType;
356
512
  }({});
357
513
 
@@ -649,148 +805,6 @@ var CompressionRegistry = class {
649
805
  }
650
806
  };
651
807
 
652
- //#endregion
653
- //#region src/types/constants.ts
654
- /**
655
- * ROM processing constants
656
- * Converted from GaiaLib/Types/RomProcessingConstants.cs
657
- */
658
- var RomProcessingConstants = class RomProcessingConstants {
659
- static PAGE_SIZE = 32768;
660
- static SNES_HEADER_SIZE = 80;
661
- static DICTIONARIES = ["dictionary_01EBA8", "dictionary_01F54D"];
662
- static DICT_COMMANDS = [214, 215];
663
- static END_CHARS = [
664
- 192,
665
- 202,
666
- 209
667
- ];
668
- static WHITESPACE = [" ", " "];
669
- static OPERATORS = ["-", "+"];
670
- static COMMA_SPACE = [
671
- ",",
672
- " ",
673
- " "
674
- ];
675
- static ADDRESS_SPACE = [
676
- "@",
677
- "&",
678
- "^",
679
- "#",
680
- "$",
681
- "%",
682
- "*"
683
- ];
684
- static SYMBOL_SPACE = [
685
- ",",
686
- " ",
687
- " ",
688
- "<",
689
- ">",
690
- "(",
691
- ")",
692
- ":",
693
- "[",
694
- "]",
695
- "{",
696
- "}",
697
- "`",
698
- "~",
699
- "|"
700
- ];
701
- static LABEL_SPACE = [
702
- "[",
703
- "{",
704
- "#",
705
- "`",
706
- "~",
707
- "|",
708
- ":"
709
- ];
710
- static OBJECT_SPACE = ["<", "["];
711
- static COP_SPLIT_CHARS = [
712
- " ",
713
- " ",
714
- ",",
715
- "(",
716
- ")",
717
- "[",
718
- "]",
719
- "$",
720
- "#"
721
- ];
722
- static WHITESPACE_REGEX = /[ \t]/;
723
- static COMMA_SPACE_REGEX = /[, \t]/;
724
- static SYMBOL_SPACE_REGEX = /[, \t<>()\:\[\]{}`~|]/;
725
- static LABEL_SPACE_REGEX = /[\[{#`~|:]/;
726
- static OBJECT_SPACE_REGEX = /[<\[]/;
727
- static ADDRESS_SPACE_REGEX = /[@&^#$%*]/;
728
- static COP_SPLIT_REGEX = /[ \t,()[\]$#]/;
729
- static COMMA_SPACE_TRIM_REGEX = /^[, \t]+|[, \t]+$/g;
730
- /**
731
- * Gets the size of an object for processing purposes
732
- * @param obj The object to get the size for
733
- * @returns Size in bytes
734
- * @throws Error when unable to determine size
735
- */
736
- static getSize(obj) {
737
- if (obj === void 0 || obj === null) return 0;
738
- switch (typeof obj) {
739
- case "object":
740
- if (Array.isArray(obj)) return obj.reduce((acc, x) => acc + RomProcessingConstants.getSize(x), 0);
741
- if ("size" in obj) return obj.size;
742
- if ("length" in obj) return obj.length;
743
- if ("_tag" in obj) switch (obj._tag) {
744
- case "Byte": return 1;
745
- case "Word": return 2;
746
- }
747
- break;
748
- case "number":
749
- if (obj <= 255) return 1;
750
- if (obj <= 65535) return 2;
751
- if (obj <= 16777215) return 3;
752
- return 4;
753
- case "string":
754
- if (!obj.length) return 0;
755
- switch (obj[0]) {
756
- case "@":
757
- case "%": return 3;
758
- case "*":
759
- case "&": return 2;
760
- case "^": return 1;
761
- }
762
- switch (obj) {
763
- case "Byte": return 1;
764
- case "Word": return 2;
765
- case "Offset": return 2;
766
- case "Address": return 3;
767
- }
768
- return obj.length;
769
- }
770
- return 0;
771
- }
772
- };
773
- /**
774
- * BlockReader specific constants
775
- */
776
- var BlockReaderConstants = class {
777
- static REF_SEARCH_MAX_RANGE = 416;
778
- static BANK_MASK_CHECK = 64;
779
- static BYTE_DELIMITER_THRESHOLD = 256;
780
- static BANK_HIGH_MEMORY_1 = 126;
781
- static BANK_HIGH_MEMORY_2 = 127;
782
- static POINTER_CHARACTERS = ["&", "@"];
783
- static WIDE_STRING_TYPE = "WideString";
784
- static BINARY_TYPE = "Binary";
785
- static CODE_TYPE = "Code";
786
- static LOCATION_FORMAT = "loc_{0:X6}";
787
- static TYPE_NAME_FORMAT = "{0}_{1:X6}";
788
- static OFFSET_FORMAT = "+{0:X}";
789
- static MARKER_FORMAT = "+M";
790
- static NEGATIVE_OFFSET_FORMAT = "-{0:X}";
791
- static NEGATIVE_MARKER_FORMAT = "-M";
792
- };
793
-
794
808
  //#endregion
795
809
  //#region src/types/files.ts
796
810
  /**
@@ -850,11 +864,11 @@ function createChunkFileFromDbFile(rom, compression, dbFile, fileType) {
850
864
  enrichWithRawDataFromDbFile(rom, chunkFile, compression, dbFile, fileType);
851
865
  return chunkFile;
852
866
  }
853
- function createChunkFileFromDbBlock(block, fileType) {
867
+ function createChunkFileFromDbBlock(block, fileType, memoryMode) {
854
868
  const chunkFile = new ChunkFile(block.name, 0, 0, fileType);
855
869
  chunkFile.group = block.group;
856
870
  chunkFile.scene = block.scene;
857
- enrichWithPartsFromDbBlock(chunkFile, block);
871
+ enrichWithPartsFromDbBlock(chunkFile, block, memoryMode);
858
872
  return chunkFile;
859
873
  }
860
874
  function combineHeader(data, position, length, header, type) {
@@ -899,12 +913,12 @@ function enrichWithRawDataFromDbFile(rom, chunkFile, compression, dbFile, fileTy
899
913
  /**
900
914
  * Enriches ChunkFile with AsmBlock parts from DbBlock
901
915
  */
902
- function enrichWithPartsFromDbBlock(chunkFile, block) {
916
+ function enrichWithPartsFromDbBlock(chunkFile, block, memoryMode) {
903
917
  chunkFile.parts = [];
904
918
  chunkFile.size = 0;
905
919
  if (!block.parts?.length) throw new Error(`Block ${block.name} has no parts`);
906
920
  chunkFile.location = block.parts[0].start;
907
- chunkFile.bank = block.movable ? void 0 : chunkFile.location >> 16;
921
+ chunkFile.bank = block.movable ? void 0 : memoryMode === MemoryMapMode.Lo ? chunkFile.location >> 15 : chunkFile.location >> 16;
908
922
  chunkFile.transforms = block.transforms;
909
923
  chunkFile.postProcess = block.postProcess;
910
924
  for (const part of block.parts) {
@@ -936,8 +950,12 @@ var ChunkFileUtils = class {
936
950
  */
937
951
  static calculateSize(chunkFile) {
938
952
  if (!chunkFile.parts) {
939
- if (typeof chunkFile.compressed === "boolean" || chunkFile.type.header === -2) chunkFile.size += 2;
940
- return chunkFile.size;
953
+ let size$1 = chunkFile.rawData?.length ?? chunkFile.size;
954
+ if (chunkFile.type.header === -2 || chunkFile.compressed === false) {
955
+ size$1 += 2;
956
+ chunkFile.size = size$1;
957
+ }
958
+ return size$1;
941
959
  }
942
960
  let size = 0;
943
961
  for (let x = 0; x < chunkFile.parts.length; x++) {
@@ -1326,7 +1344,13 @@ var CopCommandProcessor = class {
1326
1344
  * Parses a COP command based on its definition
1327
1345
  */
1328
1346
  parseCopCommand(copDef, operands) {
1329
- for (const partStr of copDef.parts) {
1347
+ for (let partStr of copDef.parts) {
1348
+ let bank = null;
1349
+ const bankHintIx = partStr.indexOf("$");
1350
+ if (bankHintIx > 0) {
1351
+ bank = parseInt(partStr.substring(bankHintIx + 1), 16);
1352
+ partStr = partStr.substring(0, bankHintIx);
1353
+ }
1330
1354
  const addrType = Address.typeFromCode(partStr[0]);
1331
1355
  const isPtr = addrType !== AddressType.Unknown;
1332
1356
  const referenceType = isPtr ? partStr.substring(1) : this._blockReader._currentAsmBlock.structName ?? "Binary";
@@ -1337,7 +1361,7 @@ var CopCommandProcessor = class {
1337
1361
  if (label) {
1338
1362
  this._romDataReader.position += this.getMemberTypeSize(memberType);
1339
1363
  operands.push(label);
1340
- } else operands.push(this.readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType));
1364
+ } else operands.push(this.readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType, bank));
1341
1365
  }
1342
1366
  }
1343
1367
  tryParseMemberType(memberTypeName) {
@@ -1354,11 +1378,11 @@ var CopCommandProcessor = class {
1354
1378
  default: throw new Error("Unsupported COP member type");
1355
1379
  }
1356
1380
  }
1357
- readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType) {
1381
+ readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType, bank) {
1358
1382
  switch (memberType) {
1359
1383
  case MemberType.Byte: return new Byte(this._romDataReader.readByte());
1360
1384
  case MemberType.Word: return new Word(this._romDataReader.readUShort());
1361
- case MemberType.Offset: return this.createCopLocation(this._romDataReader.readUShort(), null, partStr, isPtr, referenceType, addrType);
1385
+ case MemberType.Offset: return this.createCopLocation(this._romDataReader.readUShort(), bank, partStr, isPtr, referenceType, addrType);
1362
1386
  case MemberType.Address: return this.createCopLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), partStr, isPtr, referenceType, addrType);
1363
1387
  default: throw new Error("Unsupported COP member type");
1364
1388
  }
@@ -1367,7 +1391,7 @@ var CopCommandProcessor = class {
1367
1391
  if (bank === null && offset === 0) return new Word(offset);
1368
1392
  const addr = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), offset, this._blockReader._root.config.memoryMode);
1369
1393
  if (addr.isROM) {
1370
- const location = addr.toInt();
1394
+ const location = addr.toLocation();
1371
1395
  if (partStr !== "Address" && isPtr && !this._blockReader._root.rewrites[location]) this._blockReader.noteType(location, otherStr, true);
1372
1396
  if (type === AddressType.Unknown) type = this.tryParseAddressType(partStr) ?? AddressType.Unknown;
1373
1397
  return new LocationWrapper(location, type);
@@ -1501,7 +1525,7 @@ var AddressingModeHandler = class {
1501
1525
  const refLoc = this._dataReader.readUShort();
1502
1526
  const address = new Address(this._dataReader.readByte(), refLoc, reg.mode);
1503
1527
  if (address.isROM) {
1504
- const wrapper = new LocationWrapper(address.toInt(), AddressType.Address);
1528
+ const wrapper = new LocationWrapper(address.toLocation(), AddressType.Address);
1505
1529
  if (this.isJumpInstruction(mnemonic)) this._blockReader.noteType(wrapper.location, "Code", false, reg);
1506
1530
  operands.push(wrapper);
1507
1531
  } else operands.push(address);
@@ -1517,7 +1541,7 @@ var AddressingModeHandler = class {
1517
1541
  handlePCRelativeMode(operands, nextAddress, reg, isLong) {
1518
1542
  const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
1519
1543
  this._blockReader.updateRegisterState(relative, reg);
1520
- operands.push(new LocationWrapper(relative, AddressType.Relative));
1544
+ operands.push(new LocationWrapper(relative, isLong ? AddressType.WRelative : AddressType.Relative));
1521
1545
  }
1522
1546
  handleStackRelativeMode(operands) {
1523
1547
  operands.push(new Byte(this._dataReader.readByte()));
@@ -1542,7 +1566,7 @@ var AddressingModeHandler = class {
1542
1566
  const isJump = isPush || isIndexedIndirect || this.isJumpInstruction(mnemonic);
1543
1567
  const addr = new Address(xBank1 ?? (isJump ? Address.resolveBank(this._dataReader.position, registers.mode) : dataBank) ?? 129, refLoc, registers.mode);
1544
1568
  if (addr.isROM) {
1545
- const wrapper = new LocationWrapper(addr.toInt(), AddressType.Offset);
1569
+ const wrapper = new LocationWrapper(addr.toLocation(), AddressType.Offset);
1546
1570
  if (isJump) {
1547
1571
  const type = isIndexedIndirect ? "&Code" : "Code";
1548
1572
  const name = this._blockReader.noteType(wrapper.location, type, isPush, registers);
@@ -1607,16 +1631,7 @@ var TransformProcessor = class TransformProcessor {
1607
1631
  }
1608
1632
  applyDefaultTransform(operandIndex, operands, bank) {
1609
1633
  const opnd = operands[operandIndex];
1610
- const adrs = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), opnd && "value" in opnd ? opnd["value"] : opnd, this._blockReader._root.config.memoryMode);
1611
- const loc = adrs.toInt();
1612
- const nameResult = this._referenceManager.tryGetName(loc);
1613
- let referenceName;
1614
- if (!nameResult.found) {
1615
- referenceName = `loc_${adrs.toString()}`;
1616
- this._referenceManager.tryAddName(loc, referenceName);
1617
- } else referenceName = nameResult.referenceName;
1618
- this._blockReader.resolveInclude(loc, false);
1619
- operands[operandIndex] = `&${referenceName}`;
1634
+ operands[operandIndex] = new LocationWrapper(new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), opnd && "value" in opnd ? opnd["value"] : opnd, this._blockReader._root.config.memoryMode).toLocation(), AddressType.Offset);
1620
1635
  }
1621
1636
  cleanTransformName(transform) {
1622
1637
  let name = transform;
@@ -1790,7 +1805,6 @@ var DbStringDictionary = class {
1790
1805
  range;
1791
1806
  command;
1792
1807
  name;
1793
- suffix;
1794
1808
  entries;
1795
1809
  constructor(data) {
1796
1810
  this.base = data.base ?? void 0;
@@ -1798,7 +1812,6 @@ var DbStringDictionary = class {
1798
1812
  this.command = data.command ?? void 0;
1799
1813
  this.name = data.name ?? "";
1800
1814
  this.entries = data.entries ?? void 0;
1801
- this.suffix = data.suffix ?? "";
1802
1815
  if (this.base === void 0 && this.command === void 0) throw new Error("Base or command is required");
1803
1816
  if (!this.name) throw new Error("Name is required");
1804
1817
  if (!this.entries) throw new Error("Entries is required");
@@ -1820,6 +1833,7 @@ var DbStringType = class {
1820
1833
  layers;
1821
1834
  greedyTerminator;
1822
1835
  dictionaries;
1836
+ dictionaryLookup;
1823
1837
  constructor(data) {
1824
1838
  this.name = data.name ?? "";
1825
1839
  this.delimiter = data.delimiter ?? "";
@@ -1834,6 +1848,10 @@ var DbStringType = class {
1834
1848
  acc[x.id] = x;
1835
1849
  return acc;
1836
1850
  }, {});
1851
+ this.dictionaryLookup = Object.values(this.dictionaries).flatMap((y) => y.entries.map((z, ix) => ({
1852
+ text: z,
1853
+ id: y.command << 8 | y.base + ix
1854
+ }))).sort((a, b) => b.text.length - a.text.length);
1837
1855
  if (!this.name) throw new Error("Name is required");
1838
1856
  if (!this.delimiter) throw new Error("Delimiter is required");
1839
1857
  if (!this.terminator && this.terminator !== 0) throw new Error("Terminator is required");
@@ -2624,13 +2642,13 @@ var QuintetLZ = class QuintetLZ {
2624
2642
  * @param srcData Source data to compress
2625
2643
  * @returns Compressed data
2626
2644
  */
2627
- compact(srcData) {
2645
+ compact(srcData, srcPosition, srcLen) {
2628
2646
  const dictionary = new Uint8Array(QuintetLZ.DICTIONARY_SIZE);
2629
2647
  dictionary.fill(QuintetLZ.DICTIONARY_INIT);
2630
2648
  let offset = QuintetLZ.DICTIONARY_OFFSET;
2631
- let srcIx = 0;
2649
+ let srcIx = srcPosition ?? 0;
2632
2650
  let dstIx = 0;
2633
- const srcLen = srcData.length;
2651
+ if (!srcLen) srcLen = srcData.length - srcIx;
2634
2652
  const outputBuffer = new Uint8Array(srcLen * 2);
2635
2653
  outputBuffer[dstIx++] = srcLen & 255;
2636
2654
  outputBuffer[dstIx++] = srcLen >> 8 & 255;
@@ -2897,7 +2915,7 @@ var BlockWriter = class {
2897
2915
  if (typeof obj === "number") return ObjectType.Number;
2898
2916
  return ObjectType.String;
2899
2917
  }
2900
- writeObject(obj, depth, isBranch = false) {
2918
+ writeObject(obj, depth, isBranch = false, isArray = false) {
2901
2919
  const lines = [];
2902
2920
  const objType = this.getObjectType(obj);
2903
2921
  let objLines;
@@ -2915,7 +2933,7 @@ var BlockWriter = class {
2915
2933
  objLines = [this._blockReader.resolveName(obj.location, obj.type, isBranch)];
2916
2934
  break;
2917
2935
  case ObjectType.Address:
2918
- objLines = [`$${obj.toString()}`];
2936
+ objLines = isArray ? [`$#${obj.offset.toString(16).toUpperCase().padStart(4, "0")}`] : [`$${obj.toString()}`];
2919
2937
  break;
2920
2938
  case ObjectType.ByteArray:
2921
2939
  objLines = [`#${Array.from(obj).map((b) => b.toString(16).toUpperCase().padStart(2, "0")).join("")}`];
@@ -3028,13 +3046,13 @@ var BlockWriter = class {
3028
3046
  const hexStr = str.substring(ix + 1, ix + 7);
3029
3047
  const rawAddr = parseInt(hexStr, 16);
3030
3048
  const adrs = new Address(rawAddr >> 16 & 255, rawAddr & 65535, this._blockReader._root.config.memoryMode);
3031
- if (adrs.isROM) {
3032
- const addressType = char === "^" ? AddressType.Offset : AddressType.Address;
3033
- const location = adrs.toInt();
3034
- const name = this._blockReader.resolveName(location, addressType, false);
3035
- str = str.replace(str.substring(ix, ix + 7), name);
3036
- }
3037
- ix = str.indexOf(char, ix + 7);
3049
+ const addressType = char === "^" ? AddressType.Offset : AddressType.Address;
3050
+ let name = "";
3051
+ if (adrs.isROM) name = this._blockReader.resolveName(adrs.toLocation(), addressType, false);
3052
+ else if (addressType === AddressType.Offset) name = adrs.offset.toString(16).toUpperCase().padStart(4, "0");
3053
+ else name = adrs.toString();
3054
+ str = str.replace(str.substring(ix, ix + 7), name);
3055
+ ix = str.indexOf(char);
3038
3056
  }
3039
3057
  }
3040
3058
  const refChar = stringObj.type.delimiter;
@@ -3085,7 +3103,7 @@ var BlockWriter = class {
3085
3103
  lines.push("[");
3086
3104
  this._isInline = false;
3087
3105
  for (let i = 0; i < arr.length; i++) {
3088
- const objLines = this.writeObject(arr[i], depth + 1);
3106
+ const objLines = this.writeObject(arr[i], depth + 1, false, true);
3089
3107
  for (const line of objLines) lines.push(indent + " " + line);
3090
3108
  lines[lines.length - 1] += ` ;${i.toString(16).toUpperCase().padStart(2, "0")}`;
3091
3109
  }
@@ -3297,19 +3315,19 @@ var TypeParser = class {
3297
3315
  }
3298
3316
  const stringType = this._stringTypes[fixedTypeName];
3299
3317
  if (stringType) return this._stringReader.parseString(stringType, fixedSize);
3300
- const mType = this.tryParseMemberType(typeName);
3318
+ const mType = this.tryParseMemberType(fixedTypeName);
3301
3319
  if (mType !== null) switch (mType) {
3302
3320
  case MemberType.Byte: return new Byte(this._romDataReader.readByte());
3303
3321
  case MemberType.Word: return new Word(this.parseWordSafe());
3304
3322
  case MemberType.Offset: return this.parseLocation(this._romDataReader.readUShort(), bank, null, AddressType.Offset);
3305
3323
  case MemberType.Address: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Address);
3306
3324
  case MemberType.Location: return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, AddressType.Location);
3307
- case MemberType.Binary: return this.parseBinary();
3325
+ case MemberType.Binary: return this.parseBinary(fixedSize);
3308
3326
  case MemberType.Code: return this.parseCode(reg);
3309
3327
  default: throw new Error("Invalid member type");
3310
3328
  }
3311
- const parentType = this._blockReader._root.structs[typeName];
3312
- if (!parentType) throw new Error(`Unknown type: ${typeName}`);
3329
+ const parentType = this._blockReader._root.structs[fixedTypeName];
3330
+ if (!parentType) throw new Error(`Unknown type: ${fixedTypeName}`);
3313
3331
  const delimiter = parentType.delimiter;
3314
3332
  const discOffset = parentType.discriminator;
3315
3333
  const objects = [];
@@ -3321,7 +3339,7 @@ var TypeParser = class {
3321
3339
  const discPosition = this._romDataReader.position + discOffset;
3322
3340
  const desc = this._romDataReader.romData[discPosition];
3323
3341
  if (discOffset === 0) this._romDataReader.position++;
3324
- targetType = Object.values(this._blockReader._root.structs).find((x) => x.parent === typeName && x.discriminator === desc) || parentType;
3342
+ targetType = Object.values(this._blockReader._root.structs).find((x) => x.parent === fixedTypeName && x.discriminator === desc) || parentType;
3325
3343
  }
3326
3344
  const types = targetType.types;
3327
3345
  if (types && types.length > 0) {
@@ -3354,12 +3372,13 @@ var TypeParser = class {
3354
3372
  parseWordSafe() {
3355
3373
  return this._referenceManager.containsStruct(this._romDataReader.position + 1) ? this._romDataReader.readByte() : this._romDataReader.readUShort();
3356
3374
  }
3357
- parseBinary() {
3375
+ parseBinary(size) {
3358
3376
  const startPosition = this._romDataReader.position;
3359
- do
3377
+ let len = 0;
3378
+ do {
3360
3379
  this._romDataReader.position++;
3361
- while (this._blockReader.partCanContinue());
3362
- const len = this._romDataReader.position - startPosition;
3380
+ len++;
3381
+ } while (len !== size && this._blockReader.partCanContinue());
3363
3382
  const outBuffer = new Uint8Array(len);
3364
3383
  for (let i = 0; i < len; i++) outBuffer[i] = this._romDataReader.romData[startPosition + i];
3365
3384
  return outBuffer;
@@ -3372,7 +3391,7 @@ var TypeParser = class {
3372
3391
  else {
3373
3392
  adrs = new Address(bank ?? Address.resolveBank(this._romDataReader.position, this._blockReader._root.config.memoryMode), offset, this._blockReader._root.config.memoryMode);
3374
3393
  if (!adrs.isROM) return adrs;
3375
- loc = adrs.toInt();
3394
+ loc = adrs.toLocation();
3376
3395
  }
3377
3396
  if (typeName && !this._blockReader._root.rewrites[loc]) {
3378
3397
  this._referenceManager.tryAddStruct(loc, typeName);
@@ -3617,26 +3636,40 @@ var BlockReader = class {
3617
3636
  */
3618
3637
  createChunkFilesFromSfx() {
3619
3638
  let pos = this._root.config.sfxLocation;
3639
+ let offset = 0;
3620
3640
  const count = this._root.config.sfxCount;
3621
3641
  const romData = this._romDataReader.romData;
3622
3642
  const fileType = Object.values(this._root.fileTypes).find((x) => x.type === BinType.Sound);
3623
- const getSize = () => romData[pos++] | romData[pos++] << 8;
3624
- for (let i = 0; i < count; i++) {
3625
- const size = getSize();
3626
- const startPos = pos;
3627
- let remaining = size;
3628
- let end = pos + remaining;
3643
+ const getByte = this._root.config.sfxType === "Striped" ? () => {
3644
+ const byte = romData[pos + offset++];
3645
+ if (offset & RomProcessingConstants.PAGE_SIZE) offset += RomProcessingConstants.PAGE_SIZE;
3646
+ return byte;
3647
+ } : () => romData[pos + offset++];
3648
+ const getSize = () => getByte() | getByte() << 8;
3649
+ const getBytes = this._root.config.sfxType === "Striped" ? (size) => {
3650
+ let end = offset + size;
3629
3651
  let data;
3630
3652
  if (end & RomProcessingConstants.PAGE_SIZE) {
3631
3653
  const endLen = end & 32767;
3632
- remaining -= endLen;
3633
- data = romData.slice(pos, pos + remaining);
3634
- pos += remaining + RomProcessingConstants.PAGE_SIZE;
3635
- end = pos + endLen;
3636
- const data2 = romData.slice(pos, end);
3654
+ size -= endLen;
3655
+ data = romData.slice(pos + offset, pos + offset + size);
3656
+ offset += size + RomProcessingConstants.PAGE_SIZE;
3657
+ end = offset + endLen;
3658
+ const data2 = romData.slice(pos + offset, pos + end);
3637
3659
  data = new Uint8Array([...data, ...data2]);
3638
- } else data = romData.slice(pos, end);
3639
- pos = end;
3660
+ } else data = romData.slice(pos + offset, pos + end);
3661
+ offset = end;
3662
+ return data;
3663
+ } : (size) => {
3664
+ const end = offset + size;
3665
+ const data = romData.slice(pos + offset, pos + end);
3666
+ offset = end;
3667
+ return data;
3668
+ };
3669
+ for (let i = 0; i < count; i++) {
3670
+ const size = getSize();
3671
+ const startPos = pos + offset;
3672
+ const data = getBytes(size);
3640
3673
  const chunk = new ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, fileType);
3641
3674
  chunk.rawData = data;
3642
3675
  chunk.group = "sfx";
@@ -3657,7 +3690,7 @@ var BlockReader = class {
3657
3690
  */
3658
3691
  createChunkFilesFromDbBlocks() {
3659
3692
  for (const block of this._root.blocks) {
3660
- const chunkFile = createChunkFileFromDbBlock(block, Object.values(this._root.fileTypes).find((x) => x.isBlock));
3693
+ const chunkFile = createChunkFileFromDbBlock(block, Object.values(this._root.fileTypes).find((x) => x.isBlock), this._root.config.memoryMode);
3661
3694
  this._enrichedChunks.push(chunkFile);
3662
3695
  }
3663
3696
  }
@@ -3763,8 +3796,18 @@ var RomLayout = class RomLayout {
3763
3796
  bestDepth = 0;
3764
3797
  bestOffset = 0;
3765
3798
  bestRemain = 0;
3766
- constructor(files) {
3767
- this.unmatchedFiles = Array.from(files).filter((x) => (x.size || 0) > 0).sort((a, b) => {
3799
+ root;
3800
+ config;
3801
+ sfxPackType;
3802
+ sfxFiles = [];
3803
+ constructor(files, root) {
3804
+ this.root = root;
3805
+ this.config = root.config;
3806
+ this.sfxPackType = root.config.sfxPack ?? root.config.sfxType;
3807
+ this.sfxFiles = this.sfxPackType !== "Individual" ? files.filter((x) => x.type.type === "Sound").sort((a, b) => {
3808
+ 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);
3809
+ }) : [];
3810
+ this.unmatchedFiles = files.filter((x) => (x.size || 0) > 0).filter((x) => this.sfxPackType === "Individual" || x.type.type !== "Sound").sort((a, b) => {
3768
3811
  const aAsm = a.parts ? 0 : 1;
3769
3812
  const bAsm = b.parts ? 0 : 1;
3770
3813
  if (aAsm !== bAsm) return aAsm - bAsm;
@@ -3774,16 +3817,67 @@ var RomLayout = class RomLayout {
3774
3817
  });
3775
3818
  }
3776
3819
  organize() {
3777
- for (let page = 0; page < 128; page++) {
3820
+ let stripeSfx = false;
3821
+ let page;
3822
+ const maxPages = this.config.memoryMode === MemoryMapMode.Hi ? 128 : this.config.memoryMode === MemoryMapMode.ExHi ? 256 : 64;
3823
+ for (page = 0; page < maxPages; page++) {
3778
3824
  if (this.unmatchedFiles.length === 0) break;
3825
+ let start = page << 15;
3779
3826
  let remain = RomProcessingConstants.PAGE_SIZE;
3780
- if (page === 1) remain -= RomProcessingConstants.SNES_HEADER_SIZE;
3781
- this.currentUpper = (page & 1) !== 0;
3782
- this.currentBank = page >> 1;
3827
+ if (start <= this.config.sfxLocation && start + remain > this.config.sfxLocation) {
3828
+ this.config.sfxLocation - start;
3829
+ if (this.sfxPackType === "Sequential") {
3830
+ let offset = 0;
3831
+ for (const file of this.sfxFiles) {
3832
+ file.location = start + offset;
3833
+ console.log(` ${file.location.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
3834
+ offset += file.size;
3835
+ if (offset & RomProcessingConstants.PAGE_SIZE) {
3836
+ offset &= 32767;
3837
+ console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with SFX files 0 remaining`);
3838
+ start += RomProcessingConstants.PAGE_SIZE;
3839
+ page++;
3840
+ }
3841
+ }
3842
+ console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with SFX files ${remain} remaining`);
3843
+ start += offset;
3844
+ remain = RomProcessingConstants.PAGE_SIZE - offset;
3845
+ } else if (this.sfxPackType === "Striped") stripeSfx = true;
3846
+ }
3847
+ if (page === 0 && this.config.memoryMode === MemoryMapMode.Lo || page === 1 && this.config.memoryMode === MemoryMapMode.Hi) remain -= RomProcessingConstants.SNES_HEADER_SIZE;
3848
+ if (stripeSfx) {
3849
+ let offset = 0;
3850
+ while (remain > 0 && this.sfxFiles.length) {
3851
+ const file = this.sfxFiles.shift();
3852
+ file.location = start + offset;
3853
+ console.log(` ${file.location.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
3854
+ offset += file.size;
3855
+ if (offset & RomProcessingConstants.PAGE_SIZE) {
3856
+ offset &= 32767;
3857
+ console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with SFX files 0 remaining`);
3858
+ start += RomProcessingConstants.PAGE_SIZE;
3859
+ page++;
3860
+ if (offset) {
3861
+ const newFile = new ChunkFile(file.name + "_2", file.size, file.location, Object.values(this.root.fileTypes).find((x) => x.type === BinType.Binary));
3862
+ const end = file.rawData.length - offset;
3863
+ newFile.rawData = file.rawData.slice(end);
3864
+ newFile.size = newFile.rawData.length;
3865
+ this.sfxFiles.unshift(newFile);
3866
+ file.rawData = file.rawData.slice(0, end);
3867
+ file.size -= offset;
3868
+ }
3869
+ remain = 0;
3870
+ break;
3871
+ } else remain -= file.size;
3872
+ }
3873
+ start += offset;
3874
+ if (!this.sfxFiles.length) stripeSfx = false;
3875
+ }
3876
+ this.currentUpper = this.config.memoryMode === MemoryMapMode.Lo || (page & 1) !== 0;
3877
+ this.currentBank = this.config.memoryMode === MemoryMapMode.Lo ? page : page >> 1;
3783
3878
  this.bestDepth = 0;
3784
3879
  this.bestRemain = remain;
3785
3880
  this.bestOffset = 0;
3786
- const start = page << 15;
3787
3881
  this.testDepth(0, 0, remain, this.currentUpper);
3788
3882
  if (this.currentUpper) {
3789
3883
  this.bestOffset = this.bestDepth;
@@ -3794,7 +3888,7 @@ var RomLayout = class RomLayout {
3794
3888
  const file = this.unmatchedFiles[this.bestResult[i++]];
3795
3889
  file.location = position;
3796
3890
  console.log(` ${position.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
3797
- position += file.size || 0;
3891
+ position += file.size;
3798
3892
  }
3799
3893
  console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with ${this.bestDepth} files ${this.bestRemain} remaining`);
3800
3894
  this.commitPage();
@@ -3803,6 +3897,7 @@ var RomLayout = class RomLayout {
3803
3897
  const names = this.unmatchedFiles.map((x) => x.name).join("\r\n");
3804
3898
  throw new Error(`Unable to match ${this.unmatchedFiles.length} files\r\n${names}`);
3805
3899
  }
3900
+ return page;
3806
3901
  }
3807
3902
  testDepth(startIndex, depth, remain, asmMode) {
3808
3903
  for (let fileIndex = startIndex; fileIndex < this.unmatchedFiles.length; fileIndex++) {
@@ -3868,7 +3963,16 @@ var RomProcessor = class RomProcessor {
3868
3963
  const asmFiles = [];
3869
3964
  for (const file of allFiles) {
3870
3965
  if (file.type.type === "Patch") patches.push(file);
3871
- else if (file.type.type !== "Assembly") continue;
3966
+ else if (file.type.type !== "Assembly") {
3967
+ if (file.compressed === true) if (this.writer.root.config.uncompress) file.compressed = false;
3968
+ else {
3969
+ let newData = this.writer.root.compression.compact(file.rawData, file.type.header);
3970
+ if (file.type.header) newData = new Uint8Array([...file.rawData.slice(0, file.type.header), ...newData]);
3971
+ file.rawData = newData;
3972
+ file.size = newData.length;
3973
+ }
3974
+ continue;
3975
+ }
3872
3976
  asmFiles.push(file);
3873
3977
  if (!file.parts) {
3874
3978
  const { blocks, includes, reqBank } = new Assembler(this.writer.root, file.textData).parseAssembly();
@@ -3879,7 +3983,7 @@ var RomProcessor = class RomProcessor {
3879
3983
  }
3880
3984
  RomProcessor.applyPatches(asmFiles, patches);
3881
3985
  for (const file of allFiles) ChunkFileUtils.calculateSize(file);
3882
- new RomLayout(allFiles).organize();
3986
+ const pages = new RomLayout(allFiles, this.writer.root).organize();
3883
3987
  for (const file of asmFiles) ChunkFileUtils.rebase(file);
3884
3988
  for (const f of asmFiles) {
3885
3989
  const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
@@ -3889,6 +3993,7 @@ var RomProcessor = class RomProcessor {
3889
3993
  }
3890
3994
  const blockLookup = /* @__PURE__ */ new Map();
3891
3995
  for (const f of allFiles) blockLookup.set(f.name.toUpperCase(), f.location);
3996
+ this.writer.allocate(pages);
3892
3997
  for (const file of allFiles) await this.writer.writeFile(file, blockLookup);
3893
3998
  }
3894
3999
  static applyPatches(asmFiles, patches) {
@@ -3934,6 +4039,7 @@ var RomProcessor = class RomProcessor {
3934
4039
  var RomWriter = class {
3935
4040
  bpsPath;
3936
4041
  outBuffer;
4042
+ romSize;
3937
4043
  cartName;
3938
4044
  makerCode;
3939
4045
  root;
@@ -3941,7 +4047,6 @@ var RomWriter = class {
3941
4047
  this.cartName = cartName;
3942
4048
  this.makerCode = makerCode;
3943
4049
  this.root = root;
3944
- this.outBuffer = new Uint8Array(4194304);
3945
4050
  }
3946
4051
  async repack(files) {
3947
4052
  await new RomProcessor(this).repack(files);
@@ -3950,35 +4055,47 @@ var RomWriter = class {
3950
4055
  this.writeChecksum();
3951
4056
  return this.outBuffer;
3952
4057
  }
4058
+ allocate(pages) {
4059
+ const size = pages * RomProcessingConstants.PAGE_SIZE;
4060
+ let bits = 0;
4061
+ let target = 1024;
4062
+ while (target < size) {
4063
+ target <<= 1;
4064
+ bits++;
4065
+ }
4066
+ this.romSize = bits;
4067
+ this.outBuffer = new Uint8Array(target);
4068
+ }
3953
4069
  writeHeader() {
3954
4070
  const buf = this.outBuffer;
3955
- let pos = 65456;
4071
+ let pos = this.root.config.memoryMode === MemoryMapMode.Lo ? 32688 : 65456;
3956
4072
  this.writeAscii(this.makerCode.padEnd(6, " "), pos);
3957
4073
  pos += 6;
3958
4074
  for (let i = 0; i < 10; i++) buf[pos++] = 0;
3959
4075
  this.writeAscii(this.cartName.toUpperCase().padEnd(21, " "), pos);
3960
4076
  pos += 21;
3961
- buf[pos++] = 49;
3962
- buf[pos++] = 2;
3963
- buf[pos++] = 12;
3964
- buf[pos++] = 3;
4077
+ buf[pos++] = 32 | (this.root.config.cpuMode === CpuMode.Fast ? 16 : 0) | (this.root.config.memoryMode === MemoryMapMode.Hi ? 1 : this.root.config.memoryMode === MemoryMapMode.ExHi ? 5 : 0);
4078
+ buf[pos++] = this.root.config.chipset;
4079
+ buf[pos++] = this.romSize;
4080
+ buf[pos++] = this.root.config.ramSize;
3965
4081
  buf[pos++] = 1;
3966
4082
  buf[pos++] = 51;
3967
4083
  buf[pos++] = 0;
3968
4084
  }
3969
4085
  writeChecksum() {
3970
4086
  const buf = this.outBuffer;
3971
- buf[65500] = 255;
3972
- buf[65501] = 255;
3973
- buf[65502] = 0;
3974
- buf[65503] = 0;
4087
+ let pos = this.root.config.memoryMode === MemoryMapMode.Lo ? 32732 : 65500;
4088
+ buf[pos] = 255;
4089
+ buf[pos + 1] = 255;
4090
+ buf[pos + 2] = 0;
4091
+ buf[pos + 3] = 0;
3975
4092
  let sum = 0;
3976
4093
  for (let i = 0; i < buf.length; i++) sum += buf[i];
3977
- buf[65502] = sum & 255;
3978
- buf[65503] = sum >> 8 & 255;
4094
+ buf[pos + 2] = sum & 255;
4095
+ buf[pos + 3] = sum >> 8 & 255;
3979
4096
  const comp = ~sum;
3980
- buf[65500] = comp & 255;
3981
- buf[65501] = comp >> 8 & 255;
4097
+ buf[pos] = comp & 255;
4098
+ buf[pos + 1] = comp >> 8 & 255;
3982
4099
  }
3983
4100
  writeEntryPoints(asmFiles) {
3984
4101
  const buf = this.outBuffer;
@@ -3986,8 +4103,10 @@ var RomWriter = class {
3986
4103
  for (const ep of this.root.config.entryPoints) {
3987
4104
  const match = entryBlocks.find((b) => b.label === ep.name);
3988
4105
  if (match) {
3989
- buf[ep.location] = match.location & 255;
3990
- buf[ep.location + 1] = match.location >> 8 & 255;
4106
+ let location = match.location;
4107
+ if (this.root.config.memoryMode === MemoryMapMode.Lo) location += RomProcessingConstants.PAGE_SIZE;
4108
+ buf[ep.location] = location & 255;
4109
+ buf[ep.location + 1] = location >> 8 & 255;
3991
4110
  }
3992
4111
  }
3993
4112
  }
@@ -3999,19 +4118,21 @@ var RomWriter = class {
3999
4118
  const data = file.rawData;
4000
4119
  let remain = file.size;
4001
4120
  let srcPos = 0;
4002
- if (file.type.header === -2) {
4003
- remain -= 2;
4004
- buf[pos++] = remain & 255;
4005
- buf[pos++] = remain >> 8 & 255;
4006
- } else if (file.type.header > 0) {
4007
- remain -= file.type.header;
4008
- for (let i = 0; i < file.type.header; i++) buf[pos++] = data[srcPos++];
4121
+ if (file.compressed !== true) {
4122
+ if (file.type.header === -2) {
4123
+ remain -= 2;
4124
+ buf[pos++] = remain & 255;
4125
+ buf[pos++] = remain >> 8 & 255;
4126
+ } else if (file.type.header > 0) {
4127
+ remain -= file.type.header;
4128
+ for (let i = 0; i < file.type.header; i++) buf[pos++] = data[srcPos++];
4129
+ }
4009
4130
  }
4010
- if (file.compressed !== void 0) {
4131
+ if (file.compressed === false) {
4011
4132
  remain -= 2;
4012
- const inverse = 0 - remain & 65535;
4013
- buf[pos++] = inverse & 255;
4014
- buf[pos++] = inverse >> 8 & 255;
4133
+ const size = 0 - remain & 65535;
4134
+ buf[pos++] = size & 255;
4135
+ buf[pos++] = size >> 8 & 255;
4015
4136
  }
4016
4137
  while (remain > 0) {
4017
4138
  buf[pos++] = data[srcPos++];
@@ -4105,7 +4226,7 @@ var RomWriter = class {
4105
4226
  default: throw new Error(`Invalid operand '${label}'`);
4106
4227
  }
4107
4228
  }
4108
- let type = Address.typeFromCode(str[0]);
4229
+ let type = isRelative ? parentOp?.size === 3 ? AddressType.WRelative : AddressType.Relative : Address.typeFromCode(str[0]);
4109
4230
  if (type === AddressType.Unknown) type = parentOp?.size === 4 ? AddressType.Address : parentOp?.size === 2 ? AddressType.Unknown : AddressType.Offset;
4110
4231
  if (isRelative) {
4111
4232
  loc -= block.location + opos;
@@ -4119,22 +4240,24 @@ var RomWriter = class {
4119
4240
  break;
4120
4241
  } else markerOffset += RomProcessingConstants.getSize(part);
4121
4242
  }
4243
+ if (!isRelative && type !== AddressType.Location) loc = Address.fromLocation(loc, this.root.config.memoryMode, this.root.config.cpuMode).toInt();
4122
4244
  switch (type) {
4123
4245
  case AddressType.Offset:
4246
+ case AddressType.WRelative:
4124
4247
  currentObj = new Word(loc);
4125
4248
  continue;
4126
4249
  case AddressType.Bank:
4127
- currentObj = new Byte(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
4250
+ currentObj = new Byte(loc >> 16);
4128
4251
  continue;
4129
4252
  case AddressType.WBank:
4130
- currentObj = new Word(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
4253
+ currentObj = new Word(loc >> 16);
4131
4254
  continue;
4132
4255
  case AddressType.Address:
4133
- currentObj = new Long(loc | ((loc & 65535) >= 32768 ? 8388608 : 12582912));
4134
- continue;
4135
4256
  case AddressType.Location:
4136
4257
  currentObj = new Long(loc);
4137
4258
  continue;
4259
+ case AddressType.Unknown:
4260
+ case AddressType.Relative:
4138
4261
  default:
4139
4262
  currentObj = new Byte(loc);
4140
4263
  continue;
@@ -4301,6 +4424,8 @@ var DbRootUtils = class {
4301
4424
  group: groupName,
4302
4425
  scene: sceneName
4303
4426
  }));
4427
+ const stringDelimiterLookup = {};
4428
+ const stringDelimiterList = [];
4304
4429
  const stringLookup = Object.entries(module.strings).reduce((acc, x) => {
4305
4430
  const stringType = x[1];
4306
4431
  const name = x[0];
@@ -4318,12 +4443,15 @@ var DbRootUtils = class {
4318
4443
  });
4319
4444
  return acc$1;
4320
4445
  }, {});
4321
- acc[name] = new DbStringType({
4446
+ const st = new DbStringType({
4322
4447
  ...stringType,
4323
4448
  name,
4324
4449
  commands,
4325
4450
  dictionaries
4326
4451
  });
4452
+ acc[name] = st;
4453
+ stringDelimiterLookup[st.delimiter] = st;
4454
+ stringDelimiterList.push(st.delimiter);
4327
4455
  return acc;
4328
4456
  }, {});
4329
4457
  const structLookup = Object.entries(module.structs).reduce((acc, x) => {
@@ -4385,7 +4513,8 @@ var DbRootUtils = class {
4385
4513
  addrLookup,
4386
4514
  entryPoints: cfg.entryPoints,
4387
4515
  stringTypes: stringLookup,
4388
- stringDelimiters: Object.values(stringLookup).map((x) => x.delimiter),
4516
+ stringDelimiters: stringDelimiterList,
4517
+ stringDelimiterLookup,
4389
4518
  compression,
4390
4519
  groups: groupLookup,
4391
4520
  scenes: sceneLookup
@@ -4445,6 +4574,7 @@ var DbRootUtils = class {
4445
4574
  var sourceFiles = [];
4446
4575
  for (const path of inPath) sourceFiles = await this.applyFolder(root, path, sourceFiles);
4447
4576
  await saveFileAsBinary(outPath, await new RomWriter(root, "TEST", "TEST").repack(sourceFiles));
4577
+ return sourceFiles;
4448
4578
  }
4449
4579
  };
4450
4580
 
@@ -4549,7 +4679,7 @@ var StringReader = class StringReader {
4549
4679
  } else {
4550
4680
  let found = false;
4551
4681
  for (const dictionary of Object.values(dictionaries)) if (c >= dictionary.base && c <= dictionary.range) {
4552
- builder.push(dictionary.entries[c - dictionary.base] + dictionary.suffix);
4682
+ builder.push(dictionary.entries[c - dictionary.base]);
4553
4683
  found = true;
4554
4684
  break;
4555
4685
  }
@@ -4617,13 +4747,12 @@ var StringProcessor = class {
4617
4747
  context;
4618
4748
  root;
4619
4749
  stringCharLookup;
4750
+ testRegex;
4620
4751
  constructor(context) {
4621
4752
  this.context = context;
4622
4753
  this.root = context.root;
4623
- this.stringCharLookup = Object.values(this.root.stringTypes).reduce((acc, x) => {
4624
- acc[x.delimiter] = x;
4625
- return acc;
4626
- }, {});
4754
+ this.stringCharLookup = context.root.stringDelimiterLookup;
4755
+ this.testRegex = /* @__PURE__ */ new RegExp(/^[0-9A-F]+$/i);
4627
4756
  }
4628
4757
  consumeString(typeChar) {
4629
4758
  let str = null;
@@ -4646,8 +4775,7 @@ var StringProcessor = class {
4646
4775
  this.memBuffer.length = 0;
4647
4776
  this.totalSize = 0;
4648
4777
  this.fixedStr = fixedStr;
4649
- const stringType = this.stringCharLookup[typeChar];
4650
- this.processString(str, stringType);
4778
+ this.processString(str, typeChar);
4651
4779
  }
4652
4780
  flushBuffer(stringType, wrap = false) {
4653
4781
  const size = this.memBuffer.length;
@@ -4667,9 +4795,15 @@ var StringProcessor = class {
4667
4795
  this.memBuffer.length = 0;
4668
4796
  }
4669
4797
  }
4670
- processString(str, stringType) {
4671
- const dict = stringType.commands;
4798
+ processString(str, typeChar) {
4799
+ const stringType = this.stringCharLookup[typeChar];
4800
+ const dictionary = stringType.dictionaryLookup;
4801
+ const cmdLookup = stringType.commands;
4672
4802
  let lastCmd = null;
4803
+ for (const entry of dictionary) {
4804
+ let index;
4805
+ while ((index = str.indexOf(entry.text)) >= 0) str = str.substring(0, index) + `[${entry.id.toString(16).toUpperCase()}]` + str.substring(index + entry.text.length);
4806
+ }
4673
4807
  for (let x = 0; x < str.length; x++) {
4674
4808
  const c = str[x];
4675
4809
  if (c === "[") {
@@ -4685,13 +4819,19 @@ var StringProcessor = class {
4685
4819
  this.context.currentBlock.objList.push({ offset: this.context.currentBlock.size });
4686
4820
  continue;
4687
4821
  }
4688
- const cmd = dict[parts[0]];
4822
+ const cmd = cmdLookup[parts[0]];
4689
4823
  if (cmd) {
4690
4824
  lastCmd = cmd;
4691
4825
  this.memBuffer.push(cmd.id);
4692
4826
  this.processStringCommand(cmd, stringType, parts);
4693
4827
  continue;
4694
4828
  }
4829
+ if (parts.length === 1 && this.testRegex.test(parts[0])) {
4830
+ const value = parseInt(parts[0], 16);
4831
+ if (value < 256) this.memBuffer.push(value);
4832
+ else this.memBuffer.push(value >> 8 & 255, value & 255);
4833
+ continue;
4834
+ }
4695
4835
  }
4696
4836
  lastCmd = null;
4697
4837
  if (this.applyLayers(c, stringType)) continue;
@@ -4832,7 +4972,7 @@ var AssemblerState = class AssemblerState {
4832
4972
  this.dbStruct = structType === null ? null : Object.values(this.root.structs).find((x) => x.name.toLowerCase() === structType.toLowerCase()) || null;
4833
4973
  this.parentStruct = !this.dbStruct || !this.dbStruct.parent ? null : Object.values(this.root.structs).find((x) => x.name.toLowerCase() === this.dbStruct.parent.toLowerCase()) || null;
4834
4974
  this.discriminator = this.parentStruct?.discriminator ?? null;
4835
- this.delimiter = this.dbStruct?.delimiter || null;
4975
+ this.delimiter = this.dbStruct?.delimiter ?? null;
4836
4976
  this.memberOffset = 0;
4837
4977
  this.dataOffset = 0;
4838
4978
  this.memberTypes = this.dbStruct?.types || null;
@@ -6157,12 +6297,12 @@ var RomGenerator = class {
6157
6297
  this.assembleCodeFromText(asmFiles);
6158
6298
  RomProcessor.applyPatches(asmFiles, patchFiles);
6159
6299
  for (const asm of chunkFiles) ChunkFileUtils.calculateSize(asm);
6160
- new RomLayout(chunkFiles).organize();
6300
+ const pages = new RomLayout(chunkFiles, this.dbRoot).organize();
6161
6301
  for (const file of asmFiles) ChunkFileUtils.rebase(file);
6162
6302
  this.generateAsmIncludeLookups(asmFiles);
6163
6303
  const blockLookup = /* @__PURE__ */ new Map();
6164
6304
  for (const f of chunkFiles) blockLookup.set(f.name.toUpperCase(), f.location);
6165
- return await this.writeRom(blockLookup, chunkFiles, asmFiles);
6305
+ return await this.writeRom(blockLookup, chunkFiles, asmFiles, pages);
6166
6306
  }
6167
6307
  applyProjectInit(chunkFiles, asmFiles, patchFiles) {
6168
6308
  const moduleLookup = /* @__PURE__ */ new Map();
@@ -6207,8 +6347,9 @@ var RomGenerator = class {
6207
6347
  existing.size = chunkFile.size;
6208
6348
  } else chunkFiles.push(chunkFile);
6209
6349
  }
6210
- async writeRom(blockLookup, chunkFiles, asmFiles) {
6350
+ async writeRom(blockLookup, chunkFiles, asmFiles, pages) {
6211
6351
  const romWriter = new RomWriter(this.dbRoot, "GAIALABS", "01JG ");
6352
+ romWriter.allocate(pages);
6212
6353
  for (const file of chunkFiles) await romWriter.writeFile(file, blockLookup);
6213
6354
  romWriter.writeHeader();
6214
6355
  romWriter.writeEntryPoints(asmFiles);
@@ -6471,5 +6612,5 @@ const isPlatformNode = typeof process !== "undefined" && process.versions?.node;
6471
6612
  const isPlatformWebWorker = typeof importScripts !== "undefined";
6472
6613
 
6473
6614
  //#endregion
6474
- export { Address, AddressSpace, AddressType, AddressingModeHandler, AsmBlock, AsmBlockUtils, AsmReader, Assembler, AssemblerState, BinType, BitStream, BlockReader, BlockReaderConstants, BlockWriter, Byte, ChunkFile, ChunkFileUtils, CompressionAlgorithms, CompressionRegistry, CopCommandProcessor, CopDef, DbAddressingMode, DbBlock, DbFile, DbFileType, DbGroup, DbOverride, DbPart, DbRootUtils, DbScene, DbStringCommand, DbStringDictionary, DbStringType, DbStringTypeUtils, DbStruct, DbTransform, DebugExporter, GAIA_CORE_VERSION, LocationWrapper, Long, MapExt, MemberType, MemoryMapMode, ObjectType, Op, OpCode, OperationContext, PostProcessor, ProcessorStateManager, QuintetLZ, ReferenceManager, RegisterType, Registers, RomDataReader, RomGenerator, RomLayout, RomProcessingConstants, RomProcessor, RomState, RomStateUtils, RomWriter, SortedMap, SpriteFrame, SpriteGroup, SpriteMap, SpritePart, Stack, StackOperations, StatusFlags, StringProcessor, StringReader, StringSizeComparer, SupabaseErrorCode, SupabaseFromError, TableEntry, TransformProcessor, TypeParser, TypedNumber, Word, XformType, base64ToUint8Array, binaryToUtf8String, bytesToHex, clamp, crc32_buffer, crc32_text_utf16, crc32_text_utf8, createChunkFileFromDbBlock, createChunkFileFromDbFile, createDbPath, createSupabaseClient, createTempDirectory, decodeBase64, decodeBase64String, decodeDataString, encodeBase64, encodeBase64String, fileExists, fromSupabaseByGameRom, fromSupabaseById, fromSupabaseByName, fromSupabaseByProject, getDirectory, getEnvVar, getNestedProperty, getSupabaseClient, hexToBytes, hexToUint8Array, indexOfAny, isPlatformBrowser, isPlatformNode, isPlatformWebWorker, isValidBase64, listDirectory, readFileAsBinary, readFileAsText, readJsonFile, removeDirectory, resetSupabaseClient, saveFileAsBinary, saveFileAsText, summaryFromSupabaseByProject, uint8ArrayToBase64, validateEnvironmentVariables };
6615
+ export { Address, AddressSpace, AddressType, AddressingModeHandler, AsmBlock, AsmBlockUtils, AsmReader, Assembler, AssemblerState, BinType, BitStream, BlockReader, BlockReaderConstants, BlockWriter, Byte, ChunkFile, ChunkFileUtils, CompressionAlgorithms, CompressionRegistry, CopCommandProcessor, CopDef, CpuMode, DbAddressingMode, DbBlock, DbFile, DbFileType, DbGroup, DbOverride, DbPart, DbRootUtils, DbScene, DbStringCommand, DbStringDictionary, DbStringType, DbStringTypeUtils, DbStruct, DbTransform, DebugExporter, GAIA_CORE_VERSION, LocationWrapper, Long, MapExt, MemberType, MemoryMapMode, ObjectType, Op, OpCode, OperationContext, PostProcessor, ProcessorStateManager, QuintetLZ, ReferenceManager, RegisterType, Registers, RomDataReader, RomGenerator, RomLayout, RomProcessingConstants, RomProcessor, RomState, RomStateUtils, RomWriter, SortedMap, SpriteFrame, SpriteGroup, SpriteMap, SpritePart, Stack, StackOperations, StatusFlags, StringProcessor, StringReader, StringSizeComparer, SupabaseErrorCode, SupabaseFromError, TableEntry, TransformProcessor, TypeParser, TypedNumber, Word, XformType, base64ToUint8Array, binaryToUtf8String, bytesToHex, clamp, crc32_buffer, crc32_text_utf16, crc32_text_utf8, createChunkFileFromDbBlock, createChunkFileFromDbFile, createDbPath, createSupabaseClient, createTempDirectory, decodeBase64, decodeBase64String, decodeDataString, encodeBase64, encodeBase64String, fileExists, fromSupabaseByGameRom, fromSupabaseById, fromSupabaseByName, fromSupabaseByProject, getDirectory, getEnvVar, getNestedProperty, getSupabaseClient, hexToBytes, hexToUint8Array, indexOfAny, isPlatformBrowser, isPlatformNode, isPlatformWebWorker, isValidBase64, listDirectory, readFileAsBinary, readFileAsText, readJsonFile, removeDirectory, resetSupabaseClient, saveFileAsBinary, saveFileAsText, summaryFromSupabaseByProject, uint8ArrayToBase64, validateEnvironmentVariables };
6475
6616
  //# sourceMappingURL=index.mjs.map