Skip to content

Instantly share code, notes, and snippets.

@monodop
Created June 29, 2021 17:20
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save monodop/f74ee058c173d526713317e1aa2870aa to your computer and use it in GitHub Desktop.
Convert Snowrunner UWP Saves to Steam
import os
import struct
import sys
import uuid
import zipfile
import json
# Snowrunner save "converter" (Xbox Game Pass for PC -> Steam)
# Might work for other games available with the Game Pass if the app name is changed.
# Running: Just run the script with Python 3 to create a ZIP file that contains the save files
# Thanks to @Z1ni for building the original Yakuza save converted that this was modified from: https://gist.github.com/Z1ni/7a8c1927343dd5570fff0d015f425cca
# Thanks to @snoozbuster for figuring out the container format at https://github.com/goatfungus/NMSSaveEditor/issues/306
xgp_app = "FocusHomeInteractiveSA.SnowRunnerWindows10_4hny5m903y3g0"
save_zip_path = "saves.zip"
def read_utf16_str(f):
str_len = struct.unpack("<i", f.read(4))[0]
return f.read(str_len * 2).decode("utf-16")
def read_utf16_strc(f, c):
return f.read(c).decode("utf-16")
def get_file_paths():
# Find gamepass save dir
wgs_dir = os.path.expandvars(f"%LOCALAPPDATA%\\Packages\\{xgp_app}\\SystemAppData\\wgs")
# Get the correct user directory
dirs = [d for d in os.listdir(wgs_dir) if d != "t"]
dir_count = len(dirs)
if dir_count != 1:
raise Exception(f"Expected one user directory in wgs directory, found {dir_count}")
containers_dir = os.path.join(wgs_dir, dirs[0])
containers_idx_path = os.path.join(containers_dir, "containers.index")
save_files = []
# Read the index file
with open(containers_idx_path, "rb") as f:
# Unknown
f.read(4)
file_count = struct.unpack("<i", f.read(4))[0]
print(file_count)
# Unknown
f.read(4)
store_pkg_name = read_utf16_str(f)
print(store_pkg_name)
# Unknown
f.read(12)
read_utf16_str(f)
# Unknown
f.read(8)
for _ in range(file_count):
# File name
fname = read_utf16_str(f)
# Duplicate of the file name
read_utf16_str(f)
# Unknown quoted hex number
read_utf16_str(f)
# Container number
container_num = struct.unpack("B", f.read(1))[0]
# Unknown
f.read(4)
# Read container (folder) GUID
container_guid = uuid.UUID(bytes_le=f.read(16))
# Unknown
f.read(24)
print(fname, container_num, container_guid)
# Read the container file in the file directory
with open(os.path.join(containers_dir, container_guid.hex.upper(), f"container.{container_num}"), "rb") as cf:
# Unknown
cf.read(8)
while cf.read(1):
cf.seek(-1, 1)
# Output filename (steam format)
output_filename = read_utf16_strc(cf, 128).strip('\0') + '.cfg'
# File guid (uwp format)
file_guid = uuid.UUID(bytes_le=cf.read(16))
print(" - ", output_filename, file_guid)
# Unknown
cf.read(16)
save_files.append((output_filename, os.path.join(containers_dir, container_guid.hex.upper(), file_guid.hex.upper())))
return save_files
def main():
# Get save file paths with correct names
save_files = get_file_paths()
# Store the save files into a zip
try:
with zipfile.ZipFile(save_zip_path, "x") as save_zip:
for info in save_files:
orig_fname, container_fname = info
save_zip.write(container_fname, arcname=orig_fname)
except FileExistsError:
print(f"Save file container ZIP \"{save_zip_path}\" already exists.")
sys.exit(1)
print(f"Save files stored to \"{save_zip_path}\"")
sys.exit(0)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment