Skip to content

Commit

Permalink
working scene file start address shifting
Browse files Browse the repository at this point in the history
  • Loading branch information
mracsys committed Nov 24, 2024
1 parent eb89a89 commit 73f94eb
Show file tree
Hide file tree
Showing 7 changed files with 1,024 additions and 225 deletions.
7 changes: 7 additions & 0 deletions Cutscenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,10 @@ class CutsceneCommandID(IntEnum):
CutsceneCommandID.CS_CMD_CAM_EYE,
CutsceneCommandID.CS_CMD_CAM_AT,
]

# z_demo tables
ENTRANCE_CUTSCENE_TABLE_ADDRESS = 0xB65C64
UNKNOWN_LIST_CUTSCENES = 0xB65D74 # does not include NULL first entry

# z_demo_kekkai list
SAGE_CUTSCENES = 0xECF8EC
158 changes: 50 additions & 108 deletions FileDataRelocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from Rom import Rom
from MQ import align4, align8, align16, align_file
from SceneList import RecordType, SCENE_EXTERNAL_REFERENCES, SCENE_TABLE_ADDRESS


def segment_address_offset(segment_address: int) -> int:
Expand Down Expand Up @@ -61,108 +62,6 @@ def __str__(self) -> str:
return f'({self.x}, {self.y}, {self.z})'


class RecordType(str, Enum):
# Scene header record types
SceneHeader = 'SceneHeader'
AlternateHeaders = 'AlternateHeaders'
RoomList = 'RoomList'
TransitionActorList = 'TransitionActorList'
CollisionHeader = 'CollisionHeader'
EntranceList = 'EntranceList'
Points = 'Points'
PathList = 'PathList'
SpawnList = 'SpawnList'
ExitList = 'ExitList'
LightSettings = 'LightSettings'
CutsceneData = 'CutsceneData'

# Collision header record types
Vertices = 'Vertices'
Polys = 'Polys'
SurfaceTypes = 'SurfaceTypes'
Cams = 'Cams'
Waterboxes = 'Waterboxes'
CamPosData = 'CamPosData'

# Room header record types
RoomHeader = 'RoomHeader'
ObjectList = 'ObjectList'
ActorList = 'ActorList'

# Mesh header record types
MeshHeader = 'MeshHeader'
MeshHeaderImageSingle = 'MeshHeaderImage'
MeshHeaderImageMulti = 'MeshHeaderImage'
MeshHeaderCullable = 'MeshHeaderCullable'
DlistEntries = 'DlistEntries'
DlistEntry = 'DlistEntry'
DlistCullableEntries = 'DlistCullableEntries'
Dlist = 'Dlist'
Vtx = 'Vtx'
Mtx = 'Mtx'
SetTImg = 'SetTImg'
SetZImg = 'SetZImg'
SetCImg = 'SetCImg'
Backgrounds = 'Backgrounds'
Background = 'Background'
BackgroundImage = 'BackgroundImage'
BackgroundTlut = 'BackgroundTlut'
CullableEntries = 'CullableEntries'
CullableEntry = 'CullableEntry'
Cullable = 'Cullable'

# Other unparsed data blob record types
Texture = 'Texture'
TextureAnimation = 'TextureAnimation'
Symbol = 'Symbol'
Scalar = 'Scalar'
Vector = 'Vector'
Array = 'Array'
Pointer = 'Pointer'
Blob = 'Blob'

# Model-related record types
Skeleton = 'Skeleton'
LimbTable = 'LimbTable'
Limb = 'Limb'
Animation = 'Animation'
PlayerAnimation = 'PlayerAnimation'
PlayerAnimationData = 'PlayerAnimationData'
CurveAnimation = 'CurveAnimation'
LegacyAnimation = 'LegacyAnimation'

# Unknown data not referenced in scene and room files
Unknown = 'Unknown'

# Record types referenced by header commands without count
def has_unknown_count(self) -> bool:
if self == RecordType.CollisionHeader: # 0x03
return True
if self == RecordType.EntranceList: # 0x06
return True
if self == RecordType.MeshHeader: # 0x0A
return True
if self == RecordType.PathList: # 0x0D
return True
if self == RecordType.ExitList: # 0x13
return True
if self == RecordType.CutsceneData: # 0x17
return True
if self == RecordType.AlternateHeaders: # 0x18
return True
if self == RecordType.SurfaceTypes:
return True
if self == RecordType.Cams:
return True
if self == RecordType.SetTImg:
return True
if self == RecordType.SetZImg:
return True
if self == RecordType.SetCImg:
return True
return False


# File type values correspond to segment number from the segment address table
# https://wiki.cloudmodding.com/oot/Addresses#Segment_Addresses
# -----------------------------------
Expand Down Expand Up @@ -197,6 +96,7 @@ def __init__(self, file: FileDataRelocator, type: RecordType, start: int, offset
self.type: RecordType = type
self.start: int = start
self.offset: int = offset
self.vanilla_offset: int = offset
self.length: int = length
self.delay_parsing: bool = delay_parsing
self.align: int = 4
Expand Down Expand Up @@ -289,6 +189,7 @@ def __init__(self, rom: Rom, name: str, start: int, end: int, type: FileType) ->
self.name: str = name
self.start: int = start
self.end: int = end
self.rom_start: int = start
self.type: FileType = type
self.parsed: bool = False

Expand Down Expand Up @@ -458,6 +359,15 @@ def get_existing_record_by_offset(self, offset: int, record_type: RecordType) ->
f'Existing {existing_record.type.value} at 0x{existing_record.offset:08X} does not match requested type {record_type} in {self.name}')
return existing_record

# Return the existing data record matching the given file offset or None
def get_existing_record_by_vanilla_offset(self, offset: int, record_type: RecordType) -> Optional[DataRecord]:
existing_record: Optional[DataRecord] = next(
(x for x in self.data_records if x.vanilla_offset == offset), None)
if existing_record is not None and existing_record.type != record_type:
raise Exception(
f'Existing {existing_record.type.value} at 0x{existing_record.vanilla_offset:08X} does not match requested type {record_type} in {self.name}')
return existing_record

def encode(self) -> bytearray:
# Resize data records. Assumes records are sorted by offset.
# Don't build full encode at first as pointers may shift.
Expand Down Expand Up @@ -505,20 +415,52 @@ def encode(self) -> bytearray:
bytes.extend(record_bytes)
return bytes

def write(self, rom: Rom, start_address: Optional[int] = None) -> int:
# Two-stage process to write to rom.
# Start/end of all files must be determined prior to
# final encode as other files may reference this file's
# location, such as scene files referencing their room files.
def update_start_and_end(self, start_address: Optional[int] = None) -> int:
raw_file = self.encode()
if start_address is None:
new_start = align_file(self.start)
else:
new_start = align_file(start_address)
new_end = new_start + len(raw_file)
self.start = new_start
self.end = new_end
return align_file(new_end)

def write(self, rom: Rom) -> int:
raw_file = self.encode()
if self.name in SCENE_EXTERNAL_REFERENCES.keys():
for record_type, offset, data_references, code_references in SCENE_EXTERNAL_REFERENCES[self.name]:
record = self.get_existing_record_by_vanilla_offset(offset, record_type)
if record is None:
raise Exception(f'Offset {offset:0>6x} does not match any records in {self.name}')
record_address = record.get_segment_address_bytes()
for external_pointer_address in data_references:
self.write_external_data_pointer(rom, record_address, external_pointer_address)
for external_pointer_addresses in code_references:
self.write_external_code_pointer(rom, record_address, external_pointer_addresses)
file_length = len(raw_file)
while file_length < align16(file_length):
while file_length < align_file(file_length):
raw_file.extend(int.to_bytes(0, 1, 'big'))
file_length += 1
rom.write_bytes(new_start, raw_file)
new_end = new_start + len(raw_file)
rom.update_dmadata_record_by_key(self.start, new_start, new_end)
return align_file(new_end)
rom.write_bytes(self.start, raw_file)
rom.update_dmadata_record_by_key(self.rom_start, self.start, self.end)
self.rom_start = self.start
return align_file(self.end)

def write_external_data_pointer(self, rom: Rom, record_address: bytearray, external_pointer_address: int) -> None:
rom.write_bytes(external_pointer_address, record_address)

def write_external_code_pointer(self, rom: Rom, record_address: bytearray, external_pointer_addresses: tuple[int, int]) -> None:
address = int.from_bytes(record_address)
address_low = address & 0xFFFF
address_high = (address >> 16) + (1 if address_low > 0x7FFF else 0)
external_high, external_low = external_pointer_addresses
rom.write_bytes(external_high, address_high.to_bytes(2, 'big'))
rom.write_bytes(external_low, address_low.to_bytes(2, 'big'))

# Return the file data as a serializable dict

Expand Down
2 changes: 1 addition & 1 deletion FileList.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Keyed on filename
# Values: file number, VROM start, VROM end

scene_and_room_files = {
SCENE_AND_ROOM_FILES = {
"ddan_scene": (0x03EF, 0x01F12000, 0x01F27140),
"ddan_room_0": (0x03F0, 0x01F28000, 0x01F438A0),
"ddan_room_1": (0x03F1, 0x01F44000, 0x01F4DB00),
Expand Down
2 changes: 1 addition & 1 deletion MQ.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ def align16(value: int) -> int:


def align_file(value: int) -> int:
return ((value + 0xFFF) // 0x1000) * 0x1000
return align16(value)

# This function inserts space in a ovl section at the section's offset
# The section size is expanded
Expand Down
Loading

0 comments on commit 73f94eb

Please sign in to comment.