Skip to content

Commit

Permalink
patching kakariko works
Browse files Browse the repository at this point in the history
  • Loading branch information
mracsys committed Nov 17, 2024
1 parent 50309d9 commit eb89a89
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 46 deletions.
50 changes: 16 additions & 34 deletions FileDataRelocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Any, Optional

from Rom import Rom
from MQ import align4, align8, align16
from MQ import align4, align8, align16, align_file


def segment_address_offset(segment_address: int) -> int:
Expand Down Expand Up @@ -45,6 +45,13 @@ def encode(self) -> bytearray:
bytes.extend(self.z.to_bytes(2, 'big', signed=True))
return bytes

def copy(self) -> Vec3s:
return Vec3s(
self.x,
self.y,
self.z
)

def __eq__(self, other: object) -> bool:
if not isinstance(other, Vec3s):
return NotImplemented
Expand Down Expand Up @@ -498,45 +505,20 @@ def encode(self) -> bytearray:
bytes.extend(record_bytes)
return bytes

def write(self, rom: Rom, start_address_adjustment: Optional[int] = 0) -> None:
def write(self, rom: Rom, start_address: Optional[int] = None) -> int:
raw_file = self.encode()
new_start = align16(self.start + start_address_adjustment)
if start_address is None:
new_start = align_file(self.start)
else:
new_start = align_file(start_address)
file_length = len(raw_file)
while file_length < align16(file_length):
raw_file.extend(int.to_bytes(0, 1, 'big'))
file_length += 1
rom.write_bytes(new_start, raw_file)
rom.update_dmadata_record_by_key(self.start, new_start, new_start + len(raw_file))

# Functions to transform the file

def transform_by_deduplicating_records(self) -> None:
i: int = 0
while i < len(self.data_records) - 1:
record = self.data_records[i]
# Ignore unknown records
if record.type == RecordType.Unknown:
i += 1
continue
# Iterate data records after the current one
j: int = i + 1
while j < len(self.data_records):
next_record = self.data_records[j]
# Find matches
if record.type == next_record.type:
index: int = record.data.find(next_record.data)
# Partial matches are fine only if we know the count or the match goes to the end of the record
if index != -1 and (not record.type.has_unknown_count() or index == len(record.data) - len(next_record.data)):
print(
f'\tMatching records {record.type} at 0x{record.offset + index:08X} and 0x{next_record.offset:08X} in {self.name}')
# Remove data record
removed_record = self.data_records.pop(j)
assert removed_record is next_record
self.update_pointer_records(
record, removed_record, index)
continue
j += 1
i += 1
new_end = new_start + len(raw_file)
rom.update_dmadata_record_by_key(self.start, new_start, new_end)
return align_file(new_end)

# Return the file data as a serializable dict

Expand Down
3 changes: 3 additions & 0 deletions MQ.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ def align16(value: int) -> int:
return ((value + 0xF) // 0x10) * 0x10


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

# This function inserts space in a ovl section at the section's offset
# The section size is expanded
# Every relocation entry in the section after the offset is moved accordingly
Expand Down
3 changes: 3 additions & 0 deletions Notes/scene-and-room-patching.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ Param `0xA000` makes them spawn at night, `0x8000` will always spawn.
Always set for overworld scenes with changing time.
Night-only room setups may also have this set for en_sw instances.

01F12000
03470F20

# Cutscenes

Safe to use CS_END() as terminator and just read in all the raw bytes if we're feeling lazy. Decomp doesn't show any commands past it for any cutscene despite cloudmodding suggesting that there may be commands past it. Command bytes are 0xFFFFFFFF. Note that this assumption is an artifact of how ZAPD parses the cutscenes and may not be accurate!!
Expand Down
2 changes: 2 additions & 0 deletions Rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ def update_dmadata_record_by_key(self, key: Optional[int], start: int, end: int,

if from_file is None:
from_file = -1 if key is None else key
old_end = dma_entry.end
dma_entry.update(start, end, from_file)
print(f'Updated DMA entry 0x{key:08X} (length 0x{old_end - key:08X}) to start 0x{start:08X}, end 0x{end:08X}, length 0x{end-start:08X}')

# This will scan for any changes that have been made to the DMA table
# By default, this assumes any changes here are new files, so this should only be called
Expand Down
77 changes: 65 additions & 12 deletions Scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -1045,20 +1045,20 @@ def encode(self) -> bytearray:

# Data only, used in scene spawn lists and room actor lists
class ActorEntry:
def __init__(self) -> None:
self.id: int = 0
self.pos: Vec3s = Vec3s()
self.rot: Vec3s = Vec3s()
self.params: int = 0
def __init__(self, id: int, pos: Vec3s, rot: Vec3s, params: int) -> None:
self.id: int = id
self.pos: Vec3s = pos
self.rot: Vec3s = rot
self.params: int = params

@staticmethod
def decode(rom: Rom, cursor: int) -> ActorEntry:
spawn = ActorEntry()
spawn.id = rom.read_s16(cursor)
spawn.pos = Vec3s.decode(rom, cursor + 0x02)
spawn.rot = Vec3s.decode(rom, cursor + 0x08)
spawn.params = rom.read_int16(cursor + 0x0E)
return spawn
return ActorEntry(
rom.read_s16(cursor),
Vec3s.decode(rom, cursor + 0x02),
Vec3s.decode(rom, cursor + 0x08),
rom.read_int16(cursor + 0x0E)
)

def encode(self) -> bytearray:
bytes = bytearray()
Expand Down Expand Up @@ -2198,6 +2198,59 @@ def compare_parsed_data_to_rom(rom: Rom, save_files: bool = False):
print('Done comparing')


def copy_skulltulas_to_day(day_header: RoomHeader, night_header: RoomHeader) -> None:
ACTOR_EN_SW = 149
OBJECT_ST = 36
if OBJECT_ST not in day_header.object_list.objects:
day_header.object_list.objects.append(OBJECT_ST)
for actor in night_header.actor_list.actors:
if actor.id == ACTOR_EN_SW:
new_params = (actor.params & ~0xA000) | 0x8000
new_actor = ActorEntry(actor.id, actor.pos.copy(), actor.rot.copy(), new_params)
day_header.actor_list.actors.append(new_actor)


def write_scenes_to_rom(rom: Rom, scenes: list[SceneDataRelocator]) -> None:
file_start: int = 0x01F12000 # original start of scene/room files
files = [scene for scene in scenes]
files.extend([room for scene in scenes for room in scene.rooms])
files.sort(key=lambda f: f.start)
for file in files:
file_start = file.write(rom, file_start)


def make_kak_skulltulas_ignore_tod(rom: Rom):
from Main import compress_rom

# Modify room files
scene_list = parse_scene_data(rom)
kakariko_outside = scene_list[0x52].rooms[0]
child_day = kakariko_outside.headers[0]
child_night = kakariko_outside.headers[1]
adult_day = kakariko_outside.headers[2]
adult_night = kakariko_outside.headers[3]
copy_skulltulas_to_day(child_day, child_night)
copy_skulltulas_to_day(adult_day, adult_night)
write_scenes_to_rom(rom, scene_list)

# patch Pokey out of the way
rom.write_bytes(0xE5400A, [0x8C, 0x4C])
rom.write_bytes(0xE5400E, [0xB4, 0xA4])
rom.write_bytes(0xE5401C, [0x14, 0x0B])

output_dir = './Output'
uncompressed_filename = f"OOT_no_tod_skulls_uncompressed.z64"
uncompressed_path = path.join(output_dir, uncompressed_filename)
print(f"Saving Uncompressed ROM: {uncompressed_filename}")
rom.write_to_file(uncompressed_path)
print(f"Created uncompressed ROM at: {uncompressed_path}")
compressed_filename = f"OOT_no_tod_skulls.z64"
compressed_path = path.join(output_dir, compressed_filename)
print(f"Compressing ROM: {compressed_filename}")
compress_rom(uncompressed_path, compressed_path, False)
print(f"Created compressed ROM at: {compressed_path}")


if __name__ == '__main__':
uncompressed_rom = Rom('ZOOTDEC.z64')
compare_parsed_data_to_rom(uncompressed_rom)
make_kak_skulltulas_ignore_tod(uncompressed_rom)

0 comments on commit eb89a89

Please sign in to comment.