Last active
February 26, 2022 11:24
-
-
Save maliubiao/d13765f9d6ac330c8ac87389d5af269a to your computer and use it in GitHub Desktop.
backup your game save before you start to fight with a boss in elden ring and die countless times.
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
r""" | |
elden ring disk game save backup, require python3 + pynput, pip install pynput | |
as we all know, this game is difficult, you are going to die many times before a boss was defeated. | |
shift+r recover from last save | |
shift+s create a backup | |
esc quit program | |
"python elden_ring_saver.py last" will pick last manual save , overwrite current game save | |
change "last" to specified "manual-...." has same affect. | |
this program will backup game save if any content releated to character was changed, eg, weapon | |
if not character data is changed, eg, you walk around in game scence, character data will not flush to disk, then manual save will not work | |
by default, max disk backup is 10, beacuse each elden ring disk game save is quit large | |
if you want to recover specified game save, quit to game main screen, then | |
go to c:\Users\{user}\AppData\Roaming\EldenRing, delete backups newer than which you want | |
then press shift+r, load game | |
""" | |
import os | |
import re | |
import shutil | |
import time | |
import threading | |
from pynput import keyboard | |
from PIL import ImageGrab | |
import sys | |
max_save = 10 | |
file_watch_interval = 60 | |
user = os.getenv('username') | |
default = r'C:\Users\%s\AppData\Roaming\EldenRing' % user | |
config_dir = "" | |
for fn in os.listdir(default): | |
if re.match("^\d+$", fn): | |
config_dir = fn | |
break | |
if not config_dir: | |
print("no config found") | |
os.exit(1) | |
def get_save_time(): | |
si = os.stat(os.path.join(default, config_dir, "ER0000.sl2")) | |
return si.st_mtime | |
def get_current_time(): | |
return time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime(time.time())) | |
def get_all_backup(prefix="backup"): | |
print(" ".join(os.listdir(default))) | |
fns = re.findall("(%s-[\d_]+?) " % prefix, " ".join(os.listdir(default))) | |
if not fns: | |
print("previous save not exists") | |
return [] | |
fns.sort(key=lambda x: time.mktime(time.strptime(x.split("-")[1], "%Y_%m_%d_%H_%M_%S"))) | |
return fns | |
def save_screen(save_path): | |
snapshot = ImageGrab.grab() | |
snapshot.save(save_path) | |
def backup(manual=False): | |
fns = get_all_backup() | |
if fns and len(fns) > max_save: | |
for i in fns[:len(fns)-max_save]: | |
print("max save is %d, so, delete old backup" % max_save, i) | |
shutil.rmtree(os.path.join(default, i)) | |
if manual: | |
fn = "manual-%s" % get_current_time() | |
else: | |
fn = "backup-%s" % get_current_time() | |
save_screen(os.path.join(default, fn+".jpg")) | |
shutil.copytree(os.path.join(default, config_dir), os.path.join(default, fn)) | |
print("create backup", fn) | |
def find_last_save(): | |
fns = get_all_backup() | |
if not fns: | |
return [] | |
return fns[-1] | |
def recover(fn=""): | |
if not fn: | |
fn = find_last_save() | |
if not fn: | |
return | |
if fn == "last": | |
fns = get_all_backup(prefix="manual") | |
if not fns: | |
print("last manual save not found") | |
return | |
fn = fns[-1] | |
print("overwrite old save, use", fn) | |
shutil.rmtree(os.path.join(default, config_dir)) | |
shutil.copytree(os.path.join(default, fn), os.path.join(default, config_dir)) | |
def file_watch(): | |
print("config path", default, config_dir) | |
start = get_save_time() | |
while True: | |
now = get_save_time() | |
if now != start: | |
backup() | |
start = now | |
time.sleep(file_watch_interval) | |
if len(sys.argv) > 1: | |
recover(fn = sys.argv[1]) | |
t = threading.Thread(target=file_watch) | |
t.daemon=True | |
t.start() | |
cmb = [{keyboard.Key.shift, keyboard.KeyCode(char='r')}, | |
{keyboard.Key.shift, keyboard.KeyCode(char='R')}, | |
{keyboard.Key.shift, keyboard.KeyCode(char='s')}, | |
{keyboard.Key.shift, keyboard.KeyCode(char='S')}] | |
current = set() | |
def execute(key): | |
print("Detected hotkey") | |
if not getattr(key, "char", None): | |
return | |
if key.char.lower() == 's': | |
print("manual backup start") | |
backup(manual=True) | |
print("manual backup end") | |
elif key.char.lower() == 'r': | |
recover() | |
def on_press(key): | |
if key == keyboard.Key.esc: | |
print("quit") | |
exit(0) | |
if any([key in z for z in cmb]): | |
current.add(key) | |
if any(all(k in current for k in z) for z in cmb): | |
execute(key) | |
def on_release(key): | |
if any([key in z for z in cmb]): | |
try: | |
current.remove(key) | |
except KeyError: | |
current.clear() | |
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: | |
listener.join() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment