factorio-save-parser 1.0.8 → 1.0.10

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.
@@ -93,6 +93,7 @@ function parseLevelInitData(data) {
93
93
  major,
94
94
  minor,
95
95
  patch,
96
+ asString: `${major}.${minor}.${patch}`,
96
97
  };
97
98
  }
98
99
  // Single Unused byte
@@ -180,6 +181,8 @@ async function parseSaveFile(filepath) {
180
181
  return parseLevelInitData(binaryData);
181
182
  }
182
183
 
184
+ exports.InvalidArgumentError = InvalidArgumentError;
185
+ exports.InvalidSaveFileError = InvalidSaveFileError;
183
186
  exports.parseLevelInitData = parseLevelInitData;
184
187
  exports.parseSaveFile = parseSaveFile;
185
188
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../out/errors.js","../out/parser.js"],"sourcesContent":["export class InvalidArgumentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidArgumentError';\n Object.setPrototypeOf(this, InvalidArgumentError.prototype);\n }\n}\nexport class InvalidSaveFileError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidSaveFileError';\n Object.setPrototypeOf(this, InvalidSaveFileError.prototype);\n }\n}\n//# sourceMappingURL=errors.js.map","import path from 'node:path';\nimport { compare as compareVersions } from 'compare-versions';\nimport unzipper from 'unzipper';\nimport { InvalidArgumentError, InvalidSaveFileError } from './errors';\n// Written with the help of the information on https://forums.factorio.com/viewtopic.php?t=47014\nexport function parseLevelInitData(data) {\n let bytesOffset = 0;\n function readUint8() {\n // console.log(`readUint8() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint8(bytesOffset);\n bytesOffset += 1;\n return value;\n }\n function readUint16() {\n // console.log(`readUint16() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint16(bytesOffset, true);\n bytesOffset += 2;\n return value;\n }\n function readUint32() {\n // console.log(`readUint32() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint32(bytesOffset, true);\n bytesOffset += 4;\n return value;\n }\n function readBoolean() {\n return readUint8() !== 0;\n }\n function readVersion() {\n const major = readUint16();\n const minor = readUint16();\n const patch = readUint16();\n const build = readUint16();\n return {\n major,\n minor,\n patch,\n build,\n // 4-number versions officially supported by compare-versions\n asString: `${major}.${minor}.${patch}.${build}`,\n };\n }\n // Read Factorio Version\n const factorioVersion = readVersion();\n function readOptimizedUint(bitSize) {\n /* console.log('readOptimizedUint()', {\n bitSize,\n }); */\n if (compareVersions(factorioVersion.asString, '0.14.14', '>')) {\n const value = readUint8();\n if (value !== 0xff) {\n return value;\n }\n }\n switch (bitSize) {\n case 16:\n return readUint16();\n case 32:\n return readUint32();\n default:\n throw new Error(`Invalid bit size: ${bitSize}`);\n }\n }\n // has_empty_indicator=false\n function readString() {\n const length = readOptimizedUint(32);\n const bufferSlice = data.buffer.slice(bytesOffset, bytesOffset + length);\n bytesOffset += length;\n return new TextDecoder().decode(bufferSlice);\n }\n function readVersion48() {\n const major = readOptimizedUint(16);\n const minor = readOptimizedUint(16);\n const patch = readOptimizedUint(16);\n return {\n major,\n minor,\n patch,\n };\n }\n // Single Unused byte\n readBoolean();\n // Read fields\n const name = readString();\n const campaign = readString();\n const baseMod = readString();\n const difficulty = readUint8();\n const finished = readBoolean();\n const playerWon = readBoolean();\n const nextLevel = readString();\n const canContinue = readBoolean();\n const finishedButContinuing = readBoolean();\n const savingReplay = readBoolean();\n const allowNonAdminDebugOptions = readBoolean();\n const loadedFrom = readVersion48();\n const loadedFromBuild = factorioVersion.major >= 2 ? readUint32() : readUint16();\n const allowedCommands = readBoolean();\n // 2.0 has a new header with 4 extra bytes here\n if (factorioVersion.major >= 2) {\n readUint8();\n readUint8();\n readUint8();\n readUint8();\n }\n const numMods = readOptimizedUint(32);\n const mods = [];\n for (let i = 0; i < numMods; i++) {\n mods.push({\n name: readString(),\n version: readVersion48(),\n crc: readUint32(),\n });\n }\n return {\n factorioVersion,\n name,\n campaign,\n baseMod,\n difficulty,\n finished,\n playerWon,\n nextLevel,\n canContinue,\n finishedButContinuing,\n savingReplay,\n allowNonAdminDebugOptions,\n loadedFrom,\n loadedFromBuild,\n allowedCommands,\n mods,\n };\n}\nexport async function parseSaveFile(filepath) {\n let centralDirectory;\n let levelInitDatName = '';\n try {\n const normalizedPath = path.normalize(filepath);\n const resolvedPath = path.resolve(normalizedPath);\n const fileExtension = path.extname(filepath);\n const filename = path.basename(filepath).replace(fileExtension, '');\n // if zip file is called _autosave1.zip, make levelInitDatName contain _autosave1\n levelInitDatName = `${filename}/level-init.dat`;\n // if file does not end with .zip, it's likely not valid\n if (fileExtension !== '.zip') {\n throw new InvalidArgumentError('File path is not a zip file.');\n }\n centralDirectory = await unzipper.Open.file(resolvedPath);\n }\n catch (e) {\n // eslint-disable-next-line no-console\n console.error(e);\n return undefined;\n }\n const file = centralDirectory.files.find(f => f.path === levelInitDatName);\n if (!file) {\n throw new InvalidSaveFileError('File does not appear to be a save file');\n }\n const levelInitDatContent = await file?.buffer();\n if (levelInitDatContent === null) {\n throw new InvalidSaveFileError('Unable to find level-init.dat in save file');\n }\n const binaryData = new DataView(levelInitDatContent.buffer);\n return parseLevelInitData(binaryData);\n}\n//# sourceMappingURL=parser.js.map"],"names":["compareVersions"],"mappings":";;;;;;AAAO,MAAM,oBAAoB,SAAS,KAAK,CAAC;AAChD,IAAI,WAAW,CAAC,OAAO,EAAE;AACzB,QAAQ,KAAK,CAAC,OAAO,CAAC;AACtB,QAAQ,IAAI,CAAC,IAAI,GAAG,sBAAsB;AAC1C,QAAQ,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC;AACnE,IAAI;AACJ;AACO,MAAM,oBAAoB,SAAS,KAAK,CAAC;AAChD,IAAI,WAAW,CAAC,OAAO,EAAE;AACzB,QAAQ,KAAK,CAAC,OAAO,CAAC;AACtB,QAAQ,IAAI,CAAC,IAAI,GAAG,sBAAsB;AAC1C,QAAQ,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC;AACnE,IAAI;AACJ;;ACTA;AACO,SAAS,kBAAkB,CAAC,IAAI,EAAE;AACzC,IAAI,IAAI,WAAW,GAAG,CAAC;AACvB,IAAI,SAAS,SAAS,GAAG;AACzB;AACA,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;AAChD,QAAQ,WAAW,IAAI,CAAC;AACxB,QAAQ,OAAO,KAAK;AACpB,IAAI;AACJ,IAAI,SAAS,UAAU,GAAG;AAC1B;AACA,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC;AACvD,QAAQ,WAAW,IAAI,CAAC;AACxB,QAAQ,OAAO,KAAK;AACpB,IAAI;AACJ,IAAI,SAAS,UAAU,GAAG;AAC1B;AACA,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC;AACvD,QAAQ,WAAW,IAAI,CAAC;AACxB,QAAQ,OAAO,KAAK;AACpB,IAAI;AACJ,IAAI,SAAS,WAAW,GAAG;AAC3B,QAAQ,OAAO,SAAS,EAAE,KAAK,CAAC;AAChC,IAAI;AACJ,IAAI,SAAS,WAAW,GAAG;AAC3B,QAAQ,MAAM,KAAK,GAAG,UAAU,EAAE;AAClC,QAAQ,MAAM,KAAK,GAAG,UAAU,EAAE;AAClC,QAAQ,MAAM,KAAK,GAAG,UAAU,EAAE;AAClC,QAAQ,MAAM,KAAK,GAAG,UAAU,EAAE;AAClC,QAAQ,OAAO;AACf,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB;AACA,YAAY,QAAQ,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC3D,SAAS;AACT,IAAI;AACJ;AACA,IAAI,MAAM,eAAe,GAAG,WAAW,EAAE;AACzC,IAAI,SAAS,iBAAiB,CAAC,OAAO,EAAE;AACxC;AACA;AACA;AACA,QAAQ,IAAIA,uBAAe,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE;AACvE,YAAY,MAAM,KAAK,GAAG,SAAS,EAAE;AACrC,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE;AAChC,gBAAgB,OAAO,KAAK;AAC5B,YAAY;AACZ,QAAQ;AACR,QAAQ,QAAQ,OAAO;AACvB,YAAY,KAAK,EAAE;AACnB,gBAAgB,OAAO,UAAU,EAAE;AACnC,YAAY,KAAK,EAAE;AACnB,gBAAgB,OAAO,UAAU,EAAE;AACnC,YAAY;AACZ,gBAAgB,MAAM,IAAI,KAAK,CAAC,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D;AACA,IAAI;AACJ;AACA,IAAI,SAAS,UAAU,GAAG;AAC1B,QAAQ,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,CAAC;AAC5C,QAAQ,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAAC;AAChF,QAAQ,WAAW,IAAI,MAAM;AAC7B,QAAQ,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;AACpD,IAAI;AACJ,IAAI,SAAS,aAAa,GAAG;AAC7B,QAAQ,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,CAAC;AAC3C,QAAQ,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,CAAC;AAC3C,QAAQ,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,CAAC;AAC3C,QAAQ,OAAO;AACf,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,YAAY,KAAK;AACjB,SAAS;AACT,IAAI;AACJ;AACA,IAAI,WAAW,EAAE;AACjB;AACA,IAAI,MAAM,IAAI,GAAG,UAAU,EAAE;AAC7B,IAAI,MAAM,QAAQ,GAAG,UAAU,EAAE;AACjC,IAAI,MAAM,OAAO,GAAG,UAAU,EAAE;AAChC,IAAI,MAAM,UAAU,GAAG,SAAS,EAAE;AAClC,IAAI,MAAM,QAAQ,GAAG,WAAW,EAAE;AAClC,IAAI,MAAM,SAAS,GAAG,WAAW,EAAE;AACnC,IAAI,MAAM,SAAS,GAAG,UAAU,EAAE;AAClC,IAAI,MAAM,WAAW,GAAG,WAAW,EAAE;AACrC,IAAI,MAAM,qBAAqB,GAAG,WAAW,EAAE;AAC/C,IAAI,MAAM,YAAY,GAAG,WAAW,EAAE;AACtC,IAAI,MAAM,yBAAyB,GAAG,WAAW,EAAE;AACnD,IAAI,MAAM,UAAU,GAAG,aAAa,EAAE;AACtC,IAAI,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,GAAG,UAAU,EAAE;AACpF,IAAI,MAAM,eAAe,GAAG,WAAW,EAAE;AACzC;AACA,IAAI,IAAI,eAAe,CAAC,KAAK,IAAI,CAAC,EAAE;AACpC,QAAQ,SAAS,EAAE;AACnB,QAAQ,SAAS,EAAE;AACnB,QAAQ,SAAS,EAAE;AACnB,QAAQ,SAAS,EAAE;AACnB,IAAI;AACJ,IAAI,MAAM,OAAO,GAAG,iBAAiB,CAAC,EAAE,CAAC;AACzC,IAAI,MAAM,IAAI,GAAG,EAAE;AACnB,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE;AACtC,QAAQ,IAAI,CAAC,IAAI,CAAC;AAClB,YAAY,IAAI,EAAE,UAAU,EAAE;AAC9B,YAAY,OAAO,EAAE,aAAa,EAAE;AACpC,YAAY,GAAG,EAAE,UAAU,EAAE;AAC7B,SAAS,CAAC;AACV,IAAI;AACJ,IAAI,OAAO;AACX,QAAQ,eAAe;AACvB,QAAQ,IAAI;AACZ,QAAQ,QAAQ;AAChB,QAAQ,OAAO;AACf,QAAQ,UAAU;AAClB,QAAQ,QAAQ;AAChB,QAAQ,SAAS;AACjB,QAAQ,SAAS;AACjB,QAAQ,WAAW;AACnB,QAAQ,qBAAqB;AAC7B,QAAQ,YAAY;AACpB,QAAQ,yBAAyB;AACjC,QAAQ,UAAU;AAClB,QAAQ,eAAe;AACvB,QAAQ,eAAe;AACvB,QAAQ,IAAI;AACZ,KAAK;AACL;AACO,eAAe,aAAa,CAAC,QAAQ,EAAE;AAC9C,IAAI,IAAI,gBAAgB;AACxB,IAAI,IAAI,gBAAgB,GAAG,EAAE;AAC7B,IAAI,IAAI;AACR,QAAQ,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;AACvD,QAAQ,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;AACzD,QAAQ,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;AACpD,QAAQ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;AAC3E;AACA,QAAQ,gBAAgB,GAAG,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC;AACvD;AACA,QAAQ,IAAI,aAAa,KAAK,MAAM,EAAE;AACtC,YAAY,MAAM,IAAI,oBAAoB,CAAC,8BAA8B,CAAC;AAC1E,QAAQ;AACR,QAAQ,gBAAgB,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC;AACjE,IAAI;AACJ,IAAI,OAAO,CAAC,EAAE;AACd;AACA,QAAQ,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACxB,QAAQ,OAAO,SAAS;AACxB,IAAI;AACJ,IAAI,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC;AAC9E,IAAI,IAAI,CAAC,IAAI,EAAE;AACf,QAAQ,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,CAAC;AAChF,IAAI;AACJ,IAAI,MAAM,mBAAmB,GAAG,MAAM,IAAI,EAAE,MAAM,EAAE;AACpD,IAAI,IAAI,mBAAmB,KAAK,IAAI,EAAE;AACtC,QAAQ,MAAM,IAAI,oBAAoB,CAAC,4CAA4C,CAAC;AACpF,IAAI;AACJ,IAAI,MAAM,UAAU,GAAG,IAAI,QAAQ,CAAC,mBAAmB,CAAC,MAAM,CAAC;AAC/D,IAAI,OAAO,kBAAkB,CAAC,UAAU,CAAC;AACzC;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../out/errors.js","../out/parser.js"],"sourcesContent":["export class InvalidArgumentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidArgumentError';\n Object.setPrototypeOf(this, InvalidArgumentError.prototype);\n }\n}\nexport class InvalidSaveFileError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidSaveFileError';\n Object.setPrototypeOf(this, InvalidSaveFileError.prototype);\n }\n}\n//# sourceMappingURL=errors.js.map","import path from 'node:path';\nimport { compare as compareVersions } from 'compare-versions';\nimport unzipper from 'unzipper';\nimport { InvalidArgumentError, InvalidSaveFileError } from './errors';\n// Written with the help of the information on https://forums.factorio.com/viewtopic.php?t=47014\nexport function parseLevelInitData(data) {\n let bytesOffset = 0;\n function readUint8() {\n // console.log(`readUint8() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint8(bytesOffset);\n bytesOffset += 1;\n return value;\n }\n function readUint16() {\n // console.log(`readUint16() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint16(bytesOffset, true);\n bytesOffset += 2;\n return value;\n }\n function readUint32() {\n // console.log(`readUint32() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint32(bytesOffset, true);\n bytesOffset += 4;\n return value;\n }\n function readBoolean() {\n return readUint8() !== 0;\n }\n function readVersion() {\n const major = readUint16();\n const minor = readUint16();\n const patch = readUint16();\n const build = readUint16();\n return {\n major,\n minor,\n patch,\n build,\n // 4-number versions officially supported by compare-versions\n asString: `${major}.${minor}.${patch}.${build}`,\n };\n }\n // Read Factorio Version\n const factorioVersion = readVersion();\n function readOptimizedUint(bitSize) {\n /* console.log('readOptimizedUint()', {\n bitSize,\n }); */\n if (compareVersions(factorioVersion.asString, '0.14.14', '>')) {\n const value = readUint8();\n if (value !== 0xff) {\n return value;\n }\n }\n switch (bitSize) {\n case 16:\n return readUint16();\n case 32:\n return readUint32();\n default:\n throw new Error(`Invalid bit size: ${bitSize}`);\n }\n }\n // has_empty_indicator=false\n function readString() {\n const length = readOptimizedUint(32);\n const bufferSlice = data.buffer.slice(bytesOffset, bytesOffset + length);\n bytesOffset += length;\n return new TextDecoder().decode(bufferSlice);\n }\n function readVersion48() {\n const major = readOptimizedUint(16);\n const minor = readOptimizedUint(16);\n const patch = readOptimizedUint(16);\n return {\n major,\n minor,\n patch,\n asString: `${major}.${minor}.${patch}`,\n };\n }\n // Single Unused byte\n readBoolean();\n // Read fields\n const name = readString();\n const campaign = readString();\n const baseMod = readString();\n const difficulty = readUint8();\n const finished = readBoolean();\n const playerWon = readBoolean();\n const nextLevel = readString();\n const canContinue = readBoolean();\n const finishedButContinuing = readBoolean();\n const savingReplay = readBoolean();\n const allowNonAdminDebugOptions = readBoolean();\n const loadedFrom = readVersion48();\n const loadedFromBuild = factorioVersion.major >= 2 ? readUint32() : readUint16();\n const allowedCommands = readBoolean();\n // 2.0 has a new header with 4 extra bytes here\n if (factorioVersion.major >= 2) {\n readUint8();\n readUint8();\n readUint8();\n readUint8();\n }\n const numMods = readOptimizedUint(32);\n const mods = [];\n for (let i = 0; i < numMods; i++) {\n mods.push({\n name: readString(),\n version: readVersion48(),\n crc: readUint32(),\n });\n }\n return {\n factorioVersion,\n name,\n campaign,\n baseMod,\n difficulty,\n finished,\n playerWon,\n nextLevel,\n canContinue,\n finishedButContinuing,\n savingReplay,\n allowNonAdminDebugOptions,\n loadedFrom,\n loadedFromBuild,\n allowedCommands,\n mods,\n };\n}\nexport async function parseSaveFile(filepath) {\n let centralDirectory;\n let levelInitDatName = '';\n try {\n const normalizedPath = path.normalize(filepath);\n const resolvedPath = path.resolve(normalizedPath);\n const fileExtension = path.extname(filepath);\n const filename = path.basename(filepath).replace(fileExtension, '');\n // if zip file is called _autosave1.zip, make levelInitDatName contain _autosave1\n levelInitDatName = `${filename}/level-init.dat`;\n // if file does not end with .zip, it's likely not valid\n if (fileExtension !== '.zip') {\n throw new InvalidArgumentError('File path is not a zip file.');\n }\n centralDirectory = await unzipper.Open.file(resolvedPath);\n }\n catch (e) {\n // eslint-disable-next-line no-console\n console.error(e);\n return undefined;\n }\n const file = centralDirectory.files.find(f => f.path === levelInitDatName);\n if (!file) {\n throw new InvalidSaveFileError('File does not appear to be a save file');\n }\n const levelInitDatContent = await file?.buffer();\n if (levelInitDatContent === null) {\n throw new InvalidSaveFileError('Unable to find level-init.dat in save file');\n }\n const binaryData = new DataView(levelInitDatContent.buffer);\n return parseLevelInitData(binaryData);\n}\nexport * from './errors';\nexport * from './types';\n//# sourceMappingURL=parser.js.map"],"names":["compareVersions"],"mappings":"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"}
package/dist/es/index.mjs CHANGED
@@ -91,6 +91,7 @@ function parseLevelInitData(data) {
91
91
  major,
92
92
  minor,
93
93
  patch,
94
+ asString: `${major}.${minor}.${patch}`,
94
95
  };
95
96
  }
96
97
  // Single Unused byte
@@ -178,5 +179,5 @@ async function parseSaveFile(filepath) {
178
179
  return parseLevelInitData(binaryData);
179
180
  }
180
181
 
181
- export { parseLevelInitData, parseSaveFile };
182
+ export { InvalidArgumentError, InvalidSaveFileError, parseLevelInitData, parseSaveFile };
182
183
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../out/errors.js","../out/parser.js"],"sourcesContent":["export class InvalidArgumentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidArgumentError';\n Object.setPrototypeOf(this, InvalidArgumentError.prototype);\n }\n}\nexport class InvalidSaveFileError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidSaveFileError';\n Object.setPrototypeOf(this, InvalidSaveFileError.prototype);\n }\n}\n//# sourceMappingURL=errors.js.map","import path from 'node:path';\nimport { compare as compareVersions } from 'compare-versions';\nimport unzipper from 'unzipper';\nimport { InvalidArgumentError, InvalidSaveFileError } from './errors';\n// Written with the help of the information on https://forums.factorio.com/viewtopic.php?t=47014\nexport function parseLevelInitData(data) {\n let bytesOffset = 0;\n function readUint8() {\n // console.log(`readUint8() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint8(bytesOffset);\n bytesOffset += 1;\n return value;\n }\n function readUint16() {\n // console.log(`readUint16() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint16(bytesOffset, true);\n bytesOffset += 2;\n return value;\n }\n function readUint32() {\n // console.log(`readUint32() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint32(bytesOffset, true);\n bytesOffset += 4;\n return value;\n }\n function readBoolean() {\n return readUint8() !== 0;\n }\n function readVersion() {\n const major = readUint16();\n const minor = readUint16();\n const patch = readUint16();\n const build = readUint16();\n return {\n major,\n minor,\n patch,\n build,\n // 4-number versions officially supported by compare-versions\n asString: `${major}.${minor}.${patch}.${build}`,\n };\n }\n // Read Factorio Version\n const factorioVersion = readVersion();\n function readOptimizedUint(bitSize) {\n /* console.log('readOptimizedUint()', {\n bitSize,\n }); */\n if (compareVersions(factorioVersion.asString, '0.14.14', '>')) {\n const value = readUint8();\n if (value !== 0xff) {\n return value;\n }\n }\n switch (bitSize) {\n case 16:\n return readUint16();\n case 32:\n return readUint32();\n default:\n throw new Error(`Invalid bit size: ${bitSize}`);\n }\n }\n // has_empty_indicator=false\n function readString() {\n const length = readOptimizedUint(32);\n const bufferSlice = data.buffer.slice(bytesOffset, bytesOffset + length);\n bytesOffset += length;\n return new TextDecoder().decode(bufferSlice);\n }\n function readVersion48() {\n const major = readOptimizedUint(16);\n const minor = readOptimizedUint(16);\n const patch = readOptimizedUint(16);\n return {\n major,\n minor,\n patch,\n };\n }\n // Single Unused byte\n readBoolean();\n // Read fields\n const name = readString();\n const campaign = readString();\n const baseMod = readString();\n const difficulty = readUint8();\n const finished = readBoolean();\n const playerWon = readBoolean();\n const nextLevel = readString();\n const canContinue = readBoolean();\n const finishedButContinuing = readBoolean();\n const savingReplay = readBoolean();\n const allowNonAdminDebugOptions = readBoolean();\n const loadedFrom = readVersion48();\n const loadedFromBuild = factorioVersion.major >= 2 ? readUint32() : readUint16();\n const allowedCommands = readBoolean();\n // 2.0 has a new header with 4 extra bytes here\n if (factorioVersion.major >= 2) {\n readUint8();\n readUint8();\n readUint8();\n readUint8();\n }\n const numMods = readOptimizedUint(32);\n const mods = [];\n for (let i = 0; i < numMods; i++) {\n mods.push({\n name: readString(),\n version: readVersion48(),\n crc: readUint32(),\n });\n }\n return {\n factorioVersion,\n name,\n campaign,\n baseMod,\n difficulty,\n finished,\n playerWon,\n nextLevel,\n canContinue,\n finishedButContinuing,\n savingReplay,\n allowNonAdminDebugOptions,\n loadedFrom,\n loadedFromBuild,\n allowedCommands,\n mods,\n };\n}\nexport async function parseSaveFile(filepath) {\n let centralDirectory;\n let levelInitDatName = '';\n try {\n const normalizedPath = path.normalize(filepath);\n const resolvedPath = path.resolve(normalizedPath);\n const fileExtension = path.extname(filepath);\n const filename = path.basename(filepath).replace(fileExtension, '');\n // if zip file is called _autosave1.zip, make levelInitDatName contain _autosave1\n levelInitDatName = `${filename}/level-init.dat`;\n // if file does not end with .zip, it's likely not valid\n if (fileExtension !== '.zip') {\n throw new InvalidArgumentError('File path is not a zip file.');\n }\n centralDirectory = await unzipper.Open.file(resolvedPath);\n }\n catch (e) {\n // eslint-disable-next-line no-console\n console.error(e);\n return undefined;\n }\n const file = centralDirectory.files.find(f => f.path === levelInitDatName);\n if (!file) {\n throw new InvalidSaveFileError('File does not appear to be a save file');\n }\n const levelInitDatContent = await file?.buffer();\n if (levelInitDatContent === null) {\n throw new InvalidSaveFileError('Unable to find level-init.dat in save file');\n }\n const binaryData = new DataView(levelInitDatContent.buffer);\n return parseLevelInitData(binaryData);\n}\n//# sourceMappingURL=parser.js.map"],"names":["compareVersions"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../out/errors.js","../out/parser.js"],"sourcesContent":["export class InvalidArgumentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidArgumentError';\n Object.setPrototypeOf(this, InvalidArgumentError.prototype);\n }\n}\nexport class InvalidSaveFileError extends Error {\n constructor(message) {\n super(message);\n this.name = 'InvalidSaveFileError';\n Object.setPrototypeOf(this, InvalidSaveFileError.prototype);\n }\n}\n//# sourceMappingURL=errors.js.map","import path from 'node:path';\nimport { compare as compareVersions } from 'compare-versions';\nimport unzipper from 'unzipper';\nimport { InvalidArgumentError, InvalidSaveFileError } from './errors';\n// Written with the help of the information on https://forums.factorio.com/viewtopic.php?t=47014\nexport function parseLevelInitData(data) {\n let bytesOffset = 0;\n function readUint8() {\n // console.log(`readUint8() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint8(bytesOffset);\n bytesOffset += 1;\n return value;\n }\n function readUint16() {\n // console.log(`readUint16() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint16(bytesOffset, true);\n bytesOffset += 2;\n return value;\n }\n function readUint32() {\n // console.log(`readUint32() at offset ${bytesOffset.toString(16)}`);\n const value = data.getUint32(bytesOffset, true);\n bytesOffset += 4;\n return value;\n }\n function readBoolean() {\n return readUint8() !== 0;\n }\n function readVersion() {\n const major = readUint16();\n const minor = readUint16();\n const patch = readUint16();\n const build = readUint16();\n return {\n major,\n minor,\n patch,\n build,\n // 4-number versions officially supported by compare-versions\n asString: `${major}.${minor}.${patch}.${build}`,\n };\n }\n // Read Factorio Version\n const factorioVersion = readVersion();\n function readOptimizedUint(bitSize) {\n /* console.log('readOptimizedUint()', {\n bitSize,\n }); */\n if (compareVersions(factorioVersion.asString, '0.14.14', '>')) {\n const value = readUint8();\n if (value !== 0xff) {\n return value;\n }\n }\n switch (bitSize) {\n case 16:\n return readUint16();\n case 32:\n return readUint32();\n default:\n throw new Error(`Invalid bit size: ${bitSize}`);\n }\n }\n // has_empty_indicator=false\n function readString() {\n const length = readOptimizedUint(32);\n const bufferSlice = data.buffer.slice(bytesOffset, bytesOffset + length);\n bytesOffset += length;\n return new TextDecoder().decode(bufferSlice);\n }\n function readVersion48() {\n const major = readOptimizedUint(16);\n const minor = readOptimizedUint(16);\n const patch = readOptimizedUint(16);\n return {\n major,\n minor,\n patch,\n asString: `${major}.${minor}.${patch}`,\n };\n }\n // Single Unused byte\n readBoolean();\n // Read fields\n const name = readString();\n const campaign = readString();\n const baseMod = readString();\n const difficulty = readUint8();\n const finished = readBoolean();\n const playerWon = readBoolean();\n const nextLevel = readString();\n const canContinue = readBoolean();\n const finishedButContinuing = readBoolean();\n const savingReplay = readBoolean();\n const allowNonAdminDebugOptions = readBoolean();\n const loadedFrom = readVersion48();\n const loadedFromBuild = factorioVersion.major >= 2 ? readUint32() : readUint16();\n const allowedCommands = readBoolean();\n // 2.0 has a new header with 4 extra bytes here\n if (factorioVersion.major >= 2) {\n readUint8();\n readUint8();\n readUint8();\n readUint8();\n }\n const numMods = readOptimizedUint(32);\n const mods = [];\n for (let i = 0; i < numMods; i++) {\n mods.push({\n name: readString(),\n version: readVersion48(),\n crc: readUint32(),\n });\n }\n return {\n factorioVersion,\n name,\n campaign,\n baseMod,\n difficulty,\n finished,\n playerWon,\n nextLevel,\n canContinue,\n finishedButContinuing,\n savingReplay,\n allowNonAdminDebugOptions,\n loadedFrom,\n loadedFromBuild,\n allowedCommands,\n mods,\n };\n}\nexport async function parseSaveFile(filepath) {\n let centralDirectory;\n let levelInitDatName = '';\n try {\n const normalizedPath = path.normalize(filepath);\n const resolvedPath = path.resolve(normalizedPath);\n const fileExtension = path.extname(filepath);\n const filename = path.basename(filepath).replace(fileExtension, '');\n // if zip file is called _autosave1.zip, make levelInitDatName contain _autosave1\n levelInitDatName = `${filename}/level-init.dat`;\n // if file does not end with .zip, it's likely not valid\n if (fileExtension !== '.zip') {\n throw new InvalidArgumentError('File path is not a zip file.');\n }\n centralDirectory = await unzipper.Open.file(resolvedPath);\n }\n catch (e) {\n // eslint-disable-next-line no-console\n console.error(e);\n return undefined;\n }\n const file = centralDirectory.files.find(f => f.path === levelInitDatName);\n if (!file) {\n throw new InvalidSaveFileError('File does not appear to be a save file');\n }\n const levelInitDatContent = await file?.buffer();\n if (levelInitDatContent === null) {\n throw new InvalidSaveFileError('Unable to find level-init.dat in save file');\n }\n const binaryData = new DataView(levelInitDatContent.buffer);\n return parseLevelInitData(binaryData);\n}\nexport * from './errors';\nexport * from './types';\n//# sourceMappingURL=parser.js.map"],"names":["compareVersions"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ declare interface Version48 {
7
7
  major: number;
8
8
  minor: number;
9
9
  patch: number;
10
+ asString: string;
10
11
  }
11
12
  declare interface Version {
12
13
  major: number;
@@ -34,7 +35,15 @@ declare interface FactorioSaveFile {
34
35
  mods: SaveHeaderMod[];
35
36
  }
36
37
 
38
+ declare class InvalidArgumentError extends Error {
39
+ constructor(message: string);
40
+ }
41
+ declare class InvalidSaveFileError extends Error {
42
+ constructor(message: string);
43
+ }
44
+
37
45
  declare function parseLevelInitData(data: DataView): FactorioSaveFile;
38
46
  declare function parseSaveFile(filepath: string): Promise<FactorioSaveFile | undefined>;
39
47
 
40
- export { parseLevelInitData, parseSaveFile };
48
+ export { InvalidArgumentError, InvalidSaveFileError, parseLevelInitData, parseSaveFile };
49
+ export type { FactorioSaveFile, SaveHeaderMod, Version, Version48 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "factorio-save-parser",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "license": "AGPL-3.0",
5
5
  "author": "CommanderRedYT",
6
6
  "keywords": [