This repository has been archived by the owner on Nov 11, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
vdacdefs2json.py
executable file
·156 lines (118 loc) · 5.53 KB
/
vdacdefs2json.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
156
#!/usr/bin/env python3
# Usage: ./vdacdefs2json.py [...]/Steam/steamapps/common/Underlords/game/dac/scripts/units.vdacdefs_c ./units.json
# Dependencies:
# - `pip install vdf`
# - VRF decompiler (vrf_decompiler) in path necessary as well, see https://github.com/SteamDatabase/ValveResourceFormat
from typing import Union
import sys
import subprocess
import json
import vdf
input_file_path = sys.argv[1]
output_file_path = sys.argv[2]
subprocess.call(
["vrf_decompiler", "-i", input_file_path, "-o", "/tmp/vdacdefs"])
with open("/tmp/vdacdefs") as vdacdefs_file:
# Cleanup the vrf file to a useable format for the vdf python package
vdacdefs_file_content = ""
# And whenever a "[" appears, it has to be replaced with an "{" (likewise "]" --> "}")
# --> Yet whenever and array has been detected, the following lines need to be prepended with an index
vdacdefs_file_lines = []
# As arrays can be nested, a height index and a list of nested indexes will be used
array_height = -1
array_nested_index = [0]
array_object_stack = []
for line in vdacdefs_file.readlines():
# Remove every =
line = line.replace("=", "")
# As the first and last "{", "}" will be removed
# We need to intend every line back by one tab
line = line.replace("\t", "", 1)
# Strip the line of indentation for cleaner replaces
stripped_line = line.strip()
if stripped_line.startswith("["):
array_object_stack.append("arrayOpen")
if array_height > -1:
# If it's a nested array, we need to prepend the index of the previous array and a line breaks
line = line.replace(stripped_line, str(
array_nested_index[array_height]) + "\n" + stripped_line)
line = line.replace("[", "{")
array_height += 1
# If the nested level has not been reached yet, initiate the array
if array_height > len(array_nested_index) - 1:
array_nested_index.insert(array_height, 0)
elif stripped_line.startswith("]"):
array_object_stack.pop()
array_nested_index[array_height] = 0
array_height -= 1
line = line.replace("]", "}")
elif array_height > -1 and len(stripped_line):
# Inside an array, we might have different types
# Such as string, numbers or even nested objects
# Strings and numbers are fine, as they result in a
# one to one mapping
# For objects, special handling is required
if stripped_line.startswith("{"):
array_object_stack.append("objectOpen")
line = line.replace(stripped_line, str(
array_nested_index[array_height]) + "\n" + stripped_line)
array_nested_index[array_height] += 1
elif stripped_line.startswith("}"):
array_object_stack.pop()
type = array_object_stack[len(array_object_stack) - 1]
if type is not None and type is not "objectOpen" and not stripped_line.startswith("}"):
# If we are inside an array, prepend the array index
line = line.replace(stripped_line, str(
array_nested_index[array_height]) + " " + stripped_line)
array_nested_index[array_height] += 1
# Append the cleaned line to the line array
vdacdefs_file_lines.append(line)
# Skip first and last line, as those are just opening brackets
vdacdefs_file_lines = vdacdefs_file_lines[1:]
vdacdefs_file_lines = vdacdefs_file_lines[:-1]
# Join all the lines to a string
vdacdefs_file_content = "".join(vdacdefs_file_lines)
# Write the working lines to a debug file
with open("/tmp/vdacdefs_debug", "w") as debug_file:
debug_file.write(vdacdefs_file_content)
# Parse Valve's KeyValue format
dacdefs = vdf.loads(vdacdefs_file_content)
# Convert every possible float and int value to the corresponding data type
def convert_entries_to_numerical(element: Union[str, dict, list, float, int]):
if isinstance(element, str):
try:
# Try converting to float
element = float(element)
# if success, check if integer --> if true, convert to integer
if element.is_integer():
element = int(element)
except:
pass
return element
elif isinstance(element, dict):
for key, entry in element.items():
element[key] = convert_entries_to_numerical(entry)
return element
elif isinstance(element, list):
return [convert_entries_to_numerical(entry) for entry in element]
def convert_indexed_lists_to_non_indexed(element: Union[str, dict, list, float, int]):
if isinstance(element, dict):
keys = list(element.keys())
if len(keys) > 0 and keys[0] == "0":
element = list(element.values())
else:
for key, entry in element.items():
element[key] = convert_indexed_lists_to_non_indexed(entry)
return element
if isinstance(element, list):
return [convert_indexed_lists_to_non_indexed(el) for el in element]
return element
dacdefs = convert_entries_to_numerical(dacdefs)
dacdefs = convert_indexed_lists_to_non_indexed(dacdefs)
# Dump the parsed dict to a JSON file
with open(output_file_path, "w") as json_file:
json.dump(dacdefs, json_file, indent=2)
# Cleanup
subprocess.call(["rm", "/tmp/vdacdefs"])
subprocess.call(["rm", "/tmp/vdacdefs_debug"])
print(f"Successfully converted {input_file_path} to {output_file_path}")