madden-franchise 2.3.0 → 2.3.4
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 +2 -2
- package/FranchiseFileRecord.js +2 -0
- package/FranchiseFileTable.js +162 -107
- package/package.json +1 -1
package/FranchiseFileField.js
CHANGED
|
@@ -68,7 +68,7 @@ class FranchiseFileField extends EventEmitter {
|
|
|
68
68
|
this.emit('change');
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
setUnformattedValueWithoutChangeEvent(unformattedValue) {
|
|
71
|
+
setUnformattedValueWithoutChangeEvent(unformattedValue, suppressErrors) {
|
|
72
72
|
if (!utilService.isString(unformattedValue)) { throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`); }
|
|
73
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.`)}
|
|
74
74
|
else {
|
|
@@ -82,7 +82,7 @@ class FranchiseFileField extends EventEmitter {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// check for 'allowed' error - this will be true if the unformatted value is invalid.
|
|
85
|
-
if (this._offset.enum && value === unformattedValue.padStart(this._offset.length, '0')) {
|
|
85
|
+
if (this._offset.enum && value === unformattedValue.padStart(this._offset.length, '0') && !suppressErrors) {
|
|
86
86
|
throw new Error(`Argument is not a valid unformatted value for this field. You passed in ${value}.`)
|
|
87
87
|
}
|
|
88
88
|
|
package/FranchiseFileRecord.js
CHANGED
|
@@ -11,6 +11,7 @@ class FranchiseFileRecord extends EventEmitter {
|
|
|
11
11
|
this._fields = parseRecordFields(data, offsetTable);
|
|
12
12
|
this.isChanged = false;
|
|
13
13
|
this.arraySize = null;
|
|
14
|
+
this.isEmpty = false;
|
|
14
15
|
|
|
15
16
|
const that = this;
|
|
16
17
|
this._fields.forEach((field) => {
|
|
@@ -87,6 +88,7 @@ class FranchiseFileRecord extends EventEmitter {
|
|
|
87
88
|
|
|
88
89
|
empty() {
|
|
89
90
|
this.emit('empty');
|
|
91
|
+
this.isEmpty = true;
|
|
90
92
|
};
|
|
91
93
|
};
|
|
92
94
|
|
package/FranchiseFileTable.js
CHANGED
|
@@ -25,6 +25,27 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
get hexData () {
|
|
28
|
+
this.updateBuffer();
|
|
29
|
+
return this.data;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
set schema (schema) {
|
|
33
|
+
// console.time('set schema');
|
|
34
|
+
this._schema = schema;
|
|
35
|
+
const modifiedHeaderAttributes = this.strategy.parseHeaderAttributesFromSchema(schema, this.data, this.header);
|
|
36
|
+
|
|
37
|
+
this.header.headerSize = modifiedHeaderAttributes.headerSize;
|
|
38
|
+
this.header.record1Size = modifiedHeaderAttributes.record1Size;
|
|
39
|
+
this.header.table1StartIndex = modifiedHeaderAttributes.table1StartIndex;
|
|
40
|
+
this.header.table2StartIndex = modifiedHeaderAttributes.table2StartIndex;
|
|
41
|
+
// console.timeEnd('set schema');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
get schema () {
|
|
45
|
+
return this._schema;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
updateBuffer() {
|
|
28
49
|
// need to check table2 data first because it may change offsets of the legit records.
|
|
29
50
|
const table2Data = this.strategy.getTable2BinaryData(this.table2Records, this.data.slice(this.header.table2StartIndex));
|
|
30
51
|
|
|
@@ -62,23 +83,21 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
62
83
|
bufferArrays = bufferArrays.concat(table2Data);
|
|
63
84
|
|
|
64
85
|
this.data = Buffer.concat(bufferArrays);
|
|
65
|
-
return this.data;
|
|
66
86
|
};
|
|
67
87
|
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
this.
|
|
71
|
-
const modifiedHeaderAttributes = this.strategy.parseHeaderAttributesFromSchema(schema, this.data, this.header);
|
|
88
|
+
setNextRecordToUse(index, resetEmptyRecordMap) {
|
|
89
|
+
// We need to update the table header to use this row next
|
|
90
|
+
this.header.nextRecordToUse = index;
|
|
72
91
|
|
|
73
|
-
this
|
|
74
|
-
this.header.
|
|
75
|
-
this.header.table1StartIndex = modifiedHeaderAttributes.table1StartIndex;
|
|
76
|
-
this.header.table2StartIndex = modifiedHeaderAttributes.table2StartIndex;
|
|
77
|
-
// console.timeEnd('set schema');
|
|
78
|
-
};
|
|
92
|
+
// And finally update the buffer to reflect this change
|
|
93
|
+
this.data.writeUInt32BE(index, this.header.headerOffset - 4);
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
|
|
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();
|
|
99
|
+
this.emptyRecords = this._parseEmptyRecords();
|
|
100
|
+
}
|
|
82
101
|
};
|
|
83
102
|
|
|
84
103
|
// attribsToLoad is an array of attribute names (strings) to load. It is optional - if nothing is provided to the function it will load all attributes.
|
|
@@ -149,7 +168,12 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
149
168
|
record.arraySize = this.arraySizes[index];
|
|
150
169
|
}
|
|
151
170
|
|
|
171
|
+
if (this.emptyRecords.get(index)) {
|
|
172
|
+
record.isEmpty = true;
|
|
173
|
+
}
|
|
174
|
+
|
|
152
175
|
const that = this;
|
|
176
|
+
|
|
153
177
|
record.on('change', function (changedOffset) {
|
|
154
178
|
this.isChanged = true;
|
|
155
179
|
|
|
@@ -161,52 +185,75 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
161
185
|
// If so, we need to consider the record as no longer empty
|
|
162
186
|
// So we need to adjust the empty records
|
|
163
187
|
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
188
|
+
// First, check if the record's length is greater than 4 bytes (32 bits)
|
|
189
|
+
// If less than 4 bytes, it can never become empty...probably. :)
|
|
190
|
+
if (that.header.record1Size >= 4) {
|
|
191
|
+
|
|
192
|
+
// Ex: Empty record list looks like this: A -> B -> C
|
|
193
|
+
// When B's value is changed, the records need updated to: A -> C
|
|
194
|
+
const emptyRecordReference = that.emptyRecords.get(this.index);
|
|
195
|
+
const changedRecordWasEmpty = emptyRecordReference !== null && emptyRecordReference !== undefined;
|
|
196
|
+
|
|
197
|
+
if (changedRecordWasEmpty) {
|
|
198
|
+
// Check if the record's first four bytes still have a reference to the 0th table.
|
|
199
|
+
// If so, then the record is still considered empty.
|
|
200
|
+
|
|
201
|
+
// We need to check the buffer because the first field is not always a reference.
|
|
202
|
+
const referenceData = utilService.getReferenceData(this._data.slice(0, 32));
|
|
203
|
+
if (referenceData.tableId !== 0) {
|
|
204
|
+
|
|
205
|
+
// Delete the empty record entry because it is no longer empty
|
|
206
|
+
that.emptyRecords.delete(this.index);
|
|
207
|
+
|
|
208
|
+
// Set the isEmpty back to false because it's no longer empty
|
|
209
|
+
this.isEmpty = false;
|
|
210
|
+
|
|
211
|
+
// Check if there is a previous empty record
|
|
212
|
+
const previousEmptyReference = that.emptyRecords.get(emptyRecordReference.previous);
|
|
213
|
+
|
|
214
|
+
if (previousEmptyReference) {
|
|
215
|
+
// Set the previous empty record to point to the old reference's next node
|
|
216
|
+
that.emptyRecords.set(emptyRecordReference.previous, {
|
|
217
|
+
previous: that.emptyRecords.get(emptyRecordReference.previous).previous,
|
|
218
|
+
next: emptyRecordReference.next
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// change the table buffer and record buffer to reflect this change
|
|
222
|
+
changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// If there is a next empty reference, update the previous value accordingly to now point
|
|
226
|
+
// to the current record's previous index.
|
|
227
|
+
const nextEmptyReference = that.emptyRecords.get(emptyRecordReference.next);
|
|
228
|
+
|
|
229
|
+
if (nextEmptyReference) {
|
|
230
|
+
that.emptyRecords.set(emptyRecordReference.next, {
|
|
231
|
+
previous: emptyRecordReference.previous,
|
|
232
|
+
next: that.emptyRecords.get(emptyRecordReference.next).next
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!previousEmptyReference) {
|
|
236
|
+
// If no previous empty record exists and a next record exists, we need to update the header to
|
|
237
|
+
// point to this record as the next record to use.
|
|
238
|
+
that.setNextRecordToUse(emptyRecordReference.next);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// If there are no previous or next empty references
|
|
243
|
+
// Then there are no more empty references in the table
|
|
244
|
+
// Update the table header nextRecordToUse back to the table record capacity
|
|
245
|
+
if (!previousEmptyReference && !nextEmptyReference) {
|
|
246
|
+
that.setNextRecordToUse(that.header.recordCapacity);
|
|
247
|
+
}
|
|
202
248
|
}
|
|
203
249
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
+
}
|
|
210
257
|
}
|
|
211
258
|
}
|
|
212
259
|
|
|
@@ -214,57 +261,58 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
214
261
|
});
|
|
215
262
|
|
|
216
263
|
record.on('empty', function () {
|
|
217
|
-
this
|
|
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');
|
|
264
|
+
onRecordEmpty(this);
|
|
260
265
|
});
|
|
261
266
|
|
|
262
|
-
function
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
function onRecordEmpty(record) {
|
|
268
|
+
// First, check if the record is already empty. If so, don't do anything...
|
|
269
|
+
// If not empty, then we need to empty it.
|
|
270
|
+
if (!record.isEmpty) {
|
|
271
|
+
record.isChanged = true;
|
|
272
|
+
const lastEmptyRecordMapEntry = Array.from(that.emptyRecords).pop();
|
|
273
|
+
|
|
274
|
+
// When we empty a record, we need to check if another empty record exists in the table.
|
|
275
|
+
if (lastEmptyRecordMapEntry !== null && lastEmptyRecordMapEntry !== undefined) {
|
|
276
|
+
|
|
277
|
+
// If an empty record already exists, we just need to get the last empty record
|
|
278
|
+
// and update its index to point to the current record that we want to empty.
|
|
279
|
+
const lastEmptyRecordIndex = lastEmptyRecordMapEntry[0];
|
|
280
|
+
|
|
281
|
+
that.emptyRecords.set(lastEmptyRecordIndex, {
|
|
282
|
+
previous: lastEmptyRecordMapEntry[1].previous,
|
|
283
|
+
next: record.index
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Then we need to update the current record index to point to the record capacity.
|
|
287
|
+
that.emptyRecords.set(record.index, {
|
|
288
|
+
previous: lastEmptyRecordIndex,
|
|
289
|
+
next: that.header.recordCapacity
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Finally, we need to update the buffers to reflect this data.
|
|
293
|
+
// First, place the new referenced index (will be the first 4 bytes)
|
|
294
|
+
// Next, fill the rest of the record with 0s (the last bytes of the record)
|
|
295
|
+
|
|
296
|
+
// And update both record's data. This will set the unformatted and formatted values
|
|
297
|
+
// without emitting an event
|
|
298
|
+
changeRecordBuffers(lastEmptyRecordIndex, record.index);
|
|
299
|
+
changeRecordBuffers(record.index, that.header.recordCapacity);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// In this case, the record that was emptied is the first empty record in the table
|
|
303
|
+
that.emptyRecords.set(record.index, {
|
|
304
|
+
previous: null,
|
|
305
|
+
next: that.header.recordCapacity
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Finally update the table header and buffer so that the game uses this new empty
|
|
309
|
+
// record as the next record to use (or fill)
|
|
310
|
+
that.setNextRecordToUse(record.index);
|
|
311
|
+
changeRecordBuffers(record.index, that.header.recordCapacity);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
that.emit('change');
|
|
315
|
+
}
|
|
268
316
|
};
|
|
269
317
|
|
|
270
318
|
function changeRecordBuffers(index, emptyRecordReference) {
|
|
@@ -279,7 +327,14 @@ class FranchiseFileTable extends EventEmitter {
|
|
|
279
327
|
};
|
|
280
328
|
|
|
281
329
|
function setRecordInternalBuffer(index, emptyRecordReference) {
|
|
282
|
-
|
|
330
|
+
let newData = utilService.dec2bin(emptyRecordReference, 32);
|
|
331
|
+
|
|
332
|
+
const recordSizeInBits = that.header.record1Size * 8;
|
|
333
|
+
if (recordSizeInBits > 32) {
|
|
334
|
+
newData += utilService.dec2bin(0, recordSizeInBits - 32);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
that.records[index].data = newData;
|
|
283
338
|
};
|
|
284
339
|
});
|
|
285
340
|
|