Skip to content

Instantly share code, notes, and snippets.

@maliubiao
Last active February 26, 2022 11:24
Show Gist options
  • Save maliubiao/d13765f9d6ac330c8ac87389d5af269a to your computer and use it in GitHub Desktop.
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.
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