2021-03-29 02:05:56 +02:00
|
|
|
from pathlib import Path
|
|
|
|
import argparse
|
|
|
|
from struct import unpack_from
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
CONSTANTS = {}
|
2021-03-29 02:05:56 +02:00
|
|
|
def get_constants():
|
2021-04-03 19:21:49 +02:00
|
|
|
global CONSTANTS
|
2021-08-22 23:50:10 +02:00
|
|
|
valid_enums = { "StoryProgress", "ItemIDs", "PlayerAnims",
|
2021-04-03 19:21:49 +02:00
|
|
|
"ActorIDs", "Events", "SoundIDs", "SongIDs", "Locations",
|
|
|
|
"AmbientSounds", "NpcIDs", "Emotes" }
|
2021-03-29 02:05:56 +02:00
|
|
|
for enum in valid_enums:
|
2021-04-03 19:21:49 +02:00
|
|
|
CONSTANTS[enum] = {}
|
|
|
|
CONSTANTS["NPC_SPRITE"] = {}
|
2021-03-29 02:05:56 +02:00
|
|
|
|
|
|
|
include_path = Path(Path(__file__).resolve().parent.parent / "include")
|
2021-04-03 19:21:49 +02:00
|
|
|
enums = Path(include_path / "enums.h").read_text().splitlines()
|
2021-03-29 02:05:56 +02:00
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
# defines
|
|
|
|
'''
|
2021-03-29 02:05:56 +02:00
|
|
|
for line in enums.splitlines():
|
|
|
|
this_enum = ""
|
|
|
|
for enum in valid_enums:
|
|
|
|
if f"#define {enum}_" in line:
|
|
|
|
this_enum = enum
|
|
|
|
break;
|
|
|
|
|
|
|
|
if this_enum:
|
|
|
|
name = line.split(" ",2)[1]
|
|
|
|
id_ = line.split("0x", 1)[1]
|
|
|
|
if " " in id_:
|
|
|
|
id_ = id_.split(" ",1)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
CONSTANTS[this_enum][int(id_, 16)] = name
|
2021-03-29 02:05:56 +02:00
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
'''
|
|
|
|
|
|
|
|
# enums
|
|
|
|
for i,line in enumerate(enums):
|
|
|
|
if line.startswith("enum "):
|
|
|
|
enum_name = line.split(" ",1)[1].split(" {",1)[0]
|
|
|
|
if enum_name in valid_enums:
|
|
|
|
CONSTANTS[enum_name] = {}
|
|
|
|
last_num = 0
|
|
|
|
i += 1
|
|
|
|
while "}" not in enums[i]:
|
|
|
|
if not enums[i]:
|
|
|
|
i += 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
name = enums[i].strip()
|
|
|
|
val = last_num+1
|
|
|
|
if "=" in name:
|
|
|
|
name, val = name.split(" = ")
|
|
|
|
val = int(val[:-1], 0)
|
|
|
|
if val >= 0x80000000:
|
|
|
|
val -= 0x100000000
|
|
|
|
else:
|
|
|
|
name = name[:-1]
|
|
|
|
name = name.strip()
|
|
|
|
#print("\"" + name + "\"", "===", val)
|
|
|
|
|
|
|
|
CONSTANTS[enum_name][val] = name.strip()
|
|
|
|
i += 1
|
|
|
|
last_num = val
|
|
|
|
|
|
|
|
# sprites
|
2021-03-29 02:05:56 +02:00
|
|
|
sprite_path = Path(Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc")
|
|
|
|
for file in sprite_path.iterdir():
|
|
|
|
fd = file.read_text()
|
|
|
|
for line in fd.splitlines():
|
|
|
|
if "#define _NPC_SPRITE_" in line:
|
|
|
|
enum = "NPC_SPRITE"
|
|
|
|
elif "#define _NPC_PALETTE_" in line:
|
|
|
|
enum = "NPC_PALETTE"
|
|
|
|
elif "#define _NPC_ANIM_" in line:
|
|
|
|
enum = "NPC_ANIM"
|
|
|
|
else:
|
|
|
|
continue
|
|
|
|
|
|
|
|
name = line.split(" ",2)[1]
|
|
|
|
id_ = line.split("0x", 1)[1]
|
|
|
|
if " " in id_:
|
|
|
|
id_ = id_.split(" ",1)[0]
|
|
|
|
name = name.split(f"_{enum}_", 1)[1]
|
|
|
|
if enum == "NPC_SPRITE":
|
|
|
|
saved_name = name
|
|
|
|
saved_id = id_
|
|
|
|
else:
|
|
|
|
name = name.rsplit(f"{saved_name}_")[1]
|
|
|
|
|
|
|
|
if enum == "NPC_SPRITE":
|
2021-04-03 19:21:49 +02:00
|
|
|
if int(id_, 16) not in CONSTANTS["NPC_SPRITE"]:
|
|
|
|
CONSTANTS[enum][int(id_, 16)] = {"name":"", "palettes":{}, "anims":{}}
|
|
|
|
CONSTANTS[enum][int(id_, 16)]["name"] = name
|
2021-03-29 02:05:56 +02:00
|
|
|
elif enum == "NPC_PALETTE":
|
2021-04-03 19:21:49 +02:00
|
|
|
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["palettes"][int(id_, 16)] = name
|
2021-03-29 02:05:56 +02:00
|
|
|
elif enum == "NPC_ANIM":
|
2021-04-03 19:21:49 +02:00
|
|
|
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["anims"][int(id_, 16)] = name
|
2021-03-29 02:05:56 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
STRUCTS = {}
|
|
|
|
|
|
|
|
def parse_var(line):
|
|
|
|
#print(f"Parsing {line}")
|
|
|
|
if "*/ " in line:
|
|
|
|
line = line.split("*/ ",1)[1]
|
|
|
|
line = line.split(";",1)[0].strip()
|
|
|
|
|
|
|
|
if "," in line or "(*" in line:
|
|
|
|
return (None, None, None, None)
|
|
|
|
elif "union " in line:
|
|
|
|
return ("union", None, None, None)
|
|
|
|
|
|
|
|
#print(f"Parsed {line}")
|
|
|
|
if " " in line:
|
|
|
|
if line.startswith("struct "):
|
|
|
|
struct, type_, name = line.split(" ")
|
|
|
|
else:
|
|
|
|
type_, name = line.split(" ")
|
|
|
|
else:
|
|
|
|
type_ = "function"
|
|
|
|
name = line.split("(", 1)[0]
|
|
|
|
count = 1
|
|
|
|
if "[" in name:
|
|
|
|
name, *count = name.split("[")
|
|
|
|
counts = 1
|
|
|
|
count.reverse()
|
|
|
|
for dim in count:
|
|
|
|
counts *= int(dim[:-1], 0)
|
|
|
|
count = counts
|
|
|
|
|
|
|
|
is_ptr = "*" in type_ or type_ == "UNK_PTR"
|
|
|
|
return (type_, name, count, is_ptr)
|
|
|
|
|
|
|
|
def parse_file(filename):
|
|
|
|
fd = filename.read_text().splitlines()
|
|
|
|
i = 0
|
|
|
|
while i < len(fd):
|
|
|
|
#supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS]
|
|
|
|
#if any(supported):
|
|
|
|
if "typedef struct " in fd[i]:
|
|
|
|
#supported_name = [SUPPORTED_STRUCTS[i] for i,x in enumerate(supported) if x][0]
|
|
|
|
supported_name = fd[i].split("typedef struct ", 1)[1].split(" {", 1)[0]
|
|
|
|
if supported_name == "{":
|
|
|
|
supported_name = ""
|
|
|
|
#print(f"Parsing struct \"{supported_name}\"")
|
|
|
|
|
|
|
|
struct_to_add = []
|
|
|
|
i += 1
|
|
|
|
while ("} " + f"{supported_name.upper()}") not in fd[i].split(";",1)[0].upper():
|
|
|
|
type_, name, count, ptr = parse_var(fd[i])
|
|
|
|
|
|
|
|
if type_ == None:
|
|
|
|
i += 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
union = []
|
|
|
|
if type_ == "union":
|
|
|
|
i += 1
|
|
|
|
while "}" not in fd[i]:
|
|
|
|
type_, name, count, ptr = parse_var(fd[i])
|
|
|
|
union.append({"type":type_, "name": name, "num":count, "ptr":ptr})
|
|
|
|
i += 1
|
|
|
|
name = fd[i].split("}", 1)[1].split(";", 1)[0]
|
|
|
|
|
|
|
|
#print(supported_name, type_, name, count)
|
|
|
|
struct_to_add.append({"type":type_, "name": name, "num":count, "ptr":ptr, "union":union})
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
#print(f"Broke on line {fd[i]}")
|
|
|
|
#print()
|
|
|
|
if supported_name == "":
|
|
|
|
supported_name = fd[i].split("} ",1)[1].split(";",1)[0]
|
|
|
|
if "[" in supported_name:
|
|
|
|
supported_name = supported_name[:-2]
|
|
|
|
STRUCTS[supported_name] = struct_to_add
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
def get_structs():
|
|
|
|
parse_file(Path(Path(__file__).parent.parent / "include" / "map.h"))
|
|
|
|
parse_file(Path(Path(__file__).parent.parent / "include" / "common_structs.h"))
|
|
|
|
|
|
|
|
def get_vals(fd, offset, var):
|
2021-04-03 19:21:49 +02:00
|
|
|
global STRUCTS
|
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
out = []
|
|
|
|
arr = []
|
|
|
|
for i in range(var["num"]):
|
|
|
|
if var["type"] in STRUCTS:
|
|
|
|
type_ = "struct"
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "s"
|
2021-03-29 02:05:56 +02:00
|
|
|
data = []
|
|
|
|
for var2 in STRUCTS[var["type"]]:
|
|
|
|
out3, offset = get_vals(fd, offset, var2)
|
|
|
|
data.extend(out3)
|
|
|
|
#if var["num"] == 1:
|
|
|
|
# out.extend(out2)
|
|
|
|
#else:
|
|
|
|
#out.append(out2)
|
|
|
|
else:
|
|
|
|
type_ = "int"
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
if var["type"] == "s8" or var["type"] == "char":
|
|
|
|
if var["type"] == "char":
|
|
|
|
type_ = "hex"
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "X"
|
2021-03-29 02:05:56 +02:00
|
|
|
data = unpack_from('>b', fd, offset)[0]
|
|
|
|
offset += 1
|
|
|
|
elif var["type"] == "u8":
|
|
|
|
data = unpack_from('>B', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 1
|
2021-04-03 19:21:49 +02:00
|
|
|
elif var["type"] == "s16" or var["type"] in ("ItemID"):
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += offset % 2
|
|
|
|
data = unpack_from('>h', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 2
|
|
|
|
elif var["type"] == "u16":
|
|
|
|
offset += offset % 2
|
|
|
|
data = unpack_from('>H', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 2
|
2021-08-24 18:42:29 +02:00
|
|
|
elif var["type"] == "s32" or var["type"] in ("NpcID", "s32", "MessageID", "s32"):
|
2021-03-29 02:05:56 +02:00
|
|
|
poff = offset
|
|
|
|
offset += offset % 4
|
|
|
|
data = unpack_from('>i', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 4
|
|
|
|
elif var["type"] == "u32":
|
|
|
|
offset += offset % 4
|
|
|
|
data = unpack_from('>I', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 4
|
|
|
|
elif var["type"] == "f32":
|
|
|
|
offset += offset % 4
|
|
|
|
data = unpack_from('>f', fd, offset)[0]
|
|
|
|
type_ = "float"
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = ".01f"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 4
|
|
|
|
elif var["type"] == "X32":
|
|
|
|
offset += offset % 4
|
|
|
|
data = unpack_from('>f', fd, offset)[0]
|
2021-04-03 19:21:49 +02:00
|
|
|
type_ = "Xfloat"
|
|
|
|
fmt = ".01f"
|
2021-03-29 02:05:56 +02:00
|
|
|
if data < -1000.0 or data > 1000.0:
|
2021-04-03 19:21:49 +02:00
|
|
|
type_ = "Xint"
|
|
|
|
fmt = "d"
|
2021-03-29 02:05:56 +02:00
|
|
|
data = unpack_from('>i', fd, offset)[0]
|
|
|
|
offset += 4
|
|
|
|
elif var["ptr"]:
|
|
|
|
offset += offset % 4
|
|
|
|
data = unpack_from('>I', fd, offset)[0]
|
|
|
|
type_ = "ptr"
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = "08X"
|
2021-03-29 02:05:56 +02:00
|
|
|
offset += 4
|
|
|
|
else:
|
|
|
|
print(f"Unknown data type \"{var['type']}\"")
|
|
|
|
exit()
|
|
|
|
if var["num"] == 1:
|
2021-04-03 19:21:49 +02:00
|
|
|
out.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data})
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
2021-04-03 19:21:49 +02:00
|
|
|
arr.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data})
|
2021-03-29 02:05:56 +02:00
|
|
|
|
|
|
|
if var["num"] > 1:
|
|
|
|
out.append(arr)
|
|
|
|
return out, offset
|
|
|
|
|
|
|
|
def INDENT(depth):
|
|
|
|
return f" " * depth
|
|
|
|
|
|
|
|
def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
|
|
|
out = []
|
|
|
|
for val in vals:
|
|
|
|
line = ""
|
|
|
|
if needs_name:
|
|
|
|
line = INDENT(indent)
|
|
|
|
#print(val)
|
|
|
|
# array
|
|
|
|
if type(val) is list:
|
|
|
|
line += f".{val[0]['name']} = " + "{ "
|
|
|
|
struct = type(val[0]["data"]) is list
|
|
|
|
line += ", ".join(print_data(val, indent + 1, needs_name=False, is_array=True, is_struct=struct))
|
|
|
|
if struct:
|
|
|
|
line += "\n" + INDENT(indent) + "},"
|
|
|
|
else:
|
|
|
|
line += " },"
|
|
|
|
else:
|
|
|
|
if needs_name:
|
|
|
|
line += f".{val['name']} = "
|
|
|
|
if type(val["data"]) is list:
|
|
|
|
if is_struct:
|
|
|
|
line += "\n"
|
|
|
|
line += INDENT(indent)
|
|
|
|
line += "{ "
|
|
|
|
for x,val2 in enumerate(val["data"]):
|
|
|
|
if x > 0:
|
|
|
|
line += ", "
|
|
|
|
#line += f".{val2['name']} = "
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = val2["fmt"]
|
2021-03-29 02:05:56 +02:00
|
|
|
if val2["type"] == "float":
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"{val2['data']:{fmt}}f"
|
|
|
|
elif val["type"] == "Xfloat":
|
|
|
|
line += "{ .f = " + f"{val['data']:{fmt}}f" + " }"
|
|
|
|
elif val["type"] == "Xint":
|
|
|
|
line += "{ .s = " + f"{val['data']:{fmt}}" + " }"
|
2021-03-29 02:05:56 +02:00
|
|
|
elif val2["type"] == "hex":
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"0x{val2['data']:{fmt}}"
|
2021-03-29 02:05:56 +02:00
|
|
|
elif val2["type"] == "ptr":
|
|
|
|
if val2["data"] == 0:
|
|
|
|
line += f"NULL"
|
|
|
|
else:
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"0x{val2['data']:{fmt}}"
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
|
|
|
line += f"{val2['data']}"
|
|
|
|
line += " }"
|
|
|
|
if not is_array:
|
|
|
|
line += ","
|
|
|
|
else:
|
|
|
|
|
|
|
|
if "flags" in val["name"].lower() or "animations" in val["name"].lower():
|
|
|
|
if val["name"] == "flags":
|
2021-04-03 19:21:49 +02:00
|
|
|
val["fmt"] = "08X"
|
2021-03-29 02:05:56 +02:00
|
|
|
val["type"] = "hex"
|
2021-04-03 19:21:49 +02:00
|
|
|
elif "flag" in val["name"].lower():
|
|
|
|
val["type"] = "hex"
|
|
|
|
val["fmt"] = "X"
|
2021-03-29 02:05:56 +02:00
|
|
|
val["data"] = abs(val["data"])
|
2021-04-03 19:21:49 +02:00
|
|
|
elif "tattle" in val["name"]:
|
|
|
|
val["fmt"] = "06X"
|
|
|
|
val["type"] = "hex"
|
2021-08-22 23:50:10 +02:00
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
fmt = val["fmt"]
|
2021-03-29 02:05:56 +02:00
|
|
|
if val["type"] == "float":
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"{val['data']:{fmt}}f"
|
|
|
|
elif val["type"] == "Xfloat":
|
|
|
|
line += "{ .f = " + f"{val['data']:{fmt}}f" + " }"
|
|
|
|
elif val["type"] == "Xint":
|
|
|
|
line += "{ .s = " + f"{val['data']:{fmt}}" + " }"
|
2021-03-29 02:05:56 +02:00
|
|
|
elif val["type"] == "hex":
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"0x{val['data']:{fmt}}"
|
2021-03-29 02:05:56 +02:00
|
|
|
elif val["type"] == "ptr":
|
|
|
|
if val["data"] == 0:
|
|
|
|
line += f"NULL"
|
|
|
|
else:
|
2021-04-03 19:21:49 +02:00
|
|
|
line += f"0x{val['data']:{fmt}}"
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
|
|
|
line += f"{val['data']}"
|
|
|
|
|
|
|
|
if not (is_array or is_struct):
|
|
|
|
line += ","
|
|
|
|
|
|
|
|
out.append(line)
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
def output_type2(fd, count, offset, var):
|
|
|
|
ultra_out = []
|
|
|
|
for i in range(count):
|
|
|
|
out = [f"{args.type} = " + "{"]
|
|
|
|
vals = []
|
|
|
|
for var in STRUCTS[args.type]:
|
|
|
|
tmp, offset = get_vals(fd, offset, var)
|
|
|
|
vals.extend(tmp)
|
|
|
|
|
|
|
|
out.extend(print_data(vals, 1, True))
|
|
|
|
out.append("};")
|
|
|
|
ultra_out.append(out)
|
|
|
|
return offset, ultra_out #"\n".join(out)
|
2021-03-29 02:05:56 +02:00
|
|
|
|
|
|
|
def check_list(vals, depth = 0):
|
|
|
|
for x,val in enumerate(vals):
|
|
|
|
if type(val) == list:
|
|
|
|
if check_list(val, depth + 1):
|
|
|
|
return True
|
|
|
|
elif val != 0:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def recurse_check_list(vals):
|
|
|
|
res = 0
|
|
|
|
for x,val in enumerate(vals):
|
|
|
|
if type(val) == list:
|
|
|
|
if check_list(val, 1):
|
|
|
|
return len(vals) - x
|
|
|
|
elif val != 0:
|
2021-08-22 23:50:10 +02:00
|
|
|
return len(vals) - x
|
2021-03-29 02:05:56 +02:00
|
|
|
return -1
|
|
|
|
|
|
|
|
def get_single_struct_vals(fd, i):
|
|
|
|
vals = []
|
|
|
|
if not fd[i].rstrip().endswith("},"):
|
|
|
|
# must be a sub-struct over multiple lines
|
|
|
|
old_i = i
|
|
|
|
i += 1
|
|
|
|
while not ("}," in fd[i] and "." in fd[i+1]):
|
|
|
|
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
|
|
|
a = []
|
|
|
|
for x in temp:
|
|
|
|
x = x.strip()
|
|
|
|
if x != "":
|
2021-04-03 19:21:49 +02:00
|
|
|
if "." in x:
|
|
|
|
a.append(float(x[:-1]))
|
|
|
|
else:
|
|
|
|
a.append(int(x, 0))
|
2021-03-29 02:05:56 +02:00
|
|
|
vals.append(a)
|
|
|
|
i += 1
|
|
|
|
else:
|
|
|
|
# single line
|
|
|
|
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
|
|
|
a = []
|
|
|
|
for x in temp:
|
|
|
|
x = x.strip()
|
|
|
|
if x != "":
|
2021-04-03 19:21:49 +02:00
|
|
|
if "." in x:
|
|
|
|
a.append(float(x[:-1]))
|
|
|
|
else:
|
|
|
|
a.append(int(x, 0))
|
2021-03-29 02:05:56 +02:00
|
|
|
vals.extend(a)
|
|
|
|
return vals, i
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
def cull_struct(fd, i, entirely=False):
|
2021-03-29 02:05:56 +02:00
|
|
|
out = []
|
|
|
|
vals = []
|
|
|
|
#print(f"Culling Starting at {fd[i]}")
|
|
|
|
if not fd[i].rstrip().endswith("},"):
|
|
|
|
# must be a sub-struct over multiple lines
|
|
|
|
old_i = i
|
|
|
|
vals, i = get_single_struct_vals(fd, i)
|
|
|
|
|
|
|
|
# reverse and cull entries of only zeros
|
|
|
|
x = recurse_check_list(vals[::-1])
|
|
|
|
#print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
|
|
|
if x < 0:
|
|
|
|
#print(f"Ending at {fd[i]}")
|
|
|
|
return None, i
|
|
|
|
|
|
|
|
out.append(fd[old_i])
|
|
|
|
old_i += 1
|
2021-04-03 19:21:49 +02:00
|
|
|
if entirely:
|
|
|
|
x = len(vals)
|
2021-03-29 02:05:56 +02:00
|
|
|
for y in range(x):
|
|
|
|
out.append(fd[old_i])
|
|
|
|
old_i += 1
|
|
|
|
|
|
|
|
#print(f"Ending at {fd[i]}")
|
|
|
|
else:
|
|
|
|
prefix = fd[i].split("{",1)[0] + "{ "
|
|
|
|
suffix = " },"
|
|
|
|
|
|
|
|
vals, i = get_single_struct_vals(fd, i)
|
|
|
|
|
|
|
|
# reverse and cull entries of only zeros
|
|
|
|
x = recurse_check_list(vals[::-1])
|
|
|
|
#print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
|
|
|
if x < 0:
|
|
|
|
#print(f"Ending at {fd[i]}")
|
|
|
|
return None, i
|
|
|
|
|
|
|
|
#out.append(prefix)
|
2021-04-03 19:21:49 +02:00
|
|
|
if entirely:
|
|
|
|
x = len(vals)
|
2021-03-29 02:05:56 +02:00
|
|
|
temp = ""
|
|
|
|
for z,y in enumerate(range(x)):
|
|
|
|
if z > 0:
|
|
|
|
prefix += ", "
|
|
|
|
prefix += f"{vals[y]}"
|
|
|
|
out.append(prefix + suffix)
|
|
|
|
#print(f"Ending at {fd[i]}")
|
|
|
|
return "\n".join(out), i
|
|
|
|
|
|
|
|
def MacroReplaceStaticNPC(fd):
|
2021-04-03 19:21:49 +02:00
|
|
|
structs = { "unk_1C":True, "movement":False, "unk_1E0":True }
|
|
|
|
#replace_cull_struct = { "unk_1C", "movement", "unk_1E0" }
|
|
|
|
#replace_cull = { "tattle", "extraAnimations", "itemDropChance" }
|
2021-03-29 02:05:56 +02:00
|
|
|
fd = fd.splitlines()
|
|
|
|
out = []
|
|
|
|
i = 0
|
|
|
|
while i < len(fd):
|
2021-04-03 19:21:49 +02:00
|
|
|
found = ""
|
|
|
|
for x in structs:
|
|
|
|
if f".{x}" in fd[i]:
|
|
|
|
found = x
|
|
|
|
break;
|
|
|
|
if found:
|
|
|
|
# just cull it if possible
|
|
|
|
vals, i = cull_struct(fd, i, structs[found])
|
2021-03-29 02:05:56 +02:00
|
|
|
if vals:
|
|
|
|
out.append(vals)
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
continue
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
name = fd[i].split(" = ",1)[0].strip()[1:]
|
|
|
|
if name not in structs and "{" not in fd[i] and name != ";":
|
|
|
|
val = fd[i].split(" = ",1)[1][:-1]
|
|
|
|
if "0x" in val:
|
|
|
|
val = int(val, 16)
|
|
|
|
elif "NULL" in val:
|
|
|
|
val = 0
|
|
|
|
elif "." in val:
|
|
|
|
val = float(val)
|
|
|
|
else:
|
|
|
|
val = int(val, 10)
|
|
|
|
if val == 0 and name != "id":
|
|
|
|
i += 1
|
|
|
|
continue
|
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
if ".itemDrops" in fd[i]:
|
|
|
|
vals, x = cull_struct(fd, i)
|
|
|
|
|
|
|
|
indent = len(fd[i].split(".",1)[0]) // 4
|
|
|
|
new_line = fd[i].split("{",1)[0] + "{\n"
|
2021-08-22 23:50:10 +02:00
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
if not vals:
|
|
|
|
i = x
|
2021-08-22 23:50:10 +02:00
|
|
|
i += 1
|
2021-03-29 02:05:56 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
vals += "\n},\n."
|
|
|
|
vals, _ = get_single_struct_vals(vals.splitlines(), 0)
|
|
|
|
|
|
|
|
added_item = False
|
|
|
|
for item in vals:
|
|
|
|
if item[0] == 0:
|
|
|
|
continue
|
|
|
|
|
|
|
|
added_item = True
|
2021-04-03 19:21:49 +02:00
|
|
|
item_name = CONSTANTS["ItemIDs"][item[0]]
|
2021-03-29 02:05:56 +02:00
|
|
|
new_line += " " * (indent+1) + "{ " + item_name + f", {item[1]}, {item[2]}" + " },\n"
|
2021-08-22 23:50:10 +02:00
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
if added_item:
|
|
|
|
new_line += " " * indent + "},"
|
|
|
|
|
|
|
|
out.append(new_line)
|
|
|
|
i = x
|
|
|
|
|
|
|
|
elif ".animations" in fd[i]:
|
|
|
|
indent = len(fd[i].split(".",1)[0]) // 4
|
|
|
|
new_line = fd[i].split("{",1)[0] + "{\n"
|
|
|
|
|
|
|
|
vals, x = get_single_struct_vals(fd, i)
|
|
|
|
for val in vals:
|
|
|
|
sprite_id = (val & 0x00FF0000) >> 16
|
|
|
|
palette_id = (val & 0x0000FF00) >> 8
|
|
|
|
anim_id = (val & 0x000000FF) >> 0
|
2021-04-03 19:21:49 +02:00
|
|
|
sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
|
|
|
palette = CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
|
|
|
anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
2021-08-24 18:42:29 +02:00
|
|
|
new_line += " " * (indent+1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
2021-03-29 02:05:56 +02:00
|
|
|
new_line += " " * indent + "},"
|
|
|
|
out.append(new_line)
|
|
|
|
i = x
|
2021-08-22 23:50:10 +02:00
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
elif ".heartDrops" in fd[i] or ".flowerDrops" in fd[i]:
|
|
|
|
vals, x = get_single_struct_vals(fd, i)
|
|
|
|
|
|
|
|
new_line = fd[i].split("{",1)[0]
|
|
|
|
|
|
|
|
attempts = vals[0][2]
|
|
|
|
|
|
|
|
if ".heartDrops" in fd[i]:
|
|
|
|
if round(vals[0][1] / 327.67, 2) == 70 and round(vals[0][3] / 327.67, 2) == 50:
|
|
|
|
new_line += f"STANDARD_HEART_DROPS({attempts}),"
|
|
|
|
elif round(vals[0][1] / 327.67, 2) == 80 and round(vals[0][3] / 327.67, 2) == 50:
|
|
|
|
new_line += f"GENEROUS_HEART_DROPS({attempts}),"
|
|
|
|
elif round(vals[0][1] / 327.67, 2) == 80 and round(vals[0][3] / 327.67, 2) == 60:
|
|
|
|
new_line += f"GENEROUS_WHEN_LOW_HEART_DROPS({attempts}),"
|
2021-04-03 19:21:49 +02:00
|
|
|
elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0:
|
|
|
|
new_line += f"NO_DROPS,"
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
|
|
|
print(f"Unknown heart drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}")
|
|
|
|
exit()
|
|
|
|
else:
|
|
|
|
if round(vals[0][1] / 327.67, 2) == 50 and round(vals[0][3] / 327.67, 2) == 40:
|
|
|
|
new_line += f"STANDARD_FLOWER_DROPS({attempts}),"
|
|
|
|
elif round(vals[0][1] / 327.67, 2) == 70 and round(vals[0][3] / 327.67, 2) == 50:
|
|
|
|
new_line += f"GENEROUS_WHEN_LOW_FLOWER_DROPS({attempts}),"
|
|
|
|
elif round(vals[0][1] / 327.67, 2) == 40 and round(vals[0][3] / 327.67, 2) == 40:
|
|
|
|
new_line += f"REDUCED_FLOWER_DROPS({attempts}),"
|
2021-04-03 19:21:49 +02:00
|
|
|
elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0:
|
|
|
|
new_line += f"NO_DROPS,"
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
2021-04-03 19:21:49 +02:00
|
|
|
print(f"Unknown flower drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}")
|
2021-03-29 02:05:56 +02:00
|
|
|
exit()
|
|
|
|
|
|
|
|
out.append(new_line)
|
|
|
|
i = x
|
|
|
|
|
|
|
|
else:
|
|
|
|
out.append(fd[i])
|
|
|
|
i += 1
|
|
|
|
return "\n".join(out)
|
|
|
|
|
|
|
|
def MacroReplaceNpcSettings(fd):
|
|
|
|
replace_cull = { "unk_00", "unk_24" }
|
|
|
|
fd = fd.splitlines()
|
|
|
|
out = []
|
|
|
|
i = 0
|
|
|
|
while i < len(fd):
|
|
|
|
if any(f".{x}" in fd[i] for x in replace_cull):
|
|
|
|
vals, i = cull_struct(fd, i)
|
|
|
|
if vals:
|
|
|
|
out.append(vals)
|
|
|
|
else:
|
|
|
|
out.append(fd[i])
|
|
|
|
i += 1
|
|
|
|
return "\n".join(out)
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
def MacroReplaceNpcGroupList(fd):
|
|
|
|
fd = fd.splitlines()
|
|
|
|
out = []
|
|
|
|
vals = []
|
|
|
|
i = 1
|
|
|
|
while i < len(fd):
|
|
|
|
fd[i] = fd[i].strip()
|
|
|
|
if ";" not in fd[i]:
|
|
|
|
if "NULL" in fd[i]:
|
|
|
|
val = 0
|
|
|
|
else:
|
|
|
|
if "0x" in fd[i]:
|
|
|
|
val = int(fd[i].split(" = ",1)[1][:-1], 16)
|
|
|
|
else:
|
|
|
|
val = int(fd[i].split(" = ",1)[1][:-1], 10)
|
|
|
|
|
|
|
|
vals.append(val)
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
out.append(f" NPC_GROUP(N(D_{vals[1]:X}), BATTLE_ID({(vals[2] & 0xFF000000) >> 24}, {(vals[2] & 0xFF0000) >> 16}, {(vals[2] & 0xFF00) >> 8}, {vals[2] & 0xFF})),")
|
|
|
|
return "\n".join(out)
|
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("file", type=str, help="File to decompile struct from")
|
|
|
|
parser.add_argument("type", type=str, help="Struct type to decompile")
|
2021-04-03 19:21:49 +02:00
|
|
|
parser.add_argument("offset", type=lambda x: int(x, 0), help="Offset to decompile struct from")
|
|
|
|
parser.add_argument("--count", "-c", "--c", type=int, default=0, help="Num to try and decompile (NpcGroupList)")
|
2021-03-29 02:05:56 +02:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
get_constants()
|
|
|
|
get_structs()
|
|
|
|
|
|
|
|
if args.type not in STRUCTS:
|
|
|
|
print(f"Unknown struct type {args.type}")
|
|
|
|
exit()
|
|
|
|
|
|
|
|
'''
|
|
|
|
out = [f"{args.type} = " + "{\n"]
|
|
|
|
offset = args.offset
|
|
|
|
for var in STRUCTS[args.type]:
|
|
|
|
line, offset = output_type(fd, offset, var, 1)
|
|
|
|
out.append(line)
|
|
|
|
out.append("};")
|
|
|
|
'''
|
|
|
|
|
2021-04-03 19:21:49 +02:00
|
|
|
if args.count == 0:
|
|
|
|
args.count = 1
|
|
|
|
|
2021-03-29 02:05:56 +02:00
|
|
|
fd = Path(args.file).resolve().read_bytes()
|
2021-04-03 19:21:49 +02:00
|
|
|
offset, out = output_type2(fd, args.count, args.offset, STRUCTS[args.type])
|
|
|
|
|
|
|
|
for i,entry in enumerate(out):
|
|
|
|
out[i] = "\n".join(entry)
|
|
|
|
|
2021-08-22 23:50:10 +02:00
|
|
|
print(f"EvtSource range 0x{args.offset:08X} - 0x{offset:08X}")
|
2021-04-03 19:21:49 +02:00
|
|
|
print()
|
2021-03-29 02:05:56 +02:00
|
|
|
|
|
|
|
if args.type.lower() == "staticnpc":
|
2021-04-03 19:21:49 +02:00
|
|
|
print(MacroReplaceStaticNPC(out[0]))
|
2021-03-29 02:05:56 +02:00
|
|
|
elif args.type.lower() == "npcaisettings":
|
2021-04-03 19:21:49 +02:00
|
|
|
print(out[0])
|
2021-03-29 02:05:56 +02:00
|
|
|
elif args.type.lower() == "npcsettings":
|
2021-04-03 19:21:49 +02:00
|
|
|
print(MacroReplaceNpcSettings(out[0]))
|
|
|
|
elif args.type.lower() == "npcgrouplist":
|
|
|
|
print("NpcGroupList N(npcGroupList) = {")
|
|
|
|
for i in range(args.count):
|
|
|
|
print(MacroReplaceNpcGroupList(out[i]))
|
|
|
|
print("};")
|
2021-03-29 02:05:56 +02:00
|
|
|
else:
|
|
|
|
print(f"Add me type: {args.type}")
|