madden-franchise 2.2.7 → 2.3.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/FranchiseFileField.js
CHANGED
|
@@ -64,6 +64,11 @@ class FranchiseFileField extends EventEmitter {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
set unformattedValue (unformattedValue) {
|
|
67
|
+
this.setUnformattedValueWithoutChangeEvent(unformattedValue);
|
|
68
|
+
this.emit('change');
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
setUnformattedValueWithoutChangeEvent(unformattedValue) {
|
|
67
72
|
if (!utilService.isString(unformattedValue)) { throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`); }
|
|
68
73
|
else if (!utilService.stringOnlyContainsBinaryDigits(unformattedValue)) { throw new Error(`Argument must only contain binary digits 1 and 0. If you would like to set the value, please set the 'value' attribute instead.`)}
|
|
69
74
|
else {
|
|
@@ -89,10 +94,8 @@ class FranchiseFileField extends EventEmitter {
|
|
|
89
94
|
else {
|
|
90
95
|
this._unformattedValue = unformattedValue;
|
|
91
96
|
}
|
|
92
|
-
|
|
93
|
-
this.emit('change');
|
|
94
97
|
}
|
|
95
|
-
}
|
|
98
|
+
}
|
|
96
99
|
};
|
|
97
100
|
|
|
98
101
|
module.exports = FranchiseFileField;
|
package/FranchiseFileRecord.js
CHANGED
|
@@ -58,6 +58,19 @@ class FranchiseFileRecord extends EventEmitter {
|
|
|
58
58
|
return Buffer.from(utilService.binaryBlockToDecimalBlock(this._data));
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
get fields () {
|
|
62
|
+
return this._fields;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
set data (data) {
|
|
66
|
+
this._data = data;
|
|
67
|
+
|
|
68
|
+
this._fields.forEach((field) => {
|
|
69
|
+
const unformattedValue = data.slice(field.offset.offset, field.offset.offset + field.offset.length);
|
|
70
|
+
field.setUnformattedValueWithoutChangeEvent(unformattedValue);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
61
74
|
getFieldByKey(key) {
|
|
62
75
|
return this._fields.find((field) => { return field.key === key; });
|
|
63
76
|
};
|
|
@@ -72,8 +85,8 @@ class FranchiseFileRecord extends EventEmitter {
|
|
|
72
85
|
return field ? field.referenceData : null;
|
|
73
86
|
};
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
empty() {
|
|
89
|
+
this.emit('empty');
|
|
77
90
|
};
|
|
78
91
|
};
|
|
79
92
|
|
package/FranchiseFileTable.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
1
|
const EventEmitter = require('events').EventEmitter;
|
|
3
2
|
const utilService = require('./services/utilService');
|
|
4
3
|
const FranchiseFileRecord = require('./FranchiseFileRecord');
|
|
@@ -22,6 +21,7 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
22
21
|
this.records = [];
|
|
23
22
|
this.table2Records = [];
|
|
24
23
|
this.arraySizes = [];
|
|
24
|
+
this.emptyRecords = new Map();
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
get hexData () {
|
|
@@ -122,9 +122,10 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
122
122
|
} else {
|
|
123
123
|
reject('Cannot read records: Schema is not defined.');
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
this.emptyRecords = this._parseEmptyRecords();
|
|
125
127
|
|
|
126
128
|
let offsetTableToUse = this.offsetTable;
|
|
127
|
-
|
|
128
129
|
const mandatoryOffsetsToLoad = this.strategy.getMandatoryOffsets(this.offsetTable);
|
|
129
130
|
|
|
130
131
|
if (attribsToLoad) {
|
|
@@ -151,9 +152,135 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
151
152
|
const that = this;
|
|
152
153
|
record.on('change', function (changedOffset) {
|
|
153
154
|
this.isChanged = true;
|
|
154
|
-
|
|
155
|
+
|
|
156
|
+
if (that.isArray) {
|
|
157
|
+
that.arraySizes[index] = this.arraySize;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// When a record changes, we need to check if it was previously empty
|
|
161
|
+
// If so, we need to consider the record as no longer empty
|
|
162
|
+
// So we need to adjust the empty records
|
|
163
|
+
|
|
164
|
+
// Ex: Empty record list looks like this: A -> B -> C
|
|
165
|
+
// When B's value is changed, the records need updated to: A -> C
|
|
166
|
+
const emptyRecordReference = that.emptyRecords.get(this.index);
|
|
167
|
+
const changedRecordWasEmpty = emptyRecordReference !== null && emptyRecordReference !== undefined;
|
|
168
|
+
|
|
169
|
+
if (changedRecordWasEmpty) {
|
|
170
|
+
|
|
171
|
+
// Delete the empty record entry because it is no longer empty
|
|
172
|
+
that.emptyRecords.delete(this.index);
|
|
173
|
+
|
|
174
|
+
// Check if there is a previous empty record
|
|
175
|
+
const previousEmptyReference = that.emptyRecords.get(emptyRecordReference.previous);
|
|
176
|
+
|
|
177
|
+
if (previousEmptyReference) {
|
|
178
|
+
// Set the previous empty record to point to the old reference's next node
|
|
179
|
+
that.emptyRecords.set(emptyRecordReference.previous, {
|
|
180
|
+
previous: that.emptyRecords.get(emptyRecordReference.previous).previous,
|
|
181
|
+
next: emptyRecordReference.next
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// change the table buffer and record buffer to reflect this change
|
|
185
|
+
changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// If there is a next empty reference, update the previous value accordingly to now point
|
|
189
|
+
// to the current record's previous index.
|
|
190
|
+
const nextEmptyReference = that.emptyRecords.get(emptyRecordReference.next);
|
|
191
|
+
|
|
192
|
+
if (nextEmptyReference) {
|
|
193
|
+
that.emptyRecords.set(emptyRecordReference.next, {
|
|
194
|
+
previous: emptyRecordReference.previous,
|
|
195
|
+
next: that.emptyRecords.get(emptyRecordReference.next).next
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (!previousEmptyReference) {
|
|
199
|
+
// If no previous empty record exists and a next record exists, we need to update the header to
|
|
200
|
+
// point to this record as the next record to use.
|
|
201
|
+
updateNextRecordToUseHeaderAndBuffer(emptyRecordReference.next);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// If there are no previous or next empty references
|
|
206
|
+
// Then there are no more empty references in the table
|
|
207
|
+
// Update the table header nextRecordToUse back to the table record capacity
|
|
208
|
+
if (!previousEmptyReference && !nextEmptyReference) {
|
|
209
|
+
updateNextRecordToUseHeaderAndBuffer(that.header.recordCapacity);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
155
213
|
that.emit('change');
|
|
156
214
|
});
|
|
215
|
+
|
|
216
|
+
record.on('empty', function () {
|
|
217
|
+
this.isChanged = true;
|
|
218
|
+
const lastEmptyRecordMapEntry = Array.from(that.emptyRecords).pop();
|
|
219
|
+
|
|
220
|
+
// When we empty a record, we need to check if another empty record exists in the table.
|
|
221
|
+
if (lastEmptyRecordMapEntry !== null && lastEmptyRecordMapEntry !== undefined) {
|
|
222
|
+
|
|
223
|
+
// If an empty record already exists, we just need to get the last empty record
|
|
224
|
+
// and update its index to point to the current record that we want to empty.
|
|
225
|
+
const lastEmptyRecordIndex = lastEmptyRecordMapEntry[0];
|
|
226
|
+
|
|
227
|
+
that.emptyRecords.set(lastEmptyRecordIndex, {
|
|
228
|
+
previous: lastEmptyRecordMapEntry[1].previous,
|
|
229
|
+
next: this.index
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Then we need to update the current record index to point to the record capacity.
|
|
233
|
+
that.emptyRecords.set(this.index, {
|
|
234
|
+
previous: lastEmptyRecordIndex,
|
|
235
|
+
next: that.header.recordCapacity
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Finally, we need to update the buffers to reflect this data.
|
|
239
|
+
// First, place the new referenced index (will be the first 4 bytes)
|
|
240
|
+
// Next, fill the rest of the record with 0s (the last bytes of the record)
|
|
241
|
+
|
|
242
|
+
// And update both record's data. This will set the unformatted and formatted values
|
|
243
|
+
// without emitting an event
|
|
244
|
+
changeRecordBuffers(lastEmptyRecordIndex, this.index);
|
|
245
|
+
changeRecordBuffers(this.index, that.header.recordCapacity);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// In this case, the record that was emptied is the first empty record in the table
|
|
249
|
+
that.emptyRecords.set(this.index, {
|
|
250
|
+
previous: null,
|
|
251
|
+
next: that.header.recordCapacity
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Finally update the table header and buffer so that the game uses this new empty
|
|
255
|
+
// record as the next record to use (or fill)
|
|
256
|
+
updateNextRecordToUseHeaderAndBuffer(this.index);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
that.emit('change');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
function updateNextRecordToUseHeaderAndBuffer(nextRecordToUse) {
|
|
263
|
+
// We need to update the table header to use this row next
|
|
264
|
+
table.header.nextRecordToUse = nextRecordToUse;
|
|
265
|
+
|
|
266
|
+
// And finally update the buffer to reflect this change
|
|
267
|
+
that.data.writeUInt32BE(nextRecordToUse, that.header.headerOffset - 4);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
function changeRecordBuffers(index, emptyRecordReference) {
|
|
271
|
+
setBufferToEmptyRecordReference(index, emptyRecordReference);
|
|
272
|
+
setRecordInternalBuffer(index, emptyRecordReference);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
function setBufferToEmptyRecordReference(index, emptyRecordReference) {
|
|
276
|
+
const recordStartIndex = that.header.table1StartIndex + (index * that.header.record1Size)
|
|
277
|
+
that.data.writeUInt32BE(emptyRecordReference, recordStartIndex);
|
|
278
|
+
that.data.fill(0, recordStartIndex + 4, recordStartIndex + that.header.record1Size);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
function setRecordInternalBuffer(index, emptyRecordReference) {
|
|
282
|
+
that.records[index].data = utilService.dec2bin(emptyRecordReference, that.header.record1Size * 8);
|
|
283
|
+
};
|
|
157
284
|
});
|
|
158
285
|
|
|
159
286
|
this.table2Records.forEach((record, index) => {
|
|
@@ -173,6 +300,33 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
173
300
|
});
|
|
174
301
|
};
|
|
175
302
|
|
|
303
|
+
_parseEmptyRecords() {
|
|
304
|
+
const firstEmptyRecord = this.header.nextRecordToUse;
|
|
305
|
+
const sizeOfEachRecord = this.header.record1Size;
|
|
306
|
+
|
|
307
|
+
let emptyRecords = new Map();
|
|
308
|
+
|
|
309
|
+
let previousEmptyRecordIndex = null;
|
|
310
|
+
let currentEmptyRecordIndex = firstEmptyRecord;
|
|
311
|
+
|
|
312
|
+
if (this.header.nextRecordToUse !== this.header.recordCapacity) {
|
|
313
|
+
while (currentEmptyRecordIndex !== this.header.recordCapacity) {
|
|
314
|
+
let nextEmptyRecordIndex = this.data.readUInt32BE(this.header.table1StartIndex + (currentEmptyRecordIndex * sizeOfEachRecord));
|
|
315
|
+
|
|
316
|
+
emptyRecords.set(currentEmptyRecordIndex, {
|
|
317
|
+
previous: previousEmptyRecordIndex,
|
|
318
|
+
next: nextEmptyRecordIndex
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
previousEmptyRecordIndex = currentEmptyRecordIndex;
|
|
322
|
+
currentEmptyRecordIndex = nextEmptyRecordIndex;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return emptyRecords;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
|
|
176
330
|
_parseTable2Values(data, header, records) {
|
|
177
331
|
const that = this;
|
|
178
332
|
const secondTableData = data.slice(header.table2StartIndex);
|
package/package.json
CHANGED
|
@@ -45,7 +45,7 @@ M19TableHeaderStrategy.parseHeader = (data) => {
|
|
|
45
45
|
const data2RecordCapacity = data.readUInt32BE(headerOffset+48);
|
|
46
46
|
const data2IndexEntries = data.readUInt32BE(headerOffset+52);
|
|
47
47
|
const unknown4 = data.readUInt32BE(headerOffset+56);
|
|
48
|
-
const
|
|
48
|
+
const nextRecordToUse = data.readUInt32BE(headerOffset+60);
|
|
49
49
|
|
|
50
50
|
let offsetStart = 0xE4 + tableStoreLength;
|
|
51
51
|
const hasSecondTable = tableTotalLength > table1Length;
|
|
@@ -95,10 +95,10 @@ M19TableHeaderStrategy.parseHeader = (data) => {
|
|
|
95
95
|
'hasSecondTable': hasSecondTable,
|
|
96
96
|
'table1StartIndex': tableStoreLength === 0 && !isArray ? headerSize : headerSize + (data1RecordCount * 4),
|
|
97
97
|
'table2StartIndex': tableStoreLength === 0 && !isArray ? headerSize + (data1RecordCount * records1Size) : headerSize + (data1RecordCount * 4) + (data1RecordCount * records1Size),
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'
|
|
98
|
+
'recordWords': data2RecordWords,
|
|
99
|
+
'recordCapacity': data2RecordCapacity,
|
|
100
|
+
'numMembers': data2IndexEntries,
|
|
101
|
+
'nextRecordToUse': nextRecordToUse
|
|
102
102
|
};
|
|
103
103
|
};
|
|
104
104
|
|
|
@@ -46,7 +46,7 @@ M20TableHeaderStrategy.parseHeader = (data) => {
|
|
|
46
46
|
const data2RecordCapacity = data.readUInt32BE(headerOffset+48);
|
|
47
47
|
const data2IndexEntries = data.readUInt32BE(headerOffset+52);
|
|
48
48
|
const unknown4 = data.readUInt32BE(headerOffset+56);
|
|
49
|
-
const
|
|
49
|
+
const nextRecordToUse = data.readUInt32BE(headerOffset+60);
|
|
50
50
|
|
|
51
51
|
let offsetStart = 0xE8 + tableStoreLength;
|
|
52
52
|
const hasSecondTable = tableTotalLength > table1Length;
|
|
@@ -104,10 +104,10 @@ M20TableHeaderStrategy.parseHeader = (data) => {
|
|
|
104
104
|
'hasSecondTable': hasSecondTable,
|
|
105
105
|
'table1StartIndex': table1StartIndex,
|
|
106
106
|
'table2StartIndex': table2StartIndex,
|
|
107
|
-
'
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'
|
|
107
|
+
'recordWords': data2RecordWords,
|
|
108
|
+
'recordCapacity': data2RecordCapacity,
|
|
109
|
+
'numMembers': data2IndexEntries,
|
|
110
|
+
'nextRecordToUse': nextRecordToUse
|
|
111
111
|
};
|
|
112
112
|
};
|
|
113
113
|
|