Created
March 21, 2021 20:55
-
-
Save Z1ni/7a8c1927343dd5570fff0d015f425cca to your computer and use it in GitHub Desktop.
Yakuza save game "converter" for Xbox Game Pass for PC
This file contains 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 os | |
import struct | |
import sys | |
import uuid | |
import zipfile | |
# Yakuza 0 save "converter" (Xbox Game Pass for PC -> Steam) | |
# Might work for other Yakuza 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 @snoozbuster for figuring out the container format at https://github.com/goatfungus/NMSSaveEditor/issues/306 | |
xgp_app = "SEGAofAmericaInc.Yakuza0PC_s751p9cej88mt" | |
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 get_file_paths(): | |
# Find Yakuza 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] | |
# Unknown | |
f.read(4) | |
store_pkg_name = read_utf16_str(f) | |
# 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) | |
# 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(136) | |
# Read file GUID | |
file_guid = uuid.UUID(bytes_le=cf.read(16)) | |
save_files.append((fname, 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
Would be wonderful to see something like that for MH Rise