├── .gitignore ├── example-mod-settings.dat ├── LICENSE-0BSD.txt ├── README.md └── factorio_data.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | roundtrip-mod-settings.* 4 | example-mod-settings.json 5 | -------------------------------------------------------------------------------- /example-mod-settings.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitequark/factorio-data-codec/HEAD/example-mod-settings.dat -------------------------------------------------------------------------------- /LICENSE-0BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) Catherine 2 | 3 | Permission to use, copy, modify, and/or distribute this software for 4 | any purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 9 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 11 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 12 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Factorio data codec 2 | =================== 3 | 4 | The game [Factorio](https://www.factorio.com/) uses a custom binary file format to save certain game data, including the `mod-settings.dat` file. While these settings may be modified via the in-game UI, this isn't possible when running a dedicated server: the `mod-settings.dat` file must be pre-loaded with the correct values during world generation. 5 | 6 | This repository contains a codec for Factorio game data (implemented in Python) that can convert the `mod-settings.dat` file to JSON and back, making it possible to adjust the settings without having to create and upload a local world or hex-edit the file. 7 | 8 | 9 | Usage 10 | ----- 11 | 12 | To decode the settings, run in Command Prompt or PowerShell: 13 | 14 | ```doscon 15 | > python factorio_data.py c:\...\mods\mod-settings.dat 16 | Reading DAT file 'c:\...\mods\mod-settings.dat'... 17 | Writing JSON file 'c:\...\mods\mod-settings.json'... 18 | ``` 19 | 20 | Edit the `mod-settings.json` file in Notepad or any other text editor. 21 | 22 | To re-encode the settings, run in Command Prompt or PowerShell: 23 | 24 | ```doscon 25 | > python factorio_data.py c:\...\mods\mod-settings.json 26 | Reading JSON file 'c:\...\mods\mod-settings.json'... 27 | Writing DAT file 'c:\...\mods\mod-settings.dat'... 28 | ``` 29 | 30 | If no modifications were made, the resulting DAT file must be bit-for-bit identical to the original DAT file. (If it is not, please file an issue on this repository, and attach the problematic DAT file for investigation.) 31 | 32 | On Windows, running the `python` command may cause Windows Store to open and prompt you to install Python. Do so. Otherwise, if you don't have Python installed, [download and install it first](https://www.python.org/downloads/). 33 | 34 | 35 | Curiosity 36 | --------- 37 | 38 | The `mod-settings.dat` file format is very similar to several other Factorio file formats, including the `level-init.dat` and `script.dat` files from saved games. However, these files contain additional data that isn't handled by this codec, and so it cannot decode them to JSON. 39 | 40 | 41 | License 42 | ------- 43 | 44 | [0-clause BSD](LICENSE-0BSD.txt). -------------------------------------------------------------------------------- /factorio_data.py: -------------------------------------------------------------------------------- 1 | import typing 2 | import enum 3 | import struct 4 | import json 5 | 6 | 7 | class ImmutableString: 8 | value: typing.Union[None, bytes] 9 | 10 | def __init__(self, value: typing.Union[None, bytes]): 11 | self.value = value 12 | 13 | @classmethod 14 | def load(cls, stream: typing.BinaryIO): 15 | is_none, = struct.unpack("= 0xff: 32 | stream.write(struct.pack("