Skip to content
This repository has been archived by the owner on Mar 13, 2023. It is now read-only.

Commit

Permalink
Use game build at the beginning of the file where possible (see bmaup…
Browse files Browse the repository at this point in the history
  • Loading branch information
bmaupin committed Jan 4, 2018
1 parent bd90e73 commit 23f2cc5
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 23 deletions.
56 changes: 37 additions & 19 deletions src/Civ5Save.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ class Civ5Save {
* @private
*/
this._verifyFileSignature();
/**
* @private
*/
this._gameBuild = this._getGameBuild();
/**
* @private
*/
Expand Down Expand Up @@ -102,29 +98,38 @@ class Civ5Save {
* @private
*/
_getProperties() {
let previousPropertyName = '';
this._gameBuild = null;
let previousPropertyName = null;
let previousPropertySection = 0;
let properties = new Map();
let sectionOffsets = this._getSectionOffsets();
let saveGameVersion = null;
let sectionOffsets = null;

for (let propertyName in Civ5SavePropertyDefinitions) {
// Before build 310700, playerColours is a list of bytes. There isn't much value in implementing that. And
// privateGame comes after playerColours.
if (Number(this.gameBuild) < 310700) {
if (propertyName === 'playerColours' || propertyName === 'privateGame') {
// TODO: move this logic into property definitions file
if (propertyName === 'playerColours' || propertyName === 'privateGame') {
if (Number(this._gameBuild) < 310700) {
continue;
}
}

// Make propertyDefinition a copy; otherwise it will modify the property for every instance of the Civ5Save class
let propertyDefinition = Object.assign({}, Civ5SavePropertyDefinitions[propertyName]);

let propertySection = this._getPropertySection(propertyDefinition);
let propertySection = this._getPropertySection(propertyDefinition, saveGameVersion, this._gameBuild);
// If propertySection is null, it means the property isn't available for the particular game build
if (this._isNullOrUndefined(propertySection)) {
if (propertyName === 'gameBuild') {
this._gameBuild = this._getGameBuild();
sectionOffsets = this._getSectionOffsets(this._gameBuild);
}

continue;
}

// TODO: move this logic into property definitions file
if (propertyName === 'section30Skip1') {
if (properties.enabledDLC.getArray().includes('Expansion - Gods and Kings') ||
properties.enabledDLC.getArray().includes('Expansion - Brave New World')) {
Expand All @@ -142,12 +147,12 @@ class Civ5Save {
}
}

let propertyByteOffset = null;
let propertyByteOffset = 0;
if (propertySection === previousPropertySection) {
let previousProperty = properties[previousPropertyName];
propertyByteOffset = previousProperty.byteOffset + previousProperty.length;

} else {
} else if (previousPropertyName !== null) {
propertyByteOffset = sectionOffsets[propertySection - 1].start + propertyDefinition.byteOffsetInSection;
}

Expand All @@ -161,6 +166,14 @@ class Civ5Save {
throw new ParseError(`Failure parsing save at property ${propertyName}`);
}

if (propertyName === 'gameBuild') {
this._gameBuild = properties.gameBuild.getValue(this._saveData);
sectionOffsets = this._getSectionOffsets(this._gameBuild);
}
if (propertyName === 'saveGameVersion') {
saveGameVersion = properties.saveGameVersion.getValue(this._saveData);
}

previousPropertyName = propertyName;
previousPropertySection = propertySection;
}
Expand All @@ -171,7 +184,7 @@ class Civ5Save {
/**
* @private
*/
_getSectionOffsets() {
_getSectionOffsets(gameBuild) {
const SECTION_DELIMITER = [0x40, 0, 0, 0];

const LAST_PROPERTY_DEFINITION = Civ5SavePropertyDefinitions[Object.keys(Civ5SavePropertyDefinitions)[Object.keys(
Expand All @@ -190,9 +203,9 @@ class Civ5Save {
for (let byteOffset = 0; byteOffset < saveDataBytes.length; byteOffset++) {
if (this._areArraysEqual(saveDataBytes.slice(byteOffset, byteOffset + 4), SECTION_DELIMITER)) {
// Player colour section before build 310700 contains hex values, which can include the section delimiter
if (Number(this.gameBuild) < 310700) {
if (Number(gameBuild) < 310700) {
let playerColourSection = 23;
if (Number(this.gameBuild) >= 262623) {
if (Number(gameBuild) >= 262623) {
playerColourSection = 24;
}
if (sectionOffsets.length === playerColourSection) {
Expand Down Expand Up @@ -230,10 +243,15 @@ class Civ5Save {
/**
* @private
*/
_getPropertySection(propertyDefinition) {
_getPropertySection(propertyDefinition, saveGameVersion, gameBuild) {
if (propertyDefinition.hasOwnProperty('getSection')) {
return propertyDefinition.getSection(saveGameVersion);
}

let propertySection = null;

for (let build in propertyDefinition.sectionByBuild) {
if (Number.parseInt(this.gameBuild) >= Number.parseInt(build)) {
if (Number.parseInt(gameBuild) >= Number.parseInt(build)) {
propertySection = propertyDefinition.sectionByBuild[build];
}
}
Expand Down Expand Up @@ -315,9 +333,9 @@ class Civ5Save {
/**
* Game mode: one of `Civ5Save.GAME_MODES.SINGLE`, `Civ5Save.GAME_MODES.MULTI`, or `Civ5Save.GAME_MODES.HOTSEAT`.
*
* Note that this will be `undefined` if [gameBuild](#instance-get-gameBuild) is less than 230620. `undefined` is used
* instead of `null` because older save files do not have a spot for this information (`null` might incorrectly imply
* the spot is there but empty).
* Note that this will be `undefined` if [gameBuild](#instance-get-gameBuild) is less than 230620 because the meaning
* of its value is unknown. `undefined` is used instead of `null` because `null` might incorrectly imply the value is
* empty.
* @type {string|undefined}
*/
get gameMode() {
Expand Down
32 changes: 28 additions & 4 deletions src/Civ5SavePropertyDefinitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ export default {
'sectionByBuild': {
'98650': 1
},
'type': 'string'
'type': 'string',
// Game build is a later property and not yet defined, so the section will need to be determined without it
getSection(saveGameVersion) {
return 1;
},
},
'saveGameVersion': {
'byteOffsetInSection': 4,
'length': 4,
'sectionByBuild': {
'98650': 1
},
'type': 'int'
'type': 'int',
// Game build is a later property and not yet defined, so the section will need to be determined without it
getSection(saveGameVersion) {
return 1;
},
},
// This property is updated if the game is saved with a newer version of Civ 5
'gameVersion': {
Expand All @@ -22,7 +30,15 @@ export default {
'sectionByBuild': {
'230620': 1
},
'type': 'string'
'type': 'string',
// Game build is a later property and not yet defined, so the section will need to be determined without it
getSection(saveGameVersion) {
if (saveGameVersion >= 7) {
return 1;
} else {
return null;
}
},
},
// This property is updated if the game is saved with a newer version of Civ 5. The build of Civ 5 that was originally used when the save file was created is stored later, after gameOptionsMap.
'gameBuild': {
Expand All @@ -31,7 +47,15 @@ export default {
'sectionByBuild': {
'230620': 1
},
'type': 'string'
'type': 'string',
// Game build is not yet defined, so the section will need to be determined without it
getSection(saveGameVersion) {
if (saveGameVersion >= 7) {
return 1;
} else {
return null;
}
},
},
'currentTurn': {
'byteOffsetInSection': null,
Expand Down

0 comments on commit 23f2cc5

Please sign in to comment.