madden-franchise 2.3.5 → 2.3.9

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.
@@ -68,7 +68,7 @@ class FranchiseFileRecord extends EventEmitter {
68
68
 
69
69
  this._fields.forEach((field) => {
70
70
  const unformattedValue = data.slice(field.offset.offset, field.offset.offset + field.offset.length);
71
- field.setUnformattedValueWithoutChangeEvent(unformattedValue, true);
71
+ field.setUnformattedValueWithoutChangeEvent(unformattedValue);
72
72
  });
73
73
  };
74
74
 
@@ -86,16 +86,76 @@ class FranchiseFileTable extends EventEmitter {
86
86
  };
87
87
 
88
88
  setNextRecordToUse(index, resetEmptyRecordMap) {
89
+ this._setNextRecordToUseBuffer(index);
90
+
91
+ // Recalculate the empty record map if the option is set and the
92
+ // records have already been read.
93
+ if (resetEmptyRecordMap && this.recordsRead) {
94
+ this.updateBuffer();
95
+ this.emptyRecords = this._parseEmptyRecords();
96
+ }
97
+
98
+ this.emit('change');
99
+ };
100
+
101
+ _setNextRecordToUseBuffer(index) {
89
102
  // We need to update the table header to use this row next
90
103
  this.header.nextRecordToUse = index;
91
104
 
92
105
  // And finally update the buffer to reflect this change
93
106
  this.data.writeUInt32BE(index, this.header.headerOffset - 4);
107
+ };
94
108
 
95
- // Recalculate the empty record map if the option is set and the
96
- // records have already been read.
97
- if (resetEmptyRecordMap && this.recordsRead) {
98
- this.updateBuffer();
109
+ recalculateEmptyRecordReferences() {
110
+ // For this method, we are not going to assume any existing empty records.
111
+ // We're going through each record and checking if it is an empty reference.
112
+ // If so, we'll add it to the list. At the end we will check if there are any unreachable empty references
113
+ // and update those accordingly.
114
+ let emptyRecordReferenceIndicies = [];
115
+
116
+ this.records.forEach((record) => {
117
+ let isEmptyReference = false;
118
+ const firstFourBytesReference = utilService.getReferenceData(record._data.slice(0, 32));
119
+
120
+ if (firstFourBytesReference.tableId === 0 && firstFourBytesReference.rowNumber !== 0) {
121
+ // Could be a an empty record reference or a table2 field.
122
+ // Check for a table2 field reference.
123
+ const firstOffset = this.offsetTable[0];
124
+ if (firstOffset.type !== 'string') {
125
+ isEmptyReference = true;
126
+
127
+ // Save the row number that this record points to.
128
+ emptyRecordReferenceIndicies.push(firstFourBytesReference.rowNumber);
129
+ }
130
+ }
131
+
132
+ record.isEmpty = isEmptyReference;
133
+ });
134
+
135
+ // We need to determine the starting node.
136
+ // To do that, we need to find the empty record which no other empty record points to.
137
+ const unreachableRecords = this.records.filter((record) => { return record.isEmpty; }).filter((record) => {
138
+ return emptyRecordReferenceIndicies.indexOf(record.index) === -1;
139
+ });
140
+
141
+ // If there are more than 1 nodes which are not referenced, there is an issue
142
+ if (unreachableRecords.length > 1) {
143
+ const unreachableIndicies = unreachableRecords.map((record) => {
144
+ return record.index;
145
+ });
146
+
147
+ console.warn(`(${this.header.tableId}) ${this.name} - More than one unreachable records found: `
148
+ + `(${unreachableIndicies.join(', ')}). The game will most likely crash if you do not fix this problem. `
149
+ + `The nextRecordToUse has NOT been updated.`);
150
+ }
151
+ else {
152
+ let nextRecordToUse = this.header.recordCapacity;
153
+
154
+ if (unreachableRecords.length === 1) {
155
+ nextRecordToUse = unreachableRecords[0].index;
156
+ }
157
+
158
+ this._setNextRecordToUseBuffer(nextRecordToUse);
99
159
  this.emptyRecords = this._parseEmptyRecords();
100
160
  }
101
161
  };
@@ -142,8 +202,7 @@ class FranchiseFileTable extends EventEmitter {
142
202
  reject('Cannot read records: Schema is not defined.');
143
203
  }
144
204
 
145
- this.emptyRecords = this._parseEmptyRecords();
146
-
205
+
147
206
  let offsetTableToUse = this.offsetTable;
148
207
  const mandatoryOffsetsToLoad = this.strategy.getMandatoryOffsets(this.offsetTable);
149
208
 
@@ -151,18 +210,20 @@ class FranchiseFileTable extends EventEmitter {
151
210
  // get any new attributes to load plus the existing loaded offsets
152
211
  offsetTableToUse = offsetTableToUse.filter((attrib) => {
153
212
  return mandatoryOffsetsToLoad.includes(attrib.name)
154
- || attribsToLoad.includes(attrib.name)
155
- || this.loadedOffsets.find((offset) => { return offset.name === attrib.name; });
213
+ || attribsToLoad.includes(attrib.name)
214
+ || this.loadedOffsets.find((offset) => { return offset.name === attrib.name; });
156
215
  });
157
216
  }
158
-
217
+
159
218
  this.loadedOffsets = offsetTableToUse;
160
219
  this.records = readRecords(this.data, this.header, offsetTableToUse);
161
-
220
+
162
221
  if (this.header.hasSecondTable) {
163
222
  this._parseTable2Values(this.data, this.header, this.records);
164
223
  }
165
224
 
225
+ this.emptyRecords = this._parseEmptyRecords();
226
+
166
227
  this.records.forEach((record, index) => {
167
228
  if (this.isArray) {
168
229
  record.arraySize = this.arraySizes[index];
@@ -247,14 +308,6 @@ class FranchiseFileTable extends EventEmitter {
247
308
  }
248
309
  }
249
310
  }
250
- else {
251
- // The field was not empty, let's check if it is now
252
- const referenceData = utilService.getReferenceData(this._data.slice(0, 32));
253
- if (referenceData.tableId === 0) {
254
- // In this case, the record is now empty. Add an entry to the empty record map
255
- // onRecordEmpty(this);
256
- }
257
- }
258
311
  }
259
312
 
260
313
  that.emit('change');
@@ -323,7 +376,7 @@ class FranchiseFileTable extends EventEmitter {
323
376
  function setBufferToEmptyRecordReference(index, emptyRecordReference) {
324
377
  const recordStartIndex = that.header.table1StartIndex + (index * that.header.record1Size)
325
378
  that.data.writeUInt32BE(emptyRecordReference, recordStartIndex);
326
- that.data.fill(0, recordStartIndex + 4, recordStartIndex + that.header.record1Size);
379
+ // that.data.fill(0, recordStartIndex + 4, recordStartIndex + that.header.record1Size);
327
380
  };
328
381
 
329
382
  function setRecordInternalBuffer(index, emptyRecordReference) {
@@ -331,7 +384,7 @@ class FranchiseFileTable extends EventEmitter {
331
384
 
332
385
  const recordSizeInBits = that.header.record1Size * 8;
333
386
  if (recordSizeInBits > 32) {
334
- newData += utilService.dec2bin(0, recordSizeInBits - 32);
387
+ newData += that.records[index]._data.slice(32);
335
388
  }
336
389
 
337
390
  that.records[index].data = newData;
@@ -364,9 +417,10 @@ class FranchiseFileTable extends EventEmitter {
364
417
  let previousEmptyRecordIndex = null;
365
418
  let currentEmptyRecordIndex = firstEmptyRecord;
366
419
 
367
- if (this.header.nextRecordToUse !== this.header.recordCapacity) {
420
+ if (firstEmptyRecord !== this.header.recordCapacity) {
368
421
  while (currentEmptyRecordIndex !== this.header.recordCapacity) {
369
- let nextEmptyRecordIndex = this.data.readUInt32BE(this.header.table1StartIndex + (currentEmptyRecordIndex * sizeOfEachRecord));
422
+ // let nextEmptyRecordIndex = this.data.readUInt32BE(this.header.table1StartIndex + (currentEmptyRecordIndex * sizeOfEachRecord));
423
+ let nextEmptyRecordIndex = utilService.getReferenceData(this.records[currentEmptyRecordIndex]._data.slice(0, 32)).rowNumber;
370
424
 
371
425
  emptyRecords.set(currentEmptyRecordIndex, {
372
426
  previous: previousEmptyRecordIndex,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "madden-franchise",
3
- "version": "2.3.5",
3
+ "version": "2.3.9",
4
4
  "description": "Tools to read a madden franchise file and get data from it",
5
5
  "main": "FranchiseFile.js",
6
6
  "scripts": {
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const zlib = require('zlib');
3
3
 
4
- const filePath = 'D:\\Projects\\Madden 22\\Expressions\\franchise-expressions-binary.frt';
4
+ const filePath = 'D:\\Projects\\Madden 22\\Expressions\\sabo-expression-binary.frt';
5
5
  const outputPath = 'D:\\Projects\\Madden 22\\Expressions\\modified-Franchise-Expression-binary.FTC'
6
6
 
7
7
  const stream = fs.createReadStream(filePath)
@@ -13,6 +13,9 @@ CommonAlgorithms.save = (units, oldData) => {
13
13
  let oldOffsetCounter = 0;
14
14
  let bufferArrays = [];
15
15
 
16
+ // Ensure the units are sorted by index in the actual file. Otherwise, we may overwrite data
17
+ units.sort((a, b) => { return a.index - b.index; });
18
+
16
19
  units.forEach((unit, index) => {
17
20
  if (unit.offset === 0 && index > 0) {
18
21
  // there are usually trailing records at the end of the table that reference
@@ -3,7 +3,8 @@ let FranchiseTableStrategy = {};
3
3
  FranchiseTableStrategy.getTable2BinaryData = (table2Records, fullTable2Buffer) => {
4
4
  let table2Data = [];
5
5
 
6
- const changedTable2Records = table2Records.filter((record) => { return record.isChanged; });
6
+ // Make sure to sort the table2 records by index
7
+ const changedTable2Records = table2Records.filter((record) => { return record.isChanged; }).sort((a, b) => { return a.index - b.index; });
7
8
  let currentOffset = 0;
8
9
 
9
10
  for (let i = 0; i < changedTable2Records.length; i++) {