mirror of
https://github.com/NinjaCheetah/libWiiPy.git
synced 2025-04-26 13:21:01 -04:00
WADs can now be packed properly via title.py or low-level methods
This commit is contained in:
parent
640ca91716
commit
57fb0576e9
@ -157,14 +157,14 @@ class ContentRegion:
|
|||||||
self.content_records[index].content_size)
|
self.content_records[index].content_size)
|
||||||
# Hash the decrypted content and ensure that the hash matches the one in its Content Record.
|
# Hash the decrypted content and ensure that the hash matches the one in its Content Record.
|
||||||
# If it does not, then something has gone wrong in the decryption, and an error will be thrown.
|
# If it does not, then something has gone wrong in the decryption, and an error will be thrown.
|
||||||
content_dec_hash = hashlib.sha1(content_dec)
|
content_dec_hash = hashlib.sha1(content_dec).hexdigest()
|
||||||
content_record_hash = str(self.content_records[index].content_hash.decode())
|
content_record_hash = str(self.content_records[index].content_hash.decode())
|
||||||
# Compare the hash and throw a ValueError if the hash doesn't match.
|
# Compare the hash and throw a ValueError if the hash doesn't match.
|
||||||
if content_dec_hash.hexdigest() != content_record_hash:
|
if content_dec_hash != content_record_hash:
|
||||||
raise ValueError("Content hash did not match the expected hash in its record! The incorrect Title Key may "
|
raise ValueError("Content hash did not match the expected hash in its record! The incorrect Title Key may "
|
||||||
"have been used!.\n"
|
"have been used!.\n"
|
||||||
"Expected hash is: {}\n".format(content_record_hash) +
|
"Expected hash is: {}\n".format(content_record_hash) +
|
||||||
"Actual hash is: {}".format(content_dec_hash.hexdigest()))
|
"Actual hash is: {}".format(content_dec_hash))
|
||||||
return content_dec
|
return content_dec
|
||||||
|
|
||||||
def get_content_by_cid(self, cid: int, title_key: bytes) -> bytes:
|
def get_content_by_cid(self, cid: int, title_key: bytes) -> bytes:
|
||||||
@ -282,10 +282,52 @@ class ContentRegion:
|
|||||||
# Pass values to set_enc_content()
|
# Pass values to set_enc_content()
|
||||||
self.set_enc_content(enc_content, cid, index, content_type, dec_content_size, dec_content_hash)
|
self.set_enc_content(enc_content, cid, index, content_type, dec_content_size, dec_content_hash)
|
||||||
|
|
||||||
def load_enc_content(self, enc_content: bytes, index: int) -> bytes:
|
def load_enc_content(self, enc_content: bytes, index: int) -> None:
|
||||||
"""Loads the provided encrypted content into the content region at the specified index, with the assumption that
|
"""Loads the provided encrypted content into the content region at the specified index, with the assumption that
|
||||||
it matches the record at that index.
|
it matches the record at that index. Not recommended for most use cases, use decrypted content and
|
||||||
|
load_content() instead.
|
||||||
|
|
||||||
:param index:
|
Parameters
|
||||||
:return:
|
----------
|
||||||
|
enc_content : bytes
|
||||||
|
The encrypted content to load.
|
||||||
|
index : int
|
||||||
|
The content index to load the content at.
|
||||||
"""
|
"""
|
||||||
|
if (index + 1) > len(self.content_records) or len(self.content_records) == 0:
|
||||||
|
raise IndexError("No content records have been loaded, or that index is higher than the highest entry in "
|
||||||
|
"the content records.")
|
||||||
|
if (index + 1) > len(self.content_list):
|
||||||
|
self.content_list.append(enc_content)
|
||||||
|
else:
|
||||||
|
self.content_list[index] = enc_content
|
||||||
|
|
||||||
|
def load_content(self, dec_content: bytes, index: int, title_key: bytes) -> None:
|
||||||
|
"""Loads the provided decrypted content into the content region at the specified index, but first checks to make
|
||||||
|
sure it matches the record at that index before loading. This content will be encrypted when loaded.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
dec_content : bytes
|
||||||
|
The decrypted content to load.
|
||||||
|
index : int
|
||||||
|
The content index to load the content at.
|
||||||
|
title_key: bytes
|
||||||
|
The Title Key that matches the decrypted content.
|
||||||
|
"""
|
||||||
|
# Make sure that content records exist and that the provided index exists in them.
|
||||||
|
if (index + 1) > len(self.content_records) or len(self.content_records) == 0:
|
||||||
|
raise IndexError("No content records have been loaded, or that index is higher than the highest entry in "
|
||||||
|
"the content records.")
|
||||||
|
# Check the hash of the content against the hash stored in the record to ensure it matches.
|
||||||
|
content_hash = hashlib.sha1(dec_content).hexdigest()
|
||||||
|
if content_hash != self.content_records[index].content_hash.decode():
|
||||||
|
raise ValueError("The decrypted content provided does not match the record at the provided index. \n"
|
||||||
|
"Expected hash is: {}\n".format(self.content_records[index].content_hash.decode()) +
|
||||||
|
"Actual hash is: {}".format(content_hash))
|
||||||
|
# If the hash matches, encrypt the content and set it where it belongs.
|
||||||
|
enc_content = encrypt_content(dec_content, title_key, index)
|
||||||
|
if (index + 1) > len(self.content_list):
|
||||||
|
self.content_list.append(enc_content)
|
||||||
|
else:
|
||||||
|
self.content_list[index] = enc_content
|
||||||
|
@ -27,7 +27,7 @@ class Title:
|
|||||||
self.ticket: Ticket = Ticket()
|
self.ticket: Ticket = Ticket()
|
||||||
self.content: ContentRegion = ContentRegion()
|
self.content: ContentRegion = ContentRegion()
|
||||||
|
|
||||||
def set_wad(self, wad: bytes) -> None:
|
def load_wad(self, wad: bytes) -> None:
|
||||||
"""Load existing WAD data into the title and create WAD, TMD, Ticket, and ContentRegion objects based off of it
|
"""Load existing WAD data into the title and create WAD, TMD, Ticket, and ContentRegion objects based off of it
|
||||||
to allow you to modify that data. Note that this will overwrite any existing data for this title.
|
to allow you to modify that data. Note that this will overwrite any existing data for this title.
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class Title:
|
|||||||
raise ValueError("The Title IDs of the TMD and Ticket in this WAD do not match. This WAD appears to be "
|
raise ValueError("The Title IDs of the TMD and Ticket in this WAD do not match. This WAD appears to be "
|
||||||
"invalid.")
|
"invalid.")
|
||||||
|
|
||||||
def dump(self) -> bytes:
|
def dump_wad(self) -> bytes:
|
||||||
"""Dumps all title components (TMD, Ticket, and contents) back into the WAD object, and then dumps the WAD back
|
"""Dumps all title components (TMD, Ticket, and contents) back into the WAD object, and then dumps the WAD back
|
||||||
into raw data and returns it.
|
into raw data and returns it.
|
||||||
|
|
||||||
@ -73,6 +73,38 @@ class Title:
|
|||||||
wad_data = self.wad.dump()
|
wad_data = self.wad.dump()
|
||||||
return wad_data
|
return wad_data
|
||||||
|
|
||||||
|
def load_tmd(self, tmd: bytes) -> None:
|
||||||
|
"""Load existing TMD data into the title. Note that this will overwrite any existing TMD data for this title.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
tmd : bytes
|
||||||
|
The data for the WAD you wish to load.
|
||||||
|
"""
|
||||||
|
# Load TMD.
|
||||||
|
self.tmd.load(tmd)
|
||||||
|
|
||||||
|
def load_ticket(self, ticket: bytes) -> None:
|
||||||
|
"""Load existing Ticket data into the title. Note that this will overwrite any existing Ticket data for this
|
||||||
|
title.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ticket : bytes
|
||||||
|
The data for the WAD you wish to load.
|
||||||
|
"""
|
||||||
|
# Load Ticket.
|
||||||
|
self.ticket.load(ticket)
|
||||||
|
|
||||||
|
def load_content_records(self) -> None:
|
||||||
|
"""Load content records from the TMD into the ContentRegion to allow loading content files based on the records.
|
||||||
|
This requires that a TMD has already been loaded and will throw an exception if it isn't.
|
||||||
|
"""
|
||||||
|
if not self.tmd.content_records:
|
||||||
|
ValueError("No TMD appears to have been loaded, so content records cannot be read from it.")
|
||||||
|
# Load the content records into the ContentRegion object.
|
||||||
|
self.content.content_records = self.tmd.content_records
|
||||||
|
|
||||||
def set_title_id(self, title_id: str) -> None:
|
def set_title_id(self, title_id: str) -> None:
|
||||||
"""Sets the Title ID of the title in both the TMD and Ticket.
|
"""Sets the Title ID of the title in both the TMD and Ticket.
|
||||||
|
|
||||||
@ -170,3 +202,17 @@ class Title:
|
|||||||
self.content.set_content(dec_content, cid, index, content_type, self.ticket.get_title_key())
|
self.content.set_content(dec_content, cid, index, content_type, self.ticket.get_title_key())
|
||||||
# Update the TMD to match.
|
# Update the TMD to match.
|
||||||
self.tmd.content_records = self.content.content_records
|
self.tmd.content_records = self.content.content_records
|
||||||
|
|
||||||
|
def load_content(self, dec_content: bytes, index: int) -> None:
|
||||||
|
"""Loads the provided decrypted content into the content region at the specified index, but first checks to make
|
||||||
|
sure it matches the record at that index before loading. This content will be encrypted when loaded.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
dec_content : bytes
|
||||||
|
The decrypted content to load.
|
||||||
|
index : int
|
||||||
|
The content index to load the content at.
|
||||||
|
"""
|
||||||
|
# Load the decrypted content.
|
||||||
|
self.content.load_content(dec_content, index, self.ticket.get_title_key())
|
||||||
|
@ -14,8 +14,8 @@ class WAD:
|
|||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.wad_hdr_size: int = 64
|
self.wad_hdr_size: int = 64
|
||||||
self.wad_type: str = ""
|
self.wad_type: str = "Is"
|
||||||
self.wad_version: bytes = b''
|
self.wad_version: bytes = b'\x00\x00'
|
||||||
# === Sizes ===
|
# === Sizes ===
|
||||||
self.wad_cert_size: int = 0
|
self.wad_cert_size: int = 0
|
||||||
self.wad_crl_size: int = 0
|
self.wad_crl_size: int = 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user