-
Notifications
You must be signed in to change notification settings - Fork 78
/
Copy pathSaveConverter.py
155 lines (136 loc) · 5.24 KB
/
SaveConverter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python3
import argparse
import json
import os
from palworld_save_tools.gvas import GvasFile
from palworld_save_tools.json_tools import CustomEncoder
from palworld_save_tools.palsav import compress_gvas_to_sav, decompress_sav_to_gvas
from palworld_save_tools.paltypes import PALWORLD_CUSTOM_PROPERTIES, PALWORLD_TYPE_HINTS
def main():
parser = argparse.ArgumentParser(
prog="palworld-save-tools",
description="Converts Palworld save files to and from JSON",
)
parser.add_argument("filename")
parser.add_argument(
"--to-json",
action="store_true",
help="Override heuristics and convert SAV file to JSON",
)
parser.add_argument(
"--from-json",
action="store_true",
help="Override heuristics and convert JSON file to SAV",
)
parser.add_argument(
"--output",
"-o",
help="Output file (default: <filename>.json or <filename>.sav)",
)
parser.add_argument(
"--force",
"-f",
action="store_true",
help="Force overwriting output file if it already exists without prompting",
)
parser.add_argument("--minify-json", action="store_true", help="Minify JSON output")
args = parser.parse_args()
if args.to_json and args.from_json:
print("Cannot specify both --to-json and --from-json")
exit(1)
if not os.path.exists(args.filename):
print(f"{args.filename} does not exist")
exit(1)
if not os.path.isfile(args.filename):
print(f"{args.filename} is not a file")
exit(1)
if args.to_json or args.filename.endswith(".sav"):
if not args.output:
output_path = args.filename + ".json"
else:
output_path = args.output
convert_sav_to_json(args.filename, output_path, args.force, args.minify_json)
if args.from_json or args.filename.endswith(".json"):
if not args.output:
output_path = args.filename.replace(".json", "")
else:
output_path = args.output
convert_json_to_sav(args.filename, output_path, args.force)
def convert_sav_to_obj(filename):
print(f"Decompressing sav file")
with open(filename, "rb") as f:
data = f.read()
raw_gvas, _ = decompress_sav_to_gvas(data)
print(f"Loading GVAS file")
gvas_file = GvasFile.read(raw_gvas, PALWORLD_TYPE_HINTS, PALWORLD_CUSTOM_PROPERTIES)
return gvas_file.dump()
def convert_obj_to_sav(obj, output_path, force=False):
if os.path.exists(output_path):
print(f"{output_path} already exists, this will overwrite the file")
if not force:
if not confirm_prompt("Are you sure you want to continue?"):
exit(1)
gvas_file = GvasFile.load(obj)
print(f"Compressing SAV file")
if (
"Pal.PalWorldSaveGame" in gvas_file.header.save_game_class_name
or "Pal.PalLocalWorldSaveGame" in gvas_file.header.save_game_class_name
):
save_type = 0x32
else:
save_type = 0x31
sav_file = compress_gvas_to_sav(
gvas_file.write(PALWORLD_CUSTOM_PROPERTIES), save_type
)
print(f"Writing SAV file to {output_path}")
with open(output_path, "wb") as f:
f.write(sav_file)
def convert_sav_to_json(filename, output_path, force=False, minify=False):
print(f"Converting {filename} to JSON, saving to {output_path}")
if os.path.exists(output_path):
print(f"{output_path} already exists, this will overwrite the file")
if not force:
if not confirm_prompt("Are you sure you want to continue?"):
exit(1)
print(f"Decompressing sav file")
with open(filename, "rb") as f:
data = f.read()
raw_gvas, _ = decompress_sav_to_gvas(data)
print(f"Loading GVAS file")
gvas_file = GvasFile.read(raw_gvas, PALWORLD_TYPE_HINTS, PALWORLD_CUSTOM_PROPERTIES)
print(f"Writing JSON to {output_path}")
with open(output_path, "w", encoding="utf8") as f:
indent = None if minify else "\t"
json.dump(gvas_file.dump(), f, indent=indent, cls=CustomEncoder)
def convert_json_to_sav(filename, output_path, force=False):
print(f"Converting {filename} to SAV, saving to {output_path}")
if os.path.exists(output_path):
print(f"{output_path} already exists, this will overwrite the file")
if not force:
if not confirm_prompt("Are you sure you want to continue?"):
exit(1)
print(f"Loading JSON from {filename}")
with open(filename, "r", encoding="utf8") as f:
data = json.load(f)
gvas_file = GvasFile.load(data)
print(f"Compressing SAV file")
if (
"Pal.PalWorldSaveGame" in gvas_file.header.save_game_class_name
or "Pal.PalLocalWorldSaveGame" in gvas_file.header.save_game_class_name
):
save_type = 0x32
else:
save_type = 0x31
sav_file = compress_gvas_to_sav(
gvas_file.write(PALWORLD_CUSTOM_PROPERTIES), save_type
)
print(f"Writing SAV file to {output_path}")
with open(output_path, "wb") as f:
f.write(sav_file)
def confirm_prompt(question: str) -> bool:
reply = None
while reply not in ("y", "n"):
reply = input(f"{question} (y/n): ").casefold()
return reply == "y"
if __name__ == "__main__":
main()