madden-franchise 3.0.6 → 3.2.0
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/FranchiseFile.js +1 -1
- package/FranchiseFileField.js +21 -1
- package/FranchiseFileSettings.js +2 -1
- package/FranchiseFileTable.js +101 -53
- package/FranchiseFileTable3Field.js +92 -0
- package/README.md +4 -0
- package/package.json +1 -1
- package/scripts/scratchpad.js +30 -16
- package/strategies/StrategyPicker.js +5 -0
- package/strategies/common/header/m19/M19TableHeaderStrategy.js +2 -1
- package/strategies/common/header/m20/M20TableHeaderStrategy.js +2 -1
- package/strategies/common/header/m24/M24TableHeaderStrategy.js +15 -0
- package/strategies/common/table/FranchiseTableStrategy.js +3 -1
- package/strategies/common/table3Field/FranchiseTable3FieldStrategy.js +24 -0
- package/strategies/franchise/m19/M19Strategy.js +3 -1
- package/strategies/franchise/m19/M19TableStrategy.js +1 -0
- package/strategies/franchise/m20/M20Strategy.js +3 -1
- package/strategies/franchise/m20/M20TableStrategy.js +1 -0
- package/strategies/franchise/m24/M24Strategy.js +12 -0
- package/strategies/franchise/m24/M24Table3FieldStrategy.js +9 -0
- package/strategies/franchise/m24/M24TableStrategy.js +15 -0
- package/strategies/franchise-common/m19/M19FTCStrategy.js +3 -1
- package/strategies/franchise-common/m19/M19FTCTableStrategy.js +1 -0
- package/strategies/franchise-common/m20/M20FTCStrategy.js +3 -1
- package/strategies/franchise-common/m20/M20FTCTableStrategy.js +1 -0
package/FranchiseFile.js
CHANGED
|
@@ -117,7 +117,7 @@ class FranchiseFile extends EventEmitter {
|
|
|
117
117
|
|
|
118
118
|
const tableData = this.unpackedFileContents.slice(currentTable, nextTable);
|
|
119
119
|
|
|
120
|
-
const newFranchiseTable = new FranchiseFileTable(tableData, currentTable, this._gameYear, this.strategy);
|
|
120
|
+
const newFranchiseTable = new FranchiseFileTable(tableData, currentTable, this._gameYear, this.strategy, this.settings);
|
|
121
121
|
newFranchiseTable.index = i;
|
|
122
122
|
this.tables.push(newFranchiseTable);
|
|
123
123
|
|
package/FranchiseFileField.js
CHANGED
|
@@ -2,6 +2,7 @@ const { BitView } = require('bit-buffer');
|
|
|
2
2
|
|
|
3
3
|
const utilService = require('./services/utilService');
|
|
4
4
|
const FranchiseFileTable2Field = require('./FranchiseFileTable2Field');
|
|
5
|
+
const FranchiseFileTable3Field = require('./FranchiseFileTable3Field');
|
|
5
6
|
|
|
6
7
|
class FranchiseFileField {
|
|
7
8
|
constructor(key, value, offset, parent) {
|
|
@@ -16,6 +17,11 @@ class FranchiseFileField {
|
|
|
16
17
|
this.secondTableField = new FranchiseFileTable2Field(this._recordBuffer.readUInt32BE(offset.offset / 8), offset.maxLength);
|
|
17
18
|
this.secondTableField.fieldReference = this;
|
|
18
19
|
}
|
|
20
|
+
|
|
21
|
+
if (offset.valueInThirdTable) {
|
|
22
|
+
this.thirdTableField = new FranchiseFileTable3Field(this._recordBuffer.readUInt32BE(offset.offset / 8), offset.maxLength);
|
|
23
|
+
this.thirdTableField.fieldReference = this;
|
|
24
|
+
}
|
|
19
25
|
};
|
|
20
26
|
|
|
21
27
|
get key () {
|
|
@@ -64,7 +70,18 @@ class FranchiseFileField {
|
|
|
64
70
|
|
|
65
71
|
if (this.offset.valueInSecondTable) {
|
|
66
72
|
this.secondTableField.value = value.toString();
|
|
67
|
-
}
|
|
73
|
+
}
|
|
74
|
+
else if (this.offset.valueInThirdTable) {
|
|
75
|
+
if (typeof value === 'object') {
|
|
76
|
+
const newVal = JSON.stringify(value);
|
|
77
|
+
this._value = newVal;
|
|
78
|
+
this.thirdTableField.value = newVal;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.thirdTableField.value = value.toString();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
68
85
|
let actualValue;
|
|
69
86
|
|
|
70
87
|
if (this.offset.isReference) {
|
|
@@ -214,6 +231,9 @@ class FranchiseFileField {
|
|
|
214
231
|
if (offset.valueInSecondTable) {
|
|
215
232
|
return this.secondTableField.value;
|
|
216
233
|
}
|
|
234
|
+
else if (offset.valueInThirdTable) {
|
|
235
|
+
return this.thirdTableField.value;
|
|
236
|
+
}
|
|
217
237
|
else if (offset.enum) {
|
|
218
238
|
const enumUnformattedValue = utilService.dec2bin(this.unformattedValue.getBits(this.offset.offset, this.offset.length), offset.enum._maxLength);
|
|
219
239
|
|
package/FranchiseFileSettings.js
CHANGED
|
@@ -3,7 +3,8 @@ class FranchiseFileSettings {
|
|
|
3
3
|
this.saveOnChange = settings && settings.saveOnChange ? settings.saveOnChange : false;
|
|
4
4
|
this.schemaOverride = settings && settings.schemaOverride ? settings.schemaOverride: false;
|
|
5
5
|
this.schemaDirectory = settings && settings.schemaDirectory ? settings.schemaDirectory : false;
|
|
6
|
-
this.autoParse = settings && (settings.autoParse !== null && settings.autoParse !== undefined) ? settings.autoParse : true
|
|
6
|
+
this.autoParse = settings && (settings.autoParse !== null && settings.autoParse !== undefined) ? settings.autoParse : true;
|
|
7
|
+
this.autoUnempty = settings && (settings.autoUnempty !== null && settings.autoUnempty !== undefined) ? settings.autoUnempty : false;
|
|
7
8
|
}
|
|
8
9
|
};
|
|
9
10
|
|
package/FranchiseFileTable.js
CHANGED
|
@@ -2,9 +2,10 @@ const EventEmitter = require('events').EventEmitter;
|
|
|
2
2
|
const utilService = require('./services/utilService');
|
|
3
3
|
const FranchiseFileRecord = require('./FranchiseFileRecord');
|
|
4
4
|
const FranchiseFileTable2Field = require('./FranchiseFileTable2Field');
|
|
5
|
+
const FranchiseFileTable3Field = require('./FranchiseFileTable3Field');
|
|
5
6
|
|
|
6
7
|
class FranchiseFileTable extends EventEmitter {
|
|
7
|
-
constructor(data, offset, gameYear, strategy) {
|
|
8
|
+
constructor(data, offset, gameYear, strategy, settings) {
|
|
8
9
|
super();
|
|
9
10
|
this.index = -1;
|
|
10
11
|
this.data = data;
|
|
@@ -21,8 +22,10 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
21
22
|
this.isChanged = false;
|
|
22
23
|
this.records = [];
|
|
23
24
|
this.table2Records = [];
|
|
25
|
+
this.table3Records = [];
|
|
24
26
|
this.arraySizes = [];
|
|
25
27
|
this.emptyRecords = new Map();
|
|
28
|
+
this._settings = settings;
|
|
26
29
|
};
|
|
27
30
|
|
|
28
31
|
get hexData () {
|
|
@@ -51,22 +54,31 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
updateBuffer() {
|
|
54
|
-
// need to check table2 data first because it may change offsets of the legit records.
|
|
55
|
-
|
|
57
|
+
// need to check table2 & table3 data first because it may change offsets of the legit records.
|
|
58
|
+
let table2Data = this.strategy.getTable2BinaryData(this.table2Records, this.data.slice(this.header.table2StartIndex));
|
|
59
|
+
let table3Data = this.strategy.getTable3BinaryData(this.table3Records, this.data.slice(this.header.table3StartIndex));
|
|
56
60
|
|
|
57
61
|
// update table2 length and table total length in table header (only if records have been read)
|
|
58
62
|
if (this.recordsRead) {
|
|
59
63
|
let table2DataLength = 0;
|
|
64
|
+
let table3DataLength = 0;
|
|
60
65
|
|
|
61
66
|
// Get length of all table2Data sub arrays
|
|
62
67
|
table2Data.forEach((arr) => {
|
|
63
68
|
table2DataLength += arr.length;
|
|
64
69
|
});
|
|
65
70
|
|
|
71
|
+
table3Data.forEach((arr) => {
|
|
72
|
+
table3DataLength += arr.length;
|
|
73
|
+
});
|
|
74
|
+
|
|
66
75
|
this.header.table2Length = table2DataLength;
|
|
67
76
|
this.header.tableTotalLength = this.header.table1Length + this.header.table2Length;
|
|
68
77
|
|
|
78
|
+
this.header.table3Length = table3DataLength;
|
|
79
|
+
|
|
69
80
|
this.data.writeUInt32BE(this.header.table2Length , this.header.offsetStart - 44);
|
|
81
|
+
this.data.writeUInt32BE(this.header.table3Length, this.header.offsetStart - 40);
|
|
70
82
|
this.data.writeUInt32BE(this.header.tableTotalLength, this.header.offsetStart - 24);
|
|
71
83
|
}
|
|
72
84
|
|
|
@@ -102,8 +114,19 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
bufferArrays.push(this.data.slice(currentOffset, this.header.table2StartIndex));
|
|
117
|
+
|
|
118
|
+
if (!this.recordsRead && this.header.hasSecondTable && table2Data.length === 0) {
|
|
119
|
+
table2Data = this.data.slice(this.header.table2StartIndex);
|
|
120
|
+
}
|
|
121
|
+
|
|
105
122
|
bufferArrays = bufferArrays.concat(table2Data);
|
|
106
123
|
|
|
124
|
+
if (!this.recordsRead && this.header.hasThirdTable && table3Data.length === 0) {
|
|
125
|
+
table3Data = this.data.slice(this.header.table3StartIndex);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
bufferArrays = bufferArrays.concat(table3Data);
|
|
129
|
+
|
|
107
130
|
this.data = Buffer.concat(bufferArrays);
|
|
108
131
|
};
|
|
109
132
|
|
|
@@ -195,6 +218,7 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
195
218
|
this.isChanged = false;
|
|
196
219
|
this.records = [];
|
|
197
220
|
this.table2Records = [];
|
|
221
|
+
this.table3Records = [];
|
|
198
222
|
this.arraySizes = [];
|
|
199
223
|
this.emptyRecords = new Map();
|
|
200
224
|
|
|
@@ -230,7 +254,8 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
230
254
|
'name': `${this.name.substring(0, this.name.length - 2)}${i}`,
|
|
231
255
|
'offset': i * 32,
|
|
232
256
|
'type': this.name.substring(0, this.name.length - 2),
|
|
233
|
-
'valueInSecondTable': false
|
|
257
|
+
'valueInSecondTable': false,
|
|
258
|
+
'valueInThirdTable': false,
|
|
234
259
|
}
|
|
235
260
|
|
|
236
261
|
offset.isReference = !offset.enum && (offset.type[0] == offset.type[0].toUpperCase() || offset.type.includes('[]')) ? true : false,
|
|
@@ -268,6 +293,10 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
268
293
|
this._parseTable2Values(this.data, this.header, this.records);
|
|
269
294
|
}
|
|
270
295
|
|
|
296
|
+
if (this.header.hasThirdTable) {
|
|
297
|
+
this._parseTable3Values(this.data, this.header, this.records);
|
|
298
|
+
}
|
|
299
|
+
|
|
271
300
|
this.emptyRecords = this._parseEmptyRecords();
|
|
272
301
|
|
|
273
302
|
this.records.forEach((record, index) => {
|
|
@@ -379,6 +408,22 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
379
408
|
});
|
|
380
409
|
});
|
|
381
410
|
};
|
|
411
|
+
|
|
412
|
+
_parseTable3Values(data, header, records) {
|
|
413
|
+
const that = this;
|
|
414
|
+
const thirdTableData = data.slice(header.table3StartIndex);
|
|
415
|
+
|
|
416
|
+
records.forEach((record) => {
|
|
417
|
+
const fieldsReferencingThirdTable = record.fieldsArray.filter((field) => { return field.thirdTableField; });
|
|
418
|
+
|
|
419
|
+
fieldsReferencingThirdTable.forEach((field) => {
|
|
420
|
+
field.thirdTableField.unformattedValue = that.strategyBase.table3Field.getInitialUnformattedValue(field, thirdTableData);
|
|
421
|
+
field.thirdTableField.strategy = that.strategyBase.table3Field;
|
|
422
|
+
that.table3Records.push(field.thirdTableField);
|
|
423
|
+
field.thirdTableField.parent = that;
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
};
|
|
382
427
|
|
|
383
428
|
_changeRecordBuffers(index, emptyRecordReference) {
|
|
384
429
|
this._setBufferToEmptyRecordReference(index, emptyRecordReference);
|
|
@@ -426,10 +471,8 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
426
471
|
const emptyRecordReference = this.emptyRecords.get(object.index);
|
|
427
472
|
const changedRecordWasEmpty = emptyRecordReference !== null && emptyRecordReference !== undefined;
|
|
428
473
|
|
|
474
|
+
// Automatically un-empty the row if the setting is enabled and the changed record was empty.
|
|
429
475
|
if (changedRecordWasEmpty) {
|
|
430
|
-
// 1/5/23: Assume all changes to a field make the record not empty. Leaving comments
|
|
431
|
-
// below if this needs to be reverted in the future.
|
|
432
|
-
|
|
433
476
|
|
|
434
477
|
// Check if the record's first four bytes still have a reference to the 0th table.
|
|
435
478
|
// If so, then the record is still considered empty.
|
|
@@ -442,7 +485,7 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
442
485
|
// if the changed field isn't included in the first 32 bits, zero out the first 32 bits.
|
|
443
486
|
// Otherwise, it's not necessary to zero out.
|
|
444
487
|
const changedFieldsInFirst4Bytes = object.fieldsArray.filter((field) => { return field.isChanged && field.offset.indexOffset < 32; });
|
|
445
|
-
if (changedFieldsInFirst4Bytes.length === 0) {
|
|
488
|
+
if (this._settings.autoUnempty && changedFieldsInFirst4Bytes.length === 0) {
|
|
446
489
|
// set first 4 bytes to 0
|
|
447
490
|
this._changeRecordBuffers(object.index, 0);
|
|
448
491
|
|
|
@@ -452,53 +495,57 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
452
495
|
field.clearCachedValues();
|
|
453
496
|
});
|
|
454
497
|
}
|
|
498
|
+
|
|
499
|
+
// If autoUnempty is disabled, only un-empty the row if a field in the first 4 bytes changed.
|
|
500
|
+
// If autoUnempty is enabled, un-empty the row if ANY field changed.
|
|
501
|
+
if (this._settings.autoUnempty || changedFieldsInFirst4Bytes.length > 0) {
|
|
502
|
+
// if the record contains any string values, point the string values to
|
|
503
|
+
// their correct offsets
|
|
504
|
+
this.strategy.recalculateStringOffsets(this, object);
|
|
505
|
+
|
|
506
|
+
// Delete the empty record entry because it is no longer empty
|
|
507
|
+
this.emptyRecords.delete(object.index);
|
|
508
|
+
|
|
509
|
+
// Set the isEmpty back to false because it's no longer empty
|
|
510
|
+
object.isEmpty = false;
|
|
511
|
+
|
|
512
|
+
// Check if there is a previous empty record
|
|
513
|
+
const previousEmptyReference = this.emptyRecords.get(emptyRecordReference.previous);
|
|
514
|
+
|
|
515
|
+
if (previousEmptyReference) {
|
|
516
|
+
// Set the previous empty record to point to the old reference's next node
|
|
517
|
+
this.emptyRecords.set(emptyRecordReference.previous, {
|
|
518
|
+
previous: this.emptyRecords.get(emptyRecordReference.previous).previous,
|
|
519
|
+
next: emptyRecordReference.next
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// change the table buffer and record buffer to reflect object change
|
|
523
|
+
this._changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
|
|
524
|
+
}
|
|
455
525
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
this.emptyRecords.set(emptyRecordReference.previous, {
|
|
472
|
-
previous: this.emptyRecords.get(emptyRecordReference.previous).previous,
|
|
473
|
-
next: emptyRecordReference.next
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// change the table buffer and record buffer to reflect object change
|
|
477
|
-
this._changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// If there is a next empty reference, update the previous value accordingly to now point
|
|
481
|
-
// to the current record's previous index.
|
|
482
|
-
const nextEmptyReference = this.emptyRecords.get(emptyRecordReference.next);
|
|
483
|
-
|
|
484
|
-
if (nextEmptyReference) {
|
|
485
|
-
this.emptyRecords.set(emptyRecordReference.next, {
|
|
486
|
-
previous: emptyRecordReference.previous,
|
|
487
|
-
next: this.emptyRecords.get(emptyRecordReference.next).next
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
if (!previousEmptyReference) {
|
|
491
|
-
// If no previous empty record exists and a next record exists, we need to update the header to
|
|
492
|
-
// point to object record as the next record to use.
|
|
493
|
-
this.setNextRecordToUse(emptyRecordReference.next);
|
|
526
|
+
// If there is a next empty reference, update the previous value accordingly to now point
|
|
527
|
+
// to the current record's previous index.
|
|
528
|
+
const nextEmptyReference = this.emptyRecords.get(emptyRecordReference.next);
|
|
529
|
+
|
|
530
|
+
if (nextEmptyReference) {
|
|
531
|
+
this.emptyRecords.set(emptyRecordReference.next, {
|
|
532
|
+
previous: emptyRecordReference.previous,
|
|
533
|
+
next: this.emptyRecords.get(emptyRecordReference.next).next
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (!previousEmptyReference) {
|
|
537
|
+
// If no previous empty record exists and a next record exists, we need to update the header to
|
|
538
|
+
// point to object record as the next record to use.
|
|
539
|
+
this.setNextRecordToUse(emptyRecordReference.next);
|
|
540
|
+
}
|
|
494
541
|
}
|
|
495
|
-
}
|
|
496
542
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
543
|
+
// If there are no previous or next empty references
|
|
544
|
+
// Then there are no more empty references in the table
|
|
545
|
+
// Update the table header nextRecordToUse back to the table record capacity
|
|
546
|
+
if (!previousEmptyReference && !nextEmptyReference) {
|
|
547
|
+
this.setNextRecordToUse(this.header.recordCapacity);
|
|
548
|
+
}
|
|
502
549
|
}
|
|
503
550
|
// }
|
|
504
551
|
}
|
|
@@ -511,7 +558,7 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
511
558
|
this.emit('change');
|
|
512
559
|
}
|
|
513
560
|
}
|
|
514
|
-
else if (object instanceof FranchiseFileTable2Field) {
|
|
561
|
+
else if (object instanceof FranchiseFileTable2Field || object instanceof FranchiseFileTable3Field) {
|
|
515
562
|
object.isChanged = true;
|
|
516
563
|
|
|
517
564
|
// When a table2 field changes, we need to check if the record is empty. If so, we need to mark it as not empty.
|
|
@@ -633,6 +680,7 @@ function readOffsetTable(data, schema, header) {
|
|
|
633
680
|
'type': (minValue < 0 || maxValue < 0) ? 's_' + attribute.type : attribute.type,
|
|
634
681
|
'isReference': !attribute.enum && (attribute.type[0] == attribute.type[0].toUpperCase() || attribute.type.includes('[]') || attribute.type === 'record') ? true : false,
|
|
635
682
|
'valueInSecondTable': header.hasSecondTable && attribute.type === 'string',
|
|
683
|
+
'valueInThirdTable': header.hasThirdTable && attribute.type === 'binaryblob',
|
|
636
684
|
'isSigned': minValue < 0 || maxValue < 0,
|
|
637
685
|
'minValue': minValue,
|
|
638
686
|
'maxValue': maxValue,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const EventEmitter = require('events').EventEmitter;
|
|
2
|
+
const utilService = require('./services/utilService');
|
|
3
|
+
|
|
4
|
+
class FranchiseFileTable3Field {
|
|
5
|
+
constructor (index, maxLength, parent) {
|
|
6
|
+
this._value = '';
|
|
7
|
+
this.rawIndex = index;
|
|
8
|
+
this.isChanged = false;
|
|
9
|
+
this.maxLength = maxLength;
|
|
10
|
+
this.fieldReference = null;
|
|
11
|
+
this.lengthAtLastSave = null;
|
|
12
|
+
this._unformattedValue = null;
|
|
13
|
+
this.index = index;
|
|
14
|
+
this._offset = this.index;
|
|
15
|
+
this._parent = parent;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
get unformattedValue () {
|
|
19
|
+
return this._unformattedValue;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
set unformattedValue (value) {
|
|
23
|
+
this._unformattedValue = value;
|
|
24
|
+
|
|
25
|
+
if (this.lengthAtLastSave === null) {
|
|
26
|
+
this.lengthAtLastSave = getLengthOfUnformattedValue(this._unformattedValue);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this._value = null;
|
|
30
|
+
if (this._parent) {
|
|
31
|
+
this._parent.onEvent('change', this);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
get value () {
|
|
36
|
+
if (this._value === null) {
|
|
37
|
+
this._value = this._strategy.getFormattedValueFromUnformatted(this._unformattedValue);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return this._value;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
set value (value) {
|
|
44
|
+
this._value = value;
|
|
45
|
+
this._unformattedValue = this._strategy.setUnformattedValueFromFormatted(value, this.maxLength);
|
|
46
|
+
|
|
47
|
+
if (this.lengthAtLastSave === null) {
|
|
48
|
+
this.lengthAtLastSave = getLengthOfUnformattedValue(this._unformattedValue);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this._parent.onEvent('change', this);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
get hexData () {
|
|
55
|
+
return this._unformattedValue;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
get strategy () {
|
|
59
|
+
return this._strategy;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
set strategy (strategy) {
|
|
63
|
+
this._strategy = strategy;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
get offset () {
|
|
67
|
+
return this._offset;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
set offset (offset) {
|
|
71
|
+
this._offset = offset;
|
|
72
|
+
this.index = offset;
|
|
73
|
+
|
|
74
|
+
if (this.fieldReference) {
|
|
75
|
+
this.fieldReference.unformattedValue.setBits(this.fieldReference.offset.offset, offset, 32);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
get parent() {
|
|
80
|
+
return this._parent;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
set parent(parent) {
|
|
84
|
+
this._parent = parent;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
module.exports = FranchiseFileTable3Field;
|
|
89
|
+
|
|
90
|
+
function getLengthOfUnformattedValue(value) {
|
|
91
|
+
return value.length;
|
|
92
|
+
};
|
package/README.md
CHANGED
|
@@ -49,6 +49,10 @@ Franchise file settings
|
|
|
49
49
|
|
|
50
50
|
// AUTO PARSE - specify if you want the system to automatically parse all tables in the file, or if you want to explicity call file.parse()
|
|
51
51
|
'autoParse': true/false [default: true]
|
|
52
|
+
|
|
53
|
+
// AUTO UNEMPTY - specify if you want the system to automatically determine if an empty field should become un-empty once you edit it.
|
|
54
|
+
// Warning: may have unintended side-effects if you batch import. Enable with caution.
|
|
55
|
+
'autoUnempty': true/false [default: false]
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
#### fields
|
package/package.json
CHANGED
package/scripts/scratchpad.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const zlib = require('zlib');
|
|
3
|
+
|
|
1
4
|
const FranchiseFile = require('../FranchiseFile');
|
|
2
5
|
const utilService = require('../services/utilService');
|
|
3
6
|
|
|
4
|
-
const franchisePath = 'C:\\Users\\Matt\\
|
|
7
|
+
const franchisePath = 'C:\\Users\\Matt\\Documents\\Madden NFL 24\\saves\\CAREER-FANTASY';
|
|
5
8
|
const ftcPath = 'D:\\Projects\\Madden 24\\FTCs\\franchise-league-binary.FTC';
|
|
6
9
|
const schemaPath = 'C:\\Users\\Matt\\AppData\\Roaming\\madden-franchise-editor\\schemas\\M24_144_6.gz';
|
|
7
10
|
|
|
@@ -15,24 +18,35 @@ const schemaPath = 'C:\\Users\\Matt\\AppData\\Roaming\\madden-franchise-editor\\
|
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
const file = new FranchiseFile(franchisePath, {
|
|
18
|
-
schemaOverride: {
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
+
// schemaOverride: {
|
|
22
|
+
// path: schemaPath
|
|
23
|
+
// }
|
|
21
24
|
});
|
|
22
25
|
|
|
23
|
-
const ftc = new FranchiseFile(ftcPath, {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
26
|
+
// const ftc = new FranchiseFile(ftcPath, {
|
|
27
|
+
// schemaOverride: {
|
|
28
|
+
// path: schemaPath
|
|
29
|
+
// }
|
|
30
|
+
// });
|
|
28
31
|
|
|
29
|
-
await Promise.all([franchiseFileLoadPromise(file), franchiseFileLoadPromise(ftc)]);
|
|
32
|
+
// await Promise.all([franchiseFileLoadPromise(file), franchiseFileLoadPromise(ftc)]);
|
|
30
33
|
|
|
31
|
-
const tableToFind = file.getTableById(4227);
|
|
32
|
-
await tableToFind.readRecords();
|
|
34
|
+
// const tableToFind = file.getTableById(4227);
|
|
35
|
+
// await tableToFind.readRecords();
|
|
36
|
+
|
|
37
|
+
// const college = tableToFind.records[2].getFieldByKey('College');
|
|
38
|
+
// const collegeVal = utilService.bin2dec(college.value);
|
|
39
|
+
// const ref = ftc.getReferenceFromAssetId(collegeVal);
|
|
40
|
+
// console.log(ref);
|
|
41
|
+
|
|
42
|
+
await franchiseFileLoadPromise(file);
|
|
43
|
+
|
|
44
|
+
const table3s = file.tables.filter((table) => { return table.header.data1Pad4 > 0; });
|
|
45
|
+
table3s.forEach((table) => { console.log(`${table.header.tableId} - ${table.name}`)});
|
|
33
46
|
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
const buf = fs.readFileSync('D:\\Projects\\Madden 24\\franchise\\characterVisualIsolated.dat');
|
|
48
|
+
const test = zlib.gunzipSync(buf.subarray(2));
|
|
49
|
+
// fs.writeFileSync('D:\\Projects\\Madden 24\\franchise\\characterVisualUnzipped.dat', test);
|
|
50
|
+
const test2 = zlib.gzipSync(test);
|
|
51
|
+
console.log(test2);
|
|
38
52
|
})();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const Constants = require('../Constants');
|
|
2
2
|
const M19Strategy = require('./franchise/m19/M19Strategy');
|
|
3
3
|
const M20Strategy = require('./franchise/m20/M20Strategy');
|
|
4
|
+
const M24Strategy = require('./franchise/m24/M24Strategy');
|
|
4
5
|
const M19FTCStrategy = require('./franchise-common/m19/M19FTCStrategy');
|
|
5
6
|
const M20FTCStrategy = require('./franchise-common/m20/M20FTCStrategy');
|
|
6
7
|
|
|
@@ -13,8 +14,12 @@ StrategyPicker.pick = (type) => {
|
|
|
13
14
|
return M19Strategy;
|
|
14
15
|
case 20:
|
|
15
16
|
case 21:
|
|
17
|
+
case 22:
|
|
18
|
+
case 23:
|
|
16
19
|
default:
|
|
17
20
|
return M20Strategy;
|
|
21
|
+
case 24:
|
|
22
|
+
return M24Strategy;
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
else {
|
|
@@ -99,7 +99,8 @@ M19TableHeaderStrategy.parseHeader = (data) => {
|
|
|
99
99
|
'recordWords': data2RecordWords,
|
|
100
100
|
'recordCapacity': data2RecordCapacity,
|
|
101
101
|
'numMembers': data2IndexEntries,
|
|
102
|
-
'nextRecordToUse': nextRecordToUse
|
|
102
|
+
'nextRecordToUse': nextRecordToUse,
|
|
103
|
+
'hasThirdTable': false
|
|
103
104
|
};
|
|
104
105
|
};
|
|
105
106
|
|
|
@@ -108,7 +108,8 @@ M20TableHeaderStrategy.parseHeader = (data) => {
|
|
|
108
108
|
'recordWords': data2RecordWords,
|
|
109
109
|
'recordCapacity': data2RecordCapacity,
|
|
110
110
|
'numMembers': data2IndexEntries,
|
|
111
|
-
'nextRecordToUse': nextRecordToUse
|
|
111
|
+
'nextRecordToUse': nextRecordToUse,
|
|
112
|
+
'hasThirdTable': false
|
|
112
113
|
};
|
|
113
114
|
};
|
|
114
115
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const M20TableHeaderStrategy = require("../m20/M20TableHeaderStrategy");
|
|
2
|
+
|
|
3
|
+
let M24TableHeaderStrategy = {};
|
|
4
|
+
|
|
5
|
+
M24TableHeaderStrategy.parseHeader = (data) => {
|
|
6
|
+
let header = M20TableHeaderStrategy.parseHeader(data);
|
|
7
|
+
|
|
8
|
+
header.table3Length = header.data1Pad3;
|
|
9
|
+
header.hasThirdTable = header.table3Length > 0;
|
|
10
|
+
header.table3StartIndex = header.table2StartIndex + header.table2Length;
|
|
11
|
+
|
|
12
|
+
return header;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = M24TableHeaderStrategy;
|
|
@@ -24,7 +24,9 @@ FranchiseTableStrategy.getTable2BinaryData = (table2Records, fullTable2Buffer) =
|
|
|
24
24
|
currentOffset = recordOffset + recordHexData.length;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
if (table2Records.length > 0) {
|
|
28
|
+
table2Data.push(fullTable2Buffer.slice(currentOffset));
|
|
29
|
+
}
|
|
28
30
|
|
|
29
31
|
return table2Data;
|
|
30
32
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const zlib = require('zlib');
|
|
2
|
+
|
|
3
|
+
let FranchiseTable3FieldStrategy = {};
|
|
4
|
+
|
|
5
|
+
FranchiseTable3FieldStrategy.getInitialUnformattedValue = (field, data) => {
|
|
6
|
+
return data.slice(field.thirdTableField.index, (field.thirdTableField.index + field.offset.maxLength + 2));
|
|
7
|
+
// extend maxLength + 2 because the first 2 bytes are the size of the zipped data
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
FranchiseTable3FieldStrategy.getFormattedValueFromUnformatted = (unformattedValue) => {
|
|
11
|
+
return zlib.gunzipSync(unformattedValue.subarray(2)).toString(); // first 2 bytes are the size of the zipped data, so skip those.
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
FranchiseTable3FieldStrategy.setUnformattedValueFromFormatted = (formattedValue, maxLength) => {
|
|
15
|
+
let zippedData = zlib.gzipSync(formattedValue);
|
|
16
|
+
let padding = Buffer.alloc(maxLength - zippedData.length); // table3s all have the same length and are zero padded to the end.
|
|
17
|
+
|
|
18
|
+
let sizeBuf = Buffer.alloc(2);
|
|
19
|
+
sizeBuf.writeUInt16LE(zippedData.length);
|
|
20
|
+
|
|
21
|
+
return Buffer.concat([sizeBuf, zippedData, padding]);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = FranchiseTable3FieldStrategy;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const M19FileStrategy = require('./M19FileStrategy');
|
|
2
2
|
const M19TableStrategy = require('./M19TableStrategy');
|
|
3
3
|
const M19Table2FieldStrategy = require('./M19Table2FieldStrategy');
|
|
4
|
+
const M24Table3Strategy = require('../m24/M24Table3FieldStrategy');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
'name': 'M19Strategy',
|
|
7
8
|
'file': M19FileStrategy,
|
|
8
9
|
'table': M19TableStrategy,
|
|
9
|
-
'table2Field': M19Table2FieldStrategy
|
|
10
|
+
'table2Field': M19Table2FieldStrategy,
|
|
11
|
+
'table3Field': M24Table3Strategy
|
|
10
12
|
};
|
|
@@ -7,6 +7,7 @@ M19TableStrategy.parseHeader = M19TableHeaderStrategy.parseHeader;
|
|
|
7
7
|
M19TableStrategy.parseHeaderAttributesFromSchema = M19TableHeaderStrategy.parseHeaderAttributesFromSchema;
|
|
8
8
|
|
|
9
9
|
M19TableStrategy.getTable2BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
10
|
+
M19TableStrategy.getTable3BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
10
11
|
M19TableStrategy.getMandatoryOffsets = FranchiseTableStrategy.getMandatoryOffsets;
|
|
11
12
|
M19TableStrategy.recalculateStringOffsets = FranchiseTableStrategy.recalculateStringOffsets;
|
|
12
13
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const M20FileStrategy = require('./M20FileStrategy');
|
|
2
2
|
const M20TableStrategy = require('./M20TableStrategy');
|
|
3
3
|
const M20Table2FieldStrategy = require('./M20Table2FieldStrategy');
|
|
4
|
+
const M24Table3FieldStrategy = require('../m24/M24Table3FieldStrategy');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
'name': 'M20Strategy',
|
|
7
8
|
'file': M20FileStrategy,
|
|
8
9
|
'table': M20TableStrategy,
|
|
9
|
-
'table2Field': M20Table2FieldStrategy
|
|
10
|
+
'table2Field': M20Table2FieldStrategy,
|
|
11
|
+
'table3Field': M24Table3FieldStrategy
|
|
10
12
|
};
|
|
@@ -7,6 +7,7 @@ M20TableStrategy.parseHeader = M20TableHeaderStrategy.parseHeader;
|
|
|
7
7
|
M20TableStrategy.parseHeaderAttributesFromSchema = M20TableHeaderStrategy.parseHeaderAttributesFromSchema;
|
|
8
8
|
|
|
9
9
|
M20TableStrategy.getTable2BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
10
|
+
M20TableStrategy.getTable3BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
10
11
|
M20TableStrategy.getMandatoryOffsets = FranchiseTableStrategy.getMandatoryOffsets;
|
|
11
12
|
M20TableStrategy.recalculateStringOffsets = FranchiseTableStrategy.recalculateStringOffsets;
|
|
12
13
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const M20FileStrategy = require('../m20/M20FileStrategy');
|
|
2
|
+
const M24TableStrategy = require('./M24TableStrategy');
|
|
3
|
+
const M20Table2FieldStrategy = require('../m20/M20Table2FieldStrategy');
|
|
4
|
+
const M24Table3FieldStrategy = require('./M24Table3FieldStrategy');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
'name': 'M24Strategy',
|
|
8
|
+
'file': M20FileStrategy,
|
|
9
|
+
'table': M24TableStrategy,
|
|
10
|
+
'table2Field': M20Table2FieldStrategy,
|
|
11
|
+
'table3Field': M24Table3FieldStrategy
|
|
12
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const FranchiseTable3FieldStrategy = require('../../common/table3Field/FranchiseTable3FieldStrategy');
|
|
2
|
+
|
|
3
|
+
let M24Table3Strategy = {};
|
|
4
|
+
|
|
5
|
+
M24Table3Strategy.getInitialUnformattedValue = FranchiseTable3FieldStrategy.getInitialUnformattedValue;
|
|
6
|
+
M24Table3Strategy.getFormattedValueFromUnformatted = FranchiseTable3FieldStrategy.getFormattedValueFromUnformatted;
|
|
7
|
+
M24Table3Strategy.setUnformattedValueFromFormatted = FranchiseTable3FieldStrategy.setUnformattedValueFromFormatted;
|
|
8
|
+
|
|
9
|
+
module.exports = M24Table3Strategy;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const FranchiseTableStrategy = require('../../common/table/FranchiseTableStrategy');
|
|
2
|
+
const M20TableHeaderStrategy = require('../../common/header/m20/M20TableHeaderStrategy');
|
|
3
|
+
const M24TableHeaderStrategy = require('../../common/header/m24/M24TableHeaderStrategy');
|
|
4
|
+
|
|
5
|
+
let M24TableStrategy = {};
|
|
6
|
+
|
|
7
|
+
M24TableStrategy.parseHeader = M24TableHeaderStrategy.parseHeader;
|
|
8
|
+
M24TableStrategy.parseHeaderAttributesFromSchema = M20TableHeaderStrategy.parseHeaderAttributesFromSchema;
|
|
9
|
+
|
|
10
|
+
M24TableStrategy.getTable2BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
11
|
+
M24TableStrategy.getTable3BinaryData = FranchiseTableStrategy.getTable2BinaryData;
|
|
12
|
+
M24TableStrategy.getMandatoryOffsets = FranchiseTableStrategy.getMandatoryOffsets;
|
|
13
|
+
M24TableStrategy.recalculateStringOffsets = FranchiseTableStrategy.recalculateStringOffsets;
|
|
14
|
+
|
|
15
|
+
module.exports = M24TableStrategy;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const M19FTCFileStrategy = require('./M19FTCFileStrategy');
|
|
2
2
|
const M19FTCTableStrategy = require('./M19FTCTableStrategy');
|
|
3
3
|
const M19FTCTable2FieldStrategy = require('./M19FTCTable2FieldStrategy');
|
|
4
|
+
const M24Table3Strategy = require('../../franchise/m24/M24Table3FieldStrategy');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
'name': 'M19FTCStrategy',
|
|
7
8
|
'file': M19FTCFileStrategy,
|
|
8
9
|
'table': M19FTCTableStrategy,
|
|
9
|
-
'table2Field': M19FTCTable2FieldStrategy
|
|
10
|
+
'table2Field': M19FTCTable2FieldStrategy,
|
|
11
|
+
'table3Field': M24Table3Strategy
|
|
10
12
|
};
|
|
@@ -7,6 +7,7 @@ M19FTCTableStrategy.parseHeader = M19TableHeaderStrategy.parseHeader;
|
|
|
7
7
|
M19FTCTableStrategy.parseHeaderAttributesFromSchema = M19TableHeaderStrategy.parseHeaderAttributesFromSchema;
|
|
8
8
|
|
|
9
9
|
M19FTCTableStrategy.getTable2BinaryData = FTCTableStrategy.getTable2BinaryData;
|
|
10
|
+
M19FTCTableStrategy.getTable3BinaryData = FTCTableStrategy.getTable2BinaryData;
|
|
10
11
|
M19FTCTableStrategy.getMandatoryOffsets = FTCTableStrategy.getMandatoryOffsets;
|
|
11
12
|
M19FTCTableStrategy.recalculateStringOffsets = () => {};
|
|
12
13
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const M20FTCFileStrategy = require('./M20FTCFileStrategy');
|
|
2
2
|
const M20FTCTableStrategy = require('./M20FTCTableStrategy');
|
|
3
3
|
const M20FTCTable2FieldStrategy = require('./M20FTCTable2FieldStrategy');
|
|
4
|
+
const M24Table3Strategy = require('../../franchise/m24/M24Table3FieldStrategy');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
'name': 'M20FTCStrategy',
|
|
7
8
|
'file': M20FTCFileStrategy,
|
|
8
9
|
'table': M20FTCTableStrategy,
|
|
9
|
-
'table2Field': M20FTCTable2FieldStrategy
|
|
10
|
+
'table2Field': M20FTCTable2FieldStrategy,
|
|
11
|
+
'table3Field': M24Table3Strategy
|
|
10
12
|
};
|
|
@@ -7,6 +7,7 @@ M20FTCTableStrategy.parseHeader = M20TableHeaderStrategy.parseHeader;
|
|
|
7
7
|
M20FTCTableStrategy.parseHeaderAttributesFromSchema = M20TableHeaderStrategy.parseHeaderAttributesFromSchema;
|
|
8
8
|
|
|
9
9
|
M20FTCTableStrategy.getTable2BinaryData = FTCTableStrategy.getTable2BinaryData;
|
|
10
|
+
M20FTCTableStrategy.getTable3BinaryData = FTCTableStrategy.getTable2BinaryData;
|
|
10
11
|
M20FTCTableStrategy.getMandatoryOffsets = FTCTableStrategy.getMandatoryOffsets;
|
|
11
12
|
M20FTCTableStrategy.recalculateStringOffsets = () => {};
|
|
12
13
|
|