-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathprocess_game_dump.py
193 lines (168 loc) · 9.22 KB
/
process_game_dump.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import sys, shutil, os, json, glob, re
from pathlib import Path
modifiers = None
with open('dump_modifiers.json', 'r') as f:
modifiers = json.load(f)
MODULES_TO_INCLUDE = modifiers["STATICS"]["MODULES_TO_INCLUDE"]
BANNED_MODULES = modifiers["STATICS"]["BANNED_MODULES"]
FORCE_BAN_NAMES_WHEN_IMPORTED = modifiers["STATICS"]["FORCE_BAN_NAMES_WHEN_IMPORTED"]
FORCE_BAN_NAMES = modifiers["STATICS"]["FORCE_BAN_NAMES"]
DELETE_ALL_FILES_CONTAINING_IN_TITLE = modifiers["STATICS"]["DELETE_ALL_FILES_CONTAINING_IN_TITLE"]
REMOVE_BP_ACCESS = modifiers["STATICS"]["REMOVE_BP_ACCESS"]
FORCE_BAN_NAMES = FORCE_BAN_NAMES + DELETE_ALL_FILES_CONTAINING_IN_TITLE
LIST_UHT_TOKENS = ["UFUNCTION", "UPROPERTY", "UENUM", "UDELEGATE", "UCLASS"]
CROSS_MODULE_REF_REGEX = re.compile(r'-ModuleName=(\S+)\s+-ObjectName=(\S+)', )
def update_modifier_to_delete_invalid_includes(file_path, modifier):
with open(file_path, 'r') as file_handle:
while line := file_handle.readline():
if not line or len(line) < 2 or line.isspace() or line[0] == "#": continue
if not line.startswith("//CROSS-MODULE"): break
names = re.search(CROSS_MODULE_REF_REGEX, line).group(1, 2)
if not names[0] or not names[1]: continue
if names[0] in BANNED_MODULES or names[1] in FORCE_BAN_NAMES_WHEN_IMPORTED:
modifier[names[1]] = {"DELETE": True}
def process_modifiers(file_path, file_data, modifier):
if not modifier:
return
file_name = Path(file_path).stem
# iterate through every line searching for places to make changes
i = 0
while i < len(file_data): # we avoid using a for loop so that we compute len every time
line_data = file_data[i]
for criterion in modifier:
if criterion in line_data:
this_modifier_data = modifier[criterion]
if isinstance(this_modifier_data, str):
# count num spaces
num_spaces = 0
for n in range(15):
if n >= len(line_data):
break
elif line_data[n] == " ":
num_spaces += 1
else:
break
file_data.insert(i, (" " * num_spaces) + "// " + this_modifier_data + "\n")
i += 1
elif isinstance(this_modifier_data, dict):
# special cases
if "UMETA" in this_modifier_data:
dat2 = line_data.strip('\n').split(",")
if dat2[-1] != "": dat2.append("") # just in case we don't have a trailing comma in the line
file_data[i] = ",".join(dat2[:-1]) + " UMETA(" + this_modifier_data["UMETA"] + "),\n"
if "DELETE" in this_modifier_data:
if criterion == file_name:
try:
os.remove(file_path)
finally:
return
delete_start_relative_to_this = 0
delete_end_relative_to_this = 0
if this_modifier_data["DELETE"] is True:
delete_start_relative_to_this = 0
delete_end_relative_to_this = 0
if file_path.endswith(".cpp"):
delete_start_relative_to_this = 0
delete_end_relative_to_this = 0
ln = file_data[i]
if ln[0] != " " and ln.strip()[-1] == "{":
try:
while ln[0] != "}" and ln.strip() != "//}":
delete_end_relative_to_this += 1
ln = file_data[i + delete_end_relative_to_this]
except:
print("".join(file_data))
print(i)
else:
for token in LIST_UHT_TOKENS:
if token in file_data[i - 1]:
delete_start_relative_to_this = -1
break
else:
delete_start_relative_to_this = this_modifier_data["DELETE"][0] # inclusive
delete_end_relative_to_this = this_modifier_data["DELETE"][1] # inclusive
for n in range(delete_start_relative_to_this, delete_end_relative_to_this + 1):
if len(file_data[i + n]) >= 2 and file_data[i + n][:2] == "//": continue
file_data[i + n] = "//" + file_data[i + n]
if "SUBSTITUTE" in this_modifier_data:
file_data[i] = file_data[i].replace(criterion, this_modifier_data["SUBSTITUTE"])
if "SUBSTITUTE_LINE" in this_modifier_data:
file_data[i] = this_modifier_data["SUBSTITUTE_LINE"]
if "REMOVE_BP_ACCESS" in this_modifier_data:
file_data[i - 1] = file_data[i - 1].replace("BlueprintReadWrite, ", "").replace("BlueprintReadWrite)", ")").replace("BlueprintCallable, ", "").replace("BlueprintCallable)", ")")
else:
raise Exception("Unexpected type for comment data: " + str(type(this_modifier_data)))
i += 1
with open(file_path, 'w') as file_handle:
file_handle.write(''.join(file_data)) # new lines already in the strings
def process(dump_path, log = False):
global FORCE_BAN_NAMES
if log: print("Extracting source directories from dump")
for folder in MODULES_TO_INCLUDE:
try:
shutil.rmtree(os.path.join(".", "Source", folder))
except:
pass
shutil.copytree(os.path.join(dump_path, "Source", folder), os.path.join(".", "Source", folder))
# also, ban classes specified within banned modules
for folder in BANNED_MODULES:
try:
classes_within_module = [Path(pth).stem for pth in glob.glob(os.path.join(dump_path, "Source", folder, "**", "*.h"), recursive=True)]
#print(classes_within_module)
FORCE_BAN_NAMES += classes_within_module
except:
pass
if log: print("Deleting excluded files")
pathlist = glob.glob(os.path.join(".", "Source", "**", "*.h"), recursive=True) + glob.glob(os.path.join(".", "Source", "**", "*.cpp"), recursive=True)
for file_path in pathlist:
file_name = Path(file_path).stem
for criterion in DELETE_ALL_FILES_CONTAINING_IN_TITLE:
if criterion in file_name:
try:
os.remove(file_path)
except:
pass
if log: print("Processing modifiers")
pathlist = glob.glob(os.path.join(".", "Source", "**", "*.h"), recursive=True) + glob.glob(os.path.join(".", "Source", "**", "*.cpp"), recursive=True)
global_modifier = modifiers["GLOBAL_MODIFIER"] if ("GLOBAL_MODIFIER" in modifiers) else {}
for name in FORCE_BAN_NAMES:
global_modifier[name] = {"DELETE": True}
for name in REMOVE_BP_ACCESS:
global_modifier[name] = {"REMOVE_BP_ACCESS": True}
for file_path in pathlist:
file_name = Path(file_path).stem
this_modifier = None
if file_name in modifiers:
# merge with global modifier
# specific modifier takes priority
this_modifier = global_modifier | modifiers[file_name]
else:
this_modifier = global_modifier
update_modifier_to_delete_invalid_includes(file_path, this_modifier)
if this_modifier:
file_data = []
with open(file_path, 'r') as file_handle:
file_data = file_handle.readlines()
process_modifiers(file_path, file_data, this_modifier)
# modify *.Build.cs files to remove dependencies on banned modules
if log: print("Correcting *.Build.cs files")
pathlist2 = glob.glob(os.path.join(".", "Source", "**", "*.build.cs"), recursive=True)
for file_path in pathlist2:
with open(file_path, 'r') as file_handle:
file_data = file_handle.readlines()
i = 0
while i < len(file_data): # we avoid using a for loop so that we compute len every time
for banned_module in BANNED_MODULES:
if banned_module in file_data[i]:
del file_data[i]
i -= 1
break
i += 1
with open(file_path, 'w') as file_handle:
file_handle.write(''.join(file_data)) # \n characters are already in the strings
if log: print("All done!")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python " + sys.argv[0] + " <path to UE4SS UHTHeaderDump folder>")
else:
process(' '.join(sys.argv[1:]), True)