madden-franchise 2.5.4 → 3.0.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.
@@ -0,0 +1,17 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "type": "pwa-node",
9
+ "request": "launch",
10
+ "name": "Launch Program",
11
+ "skipFiles": [
12
+ "<node_internals>/**"
13
+ ],
14
+ "program": "${file}"
15
+ }
16
+ ]
17
+ }
package/FranchiseEnum.js CHANGED
@@ -45,7 +45,11 @@ class FranchiseEnum {
45
45
  };
46
46
 
47
47
  getMemberByValue(value) {
48
- return this._members.find((member) => { return member.name !== 'First_' && member.name !== 'Last_' && member.value === value; });
48
+ const matches = this._members.filter((member) => { return member.name !== 'First_' && member.name !== 'Last_' && member.value === value; });
49
+ if (matches.length === 0) { throw new Error(`Argument is not a valid enum value for this field. You passed in ${value}. Field name: ${this.name}`); }
50
+
51
+ const matchesNoUnderscore = matches.find((member) => { return member.name[member.name.length - 1] !== '_'});
52
+ return matchesNoUnderscore ? matchesNoUnderscore : matches[0];
49
53
  };
50
54
 
51
55
  getMemberByUnformattedValue(value) {
package/FranchiseFile.js CHANGED
@@ -113,7 +113,7 @@ class FranchiseFile extends EventEmitter {
113
113
 
114
114
  for (let i = 0; i < tableIndicies.length; i++) {
115
115
  const currentTable = tableIndicies[i];
116
- const nextTable = tableIndicies.length >= i+1 ? tableIndicies[i+1] : null;
116
+ const nextTable = tableIndicies.length > i+1 ? tableIndicies[i+1] : this.unpackedFileContents.length - 8; // Ignore trailing 8 bytes on last table
117
117
 
118
118
  const tableData = this.unpackedFileContents.slice(currentTable, nextTable);
119
119
 
@@ -1,22 +1,19 @@
1
- const EventEmitter = require('events').EventEmitter;
1
+ const { BitView } = require('bit-buffer');
2
+
2
3
  const utilService = require('./services/utilService');
3
4
  const FranchiseFileTable2Field = require('./FranchiseFileTable2Field');
4
5
 
5
- class FranchiseFileField extends EventEmitter {
6
- constructor(key, value, offset) {
7
- super();
6
+ class FranchiseFileField {
7
+ constructor(key, value, offset, parent) {
8
8
  this._key = key;
9
- this._unformattedValue = value;
10
- this._value = parseFieldValue(value, offset);
9
+ this._recordBuffer = value;
10
+ this._unformattedValue = null;
11
11
  this._offset = offset;
12
+ this._parent = parent;
12
13
 
13
14
  if (offset.valueInSecondTable) {
14
- this.secondTableField = new FranchiseFileTable2Field(value, offset.maxLength);
15
+ this.secondTableField = new FranchiseFileTable2Field(this._recordBuffer.readUInt32BE(offset.offset / 8), offset.maxLength);
15
16
  this.secondTableField.fieldReference = this;
16
-
17
- this.secondTableField.on('change', function () {
18
- this._value = this.secondTableField.value;
19
- }.bind(this));
20
17
  }
21
18
  };
22
19
 
@@ -29,6 +26,14 @@ class FranchiseFileField extends EventEmitter {
29
26
  };
30
27
 
31
28
  get value () {
29
+ if (this._unformattedValue === null) {
30
+ this._setUnformattedValueIfEmpty();
31
+ }
32
+
33
+ if (this._value === null) {
34
+ this._value = this._parseFieldValue(this._unformattedValue, this._offset);
35
+ }
36
+
32
37
  return this._value;
33
38
  };
34
39
 
@@ -37,193 +42,213 @@ class FranchiseFileField extends EventEmitter {
37
42
  };
38
43
 
39
44
  get referenceData () {
45
+ if (this._unformattedValue === null) {
46
+ this._setUnformattedValueIfEmpty();
47
+ }
48
+
40
49
  if (this.isReference) {
41
- return utilService.getReferenceData(this.value);
50
+ return utilService.getReferenceDataFromBitview(this._unformattedValue, this.offset.offset);
42
51
  }
43
52
 
44
53
  return null;
45
54
  };
46
55
 
47
- set value (value) {
56
+ set value (value) {
57
+ if (this._unformattedValue === null) {
58
+ this._setUnformattedValueIfEmpty();
59
+ }
60
+
61
+ this._value = value;
62
+
48
63
  if (this.offset.valueInSecondTable) {
49
64
  this.secondTableField.value = value.toString();
50
65
  } else {
66
+ let actualValue;
67
+
51
68
  if (this.offset.isReference) {
52
69
  if (!utilService.isString(value)) { throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`); }
53
70
  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.`)}
71
+ const referenceData = utilService.getReferenceData(value);
72
+ this._unformattedValue.setBits(this.offset.offset, referenceData.tableId, 15);
73
+ this._unformattedValue.setBits((this.offset.offset + 15), referenceData.rowNumber, 17);
74
+ }
75
+ else if (this.offset.enum) {
76
+ try {
77
+ let theEnum = this._getEnumFromValue(value);
78
+
79
+ // Enums can have negative values and Madden negative numbers are not standard. We need to convert it here.
80
+ // 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.
81
+ const decimalEquivalent = utilService.bin2dec(theEnum.unformattedValue);
82
+ this._unformattedValue.setBits(this.offset.offset, decimalEquivalent, this.offset.length);
83
+ this._value = theEnum.name;
84
+ }
85
+ catch (err) {
86
+ this._value = null;
87
+ throw err;
88
+ }
54
89
  }
90
+ else {
91
+ switch (this.offset.type) {
92
+ case 's_int':
93
+ actualValue = parseInt(value);
94
+ this._value = actualValue;
95
+ this._unformattedValue.setBits(this.offset.offset, actualValue - this.offset.minValue, this.offset.length);
96
+ break;
97
+ default:
98
+ case 'int':
99
+ actualValue = parseInt(value);
100
+ this._value = actualValue;
101
+
102
+ if (this.offset.minValue || this.offset.maxValue) {
103
+ // return utilService.dec2bin(formatted, offset.length);
104
+ this._unformattedValue.setBits(this.offset.offset, actualValue, this.offset.length);
105
+ }
106
+ else {
107
+ const maxValueBinary = getMaxValueBinary(this.offset);
108
+ const maxValue = utilService.bin2dec(maxValueBinary);
109
+ // return utilService.dec2bin(formatted + maxValue, offset.length);
110
+ this._unformattedValue.setBits(this.offset.offset, actualValue + maxValue, this.offset.length);
111
+ }
112
+ break;
113
+ case 'bool':
114
+ // return (formatted == 1 || (formatted.toString().toLowerCase() == 'true')) ? '1' : '0';
115
+ actualValue = (value == 1 || (value.toString().toLowerCase() == 'true'));
116
+ this._value = actualValue;
117
+ this._unformattedValue.setBits(this.offset.offset, actualValue, 1);
118
+ break;
119
+ case 'float':
120
+ actualValue = parseFloat(value);
121
+ this._value = actualValue;
122
+ // return utilService.float2Bin(formatted);
123
+ // this._unformattedValue.setBits(this.offset.offset, value, this.offset.length);
124
+ this._unformattedValue.setFloat32(this.offset.offset, actualValue);
125
+ break;
126
+ }
127
+ }
128
+
129
+ // this._value = setFormattedValue(value, this._offset);
130
+ // this._unformattedValue = parseFormattedValue(value, this._offset);
131
+
55
132
 
56
- this._value = setFormattedValue(value, this._offset);
57
- this._unformattedValue = parseFormattedValue(value, this._offset);
58
- this.emit('change');
133
+ // this.emit('change');
134
+ this._parent.onEvent('change', this);
59
135
  }
60
136
  };
61
137
 
62
138
  get unformattedValue () {
139
+ if (this._unformattedValue === null) {
140
+ this._setUnformattedValueIfEmpty();
141
+ }
142
+
63
143
  return this._unformattedValue;
64
144
  };
65
145
 
66
146
  set unformattedValue (unformattedValue) {
67
147
  this.setUnformattedValueWithoutChangeEvent(unformattedValue);
68
- this.emit('change');
148
+ this._value = null;
149
+ this._parent.onEvent('change', this);
150
+ };
151
+
152
+ _setUnformattedValueIfEmpty() {
153
+ this._value = null;
154
+ this._unformattedValue = new BitView(this._recordBuffer, this._recordBuffer.byteOffset);
155
+ this._unformattedValue.bigEndian = true;
69
156
  };
70
157
 
71
158
  setUnformattedValueWithoutChangeEvent(unformattedValue, suppressErrors) {
72
- if (!utilService.isString(unformattedValue)) { throw new Error(`Argument must be of type string. You passed in a ${typeof unformattedValue}.`); }
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.`)}
159
+ if (!(unformattedValue instanceof BitView)) { throw new Error(`Argument must be of type BitView. You passed in a(n) ${typeof unformattedValue}.`); }
74
160
  else {
75
- let value;
76
-
77
- if (this.offset.valueInSecondTable) {
78
- value = this.secondTableField.value;
79
- }
80
- else {
81
- value = parseFieldValue(unformattedValue.padStart(this._offset.length, '0'), this._offset);
82
- }
83
-
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') && !suppressErrors) {
86
- throw new Error(`Argument is not a valid unformatted value for this field. You passed in ${value}.`)
87
- }
88
-
89
- this._value = value;
90
-
91
- if (this._offset.enum) {
92
- this._unformattedValue = this._offset.enum.getMemberByName(this._value).unformattedValue.padStart(this._offset.length, '0');
93
- }
94
- else {
95
- this._unformattedValue = unformattedValue;
96
- }
97
- }
98
- }
99
- };
100
-
101
- module.exports = FranchiseFileField;
102
-
103
- function setFormattedValue(value, offset) {
104
- if (offset.enum) {
105
- const theEnum = offset.enum.getMemberByName(value);
106
- if (!theEnum) {
107
- const theEnumByValue = offset.enum.getMemberByValue(value);
108
-
109
- if (!theEnumByValue) {
110
- throw new Error(`Argument is not a valid enum value for this field. You passed in ${value}.`);
111
- }
112
- else {
113
- return theEnumByValue.name;
114
- }
115
- } else {
116
- return theEnum.name;
161
+ this._unformattedValue = unformattedValue;
162
+ this._value = null;
117
163
  }
118
164
  }
119
165
 
120
- switch (offset.type) {
121
- case 's_int':
122
- case 'int':
123
- return parseInt(value);
124
- case 'bool':
125
- return value == 1 || (value.toString().toLowerCase() == 'true');
126
- case 'float':
127
- return parseFloat(value);
128
- default:
129
- return value.toString();
130
- }
131
- };
132
-
133
- function parseFieldValue(unformatted, offset) {
134
- if (offset.enum) {
135
- try {
136
- const theEnum = offset.enum.getMemberByUnformattedValue(unformatted);
137
-
138
- if (theEnum) {
139
- return theEnum.name;
140
- }
141
- }
142
- catch (err) {
143
- // console.log(err);
144
- }
166
+ _getEnumFromValue(value) {
167
+ const enumName = this.offset.enum.getMemberByName(value);
145
168
 
146
- return unformatted;
147
- }
148
- else {
149
- switch (offset.type) {
150
- case 's_int':
151
- return utilService.bin2dec(unformatted) + offset.minValue;
152
- case 'int':
153
- if (offset.minValue || offset.maxValue) {
154
- return utilService.bin2dec(unformatted);
155
- }
156
- else {
157
- const maxValueBinary = getMaxValueBinary(offset);
158
- const maxValue = utilService.bin2dec(maxValueBinary);
159
- const newValue = utilService.bin2dec(unformatted);
160
-
161
- if (newValue === 0) {
162
- return 0;
163
- }
164
- else {
165
- return newValue - maxValue;
166
- }
167
- }
168
- case 'bool':
169
- return unformatted[0] === '1' ? true : false;
170
- case 'float':
171
- return utilService.bin2Float(unformatted);
172
- default:
173
- return unformatted;
174
- }
175
- }
176
- };
177
-
178
- function parseFormattedValue(formatted, offset) {
179
- if (offset.enum) {
180
- const enumName = offset.enum.getMemberByName(formatted);
181
-
182
169
  if (enumName) {
183
- return enumName.unformattedValue.padStart(offset.length, '0');
170
+ return enumName;
184
171
  }
185
172
  else {
186
- const formattedEnum = offset.enum.getMemberByValue(formatted)
173
+ const formattedEnum = this.offset.enum.getMemberByValue(value)
187
174
 
188
175
  if (formattedEnum) {
189
- return formattedEnum.unformattedValue.padStart(offset.length, '0');
176
+ return formattedEnum;
190
177
  }
191
178
  else {
192
- const unformattedEnum = offset.enum.getMemberByUnformattedValue(formatted);
179
+ const unformattedEnum = this.offset.enum.getMemberByUnformattedValue(value);
193
180
 
194
181
  if (unformattedEnum) {
195
- return unformattedEnum.unformattedValue.padStart(offset.length, '0');
182
+ return unformattedEnum;
196
183
  }
197
184
  else {
198
- return offset.enum.members[0].unformattedValue.padStart(offset.length, '0');
185
+ return this.offset.enum.members[0];
199
186
  }
200
187
  }
201
188
  }
202
- }
203
- else {
204
- switch (offset.type) {
205
- case 's_int':
206
- const actualValue = parseInt(formatted);
207
- return utilService.dec2bin(actualValue - offset.minValue, offset.length);
208
- case 'int':
209
- if (offset.minValue || offset.maxValue) {
210
- return utilService.dec2bin(formatted, offset.length);
211
- }
212
- else {
213
- const maxValueBinary = getMaxValueBinary(offset);
214
- const maxValue = utilService.bin2dec(maxValueBinary);
215
- return utilService.dec2bin(formatted + maxValue, offset.length);
189
+ };
190
+
191
+ _parseFieldValue(unformatted, offset) {
192
+ if (offset.valueInSecondTable) {
193
+ return this.secondTableField.value;
194
+ }
195
+ else if (offset.enum) {
196
+ const enumUnformattedValue = utilService.dec2bin(this.unformattedValue.getBits(this.offset.offset, this.offset.length), offset.enum._maxLength);
197
+
198
+ try {
199
+ const theEnum = offset.enum.getMemberByUnformattedValue(enumUnformattedValue);
200
+
201
+ if (theEnum) {
202
+ return theEnum.name;
216
203
  }
217
- case 'bool':
218
- return (formatted == 1 || (formatted.toString().toLowerCase() == 'true')) ? '1' : '0';
219
- case 'float':
220
- return utilService.float2Bin(formatted);
221
- default:
222
- return formatted;
204
+ }
205
+ catch (err) {
206
+ // console.log(err);
207
+ }
208
+
209
+ return enumUnformattedValue;
223
210
  }
224
- }
211
+ else if (offset.isReference) {
212
+ const referenceData = utilService.getReferenceDataFromBitview(this._unformattedValue, this.offset.offset);
213
+ return utilService.getBinaryReferenceData(referenceData.tableId, referenceData.rowNumber);
214
+ }
215
+ else {
216
+ switch (offset.type) {
217
+ case 's_int':
218
+ // return utilService.bin2dec(unformatted) + offset.minValue;
219
+ return unformatted.getBits(offset.offset, offset.length) + offset.minValue;
220
+ case 'int':
221
+ if (offset.minValue || offset.maxValue) {
222
+ return unformatted.getBits(offset.offset, offset.length);
223
+ }
224
+ else {
225
+ // This is for int[] tables.
226
+
227
+ const maxValueBinary = getMaxValueBinary(offset);
228
+ const maxValue = utilService.bin2dec(maxValueBinary);
229
+ const newValue = unformatted.getBits(offset.offset, offset.length);
230
+
231
+ if (newValue === 0) {
232
+ return 0;
233
+ }
234
+ else {
235
+ return newValue - maxValue;
236
+ }
237
+ }
238
+ case 'bool':
239
+ return unformatted.getBits(offset.offset, 1) ? true : false;
240
+ case 'float':
241
+ // return utilService.bin2Float(unformatted);
242
+ return unformatted.getFloat32(offset.offset, offset.length);
243
+ default:
244
+ return unformatted;
245
+ }
246
+ }
247
+ };
225
248
  };
226
249
 
250
+ module.exports = FranchiseFileField;
251
+
227
252
  function getMaxValueBinary(offset) {
228
253
  let maxValue = '1';
229
254
  for (let j = 0; j < (offset.length - 1); j++) {
@@ -1,79 +1,62 @@
1
- const EventEmitter = require('events').EventEmitter;
2
1
  const utilService = require('./services/utilService');
3
2
  const FranchiseFileField = require('./FranchiseFileField');
4
3
 
5
- class FranchiseFileRecord extends EventEmitter {
6
- constructor(data, index, offsetTable) {
7
- super();
4
+ class FranchiseFileRecord {
5
+ constructor(data, index, offsetTable, parent) {
8
6
  this._data = data;
9
7
  this._offsetTable = offsetTable;
10
8
  this.index = index;
11
- this._fields = parseRecordFields(data, offsetTable);
9
+ this._fieldsArray = [];
10
+ this._fields = this.parseRecordFields(data, offsetTable, this);
12
11
  this.isChanged = false;
13
12
  this.arraySize = null;
14
13
  this.isEmpty = false;
15
-
16
- const that = this;
17
- this._fields.forEach((field) => {
18
- Object.defineProperty(this, field.key, {
19
- set: function (value) {
20
- field.value = value;
21
- },
22
- get: function () {
23
- return field.value;
14
+ this._parent = parent;
15
+
16
+ return new Proxy(this, {
17
+ get: function (target, prop, receiver) {
18
+ return target.fields[prop] !== undefined ? target.fields[prop].value : target[prop] !== undefined ? target[prop] : null;
19
+ },
20
+ set: function (target, prop, receiver) {
21
+ if (target.fields[prop] !== undefined) {
22
+ target.fields[prop].value = receiver;
24
23
  }
25
- });
26
-
27
- field.on('change', function () {
28
- that._data = utilService.replaceAt(that._data, this.offset.offset, this.unformattedValue);
29
-
30
- // NOTE: At this time, we can only change the size of arrays of references.
31
- // I'm not sure how to change the size of non-reference arrays, or if it's even possible.
32
- if (that.arraySize !== null && that.arraySize !== undefined) {
33
- const referenceData = this.referenceData;
34
-
35
- // If the field is outside of the previous array size and was edited to a valid reference,
36
- // then reset the array size
37
- if (this.offset.index >= that.arraySize) {
38
- if (this.isReference) {
39
- if (referenceData.tableId !== 0 || referenceData.rowNumber !== 0) {
40
- that.arraySize = this.offset.index + 1;
41
- }
42
- }
43
- }
44
-
45
- // If the value was changed to 0s, then shrink the array size to this index.
46
- else if (this.isReference) {
47
- if (referenceData.tableId === 0 && referenceData.rowNumber === 0) {
48
- that.arraySize = this.offset.index;
49
- }
50
- }
24
+ else {
25
+ target[prop] = receiver;
51
26
  }
52
27
 
53
- that.emit('change', this.offset);
54
- });
55
- });
28
+ return true;
29
+ }
30
+ })
56
31
  };
57
32
 
58
33
  get hexData () {
59
- return Buffer.from(utilService.binaryBlockToDecimalBlock(this._data));
34
+ return this._data;
60
35
  };
61
36
 
62
37
  get fields () {
63
38
  return this._fields;
64
39
  };
65
40
 
41
+ get fieldsArray () {
42
+ return this._fieldsArray;
43
+ };
44
+
45
+ get data() {
46
+ return this._data;
47
+ };
48
+
66
49
  set data (data) {
67
50
  this._data = data;
68
51
 
69
- this._fields.forEach((field) => {
52
+ this._fieldsArray.forEach((field) => {
70
53
  const unformattedValue = data.slice(field.offset.offset, field.offset.offset + field.offset.length);
71
54
  field.setUnformattedValueWithoutChangeEvent(unformattedValue);
72
55
  });
73
56
  };
74
57
 
75
58
  getFieldByKey(key) {
76
- return this._fields.find((field) => { return field.key === key; });
59
+ return this._fields[key];
77
60
  };
78
61
 
79
62
  getValueByKey(key) {
@@ -86,22 +69,57 @@ class FranchiseFileRecord extends EventEmitter {
86
69
  return field ? field.referenceData : null;
87
70
  };
88
71
 
72
+ parseRecordFields(data, offsetTable, record) {
73
+ let fields = {};
74
+
75
+ for (let j = 0; j < offsetTable.length; j++) {
76
+ const offset = offsetTable[j];
77
+
78
+ // Push the entire record buffer to the field. No need to perform a calculation
79
+ // to subarray the buffer, BitView will take care of it in the Field.
80
+ fields[offset.name] = new FranchiseFileField(offset.name, data, offset, record);
81
+
82
+ this._fieldsArray.push(fields[offset.name]);
83
+ }
84
+
85
+ return fields;
86
+ };
87
+
89
88
  empty() {
90
- this.emit('empty');
89
+ this._parent.onEvent('empty', this);
91
90
  this.isEmpty = true;
92
91
  };
93
- };
94
92
 
95
- module.exports = FranchiseFileRecord;
96
-
97
- function parseRecordFields (data, offsetTable) {
98
- let fields = [];
93
+ onEvent(name, field) {
94
+ if (name === 'change') {
95
+ // this._data = utilService.replaceAt(this._data, field.offset.offset, field.unformattedValue);
96
+
97
+ // NOTE: At field time, we can only change the size of arrays of references.
98
+ // I'm not sure how to change the size of non-reference arrays, or if it's even possible.
99
+ if (this.arraySize !== null && this.arraySize !== undefined) {
100
+ const referenceData = field.referenceData;
101
+
102
+ // If the field is outside of the previous array size and was edited to a valid reference,
103
+ // then reset the array size
104
+ if (field.offset.index >= this.arraySize) {
105
+ if (field.isReference) {
106
+ if (referenceData.tableId !== 0 || referenceData.rowNumber !== 0) {
107
+ this.arraySize = field.offset.index + 1;
108
+ }
109
+ }
110
+ }
111
+
112
+ // If the value was changed to 0s, then shrink the array size to field index.
113
+ else if (field.isReference) {
114
+ if (referenceData.tableId === 0 && referenceData.rowNumber === 0) {
115
+ this.arraySize = field.offset.index;
116
+ }
117
+ }
118
+ }
99
119
 
100
- for (let j = 0; j < offsetTable.length; j++) {
101
- const offset = offsetTable[j];
102
- const unformattedValue = data.slice(offset.offset, offset.offset + offset.length);
103
- fields.push(new FranchiseFileField(offset.name, unformattedValue, offset));
104
- }
120
+ this._parent.onEvent('change', this);
121
+ }
122
+ };
123
+ };
105
124
 
106
- return fields;
107
- };
125
+ module.exports = FranchiseFileRecord;