@gaialabs/core 0.1.19 → 0.2.1

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.js DELETED
@@ -1,3977 +0,0 @@
1
- 'use strict';
2
-
3
- var shared = require('@gaialabs/shared');
4
-
5
- // src/rom/state.ts
6
- var RomState = class _RomState {
7
- constructor() {
8
- // Basic memory buffers
9
- this.cgram = new Uint8Array(512);
10
- // Color palette RAM
11
- this.vram = new Uint8Array(65536);
12
- }
13
- // Video RAM
14
- /**
15
- * Create ROM state from scene metadata.
16
- * Parameters are currently unused but kept for future implementation.
17
- */
18
- static async fromScene(_baseDir, _root, _metaFile, _id) {
19
- return new _RomState();
20
- }
21
- };
22
- var RomStateUtils = class {
23
- /** Create an empty ROM state */
24
- static createEmpty() {
25
- return new RomState();
26
- }
27
- /** Clear all data in the given ROM state */
28
- static clear(state) {
29
- state.cgram.fill(0);
30
- state.vram.fill(0);
31
- }
32
- };
33
-
34
- // src/rom/extraction/processor.ts
35
- var ProcessorStateManager = class {
36
- constructor() {
37
- this.accumulatorFlags = /* @__PURE__ */ new Map();
38
- this.indexFlags = /* @__PURE__ */ new Map();
39
- this.bankNotes = /* @__PURE__ */ new Map();
40
- this.stackPositions = /* @__PURE__ */ new Map();
41
- }
42
- /**
43
- * Hydrate processor registers with stored state
44
- * Uses Registers from gaia-core/assembly for processor state management
45
- */
46
- hydrateRegisters(position, reg) {
47
- const acc = this.accumulatorFlags.get(position);
48
- if (acc !== void 0) {
49
- reg.accumulatorFlag = acc ?? void 0;
50
- }
51
- const ind = this.indexFlags.get(position);
52
- if (ind !== void 0) {
53
- reg.indexFlag = ind ?? void 0;
54
- }
55
- const bnk = this.bankNotes.get(position);
56
- if (bnk !== void 0) {
57
- reg.dataBank = bnk ?? void 0;
58
- }
59
- const stack = this.stackPositions.get(position);
60
- if (stack !== void 0) {
61
- reg.stack.location = stack;
62
- }
63
- }
64
- // Accumulator flag methods
65
- getAccumulatorFlag(location) {
66
- return this.accumulatorFlags.get(location);
67
- }
68
- setAccumulatorFlag(location, value) {
69
- this.accumulatorFlags.set(location, value);
70
- }
71
- tryAddAccumulatorFlag(location, value) {
72
- if (this.accumulatorFlags.has(location)) {
73
- return false;
74
- }
75
- this.accumulatorFlags.set(location, value);
76
- return true;
77
- }
78
- // Index flag methods
79
- getIndexFlag(location) {
80
- return this.indexFlags.get(location);
81
- }
82
- setIndexFlag(location, value) {
83
- this.indexFlags.set(location, value);
84
- }
85
- tryAddIndexFlag(location, value) {
86
- if (this.indexFlags.has(location)) {
87
- return false;
88
- }
89
- this.indexFlags.set(location, value);
90
- return true;
91
- }
92
- // Bank note methods
93
- getBankNote(location) {
94
- return this.bankNotes.get(location);
95
- }
96
- setBankNote(location, value) {
97
- this.bankNotes.set(location, value);
98
- }
99
- // Stack position methods
100
- getStackPosition(location) {
101
- return this.stackPositions.get(location);
102
- }
103
- setStackPosition(location, value) {
104
- this.stackPositions.set(location, value);
105
- }
106
- tryAddStackPosition(location, value) {
107
- if (this.stackPositions.has(location)) {
108
- return false;
109
- }
110
- this.stackPositions.set(location, value);
111
- return true;
112
- }
113
- };
114
-
115
- // src/rom/extraction/reader.ts
116
- var RomDataReader = class {
117
- constructor(romData) {
118
- if (!romData) {
119
- throw new Error("romData cannot be null");
120
- }
121
- this.romData = romData;
122
- this.position = 0;
123
- }
124
- readByte() {
125
- return this.romData[this.position++];
126
- }
127
- readSByte() {
128
- const value = this.readByte();
129
- return value > 127 ? value - 256 : value;
130
- }
131
- readUShort() {
132
- return this.readByte() | this.readByte() << 8;
133
- }
134
- readShort() {
135
- const value = this.readUShort();
136
- return value > 32767 ? value - 65536 : value;
137
- }
138
- readAddress() {
139
- return this.readByte() | this.readByte() << 8 | this.readByte() << 16;
140
- }
141
- readInt() {
142
- return this.readByte() | this.readByte() << 8 | this.readByte() << 16 | this.readByte() << 24;
143
- }
144
- peekByte() {
145
- return this.romData[this.position];
146
- }
147
- peekShort() {
148
- return this.romData[this.position] | this.romData[this.position + 1] << 8;
149
- }
150
- peekAddress() {
151
- return this.romData[this.position] | this.romData[this.position + 1] << 8 | this.romData[this.position + 2] << 16;
152
- }
153
- };
154
- var ReferenceManager = class {
155
- constructor(root) {
156
- this.structTable = /* @__PURE__ */ new Map();
157
- this.markerTable = /* @__PURE__ */ new Map();
158
- this.nameTable = /* @__PURE__ */ new Map();
159
- if (!root) {
160
- throw new Error("root cannot be null");
161
- }
162
- this.root = root;
163
- }
164
- // Struct management
165
- tryGetStruct(location) {
166
- const chunkType = this.structTable.get(location);
167
- return { found: chunkType !== void 0, chunkType };
168
- }
169
- tryAddStruct(location, chunkType) {
170
- if (this.structTable.has(location)) {
171
- return false;
172
- }
173
- this.structTable.set(location, chunkType);
174
- return true;
175
- }
176
- containsStruct(location) {
177
- return this.structTable.has(location);
178
- }
179
- // Name management
180
- tryGetName(location) {
181
- const referenceName = this.nameTable.get(location);
182
- return { found: referenceName !== void 0, referenceName };
183
- }
184
- tryAddName(location, referenceName) {
185
- if (this.nameTable.has(location)) {
186
- return false;
187
- }
188
- this.nameTable.set(location, referenceName);
189
- return true;
190
- }
191
- // Marker management
192
- tryGetMarker(location) {
193
- const offset = this.markerTable.get(location);
194
- return { found: offset !== void 0, offset };
195
- }
196
- setMarker(location, offset) {
197
- this.markerTable.set(location, offset);
198
- }
199
- // Label creation
200
- createBranchLabel(location) {
201
- const name = `loc_${location.toString(16).toUpperCase().padStart(6, "0")}`;
202
- this.nameTable.set(location, name);
203
- return name;
204
- }
205
- createTypeName(type, location) {
206
- let name = type.toLowerCase();
207
- while (name.length > 0 && shared.BlockReaderConstants.POINTER_CHARACTERS.includes(name[0])) {
208
- name = name.substring(1) + "_list";
209
- }
210
- return `${name}_${location.toString(16).toUpperCase().padStart(6, "0")}`;
211
- }
212
- createFallbackName(location) {
213
- const fileMatch = this.root.files.find(
214
- (x) => x.start <= location && x.end > location
215
- );
216
- if (fileMatch) {
217
- const offset = location - fileMatch.start;
218
- return fileMatch.name + (offset !== 0 ? `+${offset.toString(16).toUpperCase()}` : "");
219
- }
220
- return location.toString(16).toUpperCase().padStart(6, "0");
221
- }
222
- /**
223
- * Finds a reference location by its assigned name.
224
- */
225
- findLocationByName(name) {
226
- for (const [loc, refName] of this.nameTable) {
227
- if (refName === name) {
228
- return loc;
229
- }
230
- }
231
- return void 0;
232
- }
233
- resolveName(location, type, isBranch) {
234
- const prefix = shared.Address.codeFromType(type);
235
- let name;
236
- let label = null;
237
- let resolvedLocation = location;
238
- const rewrite = this.root.rewrites[location];
239
- if (rewrite !== void 0) {
240
- const result = this.processRewrite(location, rewrite);
241
- resolvedLocation = result.location;
242
- label = result.label;
243
- }
244
- const existingName = this.nameTable.get(resolvedLocation);
245
- if (existingName) {
246
- name = existingName;
247
- } else {
248
- name = isBranch ? this.createBranchLabel(resolvedLocation) : this.findClosestReference(resolvedLocation) || this.createFallbackName(resolvedLocation);
249
- }
250
- return `${prefix || ""}${name}${label || ""}`;
251
- }
252
- findClosestReference(location) {
253
- let closestDistance = shared.BlockReaderConstants.REF_SEARCH_MAX_RANGE;
254
- let closestName = null;
255
- let closestLocation = null;
256
- for (const [entryKey, entryValue] of this.nameTable) {
257
- if (entryKey > location) {
258
- continue;
259
- }
260
- const distance = location - entryKey;
261
- if (distance >= closestDistance) {
262
- continue;
263
- }
264
- closestDistance = distance;
265
- closestName = entryValue;
266
- closestLocation = entryKey;
267
- if (closestDistance === 1) {
268
- break;
269
- }
270
- }
271
- return this.processClosestMatch(location, closestName, closestLocation, closestDistance);
272
- }
273
- processRewrite(location, rewrite) {
274
- const offset = location - rewrite;
275
- const cmd = offset < 0 ? "-" : "+";
276
- const absOffset = Math.abs(offset);
277
- let label = null;
278
- const structType = this.structTable.get(rewrite);
279
- if (structType === shared.BlockReaderConstants.WIDE_STRING_TYPE) {
280
- this.markerTable.set(rewrite, absOffset);
281
- this.markerTable.set(location, absOffset);
282
- label = cmd === "-" ? shared.BlockReaderConstants.NEGATIVE_MARKER_FORMAT : shared.BlockReaderConstants.MARKER_FORMAT;
283
- } else {
284
- const formatString = cmd === "-" ? shared.BlockReaderConstants.NEGATIVE_OFFSET_FORMAT : shared.BlockReaderConstants.OFFSET_FORMAT;
285
- label = formatString.replace("{0:X}", absOffset.toString(16).toUpperCase());
286
- }
287
- return { location: rewrite, label };
288
- }
289
- processClosestMatch(location, closestName, closestLocation, closestDistance) {
290
- if (!closestName || closestLocation === null) {
291
- return null;
292
- }
293
- let result = closestName;
294
- const structType = this.structTable.get(closestLocation);
295
- if (structType === shared.BlockReaderConstants.WIDE_STRING_TYPE) {
296
- this.markerTable.set(closestLocation, closestDistance);
297
- this.markerTable.set(location, closestDistance);
298
- result += shared.BlockReaderConstants.MARKER_FORMAT;
299
- } else {
300
- result += `+${closestDistance.toString(16).toUpperCase()}`;
301
- }
302
- return result;
303
- }
304
- };
305
-
306
- // src/rom/extraction/stack.ts
307
- var StackOperations = class {
308
- constructor(registers, blockReader) {
309
- this._registers = registers;
310
- this._blockReader = blockReader;
311
- }
312
- handleStackOperation(mnemonic) {
313
- switch (mnemonic) {
314
- case "PHD":
315
- this._registers.stack.push(this._registers.direct ?? 0);
316
- break;
317
- case "PLD":
318
- this._registers.direct = this._registers.stack.popUInt16();
319
- break;
320
- case "PHK":
321
- this._registers.stack.push(this._blockReader._romDataReader.position >> 16 | 128);
322
- break;
323
- case "PHB":
324
- this._registers.stack.push(this._registers.dataBank ?? 129);
325
- break;
326
- case "PLB":
327
- this._registers.dataBank = this._registers.stack.popByte();
328
- break;
329
- case "PHP":
330
- this._registers.stack.push(this._registers.statusFlags);
331
- break;
332
- case "PLP":
333
- this._registers.statusFlags = this._registers.stack.popByte();
334
- break;
335
- case "PHA":
336
- this.handleAccumulatorPush();
337
- break;
338
- case "PLA":
339
- this.handleAccumulatorPull();
340
- break;
341
- case "PHX":
342
- this.handleXIndexPush();
343
- break;
344
- case "PLX":
345
- this.handleXIndexPull();
346
- break;
347
- case "PHY":
348
- this.handleYIndexPush();
349
- break;
350
- case "PLY":
351
- this.handleYIndexPull();
352
- break;
353
- case "XBA":
354
- this.handleExchangeBytes();
355
- break;
356
- }
357
- }
358
- handleAccumulatorPush() {
359
- if (this._registers.accumulatorFlag === true) {
360
- this._registers.stack.push((this._registers.accumulator ?? 0) & 255);
361
- } else {
362
- this._registers.stack.pushUInt16(this._registers.accumulator ?? 0);
363
- }
364
- }
365
- handleAccumulatorPull() {
366
- if (this._registers.accumulatorFlag === true) {
367
- this._registers.accumulator = (this._registers.accumulator ?? 0) & 65280 | this._registers.stack.popByte();
368
- } else {
369
- this._registers.accumulator = this._registers.stack.popUInt16();
370
- }
371
- }
372
- handleXIndexPush() {
373
- if (this._registers.indexFlag === true) {
374
- this._registers.stack.push((this._registers.xIndex ?? 0) & 255);
375
- } else {
376
- this._registers.stack.pushUInt16(this._registers.xIndex ?? 0);
377
- }
378
- }
379
- handleXIndexPull() {
380
- if (this._registers.indexFlag === true) {
381
- this._registers.xIndex = (this._registers.xIndex ?? 0) & 65280 | this._registers.stack.popByte();
382
- } else {
383
- this._registers.xIndex = this._registers.stack.popUInt16();
384
- }
385
- }
386
- handleYIndexPush() {
387
- if (this._registers.indexFlag === true) {
388
- this._registers.stack.push((this._registers.yIndex ?? 0) & 255);
389
- } else {
390
- this._registers.stack.pushUInt16(this._registers.yIndex ?? 0);
391
- }
392
- }
393
- handleYIndexPull() {
394
- if (this._registers.indexFlag === true) {
395
- this._registers.yIndex = (this._registers.yIndex ?? 0) & 65280 | this._registers.stack.popByte();
396
- } else {
397
- this._registers.yIndex = this._registers.stack.popUInt16();
398
- }
399
- }
400
- handleExchangeBytes() {
401
- const acc = this._registers.accumulator ?? 0;
402
- this._registers.accumulator = (acc >> 8 | acc << 8) & 65535;
403
- }
404
- };
405
- var CopCommandProcessor = class {
406
- constructor(blockReader) {
407
- this._blockReader = blockReader;
408
- this._romDataReader = blockReader._romDataReader;
409
- }
410
- /**
411
- * Parses a COP command based on its definition
412
- */
413
- parseCopCommand(copDef, operands) {
414
- for (const partStr of copDef.parts) {
415
- const addrType = shared.Address.typeFromCode(partStr[0]);
416
- const isPtr = addrType !== shared.AddressType.Unknown;
417
- const referenceType = isPtr ? partStr.substring(1) : this._blockReader._currentAsmBlock.structName ?? "Binary";
418
- const memberTypeName = isPtr ? addrType.toString() : partStr;
419
- const memberType = this.tryParseMemberType(memberTypeName);
420
- if (memberType === null) {
421
- throw new Error("Cannot use structs in cop def");
422
- }
423
- const label = this._blockReader._root.labels[this._romDataReader.position];
424
- if (label) {
425
- this._romDataReader.position += this.getMemberTypeSize(memberType);
426
- operands.push(label);
427
- } else {
428
- operands.push(this.readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType));
429
- }
430
- }
431
- }
432
- tryParseMemberType(memberTypeName) {
433
- const upperName = memberTypeName.toUpperCase();
434
- for (const [key, value] of Object.entries(shared.MemberType)) {
435
- if (key.toUpperCase() === upperName) {
436
- return value;
437
- }
438
- }
439
- return null;
440
- }
441
- getMemberTypeSize(memberType) {
442
- switch (memberType) {
443
- case shared.MemberType.Byte:
444
- return 1;
445
- case shared.MemberType.Word:
446
- case shared.MemberType.Offset:
447
- return 2;
448
- case shared.MemberType.Address:
449
- return 3;
450
- default:
451
- throw new Error("Unsupported COP member type");
452
- }
453
- }
454
- readMemberTypeValue(memberType, partStr, isPtr, referenceType, addrType) {
455
- switch (memberType) {
456
- case shared.MemberType.Byte:
457
- return new shared.Byte(this._romDataReader.readByte());
458
- case shared.MemberType.Word:
459
- return new shared.Word(this._romDataReader.readUShort());
460
- case shared.MemberType.Offset:
461
- return this.createCopLocation(this._romDataReader.readUShort(), null, partStr, isPtr, referenceType, addrType);
462
- case shared.MemberType.Address:
463
- return this.createCopLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), partStr, isPtr, referenceType, addrType);
464
- default:
465
- throw new Error("Unsupported COP member type");
466
- }
467
- }
468
- createCopLocation(offset, bank, partStr, isPtr, otherStr, type) {
469
- if (bank === null && offset === 0) {
470
- return new shared.Word(offset);
471
- }
472
- const addr = new shared.Address(bank ?? this._romDataReader.position >> 16, offset);
473
- if (addr.space === shared.AddressSpace.ROM) {
474
- const location = addr.toInt();
475
- if (partStr !== "Address" && isPtr && !this._blockReader._root.rewrites[location]) {
476
- this._blockReader.noteType(location, otherStr, true);
477
- }
478
- if (type === shared.AddressType.Unknown) {
479
- type = this.tryParseAddressType(partStr) ?? shared.AddressType.Unknown;
480
- }
481
- return new shared.LocationWrapper(location, type);
482
- }
483
- return addr;
484
- }
485
- tryParseAddressType(addressTypeName) {
486
- const upperName = addressTypeName.toUpperCase();
487
- for (const [key, value] of Object.entries(shared.AddressType)) {
488
- if (key.toUpperCase() === upperName) {
489
- return value;
490
- }
491
- }
492
- return null;
493
- }
494
- };
495
-
496
- // src/rom/extraction/addressing.ts
497
- var OperationContext = class {
498
- constructor() {
499
- this.size = 0;
500
- this.nextAddress = 0;
501
- this.xForm1 = null;
502
- this.xForm2 = null;
503
- this.copDef = null;
504
- }
505
- };
506
- var AddressingModeHandler = class {
507
- constructor(blockReader, transformProcessor) {
508
- this._blockReader = blockReader;
509
- this._transformProcessor = transformProcessor;
510
- this._copProcessor = new CopCommandProcessor(blockReader);
511
- this._dataReader = blockReader._romDataReader;
512
- }
513
- processAddressingMode(code, context, reg) {
514
- const operands = [];
515
- switch (code.mode) {
516
- case "Stack":
517
- case "Implied":
518
- case "Accumulator":
519
- this.handleStackOrImpliedMode(code.mnem, reg);
520
- break;
521
- case "Immediate":
522
- this.handleImmediateMode(code.mnem, context.size, operands, reg);
523
- break;
524
- case "AbsoluteIndexedIndirect":
525
- this.handleAbsoluteMode(code.mnem, void 0, context.nextAddress, reg.dataBank, operands, reg, true);
526
- break;
527
- case "AbsoluteIndirect":
528
- case "AbsoluteIndirectLong":
529
- case "Absolute":
530
- case "AbsoluteIndexedX":
531
- case "AbsoluteIndexedY":
532
- this.handleAbsoluteMode(code.mnem, void 0, context.nextAddress, reg.dataBank, operands, reg);
533
- break;
534
- case "AbsoluteLong":
535
- case "AbsoluteLongIndexedX":
536
- this.handleAbsoluteLongMode(code.mnem, operands, reg);
537
- break;
538
- case "BlockMove":
539
- this.handleBlockMoveMode(operands, context);
540
- break;
541
- case "DirectPage":
542
- case "DirectPageIndexedIndirectX":
543
- case "DirectPageIndexedX":
544
- case "DirectPageIndexedY":
545
- case "DirectPageIndirect":
546
- case "DirectPageIndirectIndexedY":
547
- case "DirectPageIndirectLong":
548
- case "DirectPageIndirectLongIndexedY":
549
- this.handleDirectPageMode(operands);
550
- break;
551
- case "PCRelative":
552
- this.handlePCRelativeMode(operands, context.nextAddress, reg, false);
553
- break;
554
- case "PCRelativeLong":
555
- this.handlePCRelativeMode(operands, context.nextAddress, reg, true);
556
- break;
557
- case "StackRelative":
558
- case "StackRelativeIndirectIndexedY":
559
- this.handleStackRelativeMode(operands);
560
- break;
561
- case "StackInterrupt":
562
- this.handleStackInterruptMode(code.mnem, operands, context);
563
- break;
564
- default:
565
- throw new Error(`Unknown addressing mode: ${code}`);
566
- }
567
- return operands;
568
- }
569
- handleImmediateMode(mnemonic, size, operands, reg) {
570
- const operand = this.readImmediateOperand(size);
571
- operands.push(operand);
572
- this.updateRegisterForImmediateInstruction(mnemonic, operand, reg);
573
- }
574
- readImmediateOperand(size) {
575
- return size === 3 ? new shared.Word(this._dataReader.readUShort()) : new shared.Byte(this._dataReader.readByte());
576
- }
577
- updateRegisterForImmediateInstruction(mnemonic, operand, reg) {
578
- const value = typeof operand === "number" ? operand : operand.value;
579
- switch (mnemonic) {
580
- case "LDA":
581
- reg.accumulator = this.calculateRegisterValue(reg.accumulator, value);
582
- break;
583
- case "LDX":
584
- reg.xIndex = this.calculateRegisterValue(reg.xIndex, value);
585
- break;
586
- case "LDY":
587
- reg.yIndex = this.calculateRegisterValue(reg.yIndex, value);
588
- break;
589
- case "SEP":
590
- case "REP":
591
- this.updateStatusFlags(mnemonic, value);
592
- break;
593
- }
594
- }
595
- calculateRegisterValue(currentValue, operand) {
596
- if (operand > 255) {
597
- return operand;
598
- } else {
599
- return (currentValue ?? 0) & 65280 | operand;
600
- }
601
- }
602
- updateStatusFlags(mnemonic, flagValue) {
603
- const flag = flagValue;
604
- const isSep = mnemonic === "SEP";
605
- if (flag & shared.StatusFlags.AccumulatorMode) {
606
- this._blockReader.AccumulatorFlags.set(this._dataReader.position, isSep);
607
- }
608
- if (flag & shared.StatusFlags.IndexMode) {
609
- this._blockReader.IndexFlags.set(this._dataReader.position, isSep);
610
- }
611
- }
612
- handleAbsoluteLongMode(mnemonic, operands, reg) {
613
- const refLoc = this._dataReader.readUShort();
614
- const bank = this._dataReader.readByte();
615
- const address = new shared.Address(bank, refLoc);
616
- if (address.space === shared.AddressSpace.ROM) {
617
- const wrapper = new shared.LocationWrapper(address.toInt(), shared.AddressType.Address);
618
- if (this.isJumpInstruction(mnemonic)) {
619
- this._blockReader.noteType(wrapper.location, "Code", false, reg);
620
- }
621
- operands.push(wrapper);
622
- } else {
623
- operands.push(address);
624
- }
625
- }
626
- handleBlockMoveMode(operands, context) {
627
- operands.push(new shared.Byte(this._dataReader.readByte()));
628
- context.xForm2 = this._transformProcessor.getTransform();
629
- operands.push(new shared.Byte(this._dataReader.readByte()));
630
- }
631
- handleDirectPageMode(operands) {
632
- operands.push(new shared.Byte(this._dataReader.readByte()));
633
- }
634
- handlePCRelativeMode(operands, nextAddress, reg, isLong) {
635
- const relative = isLong ? nextAddress + this._dataReader.readShort() : nextAddress + this._dataReader.readSByte();
636
- this._blockReader.noteType(relative, "Code", void 0, reg);
637
- operands.push(new shared.LocationWrapper(relative, shared.AddressType.Relative));
638
- }
639
- handleStackRelativeMode(operands) {
640
- operands.push(new shared.Byte(this._dataReader.readByte()));
641
- }
642
- handleStackInterruptMode(mnemonic, operands, context) {
643
- const cmd = this._dataReader.readByte();
644
- operands.push(new shared.Byte(cmd));
645
- if (mnemonic === "COP") {
646
- const copDef = this._blockReader._root.copDef[cmd];
647
- if (!copDef) {
648
- throw new Error("Unknown COP command");
649
- }
650
- context.copDef = copDef;
651
- this._copProcessor.parseCopCommand(copDef, operands);
652
- }
653
- }
654
- handleStackOrImpliedMode(mnemonic, reg) {
655
- const stackOperations = new StackOperations(reg, this._blockReader);
656
- stackOperations.handleStackOperation(mnemonic);
657
- }
658
- handleAbsoluteMode(mnemonic, xBank1, next, dataBank, operands, registers, isIndexedIndirect = false) {
659
- let refLoc = this._dataReader.readUShort();
660
- const isPush = this.isPushInstruction(mnemonic);
661
- if (isPush) {
662
- refLoc++;
663
- }
664
- const isJump = isPush || isIndexedIndirect || this.isJumpInstruction(mnemonic);
665
- const bank = xBank1 ?? (isJump ? this._dataReader.position >> 16 : dataBank ?? 129);
666
- const addr = new shared.Address(bank, refLoc);
667
- if (addr.isROM) {
668
- const wrapper = new shared.LocationWrapper(addr.toInt(), shared.AddressType.Offset);
669
- if (isJump) {
670
- const type = isIndexedIndirect ? "&Code" : "Code";
671
- const name = this._blockReader.noteType(wrapper.location, type, isPush, registers);
672
- if (isPush) {
673
- operands.push(`&${name}-1`);
674
- return;
675
- }
676
- }
677
- operands.push(wrapper);
678
- } else {
679
- operands.push(addr);
680
- }
681
- }
682
- isJumpInstruction(mnemonic) {
683
- return mnemonic[0] === "J";
684
- }
685
- isPushInstruction(mnemonic) {
686
- return mnemonic[0] === "P";
687
- }
688
- };
689
- var TransformProcessor = class _TransformProcessor {
690
- static {
691
- // Location regex pattern: _([A-Fa-f0-9]{6})
692
- this.LOCATION_REGEX = /_([A-Fa-f0-9]{6})/;
693
- }
694
- constructor(romReader) {
695
- this._blockReader = romReader;
696
- this._romDataReader = romReader._romDataReader;
697
- this._referenceManager = romReader._referenceManager;
698
- this._labelLookup = romReader._root.labels;
699
- }
700
- /**
701
- * Retrieves transform information for the current ROM position
702
- */
703
- getTransform() {
704
- const transform = this._labelLookup[this._romDataReader.position];
705
- if (transform === "") {
706
- return transform;
707
- } else if (!transform) {
708
- return null;
709
- }
710
- const transformName = this.cleanTransformName(transform);
711
- const referenceLocation = this.resolveTransformReference(transformName);
712
- if (referenceLocation !== null) {
713
- this._blockReader.resolveInclude(referenceLocation, false);
714
- }
715
- return transform;
716
- }
717
- /**
718
- * Applies transforms to operands
719
- */
720
- applyTransforms(op1Label, op2Label, operands) {
721
- this.applyTransform(op1Label, 0, operands);
722
- this.applyTransform(op2Label, 1, operands);
723
- }
724
- applyTransform(transform, operandIndex, operands) {
725
- if (transform === null || transform === void 0 || operandIndex >= operands.length) {
726
- return;
727
- }
728
- if (transform === "") {
729
- this.applyDefaultTransform(operandIndex, operands);
730
- } else {
731
- operands[operandIndex] = transform;
732
- }
733
- }
734
- applyDefaultTransform(operandIndex, operands) {
735
- let value = this._romDataReader.position & 16711680;
736
- const opnd = operands[operandIndex];
737
- if (opnd && "value" in opnd) {
738
- value |= opnd["value"];
739
- } else {
740
- value |= opnd;
741
- }
742
- const nameResult = this._referenceManager.tryGetName(value);
743
- let referenceName;
744
- if (!nameResult.found) {
745
- referenceName = `loc_${value.toString(16).toUpperCase().padStart(6, "0")}`;
746
- this._referenceManager.tryAddName(value, referenceName);
747
- } else {
748
- referenceName = nameResult.referenceName;
749
- }
750
- this._blockReader.resolveInclude(value, false);
751
- operands[operandIndex] = `&${referenceName}`;
752
- }
753
- cleanTransformName(transform) {
754
- let name = transform;
755
- while (name.length > 0 && shared.RomProcessingConstants.ADDRESS_SPACE.includes(name[0])) {
756
- name = name.substring(1);
757
- }
758
- let mathIndex = -1;
759
- for (let i = 0; i < name.length; i++) {
760
- if (shared.RomProcessingConstants.OPERATORS.includes(name[i])) {
761
- mathIndex = i;
762
- break;
763
- }
764
- }
765
- if (mathIndex > 0) {
766
- name = name.substring(0, mathIndex);
767
- }
768
- return name;
769
- }
770
- resolveTransformReference(transformName) {
771
- const location = this._referenceManager.findLocationByName(transformName);
772
- if (location !== void 0) {
773
- return location;
774
- }
775
- const match = _TransformProcessor.LOCATION_REGEX.exec(transformName);
776
- return match ? parseInt(match[1], 16) : null;
777
- }
778
- };
779
-
780
- // src/utils/index.ts
781
- function indexOfAny(str, chars, startIndex = 0) {
782
- for (let i = startIndex; i < str.length; i++) {
783
- if (chars.includes(str[i])) {
784
- return i;
785
- }
786
- }
787
- return -1;
788
- }
789
-
790
- // src/rom/extraction/strings.ts
791
- var StringReader = class _StringReader {
792
- static {
793
- this.STRING_REFERENCE_CHARACTERS = ["~", "^"];
794
- }
795
- constructor(blockReader) {
796
- this._blockReader = blockReader;
797
- this._romDataReader = blockReader._romDataReader;
798
- }
799
- resolveCommand(cmd, builder) {
800
- if (cmd.types && cmd.types.length > 0) {
801
- builder.push(`[${cmd.value}`);
802
- let first = true;
803
- for (const t of cmd.types) {
804
- if (first) {
805
- builder.push(":");
806
- first = false;
807
- } else {
808
- builder.push(",");
809
- }
810
- switch (t) {
811
- case shared.MemberType.Byte:
812
- builder.push(this._romDataReader.readByte().toString(16).toUpperCase());
813
- break;
814
- case shared.MemberType.Word:
815
- builder.push(this._romDataReader.readUShort().toString(16).toUpperCase());
816
- break;
817
- case shared.MemberType.Offset:
818
- const loc = this._romDataReader.readUShort() | this._romDataReader.position & 4128768;
819
- builder.push(`^${loc.toString(16).toUpperCase().padStart(6, "0")}`);
820
- break;
821
- case shared.MemberType.Address:
822
- builder.push(`~${this._romDataReader.readAddress().toString(16).toUpperCase().padStart(6, "0")}`);
823
- break;
824
- case shared.MemberType.Binary:
825
- let sfirst = true;
826
- do {
827
- const r = this._romDataReader.readByte();
828
- if (cmd.delimiter !== void 0 && r === cmd.delimiter) {
829
- break;
830
- }
831
- if (sfirst) {
832
- sfirst = false;
833
- } else {
834
- builder.push(",");
835
- }
836
- builder.push(r.toString(16).toUpperCase());
837
- } while (this._blockReader.partCanContinue());
838
- break;
839
- default:
840
- throw new Error("Unsupported member type");
841
- }
842
- }
843
- builder.push("]");
844
- } else {
845
- builder.push(`[${cmd.value}]`);
846
- }
847
- }
848
- parseString(stringType) {
849
- const dict = stringType.commands;
850
- const builder = [];
851
- const strLoc = this._romDataReader.position;
852
- const map = stringType.characterMap;
853
- const terminator = stringType.terminator;
854
- do {
855
- const c = this._romDataReader.readByte();
856
- if (c === terminator) {
857
- if (stringType.greedyTerminator) {
858
- while (this._romDataReader.peekByte() === terminator && this._blockReader.partCanContinue()) {
859
- this._romDataReader.position++;
860
- }
861
- }
862
- break;
863
- }
864
- const cmd = dict[c];
865
- if (cmd) {
866
- this.resolveCommand(cmd, builder);
867
- if (cmd.halt) {
868
- break;
869
- }
870
- } else {
871
- const index = this.shiftDown(c, stringType.shiftType);
872
- if (index >= 0 && index < map.length) {
873
- builder.push(map[index]);
874
- } else {
875
- builder.push(`[${c.toString(16).toUpperCase()}]`);
876
- }
877
- }
878
- } while (this._blockReader.partCanContinue());
879
- return {
880
- string: builder.join(""),
881
- type: stringType,
882
- marker: 0,
883
- location: strLoc
884
- };
885
- }
886
- /**
887
- * Handles character shifting based on string type
888
- * This is a simplified implementation of the shift logic
889
- */
890
- shiftDown(c, shiftType) {
891
- if (!shiftType) {
892
- return c;
893
- }
894
- switch (shiftType) {
895
- case "wh2":
896
- return (c & 112) >> 1 | c & 7;
897
- case "h2":
898
- return (c & 224) >> 1 | c & 15;
899
- default:
900
- return c;
901
- }
902
- }
903
- resolveString(sw, isBranch) {
904
- let str = sw.string;
905
- let ix = indexOfAny(str, _StringReader.STRING_REFERENCE_CHARACTERS);
906
- while (ix >= 0) {
907
- if (ix + 6 < str.length) {
908
- const hexStr = str.substring(ix + 1, ix + 7);
909
- const sloc = parseInt(hexStr, 16);
910
- if (!isNaN(sloc)) {
911
- const addrs = new shared.Address(sloc >> 16, sloc & 65535);
912
- if (addrs.space === shared.AddressSpace.ROM) {
913
- this._blockReader.resolveInclude(sloc, false);
914
- const name = this._blockReader.resolveName(sloc, shared.AddressType.Unknown, false);
915
- const opix = indexOfAny(name, shared.RomProcessingConstants.OPERATORS);
916
- if (opix > 0) {
917
- const offsetStr = name.substring(opix + 1);
918
- let offset;
919
- if (offsetStr === "M") {
920
- offset = this._blockReader._referenceManager.markerTable.get(sloc) || 0;
921
- } else {
922
- offset = parseInt(offsetStr, 16) || 0;
923
- }
924
- if (name[opix] === "-") {
925
- offset = -offset;
926
- }
927
- name.substring(0, opix);
928
- const target = sloc - offset;
929
- const [isOutside, block, part] = shared.ChunkFileUtils.isOutsideWithPart(this._blockReader._enrichedChunks, this._blockReader._currentChunk, sloc);
930
- if (part != null) {
931
- const entry = part.objList?.find((x) => typeof x === "object" && x !== null && "location" in x && "object" in x && x.location === target);
932
- if (entry && entry.object) {
933
- entry.object.marker = offset;
934
- }
935
- }
936
- }
937
- }
938
- }
939
- }
940
- ix = indexOfAny(str, _StringReader.STRING_REFERENCE_CHARACTERS, ix + 7);
941
- }
942
- }
943
- };
944
- var TypeParser = class {
945
- constructor(blockReader) {
946
- this._blockReader = blockReader;
947
- this._referenceManager = blockReader._referenceManager;
948
- this._romDataReader = blockReader._romDataReader;
949
- this._stringReader = blockReader._stringReader;
950
- this._stringTypes = blockReader._root.stringTypes;
951
- }
952
- parseType(typeName, reg, depth, bank) {
953
- if (typeName[0] === "&") {
954
- return this.parseLocation(this._romDataReader.readUShort(), bank, typeName.substring(1), shared.AddressType.Offset);
955
- }
956
- if (typeName[0] === "@") {
957
- return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), typeName.substring(1), shared.AddressType.Address);
958
- }
959
- const stringType = this._stringTypes[typeName];
960
- if (stringType) {
961
- return this._stringReader.parseString(stringType);
962
- }
963
- const mType = this.tryParseMemberType(typeName);
964
- if (mType !== null) {
965
- switch (mType) {
966
- case shared.MemberType.Byte:
967
- return new shared.Byte(this._romDataReader.readByte());
968
- case shared.MemberType.Word:
969
- return new shared.Word(this.parseWordSafe());
970
- case shared.MemberType.Offset:
971
- return this.parseLocation(this._romDataReader.readUShort(), bank, null, shared.AddressType.Offset);
972
- case shared.MemberType.Address:
973
- return this.parseLocation(this._romDataReader.readUShort(), this._romDataReader.readByte(), null, shared.AddressType.Address);
974
- case shared.MemberType.Binary:
975
- return this.parseBinary();
976
- case shared.MemberType.Code:
977
- return this.parseCode(reg);
978
- default:
979
- throw new Error("Invalid member type");
980
- }
981
- }
982
- const parentType = this._blockReader._root.structs[typeName];
983
- if (!parentType) {
984
- throw new Error(`Unknown type: ${typeName}`);
985
- }
986
- const delimiter = parentType.delimiter;
987
- const discOffset = parentType.discriminator;
988
- const objects = [];
989
- let delReached;
990
- while (!(delReached = this._blockReader.delimiterReached(delimiter))) {
991
- const startPosition = this._romDataReader.position;
992
- let targetType = parentType;
993
- if (discOffset !== void 0) {
994
- const discPosition = this._romDataReader.position + discOffset;
995
- const desc = this._romDataReader.romData[discPosition];
996
- if (discOffset === 0) {
997
- this._romDataReader.position++;
998
- }
999
- const matchedStruct = Object.values(this._blockReader._root.structs).find(
1000
- (x) => x.parent === typeName && x.discriminator === desc
1001
- );
1002
- targetType = matchedStruct || parentType;
1003
- }
1004
- const types = targetType.types;
1005
- if (types && types.length > 0) {
1006
- const memberCount = types.length;
1007
- const prevPosition = this._romDataReader.position;
1008
- const parts = new Array(memberCount);
1009
- const def = { name: targetType.name, parts };
1010
- for (let i = 0; i < memberCount; i++) {
1011
- parts[i] = this.parseType(types[i], null, depth + 1, bank);
1012
- }
1013
- if (discOffset !== void 0 && discOffset === this._romDataReader.position - prevPosition) {
1014
- this._romDataReader.position++;
1015
- }
1016
- objects.push(def);
1017
- }
1018
- let checkPosition = startPosition;
1019
- while (++checkPosition < this._romDataReader.position) {
1020
- if (this._referenceManager.containsStruct(checkPosition)) {
1021
- this._romDataReader.position = checkPosition;
1022
- break;
1023
- }
1024
- }
1025
- if (!this._blockReader.partCanContinue()) {
1026
- break;
1027
- }
1028
- }
1029
- if (delReached && depth === 0) {
1030
- this._referenceManager.tryAddStruct(this._romDataReader.position, typeName);
1031
- }
1032
- return objects;
1033
- }
1034
- tryParseMemberType(memberTypeName) {
1035
- const upperName = memberTypeName.toUpperCase();
1036
- for (const [key, value] of Object.entries(shared.MemberType)) {
1037
- if (key.toUpperCase() === upperName) {
1038
- return value;
1039
- }
1040
- }
1041
- return null;
1042
- }
1043
- parseWordSafe() {
1044
- return this._referenceManager.containsStruct(this._romDataReader.position + 1) ? this._romDataReader.readByte() : this._romDataReader.readUShort();
1045
- }
1046
- parseBinary() {
1047
- const startPosition = this._romDataReader.position;
1048
- do {
1049
- this._romDataReader.position++;
1050
- } while (this._blockReader.partCanContinue());
1051
- const len = this._romDataReader.position - startPosition;
1052
- const outBuffer = new Uint8Array(len);
1053
- for (let i = 0; i < len; i++) {
1054
- outBuffer[i] = this._romDataReader.romData[startPosition + i];
1055
- }
1056
- return outBuffer;
1057
- }
1058
- parseLocation(offset, bank, typeName, addrType) {
1059
- if ((bank === void 0 || bank === null) && offset === 0) {
1060
- return new shared.Word(offset);
1061
- }
1062
- const resolvedBank = bank ?? this._romDataReader.position >> 16;
1063
- const adrs = new shared.Address(resolvedBank, offset);
1064
- if (adrs.space !== shared.AddressSpace.ROM) {
1065
- return adrs;
1066
- }
1067
- const loc = adrs.toInt();
1068
- if (this._blockReader._currentChunk && shared.ChunkFileUtils.isInside(this._blockReader._currentChunk, loc) && !this._blockReader._root.rewrites[loc]) {
1069
- const resolvedTypeName = typeName ?? this._blockReader._currentAsmBlock.structName ?? "Binary";
1070
- this._referenceManager.tryAddStruct(loc, resolvedTypeName);
1071
- const referenceName = `${resolvedTypeName.toLowerCase()}_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
1072
- this._referenceManager.tryAddName(loc, referenceName);
1073
- }
1074
- return new shared.LocationWrapper(loc, addrType);
1075
- }
1076
- parseCode(reg) {
1077
- const opList = [];
1078
- let first = true;
1079
- while (this._romDataReader.position < this._blockReader._partEnd) {
1080
- if (first) {
1081
- first = false;
1082
- } else if (this._referenceManager.containsStruct(this._romDataReader.position)) {
1083
- break;
1084
- }
1085
- if (reg) {
1086
- this._blockReader.hydrateRegisters(reg);
1087
- }
1088
- const op = this._blockReader._asmReader.parseAsm(reg);
1089
- opList.push(op);
1090
- }
1091
- return opList;
1092
- }
1093
- };
1094
- var AsmReader = class _AsmReader {
1095
- static {
1096
- // Constants
1097
- this.PROCESSOR_FLAG_MASK = 223;
1098
- }
1099
- static {
1100
- this.ACCUMULATOR_OP_MASK = 15;
1101
- }
1102
- static {
1103
- this.ACCUMULATOR_OP_VALUE = 9;
1104
- }
1105
- static {
1106
- this.VARIABLE_SIZE_INDICATOR = -2;
1107
- }
1108
- static {
1109
- this.TWO_BYTES_SIZE = 2;
1110
- }
1111
- static {
1112
- this.THREE_BYTES_SIZE = 3;
1113
- }
1114
- constructor(blockReader) {
1115
- this._blockReader = blockReader;
1116
- this._transformProcessor = new TransformProcessor(blockReader);
1117
- this._addressingModeHandler = new AddressingModeHandler(blockReader, this._transformProcessor);
1118
- this._romDataReader = blockReader._romDataReader;
1119
- }
1120
- parseAsm(reg) {
1121
- const opStart = this._romDataReader.position;
1122
- const opCode = this._romDataReader.readByte();
1123
- const code = this._blockReader._root.opCodes[opCode];
1124
- if (!code) {
1125
- throw new Error("Unknown OpCode");
1126
- }
1127
- const operationContext = this.initializeOperation(code, reg, opStart);
1128
- const operands = this._addressingModeHandler.processAddressingMode(code, operationContext, reg);
1129
- this._transformProcessor.applyTransforms(operationContext.xForm1, operationContext.xForm2, operands);
1130
- const op = new shared.Op(
1131
- code,
1132
- opStart,
1133
- operands,
1134
- this._romDataReader.position - opStart
1135
- );
1136
- if (operationContext.copDef) {
1137
- op.copDef = operationContext.copDef;
1138
- }
1139
- return op;
1140
- }
1141
- clearDestinationRegister(code, reg) {
1142
- switch (code.mnem) {
1143
- case "LDA":
1144
- reg.accumulator = void 0;
1145
- break;
1146
- case "LDX":
1147
- reg.xIndex = void 0;
1148
- break;
1149
- case "LDY":
1150
- reg.yIndex = void 0;
1151
- break;
1152
- }
1153
- }
1154
- initializeOperation(code, reg, loc) {
1155
- const size = this.calculateInstructionSize(code, reg);
1156
- const next = loc + size;
1157
- this.clearDestinationRegister(code, reg);
1158
- const context = new OperationContext();
1159
- context.size = size;
1160
- context.nextAddress = next;
1161
- context.xForm1 = this._transformProcessor.getTransform();
1162
- context.xForm2 = null;
1163
- context.copDef = null;
1164
- return context;
1165
- }
1166
- calculateInstructionSize(code, reg) {
1167
- const addrMode = this._blockReader._root.addrLookup[code.mode];
1168
- let size = addrMode.size;
1169
- if (size === _AsmReader.VARIABLE_SIZE_INDICATOR || code.mode === "Immediate") {
1170
- if ((code.code & _AsmReader.PROCESSOR_FLAG_MASK) === 194) {
1171
- size = _AsmReader.TWO_BYTES_SIZE;
1172
- } else if ((code.code & _AsmReader.ACCUMULATOR_OP_MASK) === _AsmReader.ACCUMULATOR_OP_VALUE) {
1173
- size = reg.accumulatorFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
1174
- } else {
1175
- size = reg.indexFlag ?? false ? _AsmReader.TWO_BYTES_SIZE : _AsmReader.THREE_BYTES_SIZE;
1176
- }
1177
- }
1178
- return size;
1179
- }
1180
- };
1181
-
1182
- // src/assembly/Stack.ts
1183
- var Stack = class {
1184
- constructor() {
1185
- this.bytes = new Uint8Array(70);
1186
- this.location = 10;
1187
- }
1188
- /**
1189
- * Push a byte onto the stack
1190
- */
1191
- push(value) {
1192
- this.bytes[this.location++] = value & 255;
1193
- }
1194
- /**
1195
- * Push a 16-bit value onto the stack (little-endian)
1196
- */
1197
- pushUInt16(value) {
1198
- this.bytes[this.location++] = value & 255;
1199
- this.bytes[this.location++] = value >> 8 & 255;
1200
- }
1201
- /**
1202
- * Pop a byte from the stack
1203
- */
1204
- popByte() {
1205
- return this.bytes[--this.location];
1206
- }
1207
- /**
1208
- * Pop a 16-bit value from the stack (little-endian)
1209
- */
1210
- popUInt16() {
1211
- const high = this.bytes[--this.location];
1212
- const low = this.bytes[--this.location];
1213
- return high << 8 | low;
1214
- }
1215
- /**
1216
- * Reset the stack to initial state
1217
- */
1218
- reset() {
1219
- this.bytes.fill(0);
1220
- this.location = 10;
1221
- }
1222
- };
1223
- var Registers = class {
1224
- constructor() {
1225
- this.stack = new Stack();
1226
- }
1227
- /**
1228
- * Get the current status flags
1229
- */
1230
- get statusFlags() {
1231
- let flags = 0;
1232
- if (this.accumulatorFlag ?? false) {
1233
- flags |= shared.StatusFlags.AccumulatorMode;
1234
- }
1235
- if (this.indexFlag ?? false) {
1236
- flags |= shared.StatusFlags.IndexMode;
1237
- }
1238
- return flags;
1239
- }
1240
- /**
1241
- * Set the status flags
1242
- */
1243
- set statusFlags(value) {
1244
- this.accumulatorFlag = (value & shared.StatusFlags.AccumulatorMode) !== 0;
1245
- this.indexFlag = (value & shared.StatusFlags.IndexMode) !== 0;
1246
- }
1247
- /**
1248
- * Reset all registers to initial state
1249
- */
1250
- reset() {
1251
- this.accumulatorFlag = void 0;
1252
- this.indexFlag = void 0;
1253
- this.direct = void 0;
1254
- this.dataBank = void 0;
1255
- this.accumulator = void 0;
1256
- this.xIndex = void 0;
1257
- this.yIndex = void 0;
1258
- this.stack.reset();
1259
- }
1260
- };
1261
- var QuintetLZ = class _QuintetLZ {
1262
- static {
1263
- this.DICTIONARY_SIZE = 256;
1264
- }
1265
- static {
1266
- this.DICTIONARY_INIT = 32;
1267
- }
1268
- static {
1269
- this.DICTIONARY_OFFSET = 239;
1270
- }
1271
- static {
1272
- this.DEFAULT_PAGE_SIZE = 32768;
1273
- }
1274
- /**
1275
- * Expand (decompress) data using QuintetLZ algorithm
1276
- * @param srcData Source data buffer
1277
- * @param srcPosition Starting position in source data
1278
- * @param srcLen Length of source data to process
1279
- * @returns Expanded data
1280
- */
1281
- expand(srcData, srcPosition = 0, srcLen = _QuintetLZ.DEFAULT_PAGE_SIZE) {
1282
- const bitStream = new shared.BitStream(srcData, srcPosition);
1283
- const srcStop = srcPosition + srcLen;
1284
- const dictionary = new Uint8Array(_QuintetLZ.DICTIONARY_SIZE);
1285
- dictionary.fill(_QuintetLZ.DICTIONARY_INIT);
1286
- let dictPosition = _QuintetLZ.DICTIONARY_OFFSET;
1287
- let outPosition = 0;
1288
- let dstLen = bitStream.readShort();
1289
- if (dstLen === 0) {
1290
- return srcData.slice(srcPosition + 2, srcStop);
1291
- } else if (dstLen & 32768) {
1292
- dstLen = 65536 - dstLen;
1293
- }
1294
- const outBuffer = new Uint8Array(dstLen);
1295
- while (bitStream.currentPosition < srcStop && outPosition < dstLen) {
1296
- if (bitStream.readBit()) {
1297
- const sample = bitStream.readByte();
1298
- if (outPosition < dstLen) {
1299
- outBuffer[outPosition++] = sample;
1300
- }
1301
- dictionary[dictPosition] = sample;
1302
- dictPosition = dictPosition + 1 & 255;
1303
- } else {
1304
- let wordIndex = bitStream.readByte();
1305
- let wordLength = bitStream.readNibble() + 2;
1306
- while (wordLength-- > 0) {
1307
- const sample = dictionary[wordIndex];
1308
- wordIndex = wordIndex + 1 & 255;
1309
- if (outPosition < dstLen) {
1310
- outBuffer[outPosition++] = sample;
1311
- }
1312
- dictionary[dictPosition] = sample;
1313
- dictPosition = dictPosition + 1 & 255;
1314
- }
1315
- }
1316
- }
1317
- if (outPosition < dstLen) {
1318
- return outBuffer.slice(0, outPosition);
1319
- }
1320
- return outBuffer;
1321
- }
1322
- /**
1323
- * Compact (compress) data using QuintetLZ algorithm
1324
- * @param srcData Source data to compress
1325
- * @returns Compressed data
1326
- */
1327
- compact(srcData) {
1328
- const dictionary = new Uint8Array(_QuintetLZ.DICTIONARY_SIZE);
1329
- dictionary.fill(_QuintetLZ.DICTIONARY_INIT);
1330
- let offset = _QuintetLZ.DICTIONARY_OFFSET;
1331
- let srcIx = 0;
1332
- let dstIx = 0;
1333
- const srcLen = srcData.length;
1334
- const outputBuffer = new Uint8Array(srcLen * 2);
1335
- outputBuffer[dstIx++] = srcLen & 255;
1336
- outputBuffer[dstIx++] = srcLen >> 8 & 255;
1337
- const bitStream = new shared.BitStream(outputBuffer, 2);
1338
- const getCommand = () => {
1339
- const maxLen = Math.min(srcLen - srcIx, 17);
1340
- if (maxLen < 2) {
1341
- return [0, 0];
1342
- }
1343
- let startByte = 0;
1344
- let bestLen = 0;
1345
- let bx = offset;
1346
- for (let i = 0; i < 256; i++, bx = bx + 1 & 255) {
1347
- let size = 0;
1348
- let bix = bx;
1349
- while (size < maxLen && dictionary[bix] === srcData[srcIx + size]) {
1350
- bix = bix + 1 & 255;
1351
- if (bix === offset) {
1352
- bix = bx;
1353
- }
1354
- size++;
1355
- }
1356
- if (size > bestLen) {
1357
- startByte = bx;
1358
- bestLen = size;
1359
- if (bestLen >= maxLen) {
1360
- break;
1361
- }
1362
- }
1363
- }
1364
- return [startByte, bestLen];
1365
- };
1366
- while (srcIx < srcLen) {
1367
- const [cmdStart, cmdLen] = getCommand();
1368
- if (cmdLen >= 2) {
1369
- bitStream.writeBit(false);
1370
- bitStream.writeByte(cmdStart);
1371
- bitStream.writeNibble(cmdLen - 2);
1372
- for (let i = 0; i < cmdLen; i++) {
1373
- dictionary[offset] = srcData[srcIx++];
1374
- offset = offset + 1 & 255;
1375
- }
1376
- } else {
1377
- bitStream.writeBit(true);
1378
- const val = srcData[srcIx++];
1379
- bitStream.writeByte(val);
1380
- dictionary[offset] = val;
1381
- offset = offset + 1 & 255;
1382
- }
1383
- }
1384
- bitStream.flush();
1385
- const finalSize = Math.max(dstIx, bitStream.currentPosition);
1386
- return outputBuffer.slice(0, finalSize);
1387
- }
1388
- };
1389
-
1390
- // src/compression/registry.ts
1391
- function registerCompressionProviders() {
1392
- shared.CompressionRegistry.register("QuintetLZ", () => new QuintetLZ());
1393
- }
1394
- registerCompressionProviders();
1395
-
1396
- // src/rom/extraction/blocks.ts
1397
- registerCompressionProviders();
1398
- var BlockReader = class {
1399
- constructor(romData, root) {
1400
- // Current Processing State (Database)
1401
- //public _currentBlock!: DbBlock;
1402
- //public _currentPart: DbPart | null = null;
1403
- this._partEnd = 0;
1404
- // ChunkFile Processing State
1405
- this._currentChunk = null;
1406
- this._currentAsmBlock = null;
1407
- this._enrichedChunks = [];
1408
- this._romDataReader = new RomDataReader(romData);
1409
- this._stateManager = new ProcessorStateManager();
1410
- this._referenceManager = new ReferenceManager(root);
1411
- this._root = root;
1412
- this._stringReader = new StringReader(this);
1413
- this._asmReader = new AsmReader(this);
1414
- this._typeParser = new TypeParser(this);
1415
- this.initializeOverrides();
1416
- this.initializeFileReferences();
1417
- }
1418
- static {
1419
- // Constants
1420
- this.REF_SEARCH_MAX_RANGE = 416;
1421
- }
1422
- static {
1423
- this.BANK_MASK_CHECK = 64;
1424
- }
1425
- static {
1426
- this.BYTE_DELIMITER_THRESHOLD = 256;
1427
- }
1428
- static {
1429
- this.BANK_HIGH_MEMORY_1 = 126;
1430
- }
1431
- static {
1432
- this.BANK_HIGH_MEMORY_2 = 127;
1433
- }
1434
- static {
1435
- this.POINTER_CHARACTERS = ["&", "@"];
1436
- }
1437
- static {
1438
- // Location regex pattern: _([A-Fa-f0-9]{6})
1439
- this.LOCATION_REGEX = /_([A-Fa-f0-9]{6})/;
1440
- }
1441
- // Backward Compatibility Properties
1442
- get AccumulatorFlags() {
1443
- return this._stateManager.accumulatorFlags;
1444
- }
1445
- get IndexFlags() {
1446
- return this._stateManager.indexFlags;
1447
- }
1448
- get BankNotes() {
1449
- return this._stateManager.bankNotes;
1450
- }
1451
- get StackPosition() {
1452
- return this._stateManager.stackPositions;
1453
- }
1454
- // Direct access to reference management collections
1455
- get _structTable() {
1456
- return this._referenceManager.structTable;
1457
- }
1458
- get _markerTable() {
1459
- return this._referenceManager.markerTable;
1460
- }
1461
- get _nameTable() {
1462
- return this._referenceManager.nameTable;
1463
- }
1464
- /**
1465
- * Processes predefined overrides for registers and bank notes
1466
- */
1467
- initializeOverrides() {
1468
- for (const over of Object.values(this._root.overrides)) {
1469
- switch (over.register) {
1470
- case shared.RegisterType.M:
1471
- this._stateManager.setAccumulatorFlag(over.location, over.value === 1);
1472
- break;
1473
- case shared.RegisterType.X:
1474
- this._stateManager.setIndexFlag(over.location, over.value === 1);
1475
- break;
1476
- case shared.RegisterType.B:
1477
- this._stateManager.setBankNote(over.location, over.value);
1478
- break;
1479
- }
1480
- }
1481
- }
1482
- /**
1483
- * Processes predefined file references
1484
- */
1485
- initializeFileReferences() {
1486
- for (const file of this._root.files) {
1487
- this._referenceManager.tryAddName(file.start, file.name);
1488
- }
1489
- }
1490
- /**
1491
- * Resolves mnemonic for a given address
1492
- */
1493
- resolveMnemonic(addr) {
1494
- if ((addr.bank & shared.Address.DATA_BANK_FLAG) !== 0) {
1495
- return;
1496
- }
1497
- let offset = addr.offset;
1498
- const label = this._root.mnemonics[offset];
1499
- if (!label) {
1500
- return;
1501
- }
1502
- const ix = indexOfAny(label, shared.RomProcessingConstants.OPERATORS);
1503
- if (ix >= 0) {
1504
- let opnd = parseInt(label.substring(ix + 1), 16);
1505
- const op = label[ix];
1506
- if (op === "-")
1507
- opnd = -opnd;
1508
- offset -= opnd;
1509
- }
1510
- this._currentChunk.mnemonics[offset] = label.substring(0, ix >= 0 ? ix : label.length);
1511
- }
1512
- /**
1513
- * Resolves name for a location (delegated to ReferenceManager)
1514
- */
1515
- resolveName(location, type, isBranch) {
1516
- return this._referenceManager.resolveName(location, type, isBranch);
1517
- }
1518
- /**
1519
- * Resolves include for a location
1520
- */
1521
- resolveInclude(loc, isBranch) {
1522
- const [outside, foundBlock, foundPart] = shared.ChunkFileUtils.isOutsideWithPart(this._enrichedChunks, this._currentChunk, loc);
1523
- if (outside && foundBlock && foundPart) {
1524
- this._currentAsmBlock.includes.add({ block: foundBlock, part: foundPart });
1525
- } else if (isBranch && !this._referenceManager.tryGetName(loc).found) {
1526
- const name = `loc_${loc.toString(16).toUpperCase().padStart(6, "0")}`;
1527
- this._referenceManager.tryAddName(loc, name);
1528
- }
1529
- }
1530
- /**
1531
- * Notes a type at a location and manages chunk references
1532
- */
1533
- noteType(loc, type, silent = false, reg) {
1534
- this._referenceManager.tryAddStruct(loc, type);
1535
- const nameResult = this._referenceManager.tryGetName(loc);
1536
- let name;
1537
- if (!nameResult.found) {
1538
- name = this._referenceManager.createTypeName(type, loc);
1539
- this._referenceManager.tryAddName(loc, name);
1540
- } else {
1541
- name = nameResult.referenceName;
1542
- }
1543
- if (!silent && type === shared.BlockReaderConstants.CODE_TYPE && reg) {
1544
- this.updateRegisterState(loc, reg);
1545
- }
1546
- return name;
1547
- }
1548
- updateRegisterState(loc, reg) {
1549
- if (reg.accumulatorFlag !== void 0) {
1550
- this._stateManager.tryAddAccumulatorFlag(loc, reg.accumulatorFlag);
1551
- }
1552
- if (reg.indexFlag !== void 0) {
1553
- this._stateManager.tryAddIndexFlag(loc, reg.indexFlag);
1554
- }
1555
- if (reg.stack.location > 0) {
1556
- this._stateManager.tryAddStackPosition(loc, reg.stack.location);
1557
- }
1558
- }
1559
- /**
1560
- * Checks if a delimiter has been reached
1561
- */
1562
- delimiterReached(delimiter) {
1563
- if (delimiter === void 0) {
1564
- return false;
1565
- }
1566
- if (delimiter >= shared.BlockReaderConstants.BYTE_DELIMITER_THRESHOLD) {
1567
- if (this._romDataReader.peekShort() === delimiter) {
1568
- this._romDataReader.position += 2;
1569
- return true;
1570
- }
1571
- } else if (this._romDataReader.peekByte() === delimiter) {
1572
- this._romDataReader.position++;
1573
- return true;
1574
- }
1575
- return false;
1576
- }
1577
- /**
1578
- * Checks if processing of the current part can continue
1579
- */
1580
- partCanContinue() {
1581
- return this._romDataReader.position < this._partEnd && !this._referenceManager.containsStruct(this._romDataReader.position);
1582
- }
1583
- analyzeAndResolve() {
1584
- this.createChunkFilesFromDatabase();
1585
- this.initializeBlocksAndParts();
1586
- this.analyzeChunkFiles();
1587
- this.resolveReferences();
1588
- return this._enrichedChunks;
1589
- }
1590
- /**
1591
- * Analyzes all blocks in the ROM
1592
- */
1593
- // private analyzeBlocks(): void {
1594
- // this.initializeBlocksAndParts();
1595
- // for (const chunk of this._enrichedChunks) {
1596
- // this._currentChunk = chunk;
1597
- // for (const part of chunk.parts || []) {
1598
- // this.processPart(part);
1599
- // }
1600
- // }
1601
- // }
1602
- /**
1603
- * Initializes blocks and parts with base references
1604
- */
1605
- initializeBlocksAndParts() {
1606
- for (const block of this._enrichedChunks) {
1607
- for (const part of block.parts || []) {
1608
- if (part.structName) {
1609
- this._referenceManager.tryAddStruct(part.location, part.structName);
1610
- }
1611
- if (part.label) {
1612
- this._referenceManager.tryAddName(part.location, part.label);
1613
- }
1614
- }
1615
- }
1616
- }
1617
- /**
1618
- * Processes a single part
1619
- */
1620
- processPart(part) {
1621
- this._currentAsmBlock = part;
1622
- this._romDataReader.position = part.location;
1623
- this._partEnd = part.location + part.size;
1624
- let current = part.structName || shared.BlockReaderConstants.BINARY_TYPE;
1625
- const chunks = [];
1626
- const reg = new Registers();
1627
- const bank = part.bank;
1628
- let last = null;
1629
- while (this._romDataReader.position < this._partEnd) {
1630
- const structResult = this._referenceManager.tryGetStruct(this._romDataReader.position);
1631
- if (structResult.found) {
1632
- current = structResult.chunkType;
1633
- } else if (last !== null) {
1634
- this.processContinuousEntry(current, reg, bank, last);
1635
- continue;
1636
- }
1637
- last = shared.createTableEntry(this._romDataReader.position);
1638
- chunks.push(last);
1639
- this.processNewEntry(current, reg, bank, last);
1640
- }
1641
- part.objList = chunks;
1642
- }
1643
- /**
1644
- * Processes a continuous entry (same type as previous)
1645
- */
1646
- processContinuousEntry(current, reg, bank, last) {
1647
- const obj = this._typeParser.parseType(current, reg, 0, bank);
1648
- if (!Array.isArray(last.object)) {
1649
- last.object = [last.object];
1650
- }
1651
- last.object.push(obj);
1652
- }
1653
- /**
1654
- * Processes a new entry
1655
- */
1656
- processNewEntry(current, reg, bank, last) {
1657
- let res = this._typeParser.parseType(current, reg, 0, bank);
1658
- if (shared.BlockReaderConstants.POINTER_CHARACTERS.includes(current[0]) && !Array.isArray(res)) {
1659
- res = [res];
1660
- }
1661
- last.object = res;
1662
- }
1663
- /**
1664
- * Creates ChunkFile objects from database structure
1665
- */
1666
- createChunkFilesFromDatabase() {
1667
- this._enrichedChunks = [];
1668
- this.createChunkFilesFromSfx();
1669
- this.createChunkFilesFromDbFiles();
1670
- this.createChunkFilesFromDbBlocks();
1671
- }
1672
- /**
1673
- * Creates binary ChunkFiles from DbFiles (enriched with rawData)
1674
- */
1675
- createChunkFilesFromSfx() {
1676
- let pos = this._root.config.sfxLocation;
1677
- const count = this._root.config.sfxCount;
1678
- const romData = this._romDataReader.romData;
1679
- const getSize = () => romData[pos++] | romData[pos++] << 8;
1680
- for (let i = 0; i < count; i++) {
1681
- const size = getSize();
1682
- const startPos = pos;
1683
- let remaining = size;
1684
- let end = pos + remaining;
1685
- let data;
1686
- if (end & shared.RomProcessingConstants.PAGE_SIZE) {
1687
- const endLen = end & 32767;
1688
- remaining -= endLen;
1689
- data = romData.slice(pos, pos + remaining);
1690
- pos += remaining + shared.RomProcessingConstants.PAGE_SIZE;
1691
- end = pos + endLen;
1692
- const data2 = romData.slice(pos, end);
1693
- data = new Uint8Array([...data, ...data2]);
1694
- } else {
1695
- data = romData.slice(pos, end);
1696
- }
1697
- pos = end;
1698
- const chunk = new shared.ChunkFile(`sfx${i.toString(16).toUpperCase().padStart(2, "0")}`, size, startPos, shared.BinType.Sound);
1699
- chunk.rawData = data;
1700
- this._enrichedChunks.push(chunk);
1701
- }
1702
- }
1703
- /**
1704
- * Creates binary ChunkFiles from DbFiles (enriched with rawData)
1705
- */
1706
- createChunkFilesFromDbFiles() {
1707
- for (const dbFile of this._root.files) {
1708
- const chunkFile = shared.createChunkFileFromDbFile(this._romDataReader.romData, this._root.compression, dbFile);
1709
- this._enrichedChunks.push(chunkFile);
1710
- }
1711
- }
1712
- /**
1713
- * Creates assembly ChunkFiles from DbBlocks (enriched with parts)
1714
- */
1715
- createChunkFilesFromDbBlocks() {
1716
- for (const block of this._root.blocks) {
1717
- const chunkFile = shared.createChunkFileFromDbBlock(block);
1718
- this._enrichedChunks.push(chunkFile);
1719
- }
1720
- }
1721
- /**
1722
- * Analyzes only assembly ChunkFiles (those with parts from DbBlocks)
1723
- */
1724
- analyzeChunkFiles() {
1725
- const assemblyChunks = this._enrichedChunks.filter((chunk) => chunk.type === shared.BinType.Assembly);
1726
- for (const chunkFile of assemblyChunks) {
1727
- this._currentChunk = chunkFile;
1728
- for (const asmBlock of chunkFile.parts || []) {
1729
- this._currentAsmBlock = asmBlock;
1730
- this.processPart(asmBlock);
1731
- }
1732
- }
1733
- }
1734
- // /**
1735
- // * Processes a single AsmBlock (similar to processPart)
1736
- // */
1737
- // private processAsmBlock(asmBlock: AsmBlock): void {
1738
- // this._romDataReader.position = asmBlock.location;
1739
- // this._partEnd = asmBlock.location + asmBlock.size;
1740
- // let current = asmBlock.structName || BlockReaderConstants.BINARY_TYPE;
1741
- // const chunks: TableEntry[] = [];
1742
- // const reg = new Registers();
1743
- // const bank = this._currentAsmBlock?.bank;
1744
- // let last: TableEntry | null = null;
1745
- // while (this._romDataReader.position < this._partEnd) {
1746
- // const structResult = this._referenceManager.tryGetStruct(this._romDataReader.position);
1747
- // if (structResult.found) {
1748
- // current = structResult.chunkType!;
1749
- // } else if (last !== null) {
1750
- // this.processContinuousEntry(current, reg, bank, last);
1751
- // continue;
1752
- // }
1753
- // last = createTableEntry(this._romDataReader.position);
1754
- // chunks.push(last);
1755
- // this.processNewEntry(current, reg, bank, last);
1756
- // }
1757
- // asmBlock.objList = chunks;
1758
- // }
1759
- /**
1760
- * Resolves references in assembly ChunkFiles only
1761
- */
1762
- resolveReferences() {
1763
- for (const chunkFile of this._enrichedChunks) {
1764
- this._currentChunk = chunkFile;
1765
- for (const asmBlock of chunkFile.parts || []) {
1766
- this._currentAsmBlock = asmBlock;
1767
- this.resolveObject(asmBlock.objList, false);
1768
- }
1769
- }
1770
- }
1771
- /**
1772
- * Resolves a single object and its references
1773
- */
1774
- resolveObject(obj, isBranch) {
1775
- if (typeof obj === "string") {
1776
- return;
1777
- }
1778
- if (Array.isArray(obj)) {
1779
- for (const o of obj) {
1780
- this.resolveObject(o, isBranch);
1781
- }
1782
- return;
1783
- }
1784
- if (obj instanceof shared.Address) {
1785
- this.resolveMnemonic(obj);
1786
- return;
1787
- }
1788
- if (typeof obj === "number") {
1789
- this.resolveInclude(obj, isBranch);
1790
- return;
1791
- }
1792
- if (obj instanceof shared.LocationWrapper) {
1793
- this.resolveInclude(obj.location, isBranch);
1794
- return;
1795
- }
1796
- if (obj && typeof obj === "object") {
1797
- if ("string" in obj && "type" in obj) {
1798
- this._stringReader.resolveString(obj, isBranch);
1799
- return;
1800
- }
1801
- if ("name" in obj && "parts" in obj) {
1802
- this.resolveObject(obj.parts, isBranch);
1803
- return;
1804
- }
1805
- if ("location" in obj && "object" in obj) {
1806
- this.resolveObject(obj.object, isBranch);
1807
- return;
1808
- }
1809
- if ("code" in obj && "operands" in obj) {
1810
- this.resolveOperationObject(obj);
1811
- return;
1812
- }
1813
- }
1814
- }
1815
- /**
1816
- * Resolves references in an operation object
1817
- */
1818
- resolveOperationObject(op) {
1819
- const branch = this.isBranchOperation(op);
1820
- for (const opnd of op.operands) {
1821
- this.resolveObject(opnd, branch);
1822
- }
1823
- }
1824
- /**
1825
- * Checks if an operation is a branch operation
1826
- */
1827
- isBranchOperation(op) {
1828
- return op.mode === "PCRelative" || op.mode === "PCRelativeLong" || op.mnem[0] === "J";
1829
- }
1830
- /**
1831
- * Hydrates registers with stored state
1832
- */
1833
- hydrateRegisters(reg) {
1834
- this._stateManager.hydrateRegisters(this._romDataReader.position, reg);
1835
- }
1836
- };
1837
- var PostProcessor = class {
1838
- constructor(reader) {
1839
- this._referenceManager = reader._referenceManager;
1840
- }
1841
- /**
1842
- * Execute post process directive on a block if present.
1843
- */
1844
- process(block) {
1845
- if (!block.postProcess || block.postProcess.trim() === "") {
1846
- return;
1847
- }
1848
- let signature = block.postProcess;
1849
- const parts = [];
1850
- const index = signature.indexOf("(");
1851
- if (index > 0) {
1852
- const endIx = signature.indexOf(")", index);
1853
- const params = signature.substring(
1854
- index + 1,
1855
- endIx >= 0 ? endIx : signature.length
1856
- );
1857
- if (params.length > 0) {
1858
- for (const p of params.split(",")) {
1859
- parts.push(p.trim());
1860
- }
1861
- }
1862
- signature = signature.substring(0, index);
1863
- }
1864
- const fn = this[signature];
1865
- if (typeof fn !== "function") {
1866
- throw new Error(`Unable to locate postprocess function ${signature}`);
1867
- }
1868
- fn.apply(this, [block, ...parts]);
1869
- }
1870
- /**
1871
- * Builds a lookup table from struct entries.
1872
- * Equivalent to PostProcessor.Lookup in C# implementation.
1873
- */
1874
- Lookup(block, keyIx, valueIx) {
1875
- const kix = parseInt(keyIx.trim());
1876
- const vix = parseInt(valueIx.trim());
1877
- if (!block.parts || block.parts.length === 0) {
1878
- throw new Error("Invalid block structure for Lookup post process");
1879
- }
1880
- const table = block.parts[0]?.objList;
1881
- const tableEntry = table && table[0];
1882
- const entries = tableEntry?.object;
1883
- if (!tableEntry || !entries) {
1884
- throw new Error("Invalid table structure for Lookup post process");
1885
- }
1886
- const newParts = [];
1887
- const newList = [];
1888
- newParts.push({ location: tableEntry.location, object: newList });
1889
- let eIx = 1;
1890
- for (const entry of entries) {
1891
- if (!entry || !Array.isArray(entry.parts)) {
1892
- continue;
1893
- }
1894
- const struct = entry;
1895
- let cIx = 0;
1896
- let key = null;
1897
- let value = null;
1898
- for (const obj of struct.parts) {
1899
- if (cIx === kix) {
1900
- if (obj && typeof obj === "object" && "value" in obj) {
1901
- key = obj.value;
1902
- } else {
1903
- key = obj;
1904
- }
1905
- } else if (cIx === vix) {
1906
- value = obj;
1907
- }
1908
- cIx++;
1909
- }
1910
- if (key === null || value === null) {
1911
- throw new Error("Could not locate key or value for transform");
1912
- }
1913
- const name = `entry_${key.toString(16).toUpperCase().padStart(2, "0")}`;
1914
- const loc = tableEntry.location + eIx;
1915
- newParts.push({ location: loc, object: value });
1916
- this._referenceManager.nameTable.set(loc, name);
1917
- while (newList.length <= key) {
1918
- newList.push(new shared.Word(0));
1919
- }
1920
- newList[key] = `&${name}`;
1921
- eIx++;
1922
- }
1923
- block.parts[0].objList = newParts;
1924
- }
1925
- };
1926
-
1927
- // src/rom/extraction/writer.ts
1928
- var isWindows = (() => {
1929
- if (typeof process !== "undefined" && process.platform) {
1930
- return process.platform === "win32";
1931
- }
1932
- if (typeof navigator !== "undefined" && navigator.userAgent) {
1933
- return navigator.userAgent.includes("Windows");
1934
- }
1935
- return false;
1936
- })();
1937
- var NEWLINE = isWindows ? "\r\n" : "\n";
1938
- var ObjectType = /* @__PURE__ */ ((ObjectType2) => {
1939
- ObjectType2["TableEntryArray"] = "TableEntryArray";
1940
- ObjectType2["StructDef"] = "StructDef";
1941
- ObjectType2["OpArray"] = "OpArray";
1942
- ObjectType2["LocationWrapper"] = "LocationWrapper";
1943
- ObjectType2["Address"] = "Address";
1944
- ObjectType2["StringWrapper"] = "StringWrapper";
1945
- ObjectType2["ByteArray"] = "ByteArray";
1946
- ObjectType2["Array"] = "Array";
1947
- ObjectType2["String"] = "String";
1948
- ObjectType2["Number"] = "Number";
1949
- ObjectType2["TypedNumber"] = "TypedNumber";
1950
- return ObjectType2;
1951
- })(ObjectType || {});
1952
- var BlockWriter = class {
1953
- constructor(reader) {
1954
- this._isInline = false;
1955
- this._currentPart = null;
1956
- this._blockReader = reader;
1957
- this._root = reader._root;
1958
- this._referenceManager = reader._referenceManager;
1959
- this._postProcessor = new PostProcessor(reader);
1960
- }
1961
- // async writeBlocks(outPath: string): Promise<void> {
1962
- // const res = DbRootUtils.getPath(this._root, BinType.Assembly);
1963
- // const folderPath = join(outPath, res.folder);
1964
- // for (const block of this._root.blocks) {
1965
- // const groupedFolderPath = block.group
1966
- // ? join(folderPath, block.group)
1967
- // : folderPath;
1968
- // await fs.mkdir(groupedFolderPath, { recursive: true });
1969
- // const outFile = join(groupedFolderPath, `${block.name}.${res.extension}`);
1970
- // // Check if file exists, skip if it does
1971
- // try {
1972
- // await fs.access(outFile);
1973
- // continue;
1974
- // } catch {
1975
- // // File doesn't exist, continue with creation
1976
- // }
1977
- // const content = this.generateAsm(block);
1978
- // await fs.writeFile(outFile, content);
1979
- // }
1980
- // }
1981
- generateAllAsm(chunkFiles) {
1982
- const lines = [];
1983
- for (const block of chunkFiles) {
1984
- lines.push({
1985
- name: block.name,
1986
- group: block.group,
1987
- text: this.generateAsm(block)
1988
- });
1989
- }
1990
- return lines;
1991
- }
1992
- generateAsm(block) {
1993
- if (!block.parts) {
1994
- throw new Error("Invalid block structure for generateAsm");
1995
- }
1996
- const lines = [];
1997
- if (block.bank !== void 0) {
1998
- lines.push(`?BANK ${block.bank.toString(16).toUpperCase().padStart(2, "0")}`);
1999
- }
2000
- const includes = shared.ChunkFileUtils.getIncludes(block);
2001
- if (includes && includes.length > 0) {
2002
- lines.push("");
2003
- for (const inc of includes) {
2004
- lines.push(`?INCLUDE '${inc.name}'`);
2005
- }
2006
- }
2007
- const mnemonics = this.getMnemonicsForBlock(block);
2008
- if (mnemonics && mnemonics.length > 0) {
2009
- lines.push("");
2010
- for (const [name, address] of mnemonics) {
2011
- const paddedName = name.padEnd(30, " ");
2012
- lines.push(`!${paddedName} ${address.toString(16).toUpperCase().padStart(4, "0")}`);
2013
- }
2014
- }
2015
- this._postProcessor.process(block);
2016
- for (const part of block.parts || []) {
2017
- this._currentPart = part;
2018
- this._isInline = true;
2019
- lines.push("");
2020
- lines.push("---------------------------------------------");
2021
- const objectLines = this.writeObject(part.objList, -1);
2022
- lines.push(...objectLines);
2023
- }
2024
- let content = lines.join(NEWLINE);
2025
- if (block.transforms) {
2026
- for (const x of block.transforms) {
2027
- if (x.key && x.value) {
2028
- const regex = new RegExp(x.key, "g");
2029
- content = content.replace(regex, x.value);
2030
- }
2031
- }
2032
- }
2033
- if (!isWindows) {
2034
- content = content.replace(/\r/g, "");
2035
- }
2036
- return content;
2037
- }
2038
- getMnemonicsForBlock(block) {
2039
- if (!block.mnemonics) {
2040
- return [];
2041
- }
2042
- return Object.entries(block.mnemonics).map(([k, v]) => [v, parseInt(k, 10)]).sort((a, b) => a[1] - b[1]);
2043
- }
2044
- resolveOperand(op, obj, isBranch = false) {
2045
- if (obj instanceof shared.TypedNumber) {
2046
- const len = obj.size * 2;
2047
- return obj.value.toString(16).toUpperCase().padStart(len, "0");
2048
- }
2049
- if (typeof obj === "number") {
2050
- if (op.mode === "Immediate") {
2051
- return obj;
2052
- }
2053
- return this._blockReader.resolveName(obj, shared.AddressType.Address, isBranch);
2054
- }
2055
- if (this.getObjectType(obj) === "LocationWrapper" /* LocationWrapper */) {
2056
- const lw = obj;
2057
- return this._blockReader.resolveName(lw.location, lw.type, isBranch);
2058
- }
2059
- if (this.getObjectType(obj) === "Address" /* Address */) {
2060
- const addr = obj;
2061
- if (op.size === 4) {
2062
- return addr;
2063
- }
2064
- if (addr.isCodeBank && addr.offset < shared.Address.UPPER_BANK) {
2065
- const label = this._root.mnemonics[addr.offset];
2066
- if (label) {
2067
- return label;
2068
- }
2069
- }
2070
- return addr.offset;
2071
- }
2072
- if (obj && obj instanceof shared.TypedNumber) {
2073
- return obj;
2074
- }
2075
- return obj;
2076
- }
2077
- getObjectType(obj) {
2078
- if (obj === null || obj === void 0) {
2079
- return "String" /* String */;
2080
- }
2081
- if (obj._tag) {
2082
- return obj._tag;
2083
- }
2084
- if (obj instanceof shared.TypedNumber) {
2085
- return "TypedNumber" /* TypedNumber */;
2086
- }
2087
- if (Array.isArray(obj)) {
2088
- if (obj.length > 0) {
2089
- if (obj[0] && typeof obj[0] === "object" && "location" in obj[0] && "object" in obj[0]) {
2090
- return "TableEntryArray" /* TableEntryArray */;
2091
- }
2092
- if (obj[0] instanceof shared.Op) {
2093
- return "OpArray" /* OpArray */;
2094
- }
2095
- }
2096
- return "Array" /* Array */;
2097
- }
2098
- if (obj instanceof shared.Op) {
2099
- return "OpArray" /* OpArray */;
2100
- }
2101
- if (obj instanceof shared.LocationWrapper) {
2102
- return "LocationWrapper" /* LocationWrapper */;
2103
- }
2104
- if (obj instanceof shared.Address) {
2105
- return "Address" /* Address */;
2106
- }
2107
- if (obj instanceof Uint8Array) {
2108
- return "ByteArray" /* ByteArray */;
2109
- }
2110
- if (obj && typeof obj === "object") {
2111
- if ("string" in obj && "type" in obj && "marker" in obj && "location" in obj) {
2112
- return "StringWrapper" /* StringWrapper */;
2113
- }
2114
- if ("name" in obj && "parts" in obj) {
2115
- return "StructDef" /* StructDef */;
2116
- }
2117
- }
2118
- if (typeof obj === "string") {
2119
- return "String" /* String */;
2120
- }
2121
- if (typeof obj === "number") {
2122
- return "Number" /* Number */;
2123
- }
2124
- return "String" /* String */;
2125
- }
2126
- writeObject(obj, depth, isBranch = false) {
2127
- const lines = [];
2128
- const objType = this.getObjectType(obj);
2129
- let objLines;
2130
- switch (objType) {
2131
- case "TableEntryArray" /* TableEntryArray */:
2132
- objLines = this.writeTableEntryArray(obj, depth);
2133
- break;
2134
- case "StructDef" /* StructDef */:
2135
- objLines = this.writeStructDef(obj, depth);
2136
- break;
2137
- case "OpArray" /* OpArray */:
2138
- objLines = this.writeOpArray(obj, depth);
2139
- break;
2140
- case "LocationWrapper" /* LocationWrapper */:
2141
- objLines = [
2142
- this._blockReader.resolveName(
2143
- obj.location,
2144
- obj.type,
2145
- isBranch
2146
- )
2147
- ];
2148
- break;
2149
- case "Address" /* Address */:
2150
- objLines = [`$${obj.toString()}`];
2151
- break;
2152
- case "ByteArray" /* ByteArray */:
2153
- objLines = [
2154
- `#${Array.from(obj).map((b) => b.toString(16).toUpperCase().padStart(2, "0")).join("")}`
2155
- ];
2156
- break;
2157
- case "StringWrapper" /* StringWrapper */:
2158
- objLines = this.writeStringWrapper(obj);
2159
- break;
2160
- case "Array" /* Array */:
2161
- objLines = this.writeArray(obj, depth);
2162
- break;
2163
- case "Number" /* Number */:
2164
- objLines = this.writeNumber(obj);
2165
- break;
2166
- case "TypedNumber" /* TypedNumber */:
2167
- objLines = this.writeTypedNumber(obj);
2168
- break;
2169
- case "String" /* String */:
2170
- objLines = [String(obj)];
2171
- break;
2172
- default:
2173
- objLines = [String(obj)];
2174
- break;
2175
- }
2176
- lines.push(...objLines);
2177
- return lines;
2178
- }
2179
- writeTableEntryArray(tGroup, depth) {
2180
- const lines = [];
2181
- const isInline = this._isInline;
2182
- for (const t of tGroup) {
2183
- const nameResult = this._referenceManager.tryGetName(t.location);
2184
- const name = nameResult.found ? nameResult.referenceName : `loc_${t.location.toString(16).toUpperCase().padStart(6, "0")}`;
2185
- const objectLines = this.writeObject(t.object, depth + 1);
2186
- lines.push("");
2187
- lines.push(`${name} ${objectLines[0]}`);
2188
- if (objectLines.length > 1) {
2189
- lines.push(...objectLines.slice(1));
2190
- }
2191
- }
2192
- this._isInline = isInline;
2193
- return lines;
2194
- }
2195
- writeStructDef(structObj, depth) {
2196
- const parts = [];
2197
- const isInline = this._isInline;
2198
- this._isInline = true;
2199
- for (const part of structObj.parts) {
2200
- const partLines = this.writeObject(part, depth);
2201
- parts.push(partLines.join(NEWLINE));
2202
- }
2203
- const line = `${structObj.name} < ${parts.join(", ")} >`;
2204
- this._isInline = isInline;
2205
- return [line];
2206
- }
2207
- writeOpArray(opList, depth) {
2208
- const lines = [];
2209
- lines.push("{");
2210
- const isInline = this._isInline;
2211
- this._isInline = true;
2212
- let first = true;
2213
- for (const op of opList) {
2214
- if (first) {
2215
- first = false;
2216
- } else {
2217
- const labelResult = this._referenceManager.tryGetName(op.location);
2218
- if (labelResult.found) {
2219
- lines.push("");
2220
- lines.push(` ${labelResult.referenceName}:`);
2221
- }
2222
- }
2223
- let opLine = ` ${op.mnem} `;
2224
- if (op.copDef) {
2225
- opLine += `[${op.copDef.mnem}]`;
2226
- if (op.operands && op.operands.length > 1) {
2227
- const operandStrings = [];
2228
- for (let i = 1; i < op.operands.length; i++) {
2229
- const operandLines = this.writeObject(op.operands[i], depth + 1, false);
2230
- operandStrings.push(operandLines[0]);
2231
- }
2232
- opLine += ` ( ${operandStrings.join(", ")} )`;
2233
- }
2234
- } else if (op.operands && op.operands.length > 0) {
2235
- const isBr = op.mnem[0] === "J" || op.mode === "PCRelative" || op.mode === "PCRelativeLong";
2236
- const resolvedOperand = this.resolveOperand(op, op.operands[0], isBr);
2237
- const format = this._root.addrLookup[op.mode]?.formatString;
2238
- if (format) {
2239
- let actualFormat = format;
2240
- if (op.mode === "Immediate" && op.size === 3) {
2241
- actualFormat = format.replace("X2", "X4");
2242
- }
2243
- const resolvedSecondOperand = op.operands.length > 1 ? this.resolveOperand(op, op.operands[1], isBr) : void 0;
2244
- opLine += this.formatOperand(actualFormat, [resolvedOperand, resolvedSecondOperand]);
2245
- } else {
2246
- opLine += this.formatDefaultOperand(resolvedOperand, op.size);
2247
- }
2248
- }
2249
- lines.push(opLine);
2250
- }
2251
- lines.push("}");
2252
- this._isInline = isInline;
2253
- return lines;
2254
- }
2255
- formatDefaultOperand(operand, size) {
2256
- if (typeof operand === "string") {
2257
- return operand;
2258
- }
2259
- if (operand && operand instanceof shared.TypedNumber) {
2260
- return this.formatTypedNumber(operand);
2261
- }
2262
- if (typeof operand === "number") {
2263
- const hexSize = (size - 1) * 2;
2264
- const hex = operand.toString(16).toUpperCase().padStart(hexSize, "0");
2265
- return `$${hex}`;
2266
- }
2267
- return String(operand);
2268
- }
2269
- writeStringWrapper(stringObj) {
2270
- let str = stringObj.string;
2271
- const stringReferenceChars = ["~", "^"];
2272
- for (const char of stringReferenceChars) {
2273
- let ix = str.indexOf(char);
2274
- while (ix >= 0) {
2275
- const hexStr = str.substring(ix + 1, ix + 7);
2276
- const rawAddr = parseInt(hexStr, 16);
2277
- const adrs = new shared.Address(rawAddr >> 16 & 255, rawAddr & 65535);
2278
- if (adrs.space === shared.AddressSpace.ROM) {
2279
- const addressType = char === "^" ? shared.AddressType.Offset : shared.AddressType.Address;
2280
- const location = adrs.toInt();
2281
- const name = this._blockReader.resolveName(location, addressType, false);
2282
- str = str.replace(str.substring(ix, ix + 7), name);
2283
- } else {
2284
- throw new Error("Unsupported address space");
2285
- }
2286
- ix = str.indexOf(char, ix + 7);
2287
- }
2288
- }
2289
- const refChar = stringObj.type.delimiter;
2290
- let marker = stringObj.marker;
2291
- if (marker <= 0) {
2292
- const markerResult = this._referenceManager.tryGetMarker(stringObj.location);
2293
- marker = markerResult.found ? markerResult.offset || 0 : 0;
2294
- }
2295
- if (marker > 0) {
2296
- let six = 0;
2297
- let mix = 0;
2298
- while (mix < marker) {
2299
- if (str[six] === "[") {
2300
- const eix = str.indexOf("]", ++six);
2301
- const parts = str.substring(six, eix).split(/[,: ]/);
2302
- const cmd = Object.values(stringObj.type.commands).find((x) => x.value === parts[0]);
2303
- if (cmd && cmd.types) {
2304
- for (const t of cmd.types) {
2305
- switch (t) {
2306
- case shared.MemberType.Byte:
2307
- mix += 1;
2308
- break;
2309
- case shared.MemberType.Word:
2310
- case shared.MemberType.Offset:
2311
- mix += 2;
2312
- break;
2313
- case shared.MemberType.Address:
2314
- mix += 3;
2315
- break;
2316
- case shared.MemberType.Binary:
2317
- mix += parts.length - 1;
2318
- break;
2319
- default:
2320
- throw new Error("Unsupported member type");
2321
- }
2322
- }
2323
- }
2324
- six = eix + 1;
2325
- mix++;
2326
- } else {
2327
- six++;
2328
- mix++;
2329
- }
2330
- }
2331
- str = str.substring(0, six) + "[::]" + str.substring(six);
2332
- }
2333
- return [`${refChar}${str}${refChar}`];
2334
- }
2335
- writeArray(arr, depth) {
2336
- const lines = [];
2337
- const indent = " ".repeat(Math.max(0, depth));
2338
- const isInline = this._isInline;
2339
- lines.push("[");
2340
- this._isInline = false;
2341
- for (let i = 0; i < arr.length; i++) {
2342
- const objLines = this.writeObject(arr[i], depth + 1);
2343
- for (const line of objLines) {
2344
- lines.push(indent + " " + line);
2345
- }
2346
- lines[lines.length - 1] += ` ;${i.toString(16).toUpperCase().padStart(2, "0")}`;
2347
- }
2348
- lines.push(indent + "]");
2349
- this._isInline = isInline;
2350
- return lines;
2351
- }
2352
- writeNumber(num) {
2353
- if (num <= 255) {
2354
- return [`#${num.toString(16).toUpperCase().padStart(2, "0")}`];
2355
- }
2356
- if (num <= 65535) {
2357
- return [`#$${num.toString(16).toUpperCase().padStart(4, "0")}`];
2358
- }
2359
- return [`#$${num.toString(16).toUpperCase().padStart(6, "0")}`];
2360
- }
2361
- formatTypedNumber(num) {
2362
- if (typeof num.value === "number") {
2363
- const width = num.size * 2;
2364
- const hex = num.value.toString(16).toUpperCase().padStart(width, "0");
2365
- return num.size === 1 ? `#${hex}` : `#$${hex}`;
2366
- }
2367
- return `#${num.value}`;
2368
- }
2369
- writeTypedNumber(num) {
2370
- return [this.formatTypedNumber(num)];
2371
- }
2372
- formatOperand(format, operands) {
2373
- return format.replace(/\{(\d+)(?::([^}]+))?\}/g, (match, index, formatSpec) => {
2374
- const operand = operands[parseInt(index)];
2375
- if (operand && typeof operand === "object" && operand._tag && "value" in operand) {
2376
- const value = operand.value;
2377
- if (formatSpec && typeof value === "number" && formatSpec.startsWith("X")) {
2378
- const width = parseInt(formatSpec.substring(1)) || 2;
2379
- return value.toString(16).toUpperCase().padStart(width, "0");
2380
- }
2381
- return String(value);
2382
- }
2383
- if (formatSpec && typeof operand === "number") {
2384
- if (formatSpec.startsWith("X")) {
2385
- const width = parseInt(formatSpec.substring(1)) || 2;
2386
- return operand.toString(16).toUpperCase().padStart(width, "0");
2387
- }
2388
- }
2389
- return String(operand);
2390
- });
2391
- }
2392
- };
2393
- var RomLayout = class _RomLayout {
2394
- constructor(files) {
2395
- this.bestResult = new Array(512).fill(0);
2396
- this.bestSample = new Array(512).fill(0);
2397
- this.currentBank = 0;
2398
- this.currentUpper = false;
2399
- this.bestDepth = 0;
2400
- this.bestOffset = 0;
2401
- this.bestRemain = 0;
2402
- this.unmatchedFiles = Array.from(files).filter((x) => (x.size || 0) > 0).sort((a, b) => {
2403
- const aAsm = a.parts ? 0 : 1;
2404
- const bAsm = b.parts ? 0 : 1;
2405
- if (aAsm !== bAsm) return aAsm - bAsm;
2406
- if (b.size !== a.size) return b.size - a.size;
2407
- if (a.location !== b.location) return a.location - b.location;
2408
- return a.name.localeCompare(b.name);
2409
- });
2410
- }
2411
- static {
2412
- this.MIN_ACCEPTED_REMAINING = 32;
2413
- }
2414
- organize() {
2415
- for (let page = 0; page < 128; page++) {
2416
- if (this.unmatchedFiles.length === 0) break;
2417
- let remain = shared.RomProcessingConstants.PAGE_SIZE;
2418
- if (page === 1) remain -= shared.RomProcessingConstants.SNES_HEADER_SIZE;
2419
- this.currentUpper = (page & 1) !== 0;
2420
- this.currentBank = page >> 1;
2421
- this.bestDepth = 0;
2422
- this.bestRemain = remain;
2423
- this.bestOffset = 0;
2424
- const start = page << 15;
2425
- this.testDepth(0, 0, remain, this.currentUpper);
2426
- if (this.currentUpper) {
2427
- this.bestOffset = this.bestDepth;
2428
- this.testDepth(0, this.bestDepth, this.bestRemain, false);
2429
- }
2430
- let position = start;
2431
- for (let i = 0; i < this.bestDepth; ) {
2432
- const file = this.unmatchedFiles[this.bestResult[i++]];
2433
- file.location = position;
2434
- console.log(` ${position.toString(16).toUpperCase().padStart(6, "0")}: ${file.name}`);
2435
- position += file.size || 0;
2436
- }
2437
- console.log(`Page ${start.toString(16).toUpperCase().padStart(6, "0")} matched with ${this.bestDepth} files ${this.bestRemain} remaining`);
2438
- this.commitPage();
2439
- }
2440
- if (this.unmatchedFiles.length > 0) {
2441
- const names = this.unmatchedFiles.map((x) => x.name).join("\r\n");
2442
- throw new Error(`Unable to match ${this.unmatchedFiles.length} files\r
2443
- ${names}`);
2444
- }
2445
- }
2446
- testDepth(startIndex, depth, remain, asmMode) {
2447
- for (let fileIndex = startIndex; fileIndex < this.unmatchedFiles.length; fileIndex++) {
2448
- const file = this.unmatchedFiles[fileIndex];
2449
- const fileSize = file.size || 0;
2450
- if (fileSize > remain) continue;
2451
- if (file.parts) {
2452
- if (!asmMode) {
2453
- if (!this.currentUpper || (file.bank ?? -1) >= 0) continue;
2454
- } else if (file.bank !== this.currentBank) {
2455
- continue;
2456
- }
2457
- } else if (asmMode) {
2458
- continue;
2459
- } else if (file.upper && !this.currentUpper) {
2460
- continue;
2461
- }
2462
- let inList = false;
2463
- for (let y = this.bestOffset; --y >= 0; ) {
2464
- if (this.bestResult[y] === fileIndex) {
2465
- inList = true;
2466
- break;
2467
- }
2468
- }
2469
- if (inList) continue;
2470
- this.bestSample[depth] = fileIndex;
2471
- const newRemain = remain - fileSize;
2472
- if (newRemain < this.bestRemain) {
2473
- this.bestRemain = newRemain;
2474
- this.bestDepth = depth + 1;
2475
- for (let i = this.bestOffset; i < this.bestDepth; i++) {
2476
- this.bestResult[i] = this.bestSample[i];
2477
- }
2478
- }
2479
- if (newRemain < _RomLayout.MIN_ACCEPTED_REMAINING) return true;
2480
- if (this.testDepth(fileIndex + 1, depth + 1, newRemain, asmMode)) return true;
2481
- }
2482
- return true;
2483
- }
2484
- commitPage() {
2485
- if (this.bestOffset > 0) {
2486
- for (let i = this.bestDepth; --i >= 0; ) {
2487
- let lastY = 0;
2488
- let lastX = 0;
2489
- let y = 0;
2490
- for (let x = this.bestDepth; --x >= 0; ) {
2491
- y = this.bestResult[x];
2492
- if (y > lastY) {
2493
- lastY = y;
2494
- lastX = x;
2495
- }
2496
- }
2497
- this.bestResult[lastX] = 0;
2498
- this.unmatchedFiles.splice(lastY, 1);
2499
- }
2500
- } else {
2501
- for (let i = this.bestDepth; --i >= 0; ) {
2502
- this.unmatchedFiles.splice(this.bestResult[i], 1);
2503
- }
2504
- }
2505
- }
2506
- };
2507
- var RomProcessor = class _RomProcessor {
2508
- constructor(writer) {
2509
- this.writer = writer;
2510
- }
2511
- async repack(allFiles) {
2512
- const patches = allFiles.filter((x) => x.type === shared.BinType.Patch);
2513
- const asmFiles = allFiles.filter((x) => !!x.parts);
2514
- _RomProcessor.applyPatches(asmFiles, patches);
2515
- for (const asm of asmFiles) {
2516
- shared.ChunkFileUtils.calculateSize(asm);
2517
- }
2518
- const layout = new RomLayout(allFiles);
2519
- layout.organize();
2520
- for (const file of asmFiles) {
2521
- shared.ChunkFileUtils.rebase(file);
2522
- }
2523
- for (const f of asmFiles) {
2524
- const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
2525
- f.includeLookup = /* @__PURE__ */ new Map();
2526
- for (const b of includeBlocks) {
2527
- if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
2528
- }
2529
- for (const b of (f.parts || []).filter((x) => !!x.label)) {
2530
- if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
2531
- }
2532
- }
2533
- const blockLookup = /* @__PURE__ */ new Map();
2534
- for (const f of allFiles) {
2535
- blockLookup.set(f.name.toUpperCase(), f.location);
2536
- }
2537
- for (const file of allFiles) {
2538
- await this.writer.writeFile(file, blockLookup);
2539
- }
2540
- const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
2541
- for (const ep of this.writer.entryPoints) {
2542
- const match = entryBlocks.find((b) => b.label === ep.name);
2543
- if (match) this.writer.writeTransform(ep.location, match.location & 65535);
2544
- }
2545
- }
2546
- static applyPatches(asmFiles, patches) {
2547
- for (const patch of patches.filter((x) => x.includes && x.includes.size > 0)) {
2548
- let file = null;
2549
- let dstIx = -1;
2550
- const inc = asmFiles.filter((x) => patch.includes.has(x.name.toUpperCase()));
2551
- for (let ix = 0; patch.parts && ix < patch.parts.length; ) {
2552
- const block = patch.parts[ix];
2553
- let match = null;
2554
- if (block.label) {
2555
- for (const i of inc) {
2556
- if (!i.parts) continue;
2557
- for (let y = 0; y < i.parts.length; y++) {
2558
- const check = i.parts[y];
2559
- if (check.label === block.label) {
2560
- file = i;
2561
- dstIx = y;
2562
- match = check;
2563
- break;
2564
- }
2565
- }
2566
- }
2567
- }
2568
- if (match) {
2569
- file.parts[dstIx++] = block;
2570
- } else if (dstIx >= 0) {
2571
- file.parts.splice(dstIx++, 0, block);
2572
- } else {
2573
- ix++;
2574
- continue;
2575
- }
2576
- file.includes = file.includes || /* @__PURE__ */ new Set();
2577
- file.includes.add(patch.name.toUpperCase());
2578
- patch.parts.splice(ix, 1);
2579
- }
2580
- }
2581
- }
2582
- };
2583
- var RomWriter = class {
2584
- constructor(entryPoints, cartName, makerCode) {
2585
- this.cartName = cartName;
2586
- this.makerCode = makerCode;
2587
- this.entryPoints = entryPoints;
2588
- this.outBuffer = new Uint8Array(4194304);
2589
- }
2590
- async repack(files) {
2591
- const processor = new RomProcessor(this);
2592
- await processor.repack(files);
2593
- this.writeHeader();
2594
- this.writeEntryPoints(files.filter((x) => !!x.parts));
2595
- this.writeChecksum();
2596
- return this.outBuffer;
2597
- }
2598
- writeHeader() {
2599
- const buf = this.outBuffer;
2600
- let pos = 65456;
2601
- this.writeAscii(this.makerCode.padEnd(6, " "), pos);
2602
- pos += 6;
2603
- for (let i = 0; i < 10; i++) buf[pos++] = 0;
2604
- this.writeAscii(this.cartName.toUpperCase().padEnd(21, " "), pos);
2605
- pos += 21;
2606
- buf[pos++] = 49;
2607
- buf[pos++] = 2;
2608
- buf[pos++] = 12;
2609
- buf[pos++] = 3;
2610
- buf[pos++] = 1;
2611
- buf[pos++] = 51;
2612
- buf[pos++] = 0;
2613
- }
2614
- writeChecksum() {
2615
- const buf = this.outBuffer;
2616
- buf[65500] = 255;
2617
- buf[65501] = 255;
2618
- buf[65502] = 0;
2619
- buf[65503] = 0;
2620
- let sum = 0;
2621
- for (let i = 0; i < buf.length; i++) sum += buf[i];
2622
- buf[65502] = sum & 255;
2623
- buf[65503] = sum >> 8 & 255;
2624
- const comp = ~sum;
2625
- buf[65500] = comp & 255;
2626
- buf[65501] = comp >> 8 & 255;
2627
- }
2628
- // private async generatePatch(): Promise<void> {
2629
- // const flips = this._projectRoot.flipsPath;
2630
- // if (!flips) return;
2631
- // if (typeof process === 'undefined' || !process.versions?.node) return;
2632
- // const { spawn } = await import('child_process');
2633
- // const { resolve } = await import('path');
2634
- // this.bpsPath = `${this._projectRoot.baseDir}/${this._projectRoot.name}.bps`;
2635
- // await new Promise<void>((resolvePromise) => {
2636
- // const p = spawn(flips, ['--create', '--bps', `${this._projectRoot.romPath}`, `${this.romPath}`, `${this.bpsPath}`], {
2637
- // stdio: ['ignore', 'pipe', 'pipe']
2638
- // });
2639
- // p.on('close', () => resolvePromise());
2640
- // });
2641
- // }
2642
- writeEntryPoints(asmFiles) {
2643
- this.outBuffer;
2644
- const entryBlocks = asmFiles.filter((x) => (x.bank ?? -1) === 0).flatMap((x) => x.parts || []).filter((b) => !!b.label);
2645
- for (const ep of this.entryPoints) {
2646
- const match = entryBlocks.find((b) => b.label === ep.name);
2647
- if (match) this.writeTransform(ep.location, match.location & 65535);
2648
- }
2649
- }
2650
- writeTransform(location, value, size) {
2651
- const buf = this.outBuffer;
2652
- if (size === void 0) {
2653
- if (value <= 255) {
2654
- size = 1;
2655
- } else if (value <= 65535) {
2656
- size = 2;
2657
- } else if (value <= 16777215) {
2658
- size = 3;
2659
- } else {
2660
- size = 4;
2661
- }
2662
- }
2663
- switch (size) {
2664
- case 1:
2665
- buf[location] = value & 255;
2666
- break;
2667
- case 2:
2668
- buf[location] = value & 255;
2669
- buf[location + 1] = value >> 8 & 255;
2670
- break;
2671
- case 3:
2672
- buf[location] = value & 255;
2673
- buf[location + 1] = value >> 8 & 255;
2674
- buf[location + 2] = value >> 16 & 255;
2675
- break;
2676
- case 4:
2677
- buf[location] = value & 255;
2678
- buf[location + 1] = value >> 8 & 255;
2679
- buf[location + 2] = value >> 16 & 255;
2680
- buf[location + 3] = value >> 24 & 255;
2681
- break;
2682
- default:
2683
- throw new Error(`Invalid size ${size} for writeTransform`);
2684
- }
2685
- }
2686
- async writeFile(file, _chunkLookup) {
2687
- const start = file.location;
2688
- let pos = start;
2689
- const buf = this.outBuffer;
2690
- if (file.rawData) {
2691
- const data = file.rawData;
2692
- let remain = file.size;
2693
- let srcPos = 0;
2694
- if (file.type === shared.BinType.Tilemap) {
2695
- remain -= 2;
2696
- buf[pos++] = data[srcPos++];
2697
- buf[pos++] = data[srcPos++];
2698
- } else if (file.type === shared.BinType.Meta17) {
2699
- remain -= 4;
2700
- for (let i = 0; i < 4; i++) buf[pos++] = data[srcPos++];
2701
- } else if (file.type === shared.BinType.Sound) {
2702
- remain -= 2;
2703
- buf[pos++] = remain & 255;
2704
- buf[pos++] = remain >> 8 & 255;
2705
- }
2706
- if (file.compressed !== void 0) {
2707
- remain -= 2;
2708
- const inverse = 0 - remain & 65535;
2709
- buf[pos++] = inverse & 255;
2710
- buf[pos++] = inverse >> 8 & 255;
2711
- }
2712
- while (remain > 0) {
2713
- buf[pos++] = data[srcPos++];
2714
- remain--;
2715
- }
2716
- } else {
2717
- if (file.parts && file.parts.length > 0) {
2718
- if (file.parts[0].location !== file.location && (file.location || 0) !== 0) {
2719
- throw new Error("Assembly was not based properly");
2720
- }
2721
- this.parseAssembly(file.parts, _chunkLookup, file.includeLookup);
2722
- }
2723
- }
2724
- return pos - start;
2725
- }
2726
- // No address mapping required: layout assigns absolute file offsets
2727
- writeAscii(text, pos) {
2728
- const buf = this.outBuffer;
2729
- for (let i = 0; i < text.length; i++) buf[pos + i] = text.charCodeAt(i) & 255;
2730
- }
2731
- /**
2732
- * Parse assembly blocks and write binary data to output buffer
2733
- * Converted from ext/GaiaLib/Rom/Rebuild/RomWriter.cs ParseAssembly method
2734
- */
2735
- parseAssembly(blocks, chunkLookup, includeLookup) {
2736
- if (!blocks) {
2737
- throw new Error("Assembly has not been parsed");
2738
- }
2739
- const buf = this.outBuffer;
2740
- for (const block of blocks) {
2741
- let position = block.location;
2742
- const objList = block.objList;
2743
- let opos = 0;
2744
- const processObject = (obj, parentOp) => {
2745
- let currentObj = obj;
2746
- while (true) {
2747
- if (Array.isArray(currentObj)) {
2748
- for (const obj2 of currentObj) {
2749
- processObject(obj2, parentOp);
2750
- }
2751
- break;
2752
- } else if (this.isTableEntry(currentObj)) {
2753
- currentObj = currentObj.object;
2754
- continue;
2755
- } else if (currentObj instanceof shared.Op) {
2756
- const op = currentObj;
2757
- buf[position++] = op.code & 255;
2758
- opos += op.size;
2759
- for (const operand of op.operands) {
2760
- processObject(operand, op);
2761
- }
2762
- break;
2763
- } else if (currentObj instanceof Uint8Array) {
2764
- const arr = currentObj;
2765
- for (let i = 0; i < arr.length; i++) {
2766
- buf[position + i] = arr[i];
2767
- }
2768
- position += arr.length;
2769
- opos += arr.length;
2770
- break;
2771
- } else if (typeof currentObj === "string") {
2772
- const str = currentObj;
2773
- let label = str;
2774
- let ix = 0;
2775
- while (ix < label.length && shared.RomProcessingConstants.ADDRESS_SPACE.includes(label[ix])) {
2776
- ix++;
2777
- }
2778
- if (ix > 0) {
2779
- label = label.substring(ix);
2780
- }
2781
- let loc;
2782
- const isRelative = parentOp && (parentOp.mode === "PCRelative" || parentOp.mode === "PCRelativeLong");
2783
- const operatorIdx = this.indexOfAny(label, shared.RomProcessingConstants.OPERATORS);
2784
- let offset = null;
2785
- let useMarker = false;
2786
- if (operatorIdx > 0) {
2787
- if (label[operatorIdx + 1] === "M") {
2788
- useMarker = true;
2789
- } else {
2790
- offset = parseInt(label.substring(operatorIdx + 1), 16);
2791
- if (label[operatorIdx] === "-") {
2792
- offset = -offset;
2793
- }
2794
- }
2795
- label = label.substring(0, operatorIdx);
2796
- }
2797
- const labelUpper = label.toUpperCase();
2798
- let target;
2799
- if (includeLookup.has(labelUpper)) {
2800
- target = includeLookup.get(labelUpper);
2801
- loc = target.location;
2802
- } else if (chunkLookup.has(labelUpper)) {
2803
- loc = chunkLookup.get(labelUpper);
2804
- } else {
2805
- if (label.startsWith("#")) {
2806
- label = label.substring(1);
2807
- }
2808
- if (label.startsWith("$")) {
2809
- label = label.substring(1);
2810
- }
2811
- if (isRelative && label.length > 4) {
2812
- const off = parseInt(label, 16) - (block.location + opos);
2813
- currentObj = parentOp?.size === 2 ? off & 255 : off & 65535;
2814
- continue;
2815
- } else {
2816
- switch (label.length) {
2817
- case 1:
2818
- case 2:
2819
- currentObj = new shared.Byte(parseInt(label, 16));
2820
- continue;
2821
- case 3:
2822
- case 4:
2823
- currentObj = new shared.Word(parseInt(label, 16));
2824
- continue;
2825
- case 5:
2826
- case 6:
2827
- currentObj = new shared.Long(parseInt(label, 16));
2828
- continue;
2829
- default:
2830
- throw new Error(`Invalid operand '${label}'`);
2831
- }
2832
- }
2833
- }
2834
- let type = shared.Address.typeFromCode(str[0]);
2835
- if (type === shared.AddressType.Unknown) {
2836
- type = parentOp?.size === 4 ? shared.AddressType.Address : parentOp?.size === 2 ? shared.AddressType.Unknown : shared.AddressType.Offset;
2837
- }
2838
- if (isRelative) {
2839
- loc -= block.location + opos;
2840
- if (type === shared.AddressType.Unknown && !(loc < 128 || loc >= 4194176)) {
2841
- throw new Error("Relative out of range");
2842
- }
2843
- }
2844
- if (offset !== null) {
2845
- loc += offset;
2846
- } else if (useMarker && target) {
2847
- let markerOffset = 0;
2848
- for (const part of target.objList) {
2849
- if (this.isStringMarker(part)) {
2850
- loc += markerOffset;
2851
- break;
2852
- } else {
2853
- markerOffset += shared.RomProcessingConstants.getSize(part);
2854
- }
2855
- }
2856
- }
2857
- switch (type) {
2858
- case shared.AddressType.Offset:
2859
- currentObj = new shared.Word(loc);
2860
- continue;
2861
- case shared.AddressType.Bank:
2862
- currentObj = new shared.Byte(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
2863
- continue;
2864
- case shared.AddressType.WBank:
2865
- currentObj = new shared.Word(loc >> 16 | ((loc & 65535) >= 32768 ? 128 : 192));
2866
- continue;
2867
- case shared.AddressType.Address:
2868
- currentObj = new shared.Long(loc | ((loc & 65535) >= 32768 ? 8388608 : 12582912));
2869
- continue;
2870
- default:
2871
- currentObj = new shared.Byte(loc);
2872
- continue;
2873
- }
2874
- } else if (currentObj instanceof shared.TypedNumber) {
2875
- let value = currentObj.value;
2876
- for (let i = 0; i < currentObj.size; i++) {
2877
- buf[position++] = value & 255;
2878
- value >>= 8;
2879
- }
2880
- break;
2881
- } else if (typeof currentObj === "number") {
2882
- const num = currentObj;
2883
- const size = parentOp?.size ?? 0;
2884
- if (num <= 255 && size <= 2) {
2885
- buf[position] = num & 255;
2886
- position++;
2887
- } else if (num <= 65535 && size <= 3) {
2888
- buf[position] = num & 255;
2889
- buf[position + 1] = num >> 8 & 255;
2890
- position += 2;
2891
- } else if (num <= 16777215 && size <= 4) {
2892
- buf[position] = num & 255;
2893
- buf[position + 1] = num >> 8 & 255;
2894
- buf[position + 2] = num >> 16 & 255;
2895
- position += 3;
2896
- } else {
2897
- buf[position] = num & 255;
2898
- buf[position + 1] = num >> 8 & 255;
2899
- buf[position + 2] = num >> 16 & 255;
2900
- buf[position + 3] = num >> 24 & 255;
2901
- position += 4;
2902
- }
2903
- break;
2904
- } else if (this.isStringMarker(currentObj)) {
2905
- break;
2906
- } else {
2907
- throw new Error(`Unable to process '${currentObj}'`);
2908
- }
2909
- }
2910
- };
2911
- for (const obj of objList) {
2912
- processObject(obj);
2913
- }
2914
- }
2915
- }
2916
- /**
2917
- * Helper method to find index of any character from an array in a string
2918
- */
2919
- indexOfAny(str, chars) {
2920
- for (let i = 0; i < str.length; i++) {
2921
- if (chars.includes(str[i])) {
2922
- return i;
2923
- }
2924
- }
2925
- return -1;
2926
- }
2927
- /**
2928
- * Helper method to check if an object is a StringMarker
2929
- */
2930
- isStringMarker(obj) {
2931
- return typeof obj === "object" && obj !== null && "offset" in obj;
2932
- }
2933
- isTableEntry(obj) {
2934
- return typeof obj === "object" && obj !== null && "location" in obj && "object" in obj;
2935
- }
2936
- };
2937
- var StringProcessor = class {
2938
- constructor(context) {
2939
- this.memBuffer = [];
2940
- this.context = context;
2941
- this.root = context.root;
2942
- }
2943
- dispose() {
2944
- this.memBuffer.length = 0;
2945
- }
2946
- consumeString() {
2947
- let str = null;
2948
- const typeChar = this.context.lineBuffer[0];
2949
- const endIx = this.context.lineBuffer.indexOf(typeChar, 1);
2950
- if (endIx >= 0) {
2951
- str = this.context.lineBuffer.substring(1, endIx);
2952
- this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
2953
- } else {
2954
- str = this.context.lineBuffer.substring(1);
2955
- this.context.lineBuffer = "";
2956
- }
2957
- this.memBuffer.length = 0;
2958
- const stringType = this.root.stringCharLookup[typeChar];
2959
- this.processString(str, stringType);
2960
- }
2961
- flushBuffer(stringType, wrap = false) {
2962
- const size = this.memBuffer.length;
2963
- if (size > 0) {
2964
- const buffer = new Uint8Array(this.memBuffer);
2965
- this.context.currentBlock.objList.push(buffer);
2966
- this.context.currentBlock.size += size;
2967
- this.memBuffer.length = 0;
2968
- }
2969
- }
2970
- processString(str, stringType) {
2971
- const dict = stringType.commands;
2972
- stringType.characterMap;
2973
- this.getShiftUp(stringType.shiftType);
2974
- let lastCmd = null;
2975
- for (let x = 0; x < str.length; x++) {
2976
- const c = str[x];
2977
- if (c === "[") {
2978
- const endIx = str.indexOf("]", x + 1);
2979
- const splitChars = [":", ",", " "];
2980
- const parts = str.substring(x + 1, endIx).split(new RegExp(`[${splitChars.join("")}]`)).filter((p) => p.length > 0);
2981
- x = endIx;
2982
- if (parts.length === 0) {
2983
- this.flushBuffer(stringType, true);
2984
- this.context.currentBlock.objList.push({
2985
- offset: this.context.currentBlock.size
2986
- });
2987
- continue;
2988
- }
2989
- const cmd = Object.values(dict).find((x2) => x2.value === parts[0]);
2990
- if (cmd) {
2991
- lastCmd = cmd;
2992
- this.memBuffer.push(cmd.key);
2993
- this.processStringCommand(cmd, stringType, parts);
2994
- continue;
2995
- }
2996
- }
2997
- lastCmd = null;
2998
- if (this.applyLayers(c, stringType)) {
2999
- continue;
3000
- }
3001
- }
3002
- if (lastCmd === null || !lastCmd.halt) {
3003
- this.memBuffer.push(stringType.terminator);
3004
- }
3005
- this.flushBuffer(stringType, true);
3006
- }
3007
- applyLayers(c, stringType) {
3008
- if (this.applyMap(c, stringType.characterMap, this.getShiftUp(stringType.shiftType))) {
3009
- return true;
3010
- }
3011
- if (stringType.layers) {
3012
- for (const layer of stringType.layers) {
3013
- if (this.applyMap(c, layer.map, (x) => x + layer.base)) {
3014
- return true;
3015
- }
3016
- }
3017
- }
3018
- return false;
3019
- }
3020
- applyMap(c, map, shift) {
3021
- for (let i = 0, len = map.length; i < len; i++) {
3022
- const v = map[i];
3023
- if (v != null && c === v[0]) {
3024
- this.memBuffer.push(shift(i));
3025
- return true;
3026
- }
3027
- }
3028
- return false;
3029
- }
3030
- processStringCommand(cmd, stringType, parts) {
3031
- const hasPointer = cmd.types.includes(shared.MemberType.Address) || cmd.types.includes(shared.MemberType.Offset);
3032
- if (hasPointer) {
3033
- this.flushBuffer(stringType, true);
3034
- }
3035
- for (let y = 0, pix = 1; y < cmd.types.length; y++, pix++) {
3036
- switch (cmd.types[y]) {
3037
- case shared.MemberType.Byte:
3038
- this.memBuffer.push(parseInt(parts[pix], 16));
3039
- break;
3040
- case shared.MemberType.Word:
3041
- const us = parseInt(parts[pix], 16);
3042
- this.memBuffer.push(us & 255);
3043
- this.memBuffer.push(us >> 8 & 255);
3044
- break;
3045
- case shared.MemberType.Binary:
3046
- while (pix < parts.length) {
3047
- const ch = parseInt(parts[pix], 16);
3048
- this.memBuffer.push(ch);
3049
- pix++;
3050
- }
3051
- if (cmd.delimiter !== void 0) {
3052
- this.memBuffer.push(cmd.delimiter);
3053
- }
3054
- break;
3055
- case shared.MemberType.Offset:
3056
- case shared.MemberType.Address:
3057
- this.flushBuffer(stringType, false);
3058
- this.context.currentBlock.objList.push(parts[pix]);
3059
- this.context.currentBlock.size += cmd.types[y] === shared.MemberType.Offset ? 2 : 3;
3060
- break;
3061
- }
3062
- }
3063
- if (hasPointer) {
3064
- this.flushBuffer(stringType, false);
3065
- }
3066
- }
3067
- getShiftUp(shiftType) {
3068
- switch (shiftType) {
3069
- case "h2":
3070
- return (x) => (x & 112) << 1 | x & 15;
3071
- case "wh2":
3072
- return (x) => (x & 56) << 1 | x & 7;
3073
- default:
3074
- return (x) => x;
3075
- }
3076
- }
3077
- };
3078
-
3079
- // src/rom/rebuild/sorted-map.ts
3080
- var SortedMap = class {
3081
- constructor() {
3082
- this.map = /* @__PURE__ */ new Map();
3083
- this._keys = [];
3084
- }
3085
- get size() {
3086
- return this.map.size;
3087
- }
3088
- set(key, value) {
3089
- if (!this.map.has(key)) {
3090
- this._keys.push(key);
3091
- this.sortKeys();
3092
- }
3093
- this.map.set(key, value);
3094
- return this;
3095
- }
3096
- get(key) {
3097
- return this.map.get(key);
3098
- }
3099
- has(key) {
3100
- return this.map.has(key);
3101
- }
3102
- delete(key) {
3103
- const result = this.map.delete(key);
3104
- if (result) {
3105
- const index = this._keys.indexOf(key);
3106
- if (index >= 0) {
3107
- this._keys.splice(index, 1);
3108
- }
3109
- }
3110
- return result;
3111
- }
3112
- clear() {
3113
- this.map.clear();
3114
- this._keys.length = 0;
3115
- }
3116
- keys() {
3117
- return [...this._keys];
3118
- }
3119
- values() {
3120
- return this._keys.map((key) => this.map.get(key));
3121
- }
3122
- entries() {
3123
- return this._keys.map((key) => [key, this.map.get(key)]);
3124
- }
3125
- *[Symbol.iterator]() {
3126
- for (const key of this._keys) {
3127
- yield [key, this.map.get(key)];
3128
- }
3129
- }
3130
- sortKeys() {
3131
- this._keys.sort((a, b) => {
3132
- if (a.length !== b.length) {
3133
- return b.length - a.length;
3134
- }
3135
- return a.localeCompare(b);
3136
- });
3137
- }
3138
- };
3139
- var AssemblerState = class _AssemblerState {
3140
- constructor(context, structType = null, saveDelimiter = false) {
3141
- this.context = context;
3142
- this.root = context.root;
3143
- this.dbStruct = structType === null ? null : Object.values(this.root.structs).find(
3144
- (x) => x.name.toLowerCase() === structType.toLowerCase()
3145
- ) || null;
3146
- this.parentStruct = !this.dbStruct || !this.dbStruct.parent ? null : Object.values(this.root.structs).find(
3147
- (x) => x.name.toLowerCase() === this.dbStruct.parent.toLowerCase()
3148
- ) || null;
3149
- this.discriminator = this.parentStruct?.discriminator ?? null;
3150
- this.delimiter = this.dbStruct?.delimiter || null;
3151
- this.memberOffset = 0;
3152
- this.dataOffset = 0;
3153
- this.memberTypes = this.dbStruct?.types || null;
3154
- this.currentType = this.memberTypes?.[this.memberOffset] || null;
3155
- if (saveDelimiter) {
3156
- this.context.lastDelimiter = this.dbStruct?.delimiter ?? this.parentStruct?.delimiter ?? null;
3157
- }
3158
- }
3159
- checkDisc() {
3160
- if (this.discriminator === this.dataOffset) {
3161
- this.context.currentBlock.objList.push(this.dbStruct.discriminator);
3162
- this.context.currentBlock.size += 1;
3163
- this.dataOffset += 1;
3164
- }
3165
- }
3166
- advancePart() {
3167
- if (this.currentType != null && this.discriminator != null) {
3168
- this.dataOffset += shared.RomProcessingConstants.getSize(this.currentType);
3169
- }
3170
- if (this.memberTypes != null && this.memberOffset + 1 < this.memberTypes.length) {
3171
- this.currentType = this.memberTypes[++this.memberOffset];
3172
- }
3173
- }
3174
- processOrigin() {
3175
- this.context.lineBuffer = this.context.lineBuffer.substring(3).replace(/^[\s,\t]+/, "");
3176
- if (this.context.lineBuffer.startsWith("$")) {
3177
- this.context.lineBuffer = this.context.lineBuffer.substring(1);
3178
- }
3179
- let hex;
3180
- const endIx = this.context.lineBuffer.search(/[\s,\t]/);
3181
- if (endIx >= 0) {
3182
- hex = this.context.lineBuffer.substring(0, endIx);
3183
- this.context.lineBuffer = this.context.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
3184
- } else {
3185
- hex = this.context.lineBuffer;
3186
- this.context.lineBuffer = "";
3187
- }
3188
- const location = parseInt(hex, 16);
3189
- this.context.blocks.push(this.context.currentBlock = new shared.AsmBlock(location));
3190
- this.context.blockIndex++;
3191
- }
3192
- static doMath(operand) {
3193
- const ix = operand.search(/[-+]/);
3194
- if (ix >= 0) {
3195
- const op = operand[ix];
3196
- const vix = operand.lastIndexOf("$", ix) + 1;
3197
- const valueStr = operand.substring(vix, ix);
3198
- if (valueStr.match(/^[a-fA-F0-9]+$/)) {
3199
- const value = parseInt(valueStr, 16);
3200
- let endIx = operand.substring(ix + 1).search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
3201
- if (endIx < 0) {
3202
- endIx = operand.length;
3203
- }
3204
- const number = parseInt(operand.substring(ix + 1, endIx), 16);
3205
- let result;
3206
- if (op === "-") {
3207
- result = value - number;
3208
- } else {
3209
- result = value + number;
3210
- }
3211
- const len = ix - vix <= 2 ? 2 : ix - vix <= 4 ? 4 : 6;
3212
- operand = operand.substring(0, vix) + result.toString(16).toUpperCase().padStart(len, "0") + operand.substring(endIx);
3213
- }
3214
- }
3215
- return operand;
3216
- }
3217
- tryCreateLabel(mnemonic, operand) {
3218
- if (!operand || operand.length === 0) {
3219
- return false;
3220
- }
3221
- const labelChar = operand[0];
3222
- if (!shared.RomProcessingConstants.LABEL_SPACE.includes(labelChar)) {
3223
- return false;
3224
- }
3225
- const newBlock = new shared.AsmBlock(
3226
- this.context.currentBlock.location + this.context.currentBlock.size,
3227
- 0,
3228
- this.root.stringDelimiters.includes(operand[0]),
3229
- mnemonic
3230
- );
3231
- this.context.blocks.push(newBlock);
3232
- this.context.currentBlock = newBlock;
3233
- this.context.blockIndex++;
3234
- if (labelChar === ":") {
3235
- this.context.lineBuffer = this.context.lineBuffer.substring(1);
3236
- } else if (labelChar === "[" || labelChar === "{") {
3237
- this.context.lineBuffer = operand.substring(1).replace(/^[\s,\t]+/, "");
3238
- const state = new _AssemblerState(this.context, this.currentType);
3239
- state.processText(labelChar);
3240
- this.advancePart();
3241
- }
3242
- return true;
3243
- }
3244
- processText(openTag) {
3245
- this.checkDisc();
3246
- while (!this.context.eof) {
3247
- if (!this.context.getLine()) {
3248
- return;
3249
- }
3250
- let mnemonic = null;
3251
- let operand = null;
3252
- let operand2 = null;
3253
- while (this.context.lineBuffer.length > 0) {
3254
- const lineSymbol = this.context.lineBuffer[0];
3255
- if (this.root.stringDelimiters.includes(lineSymbol)) {
3256
- this.context.stringProcessor.consumeString();
3257
- this.advancePart();
3258
- continue;
3259
- }
3260
- if (shared.RomProcessingConstants.ADDRESS_SPACE.includes(lineSymbol)) {
3261
- this.context.processRawData();
3262
- if (openTag === "[") {
3263
- this.context.lastDelimiter = null;
3264
- }
3265
- this.advancePart();
3266
- continue;
3267
- }
3268
- if (lineSymbol === ">") {
3269
- if (openTag === "<") {
3270
- this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
3271
- this.checkDisc();
3272
- }
3273
- return;
3274
- }
3275
- if (lineSymbol === "]") {
3276
- this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
3277
- this.delimiter = this.delimiter ?? this.context.lastDelimiter;
3278
- if (this.delimiter != null) {
3279
- if (this.delimiter >= 256) {
3280
- this.context.currentBlock.objList.push(this.delimiter);
3281
- this.context.currentBlock.size += 2;
3282
- } else {
3283
- this.context.currentBlock.objList.push(this.delimiter);
3284
- this.context.currentBlock.size += 1;
3285
- }
3286
- }
3287
- return;
3288
- }
3289
- if (lineSymbol === "}") {
3290
- if (openTag === "{") {
3291
- this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
3292
- }
3293
- return;
3294
- }
3295
- if (lineSymbol === "[") {
3296
- this.context.lineBuffer = this.context.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
3297
- const state = new _AssemblerState(this.context, this.currentType);
3298
- state.processText("[");
3299
- this.advancePart();
3300
- continue;
3301
- }
3302
- if (this.context.lineBuffer.startsWith("ORG")) {
3303
- this.processOrigin();
3304
- continue;
3305
- }
3306
- const symbolIndex = this.context.lineBuffer.search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
3307
- if (symbolIndex > 0) {
3308
- mnemonic = this.context.lineBuffer.substring(0, symbolIndex);
3309
- operand = this.context.lineBuffer.substring(symbolIndex).replace(/^[, \t]+/, "");
3310
- if (operand && operand.startsWith("<")) {
3311
- this.context.lineBuffer = operand.substring(1).replace(/^[, \t]+/, "");
3312
- const state = new _AssemblerState(this.context, mnemonic, openTag === "[" && this.currentType == null);
3313
- state.processText("<");
3314
- mnemonic = null;
3315
- continue;
3316
- }
3317
- this.context.lineBuffer = operand;
3318
- } else {
3319
- mnemonic = this.context.lineBuffer;
3320
- this.context.lineBuffer = "";
3321
- }
3322
- break;
3323
- }
3324
- if (mnemonic && mnemonic.length > 0) {
3325
- const codes = this.root.opLookup[mnemonic.toUpperCase()];
3326
- if (!codes || codes.length === 0) {
3327
- if (this.tryCreateLabel(mnemonic, operand || void 0)) {
3328
- continue;
3329
- }
3330
- throw new Error(`Unknown instruction line ${this.context.lineCount}: '${mnemonic}'`);
3331
- }
3332
- this.context.lineBuffer = "";
3333
- if (!operand) {
3334
- const opCode2 = codes.find((x) => this.root.addrLookup[x.mode].size === 1);
3335
- this.context.currentBlock.objList.push(new shared.Op(opCode2, 0, [], 1));
3336
- this.context.currentBlock.size++;
3337
- continue;
3338
- }
3339
- operand = _AssemblerState.doMath(operand);
3340
- let opCode = codes[0];
3341
- if (opCode?.mnem === "COP") {
3342
- const parts = operand.split(/[\s\t,()[\]$#]/).filter((p) => p.length > 0);
3343
- const cmd = parts[0];
3344
- const cop = this.root.copLookup[cmd];
3345
- if (!cop) {
3346
- throw new Error(`Unknown COP command ${cmd}`);
3347
- }
3348
- this.context.currentBlock.objList.push(new shared.Op(opCode, 0, [new shared.Byte(cop.code), ...parts.slice(1)], cop.size + 2));
3349
- this.context.currentBlock.size += cop.size + 2;
3350
- continue;
3351
- }
3352
- opCode = null;
3353
- for (const code of codes) {
3354
- if (code.mode === "PCRelative" || code.mode === "PCRelativeLong") {
3355
- opCode = code;
3356
- break;
3357
- }
3358
- const addrMode2 = this.root.addrLookup[code.mode];
3359
- const regex = addrMode2.parseRegex;
3360
- if (regex) {
3361
- const match = new RegExp(regex).exec(operand);
3362
- if (match) {
3363
- opCode = code;
3364
- operand = match[1];
3365
- if (match.length > 2) {
3366
- operand2 = match[2];
3367
- }
3368
- break;
3369
- }
3370
- }
3371
- }
3372
- if (operand.startsWith("#")) {
3373
- operand = operand.substring(1);
3374
- }
3375
- if (operand.startsWith("$")) {
3376
- operand = operand.substring(1);
3377
- }
3378
- if (opCode == null) {
3379
- const addrIx = operand.search(shared.RomProcessingConstants.ADDRESS_SPACE_REGEX);
3380
- if (addrIx && addrIx >= 0) {
3381
- const eix = operand.search(/[\s\t,\])]/);
3382
- if (eix && eix >= 0) {
3383
- operand = operand.substring(addrIx, eix);
3384
- }
3385
- }
3386
- opCode = codes.find((x) => x.mode === "Immediate") ?? codes.find((x) => x.mode === "AbsoluteLong") ?? codes.find((x) => x.mode === "Absolute") ?? null;
3387
- if (opCode == null) {
3388
- throw new Error(`Unable to determine mode/code line ${this.context.lineCount}: '${this.context.lineBuffer}'`);
3389
- }
3390
- }
3391
- const opnd1 = this.context.parseOperand(operand);
3392
- const addrMode = this.root.addrLookup[opCode.mode];
3393
- let size = addrMode.size;
3394
- if (size === AsmReader.VARIABLE_SIZE_INDICATOR || opCode.mode === "Immediate") {
3395
- size = operand?.startsWith("^") || operand?.length === 2 ? 2 : 3;
3396
- }
3397
- const operands = operand2 != null ? [opnd1, this.context.parseOperand(operand2)] : [opnd1];
3398
- this.context.currentBlock.objList.push(new shared.Op(opCode, 0, operands, size));
3399
- this.context.currentBlock.size += size;
3400
- }
3401
- }
3402
- }
3403
- hexStringToBytes(hex) {
3404
- const result = new Uint8Array(hex.length / 2);
3405
- for (let i = 0; i < hex.length; i += 2) {
3406
- result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
3407
- }
3408
- return result;
3409
- }
3410
- };
3411
-
3412
- // src/rom/rebuild/assembler.ts
3413
- var Assembler = class {
3414
- constructor(dbRoot, textData) {
3415
- this.currentLineIndex = 0;
3416
- this.lineBuffer = "";
3417
- this.includes = /* @__PURE__ */ new Set();
3418
- this.blocks = [];
3419
- this.tags = new SortedMap();
3420
- this.currentBlock = null;
3421
- this.lineCount = 0;
3422
- this.blockIndex = 0;
3423
- this.lastDelimiter = null;
3424
- this.reqBank = null;
3425
- this.eof = false;
3426
- this.root = dbRoot;
3427
- this.lines = textData.split(/\r?\n/);
3428
- this.stringProcessor = new StringProcessor(this);
3429
- }
3430
- dispose() {
3431
- this.stringProcessor.dispose();
3432
- }
3433
- parseAssembly() {
3434
- this.blocks.push(this.currentBlock = new shared.AsmBlock());
3435
- const state = new AssemblerState(this);
3436
- state.processText();
3437
- return {
3438
- blocks: this.blocks,
3439
- includes: this.includes,
3440
- reqBank: this.reqBank
3441
- };
3442
- }
3443
- getLine() {
3444
- if (this.eof) {
3445
- return false;
3446
- }
3447
- if (this.lineBuffer.length > 0) {
3448
- return true;
3449
- }
3450
- let rawLine = null;
3451
- while (true) {
3452
- if (this.currentLineIndex >= this.lines.length) {
3453
- if (this.lineBuffer.length === 0) {
3454
- this.eof = true;
3455
- return false;
3456
- } else {
3457
- rawLine = "";
3458
- }
3459
- } else {
3460
- rawLine = this.lines[this.currentLineIndex++];
3461
- this.lineBuffer += rawLine;
3462
- }
3463
- this.lineCount++;
3464
- this.trimComments("--");
3465
- this.trimComments(";");
3466
- this.trimComments("//");
3467
- this.lineBuffer = this.lineBuffer.replace(shared.RomProcessingConstants.COMMA_SPACE_TRIM_REGEX, "");
3468
- if (this.lineBuffer.length === 0) {
3469
- continue;
3470
- }
3471
- if (this.lineBuffer.endsWith("\\")) {
3472
- this.lineBuffer = this.lineBuffer.slice(0, -1);
3473
- continue;
3474
- }
3475
- if (this.lineBuffer[0] === "?") {
3476
- this.processDirectives();
3477
- this.lineBuffer = "";
3478
- continue;
3479
- }
3480
- if (this.lineBuffer[0] === "!") {
3481
- this.processTags();
3482
- this.lineBuffer = "";
3483
- continue;
3484
- }
3485
- this.resolveTags();
3486
- return true;
3487
- }
3488
- }
3489
- trimComments(sequence) {
3490
- const index = this.lineBuffer.indexOf(sequence);
3491
- if (index >= 0) {
3492
- const strDelimRegex = new RegExp(`[${this.root.stringDelimiters.map((c) => c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("")}]`);
3493
- const strIndex = this.lineBuffer.search(strDelimRegex);
3494
- if (strIndex < 0 || strIndex > index || this.lineBuffer.lastIndexOf(this.lineBuffer[strIndex] || "") < index) {
3495
- this.lineBuffer = this.lineBuffer.substring(0, index);
3496
- }
3497
- }
3498
- }
3499
- processDirectives() {
3500
- let endIx = this.lineBuffer.search(shared.RomProcessingConstants.COMMA_SPACE_REGEX);
3501
- if (endIx < 0) {
3502
- endIx = this.lineBuffer.length;
3503
- }
3504
- const value = this.lineBuffer.substring(endIx).replace(/^[\s,\t]+/, "");
3505
- switch (this.lineBuffer.substring(1, endIx).toUpperCase()) {
3506
- // This is taken care of by the loader
3507
- case "BANK":
3508
- this.reqBank = parseInt(value, 16);
3509
- break;
3510
- case "INCLUDE":
3511
- if (value.length > 0) {
3512
- this.includes.add(value.toUpperCase().replace(/'/g, ""));
3513
- }
3514
- break;
3515
- }
3516
- }
3517
- processTags() {
3518
- this.lineBuffer = this.lineBuffer.substring(1).replace(/^[\s,\t]+/, "");
3519
- while (this.lineBuffer.length > 0) {
3520
- let name = this.lineBuffer;
3521
- let value = null;
3522
- let endIx = this.lineBuffer.search(/[\s,\t]/);
3523
- if (endIx >= 0) {
3524
- name = this.lineBuffer.substring(0, endIx);
3525
- value = this.lineBuffer.substring(endIx + 1).replace(/^[\s,\t]+/, "");
3526
- const nextIx = value.search(/[\s,\t]/);
3527
- if (nextIx >= 0) {
3528
- this.lineBuffer = value.substring(nextIx + 1).replace(/^[\s,\t]+/, "");
3529
- value = value.substring(0, nextIx);
3530
- } else {
3531
- this.lineBuffer = "";
3532
- }
3533
- } else {
3534
- this.lineBuffer = "";
3535
- }
3536
- this.tags.set(name, value);
3537
- }
3538
- }
3539
- resolveTags() {
3540
- for (const [key, value] of this.tags) {
3541
- let ix;
3542
- while ((ix = this.lineBuffer.toLowerCase().indexOf(key.toLowerCase())) >= 0) {
3543
- this.lineBuffer = this.lineBuffer.substring(0, ix) + (value || "") + this.lineBuffer.substring(ix + key.length);
3544
- }
3545
- }
3546
- }
3547
- parseOperand(opnd) {
3548
- if (!opnd) return null;
3549
- if (opnd.match(/^[a-fA-F0-9]{2,6}$/)) {
3550
- switch (opnd.length) {
3551
- case 2:
3552
- return new shared.Byte(parseInt(opnd, 16));
3553
- case 4:
3554
- return new shared.Word(parseInt(opnd, 16));
3555
- case 6:
3556
- return new shared.Long(parseInt(opnd, 16));
3557
- }
3558
- }
3559
- return opnd;
3560
- }
3561
- processRawData() {
3562
- let reverse = false;
3563
- if (this.lineBuffer[0] === "#") {
3564
- this.lineBuffer = this.lineBuffer.substring(1);
3565
- }
3566
- if (this.lineBuffer[0] === "$") {
3567
- reverse = true;
3568
- this.lineBuffer = this.lineBuffer.substring(1);
3569
- }
3570
- let hex;
3571
- const symbolIx = this.lineBuffer.search(shared.RomProcessingConstants.SYMBOL_SPACE_REGEX);
3572
- if (symbolIx >= 0) {
3573
- hex = this.lineBuffer.substring(0, symbolIx);
3574
- this.lineBuffer = this.lineBuffer.substring(symbolIx).replace(/^[, \t]+/, "");
3575
- } else {
3576
- hex = this.lineBuffer;
3577
- this.lineBuffer = "";
3578
- }
3579
- if (hex.length > 0) {
3580
- if (shared.RomProcessingConstants.ADDRESS_SPACE.includes(hex[0])) {
3581
- this.currentBlock.objList.push(hex);
3582
- this.currentBlock.size += shared.RomProcessingConstants.getSize(hex);
3583
- } else {
3584
- const data = this.hexStringToBytes(hex);
3585
- if (data.length === 1) {
3586
- this.currentBlock.objList.push(new shared.Byte(data[0]));
3587
- this.currentBlock.size++;
3588
- } else {
3589
- if (reverse) {
3590
- data.reverse();
3591
- }
3592
- if (data.length === 2) {
3593
- this.currentBlock.objList.push(new shared.Word(data[0] | data[1] << 8));
3594
- this.currentBlock.size += 2;
3595
- } else {
3596
- this.currentBlock.objList.push(data);
3597
- this.currentBlock.size += data.length;
3598
- }
3599
- }
3600
- }
3601
- }
3602
- }
3603
- hexStringToBytes(hex) {
3604
- const result = new Uint8Array(hex.length / 2);
3605
- for (let i = 0; i < hex.length; i += 2) {
3606
- result[i / 2] = parseInt(hex.substring(i, i + 2), 16);
3607
- }
3608
- return result;
3609
- }
3610
- };
3611
- var RomGenerator = class {
3612
- constructor(projectName) {
3613
- this.crc = 0;
3614
- this.branchId = "";
3615
- //private chunkFiles: ChunkFile[] = [];
3616
- //private asmFiles: ChunkFile[] = [];
3617
- this.dbRoot = {};
3618
- this.sourceData = new Uint8Array();
3619
- this.projectName = projectName;
3620
- }
3621
- async initialize() {
3622
- var projectData = await shared.summaryFromSupabaseByProject(this.projectName);
3623
- this.crc = projectData.baseRomBranch.gameRomBranch.gameRom.crc;
3624
- this.branchId = projectData.id;
3625
- }
3626
- async validateAndDownload(sourceData) {
3627
- const calc = shared.crc32_buffer(sourceData);
3628
- if (calc === this.crc) {
3629
- this.dbRoot = await shared.DbRootUtils.fromSupabaseProject(this.projectName, this.branchId);
3630
- this.sourceData = sourceData;
3631
- return true;
3632
- }
3633
- return false;
3634
- }
3635
- async generateProject(modules, manualFiles, unshiftManualFiles = false) {
3636
- if (!this.dbRoot) throw new Error("Database not initialized");
3637
- if (!this.sourceData) throw new Error("Source data not initialized");
3638
- const reader = new BlockReader(this.sourceData, this.dbRoot);
3639
- const chunkFiles = reader.analyzeAndResolve();
3640
- const asmFiles = chunkFiles.filter((b) => b.type === shared.BinType.Assembly);
3641
- const patchFiles = [];
3642
- const writer = new BlockWriter(reader);
3643
- for (const block of asmFiles) block.textData = writer.generateAsm(block);
3644
- for (const chunkFile of this.dbRoot.baseRomFiles) this.applyPatchFile(chunkFile, chunkFiles, asmFiles, patchFiles);
3645
- const moduleLookup = this.applyProjectInit(chunkFiles, asmFiles, patchFiles);
3646
- if (unshiftManualFiles) {
3647
- for (const file of manualFiles ?? []) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
3648
- }
3649
- for (const module of modules) {
3650
- for (const file of moduleLookup.get(module)) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
3651
- }
3652
- if (!unshiftManualFiles) {
3653
- for (const file of manualFiles ?? []) this.applyPatchFile(file, chunkFiles, asmFiles, patchFiles);
3654
- }
3655
- this.assembleCodeFromText(asmFiles);
3656
- RomProcessor.applyPatches(asmFiles, patchFiles);
3657
- for (const asm of chunkFiles) shared.ChunkFileUtils.calculateSize(asm);
3658
- const layout = new RomLayout(chunkFiles);
3659
- layout.organize();
3660
- for (const file of asmFiles) shared.ChunkFileUtils.rebase(file);
3661
- this.generateAsmIncludeLookups(asmFiles);
3662
- const blockLookup = /* @__PURE__ */ new Map();
3663
- for (const f of chunkFiles) blockLookup.set(f.name.toUpperCase(), f.location);
3664
- const outRom = await this.writeRom(blockLookup, this.dbRoot.entryPoints, chunkFiles, asmFiles);
3665
- return outRom;
3666
- }
3667
- applyProjectInit(chunkFiles, asmFiles, patchFiles) {
3668
- const moduleLookup = /* @__PURE__ */ new Map();
3669
- for (const chunkFile of this.dbRoot.projectFiles) {
3670
- console.log(`Processing patch: ${chunkFile.name}`);
3671
- if (chunkFile.group) {
3672
- let modArray;
3673
- if (!moduleLookup.has(chunkFile.group)) moduleLookup.set(chunkFile.group, modArray = []);
3674
- else modArray = moduleLookup.get(chunkFile.group);
3675
- modArray.push(chunkFile);
3676
- } else this.applyPatchFile(chunkFile, chunkFiles, asmFiles, patchFiles);
3677
- }
3678
- return moduleLookup;
3679
- }
3680
- //Assemble code from script text
3681
- assembleCodeFromText(asmFiles) {
3682
- for (const block of asmFiles) {
3683
- const assembler = new Assembler(this.dbRoot, block.textData);
3684
- const { blocks, includes, reqBank } = assembler.parseAssembly();
3685
- block.parts = blocks;
3686
- block.includes = includes;
3687
- block.bank = reqBank ?? void 0;
3688
- }
3689
- }
3690
- // Build include lookup map per asm file
3691
- generateAsmIncludeLookups(asmFiles) {
3692
- for (const f of asmFiles) {
3693
- const includeBlocks = asmFiles.filter((x) => f.includes?.has(x.name.toUpperCase())).flatMap((x) => x.parts).filter((b) => !!b.label);
3694
- f.includeLookup = /* @__PURE__ */ new Map();
3695
- for (const b of includeBlocks) {
3696
- if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
3697
- }
3698
- for (const b of (f.parts || []).filter((x) => !!x.label)) {
3699
- if (b.label) f.includeLookup.set(b.label.toUpperCase(), b);
3700
- }
3701
- }
3702
- }
3703
- applyPatchFile(chunkFile, chunkFiles, asmFiles, patchFiles) {
3704
- console.log(`Processing patch: ${chunkFile.name}`);
3705
- const existing = chunkFiles.find((x) => x.name === chunkFile.name);
3706
- if (chunkFile.type === shared.BinType.Patch || chunkFile.type === shared.BinType.Assembly) {
3707
- if (existing) {
3708
- existing.textData = chunkFile.textData;
3709
- } else {
3710
- if (chunkFile.type === shared.BinType.Patch) {
3711
- patchFiles.push(chunkFile);
3712
- }
3713
- asmFiles.push(chunkFile);
3714
- chunkFiles.push(chunkFile);
3715
- }
3716
- } else {
3717
- if (existing) {
3718
- existing.rawData = chunkFile.rawData;
3719
- existing.size = chunkFile.size;
3720
- } else {
3721
- chunkFiles.push(chunkFile);
3722
- }
3723
- }
3724
- }
3725
- async writeRom(blockLookup, entryPoints, chunkFiles, asmFiles) {
3726
- const romWriter = new RomWriter(entryPoints, "GAIALABS", "01JG ");
3727
- for (const file of chunkFiles) await romWriter.writeFile(file, blockLookup);
3728
- romWriter.writeHeader();
3729
- romWriter.writeEntryPoints(asmFiles);
3730
- romWriter.writeChecksum();
3731
- return romWriter.outBuffer;
3732
- }
3733
- };
3734
-
3735
- // src/sprites/SpriteFrame.ts
3736
- var SpriteFrame = class {
3737
- constructor(duration = 0, groupIndex = 0, groupOffset = 0) {
3738
- this.duration = duration;
3739
- this.groupIndex = groupIndex;
3740
- this.groupOffset = groupOffset;
3741
- }
3742
- };
3743
-
3744
- // src/sprites/SpritePart.ts
3745
- var SpritePart = class {
3746
- constructor() {
3747
- this.isLarge = false;
3748
- this.xOffset = 0;
3749
- this.xOffsetMirror = 0;
3750
- this.yOffset = 0;
3751
- this.yOffsetMirror = 0;
3752
- this.vMirror = false;
3753
- this.hMirror = false;
3754
- this.someOffset = 0;
3755
- this.paletteIndex = 0;
3756
- this.tileIndex = 0;
3757
- }
3758
- };
3759
-
3760
- // src/sprites/SpriteGroup.ts
3761
- var SpriteGroup = class {
3762
- constructor() {
3763
- this.xOffset = 0;
3764
- this.xOffsetMirror = 0;
3765
- this.yOffset = 0;
3766
- this.yOffsetMirror = 0;
3767
- this.xRecoilHitboxOffset = 0;
3768
- this.yRecoilHitboxOffset = 0;
3769
- this.xRecoilHitboxTilesize = 0;
3770
- this.yRecoilHitboxTilesize = 0;
3771
- this.xHostileHitboxOffset = 0;
3772
- this.xHostileHitboxSize = 0;
3773
- this.yHostileHitboxOffset = 0;
3774
- this.yHostileHitboxSize = 0;
3775
- this.parts = [];
3776
- }
3777
- };
3778
-
3779
- // src/sprites/SpriteMap.ts
3780
- var SpriteMap = class _SpriteMap {
3781
- constructor() {
3782
- this.frameSets = [];
3783
- this.groups = [];
3784
- }
3785
- /**
3786
- * Create a SpriteMap from binary data
3787
- * @param data Binary data buffer
3788
- * @returns SpriteMap instance
3789
- */
3790
- static fromBytes(data) {
3791
- let position = 0;
3792
- const getByte = () => {
3793
- if (position >= data.length) return 0;
3794
- return data[position++];
3795
- };
3796
- const getUshort = () => {
3797
- if (position + 1 >= data.length) return 0;
3798
- const low = data[position++];
3799
- const high = data[position++];
3800
- return low | high << 8;
3801
- };
3802
- const spriteMap = new _SpriteMap();
3803
- const setOffsets = /* @__PURE__ */ new Set();
3804
- const groupOffsets = /* @__PURE__ */ new Set();
3805
- position = 0;
3806
- while (!setOffsets.has(position)) {
3807
- const offset = getUshort() - 16384;
3808
- setOffsets.add(offset);
3809
- }
3810
- for (const offStart of setOffsets) {
3811
- position = offStart;
3812
- const frameList = [];
3813
- while (true) {
3814
- const duration = getUshort();
3815
- if (duration === 65535) {
3816
- break;
3817
- }
3818
- const groupOffset = getUshort() - 16384;
3819
- frameList.push(new SpriteFrame(duration, 0, groupOffset));
3820
- groupOffsets.add(groupOffset);
3821
- }
3822
- spriteMap.frameSets.push(frameList);
3823
- }
3824
- let grpIx = 0;
3825
- const sortedGroupOffsets = Array.from(groupOffsets).sort((a, b) => a - b);
3826
- for (const offStart of sortedGroupOffsets) {
3827
- position = offStart;
3828
- for (const set of spriteMap.frameSets) {
3829
- for (const frame of set) {
3830
- if (frame.groupOffset === offStart) {
3831
- frame.groupIndex = grpIx;
3832
- }
3833
- }
3834
- }
3835
- const grp = new SpriteGroup();
3836
- grp.xOffset = getByte();
3837
- grp.xOffsetMirror = getByte();
3838
- grp.yOffset = getByte();
3839
- grp.yOffsetMirror = getByte();
3840
- grp.xRecoilHitboxOffset = getByte();
3841
- grp.yRecoilHitboxOffset = getByte();
3842
- grp.xRecoilHitboxTilesize = getByte();
3843
- grp.yRecoilHitboxTilesize = getByte();
3844
- grp.xHostileHitboxOffset = getByte();
3845
- grp.xHostileHitboxSize = getByte();
3846
- grp.yHostileHitboxOffset = getByte();
3847
- grp.yHostileHitboxSize = getByte();
3848
- let numParts = getByte();
3849
- while (numParts-- > 0) {
3850
- const part = new SpritePart();
3851
- part.isLarge = getByte() !== 0;
3852
- part.xOffset = getByte();
3853
- part.xOffsetMirror = getByte();
3854
- part.yOffset = getByte();
3855
- part.yOffsetMirror = getByte();
3856
- const props = getUshort();
3857
- part.vMirror = (props & 32768) !== 0;
3858
- part.hMirror = (props & 16384) !== 0;
3859
- part.someOffset = props >> 12 & 3;
3860
- part.paletteIndex = props >> 9 & 7;
3861
- part.tileIndex = props & 511;
3862
- grp.parts.push(part);
3863
- }
3864
- spriteMap.groups.push(grp);
3865
- grpIx++;
3866
- }
3867
- return spriteMap;
3868
- }
3869
- /**
3870
- * Convert this SpriteMap to binary data
3871
- * @returns Binary data buffer
3872
- */
3873
- toBytes() {
3874
- let size = this.frameSets.length * 2;
3875
- for (const set of this.frameSets) {
3876
- size += set.length * 4 + 2;
3877
- }
3878
- for (const grp of this.groups) {
3879
- size += grp.parts.length * 7 + 13;
3880
- }
3881
- const buffer = new Uint8Array(size);
3882
- let position = 0;
3883
- const writeShort = (val) => {
3884
- buffer[position++] = val & 255;
3885
- buffer[position++] = val >> 8 & 255;
3886
- };
3887
- const writeLoc = (val) => writeShort(val + 16384);
3888
- let pos = this.frameSets.length << 1;
3889
- const groupPos = new Array(this.groups.length);
3890
- for (const set of this.frameSets) {
3891
- writeLoc(pos);
3892
- pos += (set.length << 2) + 2;
3893
- }
3894
- for (let i = 0; i < this.groups.length; i++) {
3895
- groupPos[i] = pos;
3896
- pos += this.groups[i].parts.length * 7 + 13;
3897
- }
3898
- for (const set of this.frameSets) {
3899
- for (const frm of set) {
3900
- writeShort(frm.duration);
3901
- writeLoc(groupPos[frm.groupIndex]);
3902
- }
3903
- writeShort(65535);
3904
- }
3905
- for (const grp of this.groups) {
3906
- buffer[position++] = grp.xOffset;
3907
- buffer[position++] = grp.xOffsetMirror;
3908
- buffer[position++] = grp.yOffset;
3909
- buffer[position++] = grp.yOffsetMirror;
3910
- buffer[position++] = grp.xRecoilHitboxOffset;
3911
- buffer[position++] = grp.yRecoilHitboxOffset;
3912
- buffer[position++] = grp.xRecoilHitboxTilesize;
3913
- buffer[position++] = grp.yRecoilHitboxTilesize;
3914
- buffer[position++] = grp.xHostileHitboxOffset;
3915
- buffer[position++] = grp.xHostileHitboxSize;
3916
- buffer[position++] = grp.yHostileHitboxOffset;
3917
- buffer[position++] = grp.yHostileHitboxSize;
3918
- buffer[position++] = grp.parts.length;
3919
- for (const prt of grp.parts) {
3920
- buffer[position++] = prt.isLarge ? 1 : 0;
3921
- buffer[position++] = prt.xOffset;
3922
- buffer[position++] = prt.xOffsetMirror;
3923
- buffer[position++] = prt.yOffset;
3924
- buffer[position++] = prt.yOffsetMirror;
3925
- const accum = (prt.vMirror ? 32768 : 0) | (prt.hMirror ? 16384 : 0) | (prt.someOffset & 3) << 12 | (prt.paletteIndex & 7) << 9 | prt.tileIndex;
3926
- writeShort(accum);
3927
- }
3928
- }
3929
- return buffer.slice(0, position);
3930
- }
3931
- };
3932
-
3933
- // src/index.ts
3934
- var GAIA_CORE_VERSION = "0.1.6";
3935
- var isPlatformBrowser = typeof window !== "undefined";
3936
- var isPlatformNode = typeof process !== "undefined" && process.versions?.node;
3937
- var isPlatformWebWorker = typeof importScripts !== "undefined";
3938
-
3939
- exports.AddressingModeHandler = AddressingModeHandler;
3940
- exports.AsmReader = AsmReader;
3941
- exports.Assembler = Assembler;
3942
- exports.AssemblerState = AssemblerState;
3943
- exports.BlockReader = BlockReader;
3944
- exports.BlockWriter = BlockWriter;
3945
- exports.CopCommandProcessor = CopCommandProcessor;
3946
- exports.GAIA_CORE_VERSION = GAIA_CORE_VERSION;
3947
- exports.ObjectType = ObjectType;
3948
- exports.OperationContext = OperationContext;
3949
- exports.PostProcessor = PostProcessor;
3950
- exports.ProcessorStateManager = ProcessorStateManager;
3951
- exports.QuintetLZ = QuintetLZ;
3952
- exports.ReferenceManager = ReferenceManager;
3953
- exports.Registers = Registers;
3954
- exports.RomDataReader = RomDataReader;
3955
- exports.RomGenerator = RomGenerator;
3956
- exports.RomLayout = RomLayout;
3957
- exports.RomProcessor = RomProcessor;
3958
- exports.RomState = RomState;
3959
- exports.RomStateUtils = RomStateUtils;
3960
- exports.RomWriter = RomWriter;
3961
- exports.SortedMap = SortedMap;
3962
- exports.SpriteFrame = SpriteFrame;
3963
- exports.SpriteGroup = SpriteGroup;
3964
- exports.SpriteMap = SpriteMap;
3965
- exports.SpritePart = SpritePart;
3966
- exports.Stack = Stack;
3967
- exports.StackOperations = StackOperations;
3968
- exports.StringProcessor = StringProcessor;
3969
- exports.StringReader = StringReader;
3970
- exports.TransformProcessor = TransformProcessor;
3971
- exports.TypeParser = TypeParser;
3972
- exports.isPlatformBrowser = isPlatformBrowser;
3973
- exports.isPlatformNode = isPlatformNode;
3974
- exports.isPlatformWebWorker = isPlatformWebWorker;
3975
- exports.registerCompressionProviders = registerCompressionProviders;
3976
- //# sourceMappingURL=index.js.map
3977
- //# sourceMappingURL=index.js.map