madden-franchise 3.2.5 → 3.2.6

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.
@@ -1,4 +1,6 @@
1
- const { BitView } = require('bit-buffer');
1
+ const {
2
+ BitView
3
+ } = require('bit-buffer');
2
4
 
3
5
  const utilService = require('./services/utilService');
4
6
  const FranchiseFileTable2Field = require('./FranchiseFileTable2Field');
@@ -17,26 +19,26 @@ class FranchiseFileField {
17
19
  this.secondTableField = new FranchiseFileTable2Field(this._recordBuffer.readUInt32BE(offset.offset / 8), offset.maxLength);
18
20
  this.secondTableField.fieldReference = this;
19
21
  }
20
-
22
+
21
23
  if (offset.valueInThirdTable) {
22
24
  this.thirdTableField = new FranchiseFileTable3Field(this._recordBuffer.readUInt32BE(offset.offset / 8), offset.maxLength);
23
25
  this.thirdTableField.fieldReference = this;
24
26
  }
25
27
  };
26
28
 
27
- get key () {
29
+ get key() {
28
30
  return this._key;
29
31
  };
30
32
 
31
- get offset () {
33
+ get offset() {
32
34
  return this._offset;
33
35
  };
34
36
 
35
- get value () {
37
+ get value() {
36
38
  if (this._unformattedValue === null) {
37
39
  this._setUnformattedValueIfEmpty();
38
40
  }
39
-
41
+
40
42
  if (this._value === null) {
41
43
  this._value = this._parseFieldValue(this._unformattedValue, this._offset);
42
44
  }
@@ -44,11 +46,11 @@ class FranchiseFileField {
44
46
  return this._value;
45
47
  };
46
48
 
47
- get isReference () {
49
+ get isReference() {
48
50
  return this._offset.isReference;
49
51
  };
50
52
 
51
- get referenceData () {
53
+ get referenceData() {
52
54
  if (this._unformattedValue === null) {
53
55
  this._setUnformattedValueIfEmpty();
54
56
  }
@@ -56,11 +58,11 @@ class FranchiseFileField {
56
58
  if (this.isReference) {
57
59
  return utilService.getReferenceDataFromBitview(this._unformattedValue, this.offset.offset);
58
60
  }
59
-
61
+
60
62
  return null;
61
63
  };
62
64
 
63
- set value (value) {
65
+ set value(value) {
64
66
  if (this._unformattedValue === null) {
65
67
  this._setUnformattedValueIfEmpty();
66
68
  }
@@ -70,50 +72,46 @@ class FranchiseFileField {
70
72
 
71
73
  if (this.offset.valueInSecondTable) {
72
74
  this.secondTableField.value = value.toString();
73
- }
74
- else if (this.offset.valueInThirdTable) {
75
+ } else if (this.offset.valueInThirdTable) {
75
76
  if (typeof value === 'object') {
76
77
  const newVal = JSON.stringify(value);
77
78
  this._value = newVal;
78
79
  this.thirdTableField.value = newVal;
79
- }
80
- else {
80
+ } else {
81
81
  this.thirdTableField.value = value.toString();
82
82
  }
83
- }
84
- else {
83
+ } else {
85
84
  let actualValue;
86
85
 
87
86
  if (this.offset.isReference) {
88
- if (!utilService.isString(value)) { throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`); }
89
- else if (!utilService.stringOnlyContainsBinaryDigits(value)) { 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.`)}
87
+ if (!utilService.isString(value)) {
88
+ throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`);
89
+ } else if (!utilService.stringOnlyContainsBinaryDigits(value)) {
90
+ 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.`)
91
+ }
90
92
  const referenceData = utilService.getReferenceData(value);
91
93
  this._unformattedValue.setBits(this.offset.offset, referenceData.tableId, 15);
92
94
  this._unformattedValue.setBits((this.offset.offset + 15), referenceData.rowNumber, 17);
93
- }
94
- else if (this.offset.enum) {
95
+ } else if (this.offset.enum) {
95
96
  try {
96
97
  let theEnum = this._getEnumFromValue(value);
97
-
98
+
98
99
  // Enums can have negative values and Madden negative numbers are not standard. We need to convert it here.
99
100
  // Ex: In Madden, binary "1000" = -1 for an enum with a max length of 4. But for everything else, "1000" = 8, so we need to get the "real" value here.
100
101
  const decimalEquivalent = utilService.bin2dec(theEnum.unformattedValue);
101
102
  this._unformattedValue.setBits(this.offset.offset, decimalEquivalent, this.offset.length);
102
103
  this._value = theEnum.name;
103
- }
104
- catch (err) {
104
+ } catch (err) {
105
105
  // if user tries entering an invalid enum value, check if it's an empty record reference (will be binary)
106
106
  if (utilService.stringOnlyContainsBinaryDigits(value)) {
107
107
  this._value = value;
108
108
  this._unformattedValue.setBits(this.offset.offset, value, this.offset.length);
109
- }
110
- else {
109
+ } else {
111
110
  this._value = null;
112
111
  throw err;
113
112
  }
114
113
  }
115
- }
116
- else {
114
+ } else {
117
115
  switch (this.offset.type) {
118
116
  case 's_int':
119
117
  actualValue = parseInt(value);
@@ -128,8 +126,7 @@ class FranchiseFileField {
128
126
  if (this.offset.minValue || this.offset.maxValue) {
129
127
  // return utilService.dec2bin(formatted, offset.length);
130
128
  this._unformattedValue.setBits(this.offset.offset, actualValue, this.offset.length);
131
- }
132
- else {
129
+ } else {
133
130
  const maxValueBinary = getMaxValueBinary(this.offset);
134
131
  const maxValue = utilService.bin2dec(maxValueBinary);
135
132
  // return utilService.dec2bin(formatted + maxValue, offset.length);
@@ -161,7 +158,7 @@ class FranchiseFileField {
161
158
  }
162
159
  };
163
160
 
164
- get unformattedValue () {
161
+ get unformattedValue() {
165
162
  if (this._unformattedValue === null) {
166
163
  this._setUnformattedValueIfEmpty();
167
164
  }
@@ -169,7 +166,7 @@ class FranchiseFileField {
169
166
  return this._unformattedValue;
170
167
  };
171
168
 
172
- set unformattedValue (unformattedValue) {
169
+ set unformattedValue(unformattedValue) {
173
170
  this.setUnformattedValueWithoutChangeEvent(unformattedValue);
174
171
  this._value = null;
175
172
  this._parent.onEvent('change', this);
@@ -183,6 +180,10 @@ class FranchiseFileField {
183
180
  this._isChanged = changed;
184
181
  };
185
182
 
183
+ _bubbleChangeToParent() {
184
+ this._parent.onEvent('change', this);
185
+ };
186
+
186
187
  clearCachedValues() {
187
188
  this._value = null;
188
189
  this._unformattedValue = null;
@@ -195,8 +196,9 @@ class FranchiseFileField {
195
196
  };
196
197
 
197
198
  setUnformattedValueWithoutChangeEvent(unformattedValue, suppressErrors) {
198
- if (!(unformattedValue instanceof BitView)) { throw new Error(`Argument must be of type BitView. You passed in a(n) ${typeof unformattedValue}.`); }
199
- else {
199
+ if (!(unformattedValue instanceof BitView)) {
200
+ throw new Error(`Argument must be of type BitView. You passed in a(n) ${typeof unformattedValue}.`);
201
+ } else {
200
202
  this._unformattedValue = unformattedValue;
201
203
  this._value = null;
202
204
  }
@@ -204,23 +206,20 @@ class FranchiseFileField {
204
206
 
205
207
  _getEnumFromValue(value) {
206
208
  const enumName = this.offset.enum.getMemberByName(value);
207
-
209
+
208
210
  if (enumName) {
209
211
  return enumName;
210
- }
211
- else {
212
+ } else {
212
213
  const formattedEnum = this.offset.enum.getMemberByValue(value)
213
214
 
214
215
  if (formattedEnum) {
215
216
  return formattedEnum;
216
- }
217
- else {
217
+ } else {
218
218
  const unformattedEnum = this.offset.enum.getMemberByUnformattedValue(value);
219
219
 
220
220
  if (unformattedEnum) {
221
221
  return unformattedEnum;
222
- }
223
- else {
222
+ } else {
224
223
  return this.offset.enum.members[0];
225
224
  }
226
225
  }
@@ -230,31 +229,26 @@ class FranchiseFileField {
230
229
  _parseFieldValue(unformatted, offset) {
231
230
  if (offset.valueInSecondTable) {
232
231
  return this.secondTableField.value;
233
- }
234
- else if (offset.valueInThirdTable) {
232
+ } else if (offset.valueInThirdTable) {
235
233
  return this.thirdTableField.value;
236
- }
237
- else if (offset.enum) {
234
+ } else if (offset.enum) {
238
235
  const enumUnformattedValue = utilService.dec2bin(this.unformattedValue.getBits(this.offset.offset, this.offset.length), offset.enum._maxLength);
239
-
236
+
240
237
  try {
241
238
  const theEnum = offset.enum.getMemberByUnformattedValue(enumUnformattedValue);
242
-
239
+
243
240
  if (theEnum) {
244
241
  return theEnum.name;
245
242
  }
246
- }
247
- catch (err) {
243
+ } catch (err) {
248
244
  // console.log(err);
249
245
  }
250
-
246
+
251
247
  return enumUnformattedValue;
252
- }
253
- else if (offset.isReference) {
248
+ } else if (offset.isReference) {
254
249
  const referenceData = utilService.getReferenceDataFromBitview(this._unformattedValue, this.offset.offset);
255
250
  return utilService.getBinaryReferenceData(referenceData.tableId, referenceData.rowNumber);
256
- }
257
- else {
251
+ } else {
258
252
  switch (offset.type) {
259
253
  case 's_int':
260
254
  // return utilService.bin2dec(unformatted) + offset.minValue;
@@ -262,28 +256,26 @@ class FranchiseFileField {
262
256
  case 'int':
263
257
  if (offset.minValue || offset.maxValue) {
264
258
  return unformatted.getBits(offset.offset, offset.length);
265
- }
266
- else {
259
+ } else {
267
260
  // This is for int[] tables.
268
-
261
+
269
262
  const maxValueBinary = getMaxValueBinary(offset);
270
263
  const maxValue = utilService.bin2dec(maxValueBinary);
271
264
  const newValue = unformatted.getBits(offset.offset, offset.length);
272
-
265
+
273
266
  if (newValue === 0) {
274
267
  return 0;
275
- }
276
- else {
268
+ } else {
277
269
  return newValue - maxValue;
278
270
  }
279
271
  }
280
- case 'bool':
281
- return unformatted.getBits(offset.offset, 1) ? true : false;
282
- case 'float':
283
- // return utilService.bin2Float(unformatted);
284
- return unformatted.getFloat32(offset.offset, offset.length);
285
- default:
286
- return unformatted;
272
+ case 'bool':
273
+ return unformatted.getBits(offset.offset, 1) ? true : false;
274
+ case 'float':
275
+ // return utilService.bin2Float(unformatted);
276
+ return unformatted.getFloat32(offset.offset, offset.length);
277
+ default:
278
+ return unformatted;
287
279
  }
288
280
  }
289
281
  };
@@ -14,7 +14,7 @@ class FranchiseFileTable extends EventEmitter {
14
14
  this.strategyBase = strategy;
15
15
  this.strategy = this.strategyBase.table;
16
16
  this.recordsRead = false;
17
- this._gameYear = gameYear;
17
+ this._gameYear = gameYear;
18
18
  this.header = this.strategy.parseHeader(this.data);
19
19
  this.name = this.header.name;
20
20
  this.isArray = this.header.isArray;
@@ -28,12 +28,12 @@ class FranchiseFileTable extends EventEmitter {
28
28
  this._settings = settings;
29
29
  };
30
30
 
31
- get hexData () {
31
+ get hexData() {
32
32
  this.updateBuffer();
33
33
  return this.data;
34
34
  };
35
35
 
36
- set schema (schema) {
36
+ set schema(schema) {
37
37
  // console.time('set schema');
38
38
  this._schema = schema;
39
39
  const modifiedHeaderAttributes = this.strategy.parseHeaderAttributesFromSchema(schema, this.data, this.header);
@@ -45,23 +45,23 @@ class FranchiseFileTable extends EventEmitter {
45
45
  // console.timeEnd('set schema');
46
46
  };
47
47
 
48
- get schema () {
48
+ get schema() {
49
49
  return this._schema;
50
50
  };
51
51
 
52
52
  getBinaryReferenceToRecord(index) {
53
53
  return utilService.getBinaryReferenceData(this.header.tableId, index);
54
54
  };
55
-
55
+
56
56
  updateBuffer() {
57
57
  // need to check table2 & table3 data first because it may change offsets of the legit records.
58
58
  let table2Data = this.strategy.getTable2BinaryData(this.table2Records, this.data.slice(this.header.table2StartIndex));
59
59
  let table3Data = [];
60
-
60
+
61
61
  if (this.header.table3StartIndex) {
62
62
  table3Data = this.strategy.getTable3BinaryData(this.table3Records, this.data.slice(this.header.table3StartIndex));
63
63
  }
64
-
64
+
65
65
  // update table2 length and table total length in table header (only if records have been read)
66
66
  if (this.recordsRead) {
67
67
  let table2DataLength = 0;
@@ -81,12 +81,15 @@ class FranchiseFileTable extends EventEmitter {
81
81
 
82
82
  this.header.table3Length = table3DataLength;
83
83
 
84
- this.data.writeUInt32BE(this.header.table2Length , this.header.offsetStart - 44);
84
+ this.data.writeUInt32BE(this.header.table2Length, this.header.offsetStart - 44);
85
85
  this.data.writeUInt32BE(this.header.table3Length, this.header.offsetStart - 40);
86
86
  this.data.writeUInt32BE(this.header.tableTotalLength, this.header.offsetStart - 24);
87
87
  }
88
88
 
89
- const changedRecords = this.records.filter((record) => { return record.isChanged; });
89
+ const changedRecords = this.records.filter((record) => {
90
+ return record.isChanged;
91
+ });
92
+
90
93
  let currentOffset = 0;
91
94
  let bufferArrays = [];
92
95
 
@@ -109,10 +112,10 @@ class FranchiseFileTable extends EventEmitter {
109
112
  let record = changedRecords[i];
110
113
  record.isChanged = false;
111
114
  const recordOffset = this.header.table1StartIndex + (record.index * this.header.record1Size);
112
-
115
+
113
116
  bufferArrays.push(this.data.slice(currentOffset, recordOffset));
114
117
  const recordHexData = record.hexData;
115
-
118
+
116
119
  bufferArrays.push(recordHexData);
117
120
  currentOffset = recordOffset + recordHexData.length;
118
121
  }
@@ -172,7 +175,7 @@ class FranchiseFileTable extends EventEmitter {
172
175
  const firstOffset = this.offsetTable[0];
173
176
  if (firstOffset.type !== 'string') {
174
177
  isEmptyReference = true;
175
-
178
+
176
179
  // Save the row number that this record points to.
177
180
  emptyRecordReferenceIndicies.push(firstFourBytesReference.rowNumber);
178
181
  }
@@ -183,7 +186,9 @@ class FranchiseFileTable extends EventEmitter {
183
186
 
184
187
  // We need to determine the starting node.
185
188
  // To do that, we need to find the empty record which no other empty record points to.
186
- const unreachableRecords = this.records.filter((record) => { return record.isEmpty; }).filter((record) => {
189
+ const unreachableRecords = this.records.filter((record) => {
190
+ return record.isEmpty;
191
+ }).filter((record) => {
187
192
  return emptyRecordReferenceIndicies.indexOf(record.index) === -1;
188
193
  });
189
194
 
@@ -193,11 +198,10 @@ class FranchiseFileTable extends EventEmitter {
193
198
  return record.index;
194
199
  });
195
200
 
196
- console.warn(`(${this.header.tableId}) ${this.name} - More than one unreachable records found: `
197
- + `(${unreachableIndicies.join(', ')}). The game will most likely crash if you do not fix this problem. `
198
- + `The nextRecordToUse has NOT been updated.`);
199
- }
200
- else {
201
+ console.warn(`(${this.header.tableId}) ${this.name} - More than one unreachable records found: ` +
202
+ `(${unreachableIndicies.join(', ')}). The game will most likely crash if you do not fix this problem. ` +
203
+ `The nextRecordToUse has NOT been updated.`);
204
+ } else {
201
205
  let nextRecordToUse = this.header.recordCapacity;
202
206
 
203
207
  if (unreachableRecords.length === 1) {
@@ -235,7 +239,7 @@ class FranchiseFileTable extends EventEmitter {
235
239
  };
236
240
 
237
241
  // 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.
238
- readRecords (attribsToLoad) {
242
+ readRecords(attribsToLoad) {
239
243
  return new Promise((resolve, reject) => {
240
244
  if (!this.recordsRead || isLoadingNewOffsets(this.loadedOffsets, attribsToLoad, this.offsetTable)) {
241
245
  if (this.schema) {
@@ -261,10 +265,10 @@ class FranchiseFileTable extends EventEmitter {
261
265
  'valueInSecondTable': false,
262
266
  'valueInThirdTable': false,
263
267
  }
264
-
268
+
265
269
  offset.isReference = !offset.enum && (offset.type[0] == offset.type[0].toUpperCase() || offset.type.includes('[]')) ? true : false,
266
270
 
267
- offsetTable.push(offset);
271
+ offsetTable.push(offset);
268
272
  }
269
273
 
270
274
  for (let i = 0; i < this.header.data1RecordCount; i++) {
@@ -276,23 +280,25 @@ class FranchiseFileTable extends EventEmitter {
276
280
  } else {
277
281
  reject('Cannot read records: Schema is not defined.');
278
282
  }
279
-
283
+
280
284
  let offsetTableToUse = this.offsetTable;
281
285
  const mandatoryOffsetsToLoad = this.strategy.getMandatoryOffsets(this.offsetTable);
282
-
286
+
283
287
  if (attribsToLoad) {
284
288
  // get any new attributes to load plus the existing loaded offsets
285
- offsetTableToUse = offsetTableToUse.filter((attrib) => {
286
- return mandatoryOffsetsToLoad.includes(attrib.name)
287
- || attribsToLoad.includes(attrib.name)
288
- || this.loadedOffsets.find((offset) => { return offset.name === attrib.name; });
289
+ offsetTableToUse = offsetTableToUse.filter((attrib) => {
290
+ return mandatoryOffsetsToLoad.includes(attrib.name) ||
291
+ attribsToLoad.includes(attrib.name) ||
292
+ this.loadedOffsets.find((offset) => {
293
+ return offset.name === attrib.name;
294
+ });
289
295
  });
290
296
  }
291
-
297
+
292
298
  this.loadedOffsets = offsetTableToUse;
293
299
 
294
300
  this.records = readRecords(this.data, this.header, offsetTableToUse, this);
295
-
301
+
296
302
  if (this.header.hasSecondTable) {
297
303
  this._parseTable2Values(this.data, this.header, this.records);
298
304
  }
@@ -379,8 +385,7 @@ class FranchiseFileTable extends EventEmitter {
379
385
  // without emitting an event
380
386
  this._changeRecordBuffers(lastEmptyRecordIndex, record.index);
381
387
  this._changeRecordBuffers(record.index, this.header.recordCapacity);
382
- }
383
- else {
388
+ } else {
384
389
  // In this case, the record that was emptied is the first empty record in the table
385
390
  this.emptyRecords.set(record.index, {
386
391
  previous: null,
@@ -392,7 +397,7 @@ class FranchiseFileTable extends EventEmitter {
392
397
  this.setNextRecordToUse(record.index);
393
398
  this._changeRecordBuffers(record.index, this.header.recordCapacity);
394
399
  }
395
-
400
+
396
401
  this.emit('change');
397
402
  }
398
403
  };
@@ -400,10 +405,12 @@ class FranchiseFileTable extends EventEmitter {
400
405
  _parseTable2Values(data, header, records) {
401
406
  const that = this;
402
407
  const secondTableData = data.slice(header.table2StartIndex);
403
-
408
+
404
409
  records.forEach((record) => {
405
- const fieldsReferencingSecondTable = record.fieldsArray.filter((field) => { return field.secondTableField; });
406
-
410
+ const fieldsReferencingSecondTable = record.fieldsArray.filter((field) => {
411
+ return field.secondTableField;
412
+ });
413
+
407
414
  fieldsReferencingSecondTable.forEach((field) => {
408
415
  field.secondTableField.unformattedValue = that.strategyBase.table2Field.getInitialUnformattedValue(field, secondTableData);
409
416
  field.secondTableField.strategy = that.strategyBase.table2Field;
@@ -412,14 +419,16 @@ class FranchiseFileTable extends EventEmitter {
412
419
  });
413
420
  });
414
421
  };
415
-
422
+
416
423
  _parseTable3Values(data, header, records) {
417
424
  const that = this;
418
425
  const thirdTableData = data.slice(header.table3StartIndex);
419
426
 
420
427
  records.forEach((record) => {
421
- const fieldsReferencingThirdTable = record.fieldsArray.filter((field) => { return field.thirdTableField; });
422
-
428
+ const fieldsReferencingThirdTable = record.fieldsArray.filter((field) => {
429
+ return field.thirdTableField;
430
+ });
431
+
423
432
  fieldsReferencingThirdTable.forEach((field) => {
424
433
  field.thirdTableField.unformattedValue = that.strategyBase.table3Field.getInitialUnformattedValue(field, thirdTableData);
425
434
  field.thirdTableField.strategy = that.strategyBase.table3Field;
@@ -469,7 +478,7 @@ class FranchiseFileTable extends EventEmitter {
469
478
  // First, check if the record's length is greater than 4 bytes (32 bits)
470
479
  // If less than 4 bytes, it can never become empty...probably. :)
471
480
  if (this.header.record1Size >= 4) {
472
-
481
+
473
482
  // Ex: Empty record list looks like object: A -> B -> C
474
483
  // When B's value is changed, the records need updated to: A -> C
475
484
  const emptyRecordReference = this.emptyRecords.get(object.index);
@@ -478,99 +487,100 @@ class FranchiseFileTable extends EventEmitter {
478
487
  // Automatically un-empty the row if the setting is enabled and the changed record was empty.
479
488
  if (changedRecordWasEmpty) {
480
489
 
481
- // Check if the record's first four bytes still have a reference to the 0th table.
482
- // If so, then the record is still considered empty.
483
-
484
- // We need to check the buffer because the first field is not always a reference.
485
- // const referenceData = utilService.getReferenceDataFromBuffer(object.data.slice(0, 4));
486
- // if (referenceData.tableId !== 0 || referenceData.rowNumber > this.header.recordCapacity) {
487
-
488
-
489
- // if the changed field isn't included in the first 32 bits, zero out the first 32 bits.
490
- // Otherwise, it's not necessary to zero out.
491
- const changedFieldsInFirst4Bytes = object.fieldsArray.filter((field) => { return field.isChanged && field.offset.indexOffset < 32; });
492
- if (this._settings.autoUnempty && changedFieldsInFirst4Bytes.length === 0) {
493
- // set first 4 bytes to 0
494
- this._changeRecordBuffers(object.index, 0);
495
-
496
- // invalidate the cached values since we set the buffer directly
497
- const fieldsInFirst4Bytes = object.fieldsArray.filter((field) => { return field.offset.indexOffset < 32; });
498
- fieldsInFirst4Bytes.forEach((field) => {
499
- field.clearCachedValues();
490
+ // Check if the record's first four bytes still have a reference to the 0th table.
491
+ // If so, then the record is still considered empty.
492
+
493
+ // We need to check the buffer because the first field is not always a reference.
494
+ // const referenceData = utilService.getReferenceDataFromBuffer(object.data.slice(0, 4));
495
+ // if (referenceData.tableId !== 0 || referenceData.rowNumber > this.header.recordCapacity) {
496
+
497
+
498
+ // if the changed field isn't included in the first 32 bits, zero out the first 32 bits.
499
+ // Otherwise, it's not necessary to zero out.
500
+ const changedFieldsInFirst4Bytes = object.fieldsArray.filter((field) => {
501
+ return field.isChanged && field.offset.indexOffset < 32;
502
+ });
503
+ if (this._settings.autoUnempty && changedFieldsInFirst4Bytes.length === 0) {
504
+ // set first 4 bytes to 0
505
+ this._changeRecordBuffers(object.index, 0);
506
+
507
+ // invalidate the cached values since we set the buffer directly
508
+ const fieldsInFirst4Bytes = object.fieldsArray.filter((field) => {
509
+ return field.offset.indexOffset < 32;
510
+ });
511
+ fieldsInFirst4Bytes.forEach((field) => {
512
+ field.clearCachedValues();
513
+ });
514
+ }
515
+
516
+ // If autoUnempty is disabled, only un-empty the row if a field in the first 4 bytes changed.
517
+ // If autoUnempty is enabled, un-empty the row if ANY field changed.
518
+ if (this._settings.autoUnempty || changedFieldsInFirst4Bytes.length > 0) {
519
+ // if the record contains any string values, point the string values to
520
+ // their correct offsets
521
+ this.strategy.recalculateStringOffsets(this, object);
522
+ this.strategy.recalculateBlobOffsets(this, object);
523
+
524
+ // Delete the empty record entry because it is no longer empty
525
+ this.emptyRecords.delete(object.index);
526
+
527
+ // Set the isEmpty back to false because it's no longer empty
528
+ object.isEmpty = false;
529
+
530
+ // Check if there is a previous empty record
531
+ const previousEmptyReference = this.emptyRecords.get(emptyRecordReference.previous);
532
+
533
+ if (previousEmptyReference) {
534
+ // Set the previous empty record to point to the old reference's next node
535
+ this.emptyRecords.set(emptyRecordReference.previous, {
536
+ previous: this.emptyRecords.get(emptyRecordReference.previous).previous,
537
+ next: emptyRecordReference.next
500
538
  });
539
+
540
+ // change the table buffer and record buffer to reflect object change
541
+ this._changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
501
542
  }
502
-
503
- // If autoUnempty is disabled, only un-empty the row if a field in the first 4 bytes changed.
504
- // If autoUnempty is enabled, un-empty the row if ANY field changed.
505
- if (this._settings.autoUnempty || changedFieldsInFirst4Bytes.length > 0) {
506
- // if the record contains any string values, point the string values to
507
- // their correct offsets
508
- this.strategy.recalculateStringOffsets(this, object);
509
- this.strategy.recalculateBlobOffsets(this, object);
510
-
511
- // Delete the empty record entry because it is no longer empty
512
- this.emptyRecords.delete(object.index);
513
-
514
- // Set the isEmpty back to false because it's no longer empty
515
- object.isEmpty = false;
516
-
517
- // Check if there is a previous empty record
518
- const previousEmptyReference = this.emptyRecords.get(emptyRecordReference.previous);
519
-
520
- if (previousEmptyReference) {
521
- // Set the previous empty record to point to the old reference's next node
522
- this.emptyRecords.set(emptyRecordReference.previous, {
523
- previous: this.emptyRecords.get(emptyRecordReference.previous).previous,
524
- next: emptyRecordReference.next
525
- });
526
-
527
- // change the table buffer and record buffer to reflect object change
528
- this._changeRecordBuffers(emptyRecordReference.previous, emptyRecordReference.next);
529
- }
530
543
 
531
- // If there is a next empty reference, update the previous value accordingly to now point
532
- // to the current record's previous index.
533
- const nextEmptyReference = this.emptyRecords.get(emptyRecordReference.next);
534
-
535
- if (nextEmptyReference) {
536
- this.emptyRecords.set(emptyRecordReference.next, {
537
- previous: emptyRecordReference.previous,
538
- next: this.emptyRecords.get(emptyRecordReference.next).next
539
- });
540
-
541
- if (!previousEmptyReference) {
542
- // If no previous empty record exists and a next record exists, we need to update the header to
543
- // point to object record as the next record to use.
544
- this.setNextRecordToUse(emptyRecordReference.next);
545
- }
546
- }
544
+ // If there is a next empty reference, update the previous value accordingly to now point
545
+ // to the current record's previous index.
546
+ const nextEmptyReference = this.emptyRecords.get(emptyRecordReference.next);
547
+
548
+ if (nextEmptyReference) {
549
+ this.emptyRecords.set(emptyRecordReference.next, {
550
+ previous: emptyRecordReference.previous,
551
+ next: this.emptyRecords.get(emptyRecordReference.next).next
552
+ });
547
553
 
548
- // If there are no previous or next empty references
549
- // Then there are no more empty references in the table
550
- // Update the table header nextRecordToUse back to the table record capacity
551
- if (!previousEmptyReference && !nextEmptyReference) {
552
- this.setNextRecordToUse(this.header.recordCapacity);
554
+ if (!previousEmptyReference) {
555
+ // If no previous empty record exists and a next record exists, we need to update the header to
556
+ // point to object record as the next record to use.
557
+ this.setNextRecordToUse(emptyRecordReference.next);
553
558
  }
554
559
  }
560
+
561
+ // If there are no previous or next empty references
562
+ // Then there are no more empty references in the table
563
+ // Update the table header nextRecordToUse back to the table record capacity
564
+ if (!previousEmptyReference && !nextEmptyReference) {
565
+ this.setNextRecordToUse(this.header.recordCapacity);
566
+ }
567
+ }
555
568
  // }
556
569
  }
557
570
  }
558
571
 
559
572
  this.emit('change');
560
- }
561
- else if (name === 'empty') {
573
+ } else if (name === 'empty') {
562
574
  this._onRecordEmpty(object);
563
575
  this.emit('change');
564
576
  }
565
- }
566
- else if (object instanceof FranchiseFileTable2Field || object instanceof FranchiseFileTable3Field) {
577
+ } else if (object instanceof FranchiseFileTable2Field || object instanceof FranchiseFileTable3Field) {
567
578
  object.isChanged = true;
568
-
579
+
569
580
  // When a table2 field changes, we need to check if the record is empty. If so, we need to mark it as not empty.
570
581
  if (object.fieldReference) {
571
582
  this.onEvent('change', object.fieldReference._parent);
572
- }
573
- else {
583
+ } else {
574
584
  // Only emit change here if the field reference is empty.
575
585
  // the onEvent call will emit a change in the above condition.
576
586
  this.emit('change');
@@ -591,24 +601,23 @@ function readOffsetTable(data, schema, header) {
591
601
  return offset.final || offset.const || offset.type.indexOf('()') >= 0 || offset.type === 'ITransaction_Sleep';
592
602
  };
593
603
 
594
- for(let i = 0; i < offsetTable.length; i++) {
604
+ for (let i = 0; i < offsetTable.length; i++) {
595
605
  let curOffset = offsetTable[i];
596
- let nextOffset = offsetTable.length > i + 1 ? offsetTable[i+1] : null;
606
+ let nextOffset = offsetTable.length > i + 1 ? offsetTable[i + 1] : null;
597
607
 
598
608
  if (nextOffset) {
599
- let curIndex = i+2;
600
- while(nextOffset && isSkippedOffset(nextOffset)) {
609
+ let curIndex = i + 2;
610
+ while (nextOffset && isSkippedOffset(nextOffset)) {
601
611
  nextOffset = offsetTable[curIndex];
602
612
  curIndex += 1;
603
613
  }
604
614
 
605
615
  if (nextOffset) {
606
- curOffset.length = nextOffset.indexOffset - curOffset.indexOffset;
616
+ curOffset.length = nextOffset.indexOffset - curOffset.indexOffset;
607
617
  } else {
608
618
  curOffset.length = (header.record1Size * 8) - curOffset.indexOffset;
609
- }
610
- }
611
- else {
619
+ }
620
+ } else {
612
621
  curOffset.length = (header.record1Size * 8) - curOffset.indexOffset;
613
622
  }
614
623
 
@@ -619,7 +628,7 @@ function readOffsetTable(data, schema, header) {
619
628
 
620
629
  let currentOffsetIndex = 0;
621
630
  let chunked32bit = [];
622
-
631
+
623
632
  for (let i = 0; i < header.record1Size * 8; i += 32) {
624
633
  let chunkedOffsets = [];
625
634
  let offsetLength = i % 32;
@@ -632,15 +641,15 @@ function readOffsetTable(data, schema, header) {
632
641
  currentOffsetIndex += 1;
633
642
  continue;
634
643
  }
635
-
644
+
636
645
  offsetLength += currentOffset.length;
637
646
  chunkedOffsets.push(currentOffset);
638
-
647
+
639
648
  currentOffsetIndex += 1;
640
649
  } else {
641
650
  break;
642
651
  }
643
- } while((currentOffsetIndex < offsetTable.length) && offsetLength < 32);
652
+ } while ((currentOffsetIndex < offsetTable.length) && offsetLength < 32);
644
653
 
645
654
  chunked32bit.push(chunkedOffsets);
646
655
  }
@@ -651,16 +660,20 @@ function readOffsetTable(data, schema, header) {
651
660
  offsetArray[offsetArray.length - 1].offset = currentOffset;
652
661
 
653
662
  for (let i = offsetArray.length - 2; i >= 0; i--) {
654
- let previousOffset = offsetArray[i+1];
663
+ let previousOffset = offsetArray[i + 1];
655
664
  let offset = offsetArray[i];
656
665
  offset.offset = previousOffset.offset + previousOffset.length;
657
666
  }
658
667
  }
659
668
  });
660
669
 
661
- offsetTable = offsetTable.filter((offset) => { return !(isSkippedOffset(offset)) });
662
- offsetTable.sort((a,b) => { return a.offset - b.offset; });
663
-
670
+ offsetTable = offsetTable.filter((offset) => {
671
+ return !(isSkippedOffset(offset))
672
+ });
673
+ offsetTable.sort((a, b) => {
674
+ return a.offset - b.offset;
675
+ });
676
+
664
677
  for (let i = 0; i < offsetTable.length; i++) {
665
678
  schema.attributes[offsetTable[i].index].offsetIndex = i;
666
679
  }
@@ -668,7 +681,9 @@ function readOffsetTable(data, schema, header) {
668
681
  return offsetTable;
669
682
 
670
683
  function sortOffsetTableByIndexOffset() {
671
- offsetTable.sort((a, b) => { return a.indexOffset - b.indexOffset; });
684
+ offsetTable.sort((a, b) => {
685
+ return a.indexOffset - b.indexOffset;
686
+ });
672
687
  };
673
688
 
674
689
  function parseOffsetTableFromData() {
@@ -722,16 +737,17 @@ function readRecords(data, header, offsetTable, table) {
722
737
  };
723
738
 
724
739
  function isLoadingNewOffsets(currentlyLoaded, attribsToLoad, offsetTable) {
725
- const names = currentlyLoaded.map((currentlyLoadedOffset) => { return currentlyLoadedOffset.name; });
740
+ const names = currentlyLoaded.map((currentlyLoadedOffset) => {
741
+ return currentlyLoadedOffset.name;
742
+ });
726
743
 
727
744
  if (attribsToLoad) {
728
745
  let newAttribs = attribsToLoad.filter((attrib) => {
729
746
  return !names.includes(attrib);
730
747
  });
731
-
748
+
732
749
  return newAttribs.length > 0;
733
- }
734
- else {
750
+ } else {
735
751
  return currentlyLoaded.length !== offsetTable.length;
736
752
  }
737
753
  };
@@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter;
2
2
  const utilService = require('./services/utilService');
3
3
 
4
4
  class FranchiseFileTable2Field {
5
- constructor (index, maxLength, parent) {
5
+ constructor(index, maxLength, parent) {
6
6
  this._value = '';
7
7
  this.rawIndex = index;
8
8
  this.isChanged = false;
@@ -15,11 +15,11 @@ class FranchiseFileTable2Field {
15
15
  this._parent = parent;
16
16
  };
17
17
 
18
- get unformattedValue () {
18
+ get unformattedValue() {
19
19
  return this._unformattedValue;
20
20
  };
21
21
 
22
- set unformattedValue (value) {
22
+ set unformattedValue(value) {
23
23
  this._unformattedValue = value;
24
24
 
25
25
  if (this.lengthAtLastSave === null) {
@@ -32,52 +32,55 @@ class FranchiseFileTable2Field {
32
32
  }
33
33
  };
34
34
 
35
- get value () {
35
+ get value() {
36
36
  if (this._value === null) {
37
- this._value = this._unformattedValue.toString().replace(/\0.*$/g,'');
37
+ this._value = this._unformattedValue.toString().replace(/\0.*$/g, '');
38
38
  }
39
39
 
40
40
  return this._value;
41
41
  };
42
42
 
43
- set value (value) {
43
+ set value(value) {
44
44
  this._value = value;
45
45
 
46
46
  if (value.length > this.maxLength) {
47
47
  value = value.substring(0, this.maxLength);
48
48
  }
49
-
49
+
50
50
  this._unformattedValue = this._strategy.setUnformattedValueFromFormatted(value, this.maxLength);
51
51
 
52
52
  if (this.lengthAtLastSave === null) {
53
53
  this.lengthAtLastSave = getLengthOfUnformattedValue(this._unformattedValue);
54
54
  }
55
-
55
+
56
56
  this._parent.onEvent('change', this);
57
57
  };
58
58
 
59
- get hexData () {
59
+ get hexData() {
60
60
  return this._unformattedValue;
61
61
  };
62
62
 
63
- get strategy () {
63
+ get strategy() {
64
64
  return this._strategy;
65
65
  };
66
66
 
67
- set strategy (strategy) {
67
+ set strategy(strategy) {
68
68
  this._strategy = strategy;
69
69
  };
70
70
 
71
- get offset () {
71
+ get offset() {
72
72
  return this._offset;
73
73
  };
74
74
 
75
- set offset (offset) {
75
+ set offset(offset) {
76
+ const offsetChanged = this._offset !== offset;
76
77
  this._offset = offset;
77
78
  this.index = offset;
78
79
 
79
- if (this.fieldReference) {
80
+ if (offsetChanged && this.fieldReference) {
80
81
  this.fieldReference.unformattedValue.setBits(this.fieldReference.offset.offset, offset, 32);
82
+ this.fieldReference.isChanged = true;
83
+ this.fieldReference._bubbleChangeToParent();
81
84
  }
82
85
  };
83
86
 
@@ -93,5 +96,5 @@ class FranchiseFileTable2Field {
93
96
  module.exports = FranchiseFileTable2Field;
94
97
 
95
98
  function getLengthOfUnformattedValue(value) {
96
- return value.length;
99
+ return value.length;
97
100
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "madden-franchise",
3
- "version": "3.2.5",
3
+ "version": "3.2.6",
4
4
  "description": "Tools to read a madden franchise file and get data from it",
5
5
  "main": "FranchiseFile.js",
6
6
  "scripts": {
@@ -2,7 +2,9 @@ let CommonAlgorithms = {};
2
2
 
3
3
  CommonAlgorithms.save = (units, oldData) => {
4
4
  // first check if any records changed. If not, we can return immediately because nothing changed.
5
- const changedUnits = units.find((unit) => { return unit.isChanged; });
5
+ const changedUnits = units.find((unit) => {
6
+ return unit.isChanged;
7
+ });
6
8
 
7
9
  if (!changedUnits) {
8
10
  return oldData;
@@ -14,7 +16,9 @@ CommonAlgorithms.save = (units, oldData) => {
14
16
  let bufferArrays = [];
15
17
 
16
18
  // 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; });
19
+ units.sort((a, b) => {
20
+ return a.index - b.index;
21
+ });
18
22
 
19
23
  units.forEach((unit, index) => {
20
24
  if (unit.offset === 0 && index > 0) {
@@ -50,7 +54,7 @@ CommonAlgorithms.save = (units, oldData) => {
50
54
  unit.isChanged = false;
51
55
  }
52
56
  });
53
-
57
+
54
58
  // Next, we need to push the remainder of data onto the array.
55
59
 
56
60
  // For example, think if a user changed the 3rd record out of 100.