Skip to content

Commit

Permalink
Fix mypy errors in Models.py and MQ.py
Browse files Browse the repository at this point in the history
  • Loading branch information
fenhl committed Mar 27, 2024
1 parent 9d71f80 commit db6d4ac
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 38 deletions.
53 changes: 39 additions & 14 deletions MQ.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,18 @@ def __init__(self, name: str, start: int = 0, end: Optional[int] = None, remap:
self.from_file: int = self.start

# used to update the file's associated dmadata record
self.dma_key: int = self.start
self.dma_key: Optional[int] = self.start

@classmethod
def from_json(cls, file: JsonFile) -> File:
start = file.get('Start', None)
end = file.get('End', None)
remap_start = file.get('RemapStart', None)
return cls(
file['Name'],
int(file['Start'], 16) if file.get('Start', None) is not None else 0,
int(file['End'], 16) if file.get('End', None) is not None else None,
int(file['RemapStart'], 16) if file.get('RemapStart', None) is not None else None
0 if start is None else int(start, 16),
None if end is None else int(end, 16),
None if remap_start is None else int(remap_start, 16),
)

def __repr__(self) -> str:
Expand Down Expand Up @@ -120,16 +123,29 @@ def write_to_scene(self, rom: Rom, start: int) -> None:
rom.write_int32s(addr, [self.poly_addr, self.polytypes_addr, self.camera_data_addr])


class JsonColDelta(TypedDict):
IsLarger: bool
Polys: list[dict[str, int]]
PolyTypes: list[dict[str, int]]
Cams: list[dict[str, int]]


class ColDelta:
def __init__(self, delta: dict[str, bool | list[dict[str, int]]]) -> None:
def __init__(self, delta: JsonColDelta) -> None:
self.is_larger: bool = delta['IsLarger']
self.polys: list[dict[str, int]] = delta['Polys']
self.polytypes: list[dict[str, int]] = delta['PolyTypes']
self.cams: list[dict[str, int]] = delta['Cams']


class JsonIcon(TypedDict):
Icon: int
Count: int
IconPoints: list[dict[str, int]]


class Icon:
def __init__(self, data: dict[str, int | list[dict[str, int]]]) -> None:
def __init__(self, data: JsonIcon) -> None:
self.icon: int = data["Icon"]
self.count: int = data["Count"]
self.points: list[IconPoint] = [IconPoint(x) for x in data["IconPoints"]]
Expand Down Expand Up @@ -374,8 +390,15 @@ def append_path_data(self, rom: Rom) -> int:
return records_offset


class JsonRoom(TypedDict):
File: JsonFile
Id: int
Objects: list[str]
Actors: list[str]


class Room:
def __init__(self, room: dict[str, int | list[str] | dict[str, Optional[str]]]):
def __init__(self, room: JsonRoom):
self.file: File = File.from_json(room['File'])
self.id: int = room['Id']
self.objects: list[int] = [int(x, 16) for x in room['Objects']]
Expand Down Expand Up @@ -607,25 +630,25 @@ def insert_space(rom: Rom, file: File, vram_start: int, insert_section: int, ins
(offset + insert_size))

# value contains the vram address
value = rom.read_int32(address)
base_value = rom.read_int32(address)
reg = None
if type == 2:
# Data entry: value is the raw vram address
pass
value = base_value
elif type == 4:
# Jump OP: Get the address from a Jump instruction
value = 0x80000000 | (value & 0x03FFFFFF) << 2
value = 0x80000000 | (base_value & 0x03FFFFFF) << 2
elif type == 5:
# Load High: Upper half of an address load
reg = (value >> 16) & 0x1F
val_hi[reg] = (value & 0x0000FFFF) << 16
reg = (base_value >> 16) & 0x1F
val_hi[reg] = (base_value & 0x0000FFFF) << 16
adr_hi[reg] = address
# Do not process, wait until the lower half is read
value = None
elif type == 6:
# Load Low: Lower half of the address load
reg = (value >> 21) & 0x1F
val_low = value & 0x0000FFFF
reg = (base_value >> 21) & 0x1F
val_low = base_value & 0x0000FFFF
val_low = unpack('h', pack('H', val_low))[0]
# combine with previous load high
value = val_hi[reg] + val_low
Expand All @@ -648,6 +671,8 @@ def insert_space(rom: Rom, file: File, vram_start: int, insert_section: int, ins
new_value = op | new_value
rom.write_int32(address, new_value)
elif type == 6:
assert reg is not None

# Load Low: Lower half of the address load
op = rom.read_int32(address) & 0xFFFF0000
new_val_low = new_value & 0x0000FFFF
Expand Down
48 changes: 24 additions & 24 deletions Models.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,35 +93,35 @@ def WriteModelDataLo(self, data: int) -> None:

# Either return the starting index of the requested data (when start == 0)
# or the offset of the element in the footer, if it exists (start > 0)
def scan(bytes: bytearray, data: bytearray | str, start: int = 0) -> int:
def scan(bytes_to_scan: bytearray, data: bytearray | str, start: int = 0) -> int:
databytes: bytearray | bytes
# If a string was passed, encode string as bytes
if isinstance(data, str):
databytes = data.encode()
else:
databytes = data
dataindex = 0
for i in range(start, len(bytes)):
for i in range(start, len(bytes_to_scan)):
# Byte matches next byte in string
if bytes[i] == databytes[dataindex]:
if bytes_to_scan[i] == databytes[dataindex]:
dataindex += 1
# Special case: Bottle, Bow, Slingshot, Fist.L, and Fist.R are subsets of
# Bottle.Hand.L, Bow.String, Slingshot.String, Gauntlet.Fist.L, and Gauntlet.Fist.R respectively
# And Hookshot which is a subset of Hookshot.Spike, Hookshot.Chain, Hookshot.Aiming.Reticule
# This leads to false positives. So if the next byte is . (0x2E) then reset the count.
if isinstance(data, str) and data in ["Bottle", "Bow", "Slingshot", "Hookshot", "Fist.L", "Fist.R", "Blade.3"] and i < len(bytes) - 1 and bytes[i+1] == 0x2E:
if isinstance(data, str) and data in ["Bottle", "Bow", "Slingshot", "Hookshot", "Fist.L", "Fist.R", "Blade.3"] and i < len(bytes_to_scan) - 1 and bytes_to_scan[i+1] == 0x2E:
# Blade.3 is even wackier, as it is a subset of Blade.3.Break,
# and also a forward subset of Broken.Blade.3, and has a period in it
if data == "Blade.3":
resetCount = False
# If current byte is the "e" in "Blade.3", the period detected is the expected one- Carry on
# If it isn't, then reset the count
if bytes[i] != 0x65:
if bytes_to_scan[i] != 0x65:
resetCount = True
# Make sure i is large enough, "Broken.Blad" is 11 chars (remember we're currently at the e)
if not resetCount and i > 10:
# Check if "Broken." immediately preceeds this string
preceedingBytes = bytes[i-11:i-4]
preceedingBytes = bytes_to_scan[i-11:i-4]
if preceedingBytes == bytearray(b'Broken.'):
resetCount = True
if resetCount:
Expand All @@ -130,7 +130,7 @@ def scan(bytes: bytearray, data: bytearray | str, start: int = 0) -> int:
# "Gauntlet.Fis" is 12 chars (we are currently at the t)
elif data in ["Fist.L", "Fist.R"] and i > 11:
# Check if "Gauntlet." immediately preceeds this string
preceedingBytes = bytes[i-12:i-3]
preceedingBytes = bytes_to_scan[i-12:i-3]
if preceedingBytes == bytearray(b'Gauntlet.'):
dataindex = 0
# Default case for Bottle, Bow, Slingshot, Hookshot, reset count
Expand All @@ -140,28 +140,28 @@ def scan(bytes: bytearray, data: bytearray | str, start: int = 0) -> int:
# (Blade.3 and fists can check in the previous stanza since a . will be encountered at some point)
if isinstance(data, str) and data == "Hookshot" and dataindex == 1 and i > 3:
# Check if "FPS." immediately preceeds this string
preceedingBytes = bytes[i-4:i]
preceedingBytes = bytes_to_scan[i-4:i]
if preceedingBytes == bytearray(b'FPS.'):
dataindex = 0
# More special cases added by the new pipeline...
# Hand.L and Hand.R are forward subsets of Gauntlet.Hand.X, FPS.Hand.X
# And Hand.L specifically is a forward subset of Bottle.Hand.L
if isinstance(data, str) and data in ["Hand.L", "Hand.R"] and dataindex == 1:
if i > 8:
preceedingBytes = bytes[i-9:i]
preceedingBytes = bytes_to_scan[i-9:i]
if preceedingBytes == bytearray(b'Gauntlet.'):
dataindex = 0
if dataindex == 1 and i > 3:
preceedingBytes = bytes[i-4:i]
preceedingBytes = bytes_to_scan[i-4:i]
if preceedingBytes == bytearray(b'FPS.'):
dataindex = 0
if data == "Hand.L" and dataindex == 1 and i > 6:
preceedingBytes = bytes[i-7:i]
preceedingBytes = bytes_to_scan[i-7:i]
if preceedingBytes == bytearray(b'Bottle.'):
dataindex = 0
# Forearm.L and Forearm.R are forward subsets of FPS.Forearm.X
if isinstance(data, str) and data in ["Forearm.L", "Forearm.R"] and dataindex == 1 and i > 3:
preceedingBytes = bytes[i-4:i]
preceedingBytes = bytes_to_scan[i-4:i]
if preceedingBytes == bytearray(b'FPS.'):
dataindex = 0
# All bytes have been found, so a match
Expand All @@ -174,7 +174,7 @@ def scan(bytes: bytearray, data: bytearray | str, start: int = 0) -> int:
i += 2
offsetbytes = []
for j in range(4):
offsetbytes.append(bytes[i + j])
offsetbytes.append(bytes_to_scan[i + j])
return int.from_bytes(offsetbytes, 'big')
# Match has been broken, reset to start of string
else:
Expand Down Expand Up @@ -211,9 +211,9 @@ def LoadVanilla(rom: Rom, missing: list[str], rebase: int, linkstart: int, links
for i in range(linksize):
vanillaData.append(rom.buffer[linkstart + i])
segment = 0x06
vertices = {}
matrices = {}
textures = {}
vertices: dict[int, list[int]] = {}
matrices: dict[int, list[int]] = {}
textures: dict[int, list[int]] = {}
displayLists = {}
# For each missing piece, grab data from its vanilla display list
for item in missing:
Expand Down Expand Up @@ -285,7 +285,7 @@ def LoadVanilla(rom: Rom, missing: list[str], rebase: int, linkstart: int, links
# Grab the address from the low byte without the base offset
texOffset = lo & 0x00FFFFFF
numTexels = -1
returnStack = []
returnStack: list[int] = []
j = i+8
# The point of this loop is just to find the number of texels
# so that it may be multiplied by the bytesPerTexel so we know
Expand Down Expand Up @@ -345,7 +345,7 @@ def LoadVanilla(rom: Rom, missing: list[str], rebase: int, linkstart: int, links
i += 8
displayLists[item] = (displayList, offset)
# Create vanilla zobj of the pieces from data collected during crawl
vanillaZobj = []
vanillaZobj: list[int] = []
# Add textures, vertices, and matrices to the beginning of the zobj
# Textures
oldTex2New = {}
Expand Down Expand Up @@ -475,13 +475,13 @@ def CorrectSkeleton(zobj: bytearray, skeleton: list[list[int]], agestr: str) ->
hasVanillaSkeleton = True
for i in range(21):
offset = limb + i * 0x10
bytes = []
bytes.extend(int.to_bytes(skeleton[i][0], 2, 'big'))
bytes.extend(int.to_bytes(skeleton[i][1], 2, 'big'))
bytes.extend(int.to_bytes(skeleton[i][2], 2, 'big'))
skeleton_bytes: list[int] = []
skeleton_bytes.extend(int.to_bytes(skeleton[i][0], 2, 'big'))
skeleton_bytes.extend(int.to_bytes(skeleton[i][1], 2, 'big'))
skeleton_bytes.extend(int.to_bytes(skeleton[i][2], 2, 'big'))
# Overwrite the X, Y, Z bytes with their vanilla values
for j in range(6):
zobj[offset+j] = bytes[j]
zobj[offset+j] = skeleton_bytes[j]
return hasVanillaSkeleton


Expand Down Expand Up @@ -532,7 +532,7 @@ def LoadModel(rom: Rom, model: str, age: int) -> int:
# Find which pieces, if any, are missing from this model
missing = []
present = {}
DLOffsets = {}
DLOffsets: dict[str, int] = {}
for piece in pieces:
offset = scan(zobj, piece, footerstart)
if offset == -1:
Expand Down

0 comments on commit db6d4ac

Please sign in to comment.