From 274cc0c495be804f6c8a028c7c2f68d221438c95 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Sat, 10 Jun 2023 13:21:39 +0200 Subject: [PATCH] Ported the save extractor --- SaveExtractor/README.md | 36 +++++++++++ SaveExtractor/saveextractor.py | 114 +++++++++++++++++++++++++++++++++ metadata.xml | 15 +++-- 3 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 SaveExtractor/README.md create mode 100644 SaveExtractor/saveextractor.py diff --git a/SaveExtractor/README.md b/SaveExtractor/README.md new file mode 100644 index 0000000..7d24390 --- /dev/null +++ b/SaveExtractor/README.md @@ -0,0 +1,36 @@ +# SaveExtractor.py + +A save file extractor for The Binding Of Isaac: Repentance. + +Ported from [wofsauge/IsaacSavegameReader](https://github.com/wofsauge/IsaacSavegameReader/blob/main/Program.cs#L228), in order to be more easily ran on Linux. + +It generates files that can be used in External Item Descriptions, and Dead God Helper. Theses files contain information about which items were already picked up. + +## Usage + +```sh +python ./saveextractor.py path/to/savefile.dat saveid +``` + +It will generate the following files in the current directory: +- `eid_savegame.lua` to be put in EID's mod folder, inside the `scripts` subfolder +- `saveX.dat` to be put in DeadGod Helper's data folder + +## Example + +Since I'm on Linux (and using the Steam version of TBOI), folders are a little bit different than on Windows, more info for save files location [here](https://github.com/Zamiell/isaac-faq/blob/main/directories-and-save-files.md) + +### Extracting information from save file 2 + +The number `196602716` corresponds to my Steam Account ID, it will be different on your side. + +```sh +python ./saveextractor.py ~/.steam/steam/userdata/196602716/250900/remote/rep_persistentgamedata2.dat 2 +``` + +### Moving generated files to the corresponding location + +```sh +mv save2.dat ~/.local/share/Steam/steamapps/common/The\ Binding\ of\ Isaac\ Rebirth/data/deadgod\ helper/ +mv eid_savegames.lua ~/.local/share/Steam/steamapps/common/The\ Binding\ of\ Isaac\ Rebirth/mods/external\ item\ descriptions_836319872/scripts/ +``` diff --git a/SaveExtractor/saveextractor.py b/SaveExtractor/saveextractor.py new file mode 100644 index 0000000..3898183 --- /dev/null +++ b/SaveExtractor/saveextractor.py @@ -0,0 +1,114 @@ +#!/bin/env python + +# Ported from https://github.com/wofsauge/IsaacSavegameReader/blob/main/Program.cs#L228 + +import sys + +REP_ITEMSTART = 0x00000AB6 + 4 +REP_NBITEMS = 732 + +EID_FILE = "eid_savegames.lua" + +DGH_FILE1 = "saveX.dat" + +def loadFromFile(filename, itemstart, nbitems): + table = [] + try: + file = open(filename, "rb") + except FileNotFoundError: + print(f"File not found: {filename}", file=sys.stderr) + for i in range(nbitems): + table.append(0) + return table + data = file.read(1) + i = 0 + while data: + if (i > itemstart) and i - itemstart <= nbitems: + table.append(int.from_bytes(data, byteorder='big')) + # Should be 1 (for already seen), or 0 + data = file.read(1) + i += 1 + file.close() + + return table + + +def eidfile(t1, saveid): + content = "-- Auto generated from a python script\n" + content += "if EID then\n" + content += "\tEID.SaveGame = {}\n" + content += "\tEID.SaveGame.Platform = \"Steam\"\n" + content += "\tEID.SaveGame.UserID = \"0\"\n" + content += "\tEID.SaveGame.UserName = \"username\"\n" + + for savefileindex in range(3): + + content += f"\tEID.SaveGame[{savefileindex+1}] = {{\n" + content += "\t\tItemCollection = {\n\t\t\t" + for i, v in enumerate(table): + if savefileindex + 1 == saveid: + content += f"[{i+1}]={'true' if v==1 else 'false'}, " + else: + content += f"[{i+1}]=false, " + content += "\n\t\t},\n" + content += "\t\tItemNeedsPickup = {\n\t\t\t" + for i, v in enumerate(table): + if savefileindex + 1 == saveid: + content += f"[{i+1}]={'true' if v==0 else 'false'}, " + else: + content += f"[{i+1}]=true, " + content += "\n\t\t}\n\t}\n" + + content += "end" + + return content + + +def dghfile(table): + content = '{' + content += '"settings":{"visual":true,"eid":true,"showonmodded":true,"showonblind":false},' + content += '"seen":[' + for i, v in enumerate(table): + if table[i] == 1: + content += "true" + else: + content += "false" + if i < len(table) - 1: + content += ", " + content += "]," + content += ' "seenmodded":{}}' + return content + + +def isHelp(args): + return ("--help" in args) or ("-h" in args) + + +if __name__ == "__main__": + args = sys.argv + + if len(args) < 3 or isHelp(args): + print(f"USAGE: python {args[0]} path/to/persistengamedata.dat slotnumber") + print("This will generate the following files:") + print("\t- eid_savegames.lua , used by External Items Description (to be placed in EID's script folder)") + print("\t- saveX.dat , used by Dead God Helper (to be placed in Dead God Helper data folder)") + else: + saveid = 1 + error = False + try: + saveid = int(args[2]) + if not (saveid <= 3 and saveid >= 1): + error = True + except ValueError: + error = True + + if error: + print(f"Invalid save ID: '{args[2]}'") + print("Please retry with a number between 1 and 3!") + exit(1) + + table = loadFromFile(args[1], REP_ITEMSTART, REP_NBITEMS) + with open(EID_FILE, "w") as f: + f.write(eidfile(table, saveid)) + with open(DGH_FILE1.replace("X", str(saveid)), "w") as f: + f.write(dghfile(table)) diff --git a/metadata.xml b/metadata.xml index 1b2c12a..5fab676 100644 --- a/metadata.xml +++ b/metadata.xml @@ -7,20 +7,23 @@ Easily spot items that were not previously picked up on this savefile. Very useful when you're trying to get a second or third Dead God. +Now supports modded items! + I made it mainly for myself, because it was annoying to go back to the menu every time I encountered an item I was unsure about. There's an integration with External Item Description. It is also compatible with Mod Config Menu, if you want to toggle things (such as displaying info when on Curse Of The Blind) + +Since mods can't directly access the savefile, this mod takes it upon itself to track wich items are picked up. This means that the first time you install this mod, everything will be marked as unseen. -[strike]There's however a limitation: since (to my knowledge) there's no way to get information about seen items from the savefile, this mod takes it upon itself to track which item you picked up. (This means that when you install the mod for the first time, every item will be tagged as unseen)[/strike] - -EDIT: Turns out EID already has a support for this, using a save extractor, if you want to go down that route, here's the link to the extractor : https://github.com/wofsauge/External-Item-Descriptions/tree/master/scripts -This mod is still somewhat relevant if you do not want to use external tools ^^ +Turns out EID already has a some support for this, using a save extractor. If you want to go down that route, here's the link to the extractor : https://github.com/wofsauge/External-Item-Descriptions/tree/master/scripts +However, EID's save extractor doesn't really work on Linux, so I made a port of it in python, in order to be more easily ran. This port is available here : https://forge.chapril.org/ayte/DeadGodHelper/src/branch/main/SaveExtractor +This mod is still relevant in itself if you do not want to use external tools, or you want to track modded items ^^ Anyway, I hope that this mod will be useful to someone :) -(Source code is available at https://forge.chapril.org/ayte/DeadGodHelper) - 1.9 +Source code is available at https://forge.chapril.org/ayte/DeadGodHelper + 1.10 Public