Add option to skip hash checks when unpacking a WAD

This commit is contained in:
Campbell 2024-07-25 21:14:12 -04:00
parent f7f67d3414
commit 102da808e6
Signed by: NinjaCheetah
GPG Key ID: 670C282B3291D63D
2 changed files with 29 additions and 13 deletions

View File

@ -172,7 +172,7 @@ class ContentRegion:
""" """
return self.content_list return self.content_list
def get_content_by_index(self, index: int, title_key: bytes) -> bytes: def get_content_by_index(self, index: int, title_key: bytes, skip_hash=False) -> bytes:
""" """
Gets an individual content from the content region based on the provided index, in decrypted form. Gets an individual content from the content region based on the provided index, in decrypted form.
@ -186,6 +186,8 @@ class ContentRegion:
The content index of the content you want to get. The content index of the content you want to get.
title_key : bytes title_key : bytes
The Title Key for the title the content is from. The Title Key for the title the content is from.
skip_hash : bool, optional
Skip the hash check and return the content regardless of its hash. Defaults to false.
Returns Returns
------- -------
@ -207,13 +209,16 @@ class ContentRegion:
content_record_hash = str(self.content_records[target_index].content_hash.decode()) content_record_hash = str(self.content_records[target_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 != 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 " if skip_hash:
"have been used!\n" print("Ignoring hash mismatch for content index " + str(index))
"Expected hash is: {}\n".format(content_record_hash) + else:
"Actual hash is: {}".format(content_dec_hash)) raise ValueError("Content hash did not match the expected hash in its record! The incorrect Title Key "
"may have been used!\n"
"Expected hash is: {}\n".format(content_record_hash) +
"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, skip_hash=False) -> bytes:
""" """
Gets an individual content from the content region based on the provided Content ID, in decrypted form. Gets an individual content from the content region based on the provided Content ID, in decrypted form.
@ -223,6 +228,8 @@ class ContentRegion:
The Content ID of the content you want to get. Expected to be in decimal form, not hex. The Content ID of the content you want to get. Expected to be in decimal form, not hex.
title_key : bytes title_key : bytes
The Title Key for the title the content is from. The Title Key for the title the content is from.
skip_hash : bool, optional
Skip the hash check and return the content regardless of its hash. Defaults to false.
Returns Returns
------- -------
@ -239,10 +246,10 @@ class ContentRegion:
# Get the content index associated with the CID we now know exists. # Get the content index associated with the CID we now know exists.
target_index = content_ids.index(cid) target_index = content_ids.index(cid)
content_index = self.content_records[target_index].index content_index = self.content_records[target_index].index
content_dec = self.get_content_by_index(content_index, title_key) content_dec = self.get_content_by_index(content_index, title_key, skip_hash)
return content_dec return content_dec
def get_contents(self, title_key: bytes) -> List[bytes]: def get_contents(self, title_key: bytes, skip_hash=False) -> List[bytes]:
""" """
Gets a list of all contents from the content region, in decrypted form. Gets a list of all contents from the content region, in decrypted form.
@ -250,6 +257,8 @@ class ContentRegion:
---------- ----------
title_key : bytes title_key : bytes
The Title Key for the title the content is from. The Title Key for the title the content is from.
skip_hash : bool, optional
Skip the hash check and return the content regardless of its hash. Defaults to false.
Returns Returns
------- -------
@ -259,7 +268,7 @@ class ContentRegion:
dec_contents: List[bytes] = [] dec_contents: List[bytes] = []
# Iterate over every content, get the decrypted version of it, then add it to a list and return it. # Iterate over every content, get the decrypted version of it, then add it to a list and return it.
for content in range(self.num_contents): for content in range(self.num_contents):
dec_contents.append(self.get_content_by_index(content, title_key)) dec_contents.append(self.get_content_by_index(content, title_key, skip_hash))
return dec_contents return dec_contents
def add_enc_content(self, enc_content: bytes, cid: int, index: int, content_type: int, content_size: int, def add_enc_content(self, enc_content: bytes, cid: int, index: int, content_type: int, content_size: int,
@ -362,6 +371,9 @@ class ContentRegion:
self.content_records[target_index].content_id = cid self.content_records[target_index].content_id = cid
if content_type is not None: if content_type is not None:
self.content_records[target_index].content_type = content_type self.content_records[target_index].content_type = content_type
# Add blank entries to the list to ensure that its length matches the length of the content record list.
while len(self.content_list) < len(self.content_records):
self.content_list.append(b'')
self.content_list[target_index] = enc_content self.content_list[target_index] = enc_content
def set_content(self, dec_content: bytes, index: int, title_key: bytes, cid: int = None, def set_content(self, dec_content: bytes, index: int, title_key: bytes, cid: int = None,

View File

@ -145,7 +145,7 @@ class Title:
self.tmd.set_title_version(title_version) self.tmd.set_title_version(title_version)
self.ticket.set_title_version(title_version) self.ticket.set_title_version(title_version)
def get_content_by_index(self, index: id) -> bytes: def get_content_by_index(self, index: id, skip_hash=False) -> bytes:
""" """
Gets an individual content from the content region based on the provided index, in decrypted form. Gets an individual content from the content region based on the provided index, in decrypted form.
@ -153,16 +153,18 @@ class Title:
---------- ----------
index : int index : int
The index of the content you want to get. The index of the content you want to get.
skip_hash : bool, optional
Skip the hash check and return the content regardless of its hash. Defaults to false.
Returns Returns
------- -------
bytes bytes
The decrypted content listed in the content record. The decrypted content listed in the content record.
""" """
dec_content = self.content.get_content_by_index(index, self.ticket.get_title_key()) dec_content = self.content.get_content_by_index(index, self.ticket.get_title_key(), skip_hash)
return dec_content return dec_content
def get_content_by_cid(self, cid: int) -> bytes: def get_content_by_cid(self, cid: int, skip_hash=False) -> bytes:
""" """
Gets an individual content from the content region based on the provided Content ID, in decrypted form. Gets an individual content from the content region based on the provided Content ID, in decrypted form.
@ -170,13 +172,15 @@ class Title:
---------- ----------
cid : int cid : int
The Content ID of the content you want to get. Expected to be in decimal form. The Content ID of the content you want to get. Expected to be in decimal form.
skip_hash : bool, optional
Skip the hash check and return the content regardless of its hash. Defaults to false.
Returns Returns
------- -------
bytes bytes
The decrypted content listed in the content record. The decrypted content listed in the content record.
""" """
dec_content = self.content.get_content_by_cid(cid, self.ticket.get_title_key()) dec_content = self.content.get_content_by_cid(cid, self.ticket.get_title_key(), skip_hash)
return dec_content return dec_content
def set_enc_content(self, enc_content: bytes, index: int, content_size: int, content_hash: bytes, cid: int = None, def set_enc_content(self, enc_content: bytes, index: int, content_size: int, content_hash: bytes, cid: int = None,