Skip to content

Commit

Permalink
Full C table functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Lilaa3 committed Apr 19, 2024
1 parent dac1166 commit d68f843
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 148 deletions.
112 changes: 88 additions & 24 deletions fast64_internal/sm64/animation/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class SM64_AnimPair:
# For compressing
offset: int = 0

def clean_frames(self):
def clean_frames(self): # TODO: How the fuck do you write this
if len(self.values) <= 1:
return

Expand Down Expand Up @@ -145,12 +145,16 @@ class SM64_AnimHeader:
loop_end: int = 1
bone_count: int = 0

indice_reference: str | int = None
values_reference: str | int = None
indice_reference: Optional[str | int] = None
values_reference: Optional[str | int] = None

enum_reference: str = ""
file_name: str = ""

data: Optional[SM64_AnimData] = None

# Imports
header_variant: int = 0
file_name: str = ""

def get_c_flags(self, is_dma_structure: bool, refresh_version: str):
if is_dma_structure:
Expand Down Expand Up @@ -441,26 +445,28 @@ def read_binary(self, rom_data: bytes, address: int):
@dataclasses.dataclass
class SM64_AnimTable:
reference: str | int = None
enum_list_reference: str = ""
file_name: str = ""
values_reference: str = ""
elements: list[SM64_AnimHeader, SM64_AnimData] = dataclasses.field(default_factory=list)
elements: list[SM64_AnimHeader] = dataclasses.field(default_factory=list)

def get_sets(self):
def get_sets(self) -> tuple[list[SM64_AnimHeader], list[SM64_AnimData]]:
# Remove duplicates of data and headers, keep order by using a list
data_set = []
headers_set = []
for anim_header, anim_data in self.elements:
if anim_data and not anim_data in data_set:
data_set.append(anim_data)
for anim_header in self.elements:
if anim_header.data and not anim_header.data in data_set:
data_set.append(anim_header.data)
if not anim_header in headers_set:
headers_set.append(anim_header)
return data_set, headers_set
return headers_set, data_set

def to_binary(self, is_dma: bool, start_address: int):
# TODO: Handle dma exports
data: bytearray = bytearray()
ptrs: list[int] = []

data_set, headers_set = self.get_sets()
headers_set, data_set = self.get_sets()

# Calculate offsets and generate tables
table_offset = start_address
Expand All @@ -474,16 +480,16 @@ def to_binary(self, is_dma: bool, start_address: int):
values_table_offset = indice_tables_length

# Add the animation table
for anim_header, anim_data in self.elements:
for anim_header in self.elements:
ptrs.append(len(data))
header_offset = headers_offset + (headers_set.index(anim_header) * HEADER_SIZE)
data.extend(header_offset.to_bytes(4, byteorder="big", signed=False))

# Add all the headers
for anim_header, anim_data in self.elements:
if anim_data:
for anim_header in self.elements:
if anim_header.data:
indice_offset = sum(
[len(indice_table.data) for indice_table in indice_tables[: data_set.index(anim_data)]]
[len(indice_table.data) for indice_table in indice_tables[: data_set.index(anim_header.data)]]
)
data.extend(
anim_header.to_binary(
Expand All @@ -500,27 +506,85 @@ def to_binary(self, is_dma: bool, start_address: int):
data.extend(indice_table.to_binary())
data.extend(value_table.to_binary())

def data_to_c(self, is_dma_structure: bool, refresh_version: str):
return data

def data_and_headers_to_c(self, is_dma_structure: bool, refresh_version: str) -> list[os.PathLike, str]:
files_data: dict[str, str] = {}

headers_set = self.get_sets()[0]
for anim_header in headers_set:
data = StringIO()
same_reference_headers = [anim_header]
for other_header in headers_set:
if (
not anim_header is other_header
and anim_header.indice_reference == other_header.indice_reference
and anim_header.values_reference == other_header.values_reference
):
same_reference_headers.append(other_header)
headers_set.remove(other_header)

for same_reference_header in same_reference_headers:
if same_reference_header.data:
value_table, indice_tables = create_tables([same_reference_header.data])
data.write(value_table.to_c())
data.write("\n")
for indice_table in indice_tables:
data.write(indice_table.to_c())
data.write("\n")
break

for same_reference_header in same_reference_headers:
data.write(same_reference_header.to_c(is_dma_structure, refresh_version))
data.write("\n")

files_data[anim_header.file_name] = data.getvalue()

return files_data

def combined_data_and_headers_to_c(self, is_dma_structure: bool, refresh_version: str):
data = StringIO()

data_set, headers_set = self.get_sets()
headers_set, data_set = self.get_sets()
if data_set:
value_table, indice_tables = create_tables(data_set, self.values_reference)
data.write(value_table.to_c())
data.write("\n")
for indice_table in indice_tables:
data.write(indice_table.to_c())
data.write("\n\n")
data.write(value_table.to_c())
data.write("\n\n")
data.write("\n")

# Add all the headers
for anim_header, anim_data in self.elements:
for anim_header in headers_set:
data.write(anim_header.to_c(is_dma_structure, refresh_version))
data.write("\n")

return data.getvalue()

def to_c(self, is_dma_structure: bool, refresh_version: str):
pass
def enum_list_to_c(self):
data = StringIO()

data.write(f"enum {self.enum_list_reference} {{\n")
for anim_header in self.elements:
data.write(f"\t{anim_header.enum_reference},\n")
data.write("};\n")

return data.getvalue()

def table_to_c(self, generate_enums: bool):
data = StringIO()

if generate_enums:
data.write(f'#include "table_enum.h"\n')

data.write(f"const struct Animation *const {self.reference}[] = {{\n")
for anim_header in self.elements:
if generate_enums:
data.write(f"\t[{anim_header.enum_reference}] = &{anim_header.reference},\n")
else:
data.write(f"\t&{anim_header.reference},\n")
data.write("\tNULL,\n};\n")

return data.getvalue()


def create_tables(anims_data: list[SM64_AnimData], values_name: str = None):
Expand Down
53 changes: 1 addition & 52 deletions fast64_internal/sm64/animation/exporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def update_enum_file(

enum_list_index = text.find(enum_list_name)
if enum_list_index == -1: # If there is no enum list, add one and find again
text = f"enum {enum_list_name} {{\n}};\n\n" + text
text = f"enum {enum_list_name} {{\n}};\n" + text
enum_list_index = text.find(enum_list_name)

for enum_name in enum_names:
Expand Down Expand Up @@ -192,54 +192,3 @@ def update_data_file(data_file_path, anim_file_names: list):

for anim_file_name in anim_file_names:
writeIfNotFound(data_file_path, f'#include "{anim_file_name}"\n', "")


def exportAnimTableInsertableBinary(
armature_obj: Object,
scene: Scene,
table_headers: list["SM64_AnimHeaderProps"],
sm64Anim: SM64_Anim,
) -> str:
sm64Props = scene.fast64.sm64
anim_export_props = sm64Props.anim_export
table_props = anim_export_props.table

data: bytearray = bytearray()
ptrs: list[int] = []
tableDict: dict = {}

# Allocate table data.
for header in table_headers:
tableOffset = len(data)
ptrs.append(tableOffset)
data.extend(bytearray([0x00] * 4))

offsets = tableDict.get(header, [])
offsets.append(tableOffset)
tableDict[header] = offsets

data.extend(NULL.to_bytes(4, byteorder="big", signed=False)) # NULL represents the end of a table

for action in table_props.get_actions(): # Iterates through all actions in table
offset = len(data)

actionHeaders = [] # Get all headers needed for table export in order
for tableHeader, offsets in tableDict.items():
if action != tableHeader.action:
continue
for tableOffset in offsets:
headerOffset = offset + (len(actionHeaders) * 4)
data[tableOffset : tableOffset + 4] = headerOffset.to_bytes(4, byteorder="big", signed=False)
actionHeaders.append(header)

sm64Anim = get_animation_data(armature_obj, scene, action, actionHeaders)
animResult = sm64Anim.toBinary(anim_export_props.mergeValues, False, offset)

data.extend(animResult[0])
ptrs.extend(animResult[1])

directory = abspath(anim_export_props.binary.insertableDirectory)
animTableFileName = table_props.getAnimTableFileName(sm64Props)
writeInsertableFile(os.path.join(directory, animTableFileName), 2, ptrs, 0, data)

return "Success!"
67 changes: 38 additions & 29 deletions fast64_internal/sm64/animation/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,21 @@ def execute_operator(self, context: Context):
anim_export_props = sm64_props.anim_export
table_props = anim_export_props.table

actions = table_props.get_actions()
actions = table_props.actions
armature_obj: Object = context.selected_objects[0]

for action in actions:
stashActionInArmature(armature_obj, action)

actor_name = anim_export_props.actor_name

table: SM64_AnimTable = table_props.to_table_class(
armature_obj,
sm64_props.blender_to_sm64_scale,
sm64_props.export_type == "C" or not anim_export_props.is_binary_dma,
actor_name,
)

if sm64_props.export_type == "C":
header_type = anim_export_props.header_type

Expand All @@ -280,43 +288,44 @@ def execute_operator(self, context: Context):
if header_type != "Custom":
applyBasicTweaks(abspath(sm64_props.decomp_path))

for action in actions:
action_props = action.fast64.sm64
animation = action_props.to_animation_class(
action,
armature_obj,
sm64_props.blender_to_sm64_scale,
sm64_props.export_type == "C" or not anim_export_props.is_binary_dma,
actor_name,
if header_type == "DMA" or table_props.export_seperately:
files_data = table.data_and_headers_to_c(
anim_export_props.is_c_dma_structure,
sm64_props.refresh_version,
)
anim_file_name = action_props.get_anim_file_name(action)
anim_path = os.path.join(anim_dir_path, anim_file_name)
with open(anim_path, "w", newline="\n") as file:
for file_name, file_data in files_data.items():
with open(os.path.join(anim_dir_path, file_name), "w", newline="\n") as file:
file.write(file_data)
update_data_file(os.path.join(anim_dir_path, "data.inc.c"), [file_name])
else:
with open(os.path.join(anim_dir_path, "data.inc.c"), "w", newline="\n") as file:
file.write(
animation.to_c(
table.combined_data_and_headers_to_c(
anim_export_props.is_c_dma_structure,
sm64_props.refresh_version,
)
)

if header_type != "DMA":
table_name = table_props.get_anim_table_name(actor_name)
enum_list_name = table_props.get_enum_list_name(actor_name)

write_anim_header(os.path.join(geo_dir_path, "anim_header.h"), table_name, table_props.generate_enums)
update_table_file(
os.path.join(anim_dir_path, "table.inc.c"),
table_props.get_enum_and_header_names(actor_name),
table_name,
table_props.generate_enums,
table_props.override_files,
os.path.join(anim_dir_path, "table_enum.h"),
enum_list_name,
)
update_data_file(
os.path.join(anim_dir_path, "data.inc.c"),
[action.fast64.sm64.get_anim_file_name(action) for action in actions],
write_anim_header(
os.path.join(geo_dir_path, "anim_header.h"), table.reference, table_props.generate_enums
)
if table_props.override_files:
with open(os.path.join(anim_dir_path, "table.inc.c"), "w", newline="\n") as file:
file.write(table.table_to_c(table_props.generate_enums))
if table_props.generate_enums:
with open(os.path.join(anim_dir_path, "table_enum.h"), "w", newline="\n") as file:
file.write(table.enum_list_to_c())
else:
update_table_file(
os.path.join(anim_dir_path, "table.inc.c"),
table_props.get_enum_and_header_names(actor_name),
table.reference,
table_props.generate_enums,
False,
os.path.join(anim_dir_path, "table_enum.h"),
table.enum_list_reference,
)

if not header_type in {"Custom", "DMA"}:
update_includes(
Expand Down
Loading

0 comments on commit d68f843

Please sign in to comment.