Created
November 29, 2017 20:20
-
-
Save zvodd/75adb2607856d0ffb4d593be5bbe3e32 to your computer and use it in GitHub Desktop.
Save scummer for "Getting Over It with Bennett Foddy"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import winreg | |
from pprint import pprint | |
import codecs | |
import xml.etree.ElementTree as ET | |
import os | |
import json | |
from datetime import datetime | |
import traceback | |
import sys | |
LOCATION_REG_KEY = "SOFTWARE\\Bennett Foddy\\Getting Over It" | |
IMPORTANT_KEYS = ["SaveGame0_h1867918426", | |
"SaveGame1_h1867918427", "NumSaves_h765021473"] | |
SAVE_FOLDER = "GoI_Saves" | |
SAVE_EXTENTION = "goi-scum-json" | |
MENU_TEXT = ( | |
""" Type an Option: | |
X = exit | |
Q = exit | |
S = Save Current State | |
L = Load last save-file as Current State | |
O = Open specific file and load as Current State | |
* Current State is what is written in the windows registry. | |
i.e. what the game will read. | |
* Rember to close the game before saving and loading. | |
""") | |
def print_exception(ex, msg, do_print=True): | |
tb = ''.join(traceback.format_exception( | |
etype=type(ex), value=ex, tb=ex.__traceback__)) | |
out = "Exception:\n{}\n{}".format(msg, tb) | |
if do_print: | |
print(out, file=sys.stderr) | |
else: | |
return out | |
class CustomException(Exception): | |
pass | |
class BadXMLValues(CustomException): | |
pass | |
class NoSavesFound(CustomException): | |
pass | |
class BadSaveFile(CustomException): | |
pass | |
def read_registry_values(key_location, value_names): | |
reg_con = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) | |
open_key = winreg.OpenKey(reg_con, key_location, 0, winreg.KEY_ALL_ACCESS) | |
registry_dict = dict() | |
for key_name in value_names: | |
val, rtype = winreg.QueryValueEx(open_key, key_name) | |
if rtype == 3: | |
val = codecs.encode(val, 'hex').decode("utf-8") | |
registry_dict[key_name] = (val, rtype) | |
return registry_dict | |
def write_registry_values(key_location, values_dict): | |
reg_con = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) | |
open_key = winreg.OpenKey(reg_con, key_location, 0, winreg.KEY_ALL_ACCESS) | |
for key_name, vtuple in values_dict.items(): | |
val, rtype = vtuple | |
if rtype == 3: | |
val = codecs.decode(val, 'hex') | |
winreg.SetValueEx(open_key, key_name, 0, rtype, val) | |
def read_and_check(): | |
registry_dict = read_registry_values(LOCATION_REG_KEY, IMPORTANT_KEYS) | |
did_find_saves = False | |
for key_name, val_n_type in registry_dict.items(): | |
value, rtype = val_n_type | |
if not key_name.startswith('SaveGame'): | |
continue | |
else: | |
did_find_saves = True | |
try: | |
xml_string = codecs.decode(value, 'hex').decode('utf-8')[:-1] | |
ET.fromstring(xml_string) | |
except Exception as ex: | |
# print_exception(ex, "OH NOE!") | |
raise BadXMLValues( | |
"A saved game in registry is malformed, try agian later") | |
if not did_find_saves: | |
raise NoSavesFound( | |
"Hey! You need to play the game first. There are no Saved Games") | |
return registry_dict | |
def generate_save_name(): | |
ts = datetime.utcfromtimestamp( | |
datetime.now().timestamp()).timestamp() * 1000.0 | |
tstr = [n for n in reversed(str(ts).split('.'))].pop() | |
return "{}.{}".format(tstr, SAVE_EXTENTION) | |
def check_make_dir(dir): | |
if not os.path.isdir(dir): | |
os.mkdir(dir) | |
def do_save(): | |
check_make_dir(SAVE_FOLDER) | |
filename = None | |
try: | |
save_json = json.dumps(read_and_check()) | |
filename = generate_save_name() | |
filepath = os.path.join(SAVE_FOLDER, filename) | |
with open(filepath, 'w') as fh: | |
fh.write(save_json) | |
except CustomException as ex: | |
print(ex) | |
else: | |
print('Saved game to "{}"'.format(filename)) | |
def checked_load(filepath): | |
registry_dict = None | |
with open(filepath, 'r') as fp: | |
registry_dict = json.load(fp) | |
# pprint(registry_dict) | |
if registry_dict is None: | |
raise BadSaveFile() | |
if len(list(filter(lambda k: k in IMPORTANT_KEYS, registry_dict))) is not len(IMPORTANT_KEYS): | |
raise BadSaveFile() | |
write_registry_values(LOCATION_REG_KEY, registry_dict) | |
def do_load(): | |
check_make_dir(SAVE_FOLDER) | |
files = os.listdir(SAVE_FOLDER) | |
known_saves = filter(lambda fn: fn.endswith(SAVE_EXTENTION), files) | |
last_save = sorted(known_saves).pop() | |
print('Would you like to load "{}"?'.format(last_save)) | |
selection = input("Y/N > ").upper().lstrip().strip() | |
if not selection.startswith("Y"): | |
print("Will not load save, due to your selection.") | |
return | |
filepath = os.path.join(SAVE_FOLDER, last_save) | |
try: | |
checked_load(filepath) | |
except BadSaveFile as ex: | |
print_exception(ex, "Whoa, save file is bad.") | |
def do_open_and_load(): | |
print("Enter file name (including path):") | |
filename = input("> ").lstrip().strip() | |
if not os.path.isfile(filename): | |
print("File doesnt exist!") | |
return | |
print('Are you sure you want to load "{}" into registry - making it the current save game?'.format(filename)) | |
selection = input("Y/N > ").upper().lstrip().strip() | |
if not selection.startswith("Y"): | |
print("Will not load save, due to your selection.") | |
return | |
try: | |
checked_load(filename) | |
except BadSaveFile as ex: | |
print_exception(ex, "Whoa, save file is bad.") | |
def main(): | |
stay_running = True | |
while stay_running: | |
print() | |
print() | |
print(MENU_TEXT) | |
selection = input("Enter option> ").upper().lstrip().strip() | |
if selection.startswith("X") or selection.startswith("Q"): | |
print('bye!') | |
stay_running = False | |
elif selection.startswith("S"): | |
do_save() | |
elif selection.startswith("L"): | |
do_load() | |
elif selection.startswith("O"): | |
do_open_and_load() | |
print("#################") | |
continue | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment