Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/mario_anims_converter.py
7854 views
1
#!/usr/bin/env python3
2
import re
3
import os
4
import traceback
5
import sys
6
7
num_headers = 0
8
items = []
9
len_mapping = {}
10
order_mapping = {}
11
line_number_mapping = {}
12
13
def raise_error(filename, lineindex, msg):
14
raise SyntaxError("Error in " + filename + ":" + str(line_number_mapping[lineindex] + 1) + ": " + msg)
15
16
def parse_struct(filename, lines, lineindex, name):
17
global items, order_mapping
18
lineindex += 1
19
if lineindex + 9 >= len(lines):
20
raise_error(filename, lineindex, "struct Animation must be 11 lines")
21
v1 = int(lines[lineindex + 0].rstrip(","), 0)
22
v2 = int(lines[lineindex + 1].rstrip(","), 0)
23
v3 = int(lines[lineindex + 2].rstrip(","), 0)
24
v4 = int(lines[lineindex + 3].rstrip(","), 0)
25
v5 = int(lines[lineindex + 4].rstrip(","), 0)
26
values = lines[lineindex + 6].rstrip(",")
27
indices = lines[lineindex + 7].rstrip(",")
28
items.append(("header", name, (v1, v2, v3, v4, v5, values, indices)))
29
if lines[lineindex + 9] != "};":
30
raise_error(filename, lineindex + 9, "Expected \"};\" but got " + lines[lineindex + 9])
31
order_mapping[name] = len(items)
32
lineindex += 10
33
return lineindex
34
35
def parse_array(filename, lines, lineindex, name, is_indices):
36
global items, len_mapping, order_mapping
37
lineindex += 1
38
values = []
39
while lineindex < len(lines) and lines[lineindex] != "};":
40
line = lines[lineindex].rstrip(",")
41
if line:
42
values.extend(line.split(","))
43
lineindex += 1
44
if lineindex >= len(lines):
45
raise_error(filename, lineindex, "Expected \"};\" but reached end of file")
46
items.append(("array", name, (is_indices, values)))
47
len_mapping[name] = len(values)
48
order_mapping[name] = len(items)
49
lineindex += 1
50
return lineindex
51
52
def parse_file(filename, lines):
53
global num_headers
54
lineindex = 0
55
while lineindex < len(lines):
56
line = lines[lineindex]
57
for prefix in ["static ", "const "]:
58
if line.startswith(prefix):
59
line = line[len(prefix):]
60
lines[lineindex] = line
61
62
is_struct = line.startswith("struct Animation ") and line.endswith("[] = {")
63
is_indices = line.startswith("u16 ") and line.endswith("[] = {")
64
is_values = line.startswith("s16 ") and line.endswith("[] = {")
65
if not is_struct and not is_indices and not is_values:
66
raise_error(filename, lineindex, "\"" + line + "\" does not follow the pattern \"static const struct Animation anim_x[] = {\", \"static const u16 anim_x_indices[] = {\" or \"static const s16 anim_x_values[] = {\"")
67
68
if is_struct:
69
name = lines[lineindex][len("struct Animation "):-6]
70
lineindex = parse_struct(filename, lines, lineindex, name)
71
num_headers += 1
72
else:
73
name = lines[lineindex][len("s16 "):-6]
74
lineindex = parse_array(filename, lines, lineindex, name, is_indices)
75
76
try:
77
files = os.listdir("assets/anims")
78
files.sort()
79
80
for filename in files:
81
if filename.endswith(".inc.c"):
82
lines = []
83
with open("assets/anims/" + filename) as f:
84
for i, line in enumerate(f):
85
line = re.sub(r"/\*.*?\*/", "", line)
86
if "/*" in line:
87
line_number_mapping[-1] = i
88
raise_error(filename, -1, "Multiline comments are not supported")
89
line = line.split("//", 1)[0].strip()
90
if line:
91
line_number_mapping[len(lines)] = i
92
lines.append(line)
93
if lines:
94
parse_file(filename, lines)
95
96
structdef = ["u32 numEntries;", "const struct Animation *addrPlaceholder;", "struct OffsetSizePair entries[" + str(num_headers) + "];"]
97
structobj = [str(num_headers) + ",", "NULL,","{"]
98
99
for item in items:
100
type, name, obj = item
101
if type == "header":
102
v1, v2, v3, v4, v5, values, indices = obj
103
if order_mapping[indices] < order_mapping[name]:
104
raise SyntaxError("Error: Animation struct must be written before indices array for " + name)
105
if order_mapping[values] < order_mapping[indices]:
106
raise SyntaxError("Error: values array must be written after indices array for " + name)
107
values_num_values = len_mapping[values]
108
offset_to_struct = "offsetof(struct MarioAnimsObj, " + name + ")"
109
offset_to_end = "offsetof(struct MarioAnimsObj, " + values + ") + sizeof(gMarioAnims." + values + ")"
110
structobj.append("{" + offset_to_struct + ", " + offset_to_end + " - " + offset_to_struct + "},")
111
structobj.append("},")
112
113
for item in items:
114
type, name, obj = item
115
if type == "header":
116
v1, v2, v3, v4, v5, values, indices = obj
117
indices_len = len_mapping[indices] // 6 - 1
118
values_num_values = len_mapping[values]
119
offset_to_struct = "offsetof(struct MarioAnimsObj, " + name + ")"
120
offset_to_end = "offsetof(struct MarioAnimsObj, " + values + ") + sizeof(gMarioAnims." + values + ")"
121
structdef.append("struct Animation " + name + ";")
122
structobj.append("{" + ", ".join([
123
str(v1),
124
str(v2),
125
str(v3),
126
str(v4),
127
str(v5),
128
str(indices_len),
129
"(const s16 *)(offsetof(struct MarioAnimsObj, " + values + ") - " + offset_to_struct + ")",
130
"(const u16 *)(offsetof(struct MarioAnimsObj, " + indices + ") - " + offset_to_struct + ")",
131
offset_to_end + " - " + offset_to_struct
132
]) + "},")
133
else:
134
is_indices, arr = obj
135
type = "u16" if is_indices else "s16"
136
structdef.append("{} {}[{}];".format(type, name, len(arr)))
137
structobj.append("{" + ",".join(arr) + "},")
138
139
print("#include \"game/memory.h\"")
140
print("#include <stddef.h>")
141
print("")
142
143
print("const struct MarioAnimsObj {")
144
for s in structdef:
145
print(s)
146
print("} gMarioAnims = {")
147
for s in structobj:
148
print(s)
149
print("};")
150
151
except Exception as e:
152
note = "NOTE! The mario animation C files are not processed by a normal C compiler, but by the script in tools/mario_anims_converter.py. The format is much more strict than normal C, so please follow the syntax of existing files.\n"
153
if e is SyntaxError:
154
e.msg = note + e.msg
155
else:
156
print(note, file=sys.stderr)
157
traceback.print_exc()
158
sys.exit(1)
159
160