From b0e48eb63c957ba7ed21baaee7c52e63f335954d Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:55:31 -0500 Subject: [PATCH 1/3] Fully documented wad.py --- src/libWiiPy/wad.py | 107 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 12 deletions(-) diff --git a/src/libWiiPy/wad.py b/src/libWiiPy/wad.py index 61af8fd..6515b45 100644 --- a/src/libWiiPy/wad.py +++ b/src/libWiiPy/wad.py @@ -8,7 +8,14 @@ import binascii class WAD: - """Creates a WAD object to parse the header of a WAD file and retrieve the data contained in it.""" + """ + Creates a WAD object to parse the header of a WAD file and retrieve the data contained in it. + + Attributes: + ---------- + wad : bytes + A bytes object containing the contents of a WAD file. + """ def __init__(self, wad): self.wad = wad self.wad_hdr_size: int @@ -73,59 +80,135 @@ class WAD: self.wad_meta_offset = int(64 * round((self.wad_content_offset + self.wad_content_size) / 64)) def get_cert_region(self): - """Returns the offset and size for the cert data.""" + """Gets the offset and size of the certificate data. + + Returns + ------- + int + The offset of the certificate data in the WAD. + int + The size of the certificate data in the WAD. + """ return self.wad_cert_offset, self.wad_cert_size def get_crl_region(self): - """Returns the offset and size for the crl data.""" + """Gets the offset and size of the crl data. + + Returns + ------- + int + The offset of the crl data in the WAD. + int + The size of the crl data in the WAD. + """ return self.wad_crl_offset, self.wad_crl_size def get_ticket_region(self): - """Returns the offset and size for the ticket data.""" + """Gets the offset and size of the ticket data. + + Returns + ------- + int + The offset of the ticket data in the WAD. + int + The size of the ticket data in the WAD. + """ return self.wad_tik_offset, self.wad_tik_size def get_tmd_region(self): - """Returns the offset and size for the TMD data.""" + """Gets the offset and size of the TMD data. + + Returns + ------- + int + The offset of the TMD data in the WAD. + int + The size of the TMD data in the WAD. + """ return self.wad_tmd_offset, self.wad_tmd_size def get_content_region(self): - """Returns the offset and size for the content of the WAD.""" + """Gets the offset and size of the content of the WAD. + + Returns + ------- + int + The offset of the content data in the WAD. + int + The size of the content data in the WAD. + """ return self.wad_content_offset, self.wad_tmd_size def get_wad_type(self): - """Returns the type of the WAD. This is 'Is' unless the WAD contains boot2 where it is 'ib'.""" + """Gets the type of the WAD. + + Returns + ------- + str + The type of the WAD. This is 'Is', unless the WAD contains boot2, where it is 'ib'. + """ return self.wad_type def get_cert_data(self): - """Returns the certificate data from the WAD.""" + """Gets the certificate data from the WAD. + + Returns + ------- + bytes + The certificate data. + """ waddata = io.BytesIO(self.wad) waddata.seek(self.wad_cert_offset) cert_data = waddata.read(self.wad_cert_size) return cert_data def get_crl_data(self): - """Returns the crl data from the WAD, if it exists.""" + """Gets the crl data from the WAD, if it exists. + + Returns + ------- + bytes + The crl data. + """ waddata = io.BytesIO(self.wad) waddata.seek(self.wad_crl_offset) crl_data = waddata.read(self.wad_crl_size) return crl_data def get_ticket_data(self): - """Returns the ticket data from the WAD.""" + """Gets the ticket data from the WAD. + + Returns + ------- + bytes + The ticket data. + """ waddata = io.BytesIO(self.wad) waddata.seek(self.wad_tik_offset) ticket_data = waddata.read(self.wad_tik_size) return ticket_data def get_tmd_data(self): - """Returns the TMD data from the WAD.""" + """Returns the TMD data from the WAD. + + Returns + ------- + bytes + The TMD data. + """ waddata = io.BytesIO(self.wad) waddata.seek(self.wad_tmd_offset) tmd_data = waddata.read(self.wad_tmd_size) return tmd_data def get_content_data(self): - """Returns the content of the WAD.""" + """Gets the content of the WAD. + + Returns + ------- + bytes + The content data. + """ waddata = io.BytesIO(self.wad) waddata.seek(self.wad_content_offset) content_data = waddata.read(self.wad_content_size) From 6c5c045bb138a8f8b99ec9520564714b55d2764b Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:22:34 -0500 Subject: [PATCH 2/3] Fully documented tmd.py --- src/libWiiPy/tmd.py | 123 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 13 deletions(-) diff --git a/src/libWiiPy/tmd.py b/src/libWiiPy/tmd.py index 24b4241..4e577bb 100644 --- a/src/libWiiPy/tmd.py +++ b/src/libWiiPy/tmd.py @@ -12,7 +12,22 @@ from typing import List @dataclass class ContentRecord: - """Creates a content record object that contains the details of a content contained in a title.""" + """ + Creates a content record object that contains the details of a content contained in a title. + + Attributes: + ---------- + cid : int + ID of the content. + index : int + Index of the content in the list of contents. + content_type : int + The type of the content. + content_size : int + The size of the content. + content_hash + The SHA-1 hash of the decrypted content. + """ cid: int # Content ID index: int # Index in the list of contents content_type: int # Normal: 0x0001, DLC: 0x4001, Shared: 0x8001 @@ -21,7 +36,14 @@ class ContentRecord: class TMD: - """Creates a TMD object to parse a TMD file to retrieve information about a title.""" + """ + Creates a TMD object to parse a TMD file to retrieve information about a title. + + Attributes: + ---------- + tmd : bytes + A bytes object containing the contents of a TMD file. + """ def __init__(self, tmd): self.tmd = tmd self.sig_type: int @@ -115,15 +137,36 @@ class TMD: binascii.hexlify(content_record_hdr[4]))) def get_title_id(self): - """Returns the TID of the TMD's associated title.""" + """Gets the TID of the TMD's associated title. + + Returns + ------- + str + The Title ID. + """ return self.title_id def get_title_version(self): - """Returns the version of the TMD's associated title.""" + """Gets the version of the TMD's associated title. + + Returns + ------- + int + The version of the title. + """ return self.title_version def get_title_region(self): - """Returns the region of the TMD's associated title.""" + """Gets the region of the TMD's associated title. + + Can be one of several possible values: + 'JAP', 'USA', 'EUR', 'NONE', or 'KOR'. + + Returns + ------- + str + The region of the title. + """ match self.region: case 0: return "JAP" @@ -137,26 +180,59 @@ class TMD: return "KOR" def get_is_vwii_title(self): - """Returns whether the TMD is designed for the vWii or not.""" + """Gets whether the TMD is designed for the vWii or not. + + Returns + ------- + bool + If the title is for vWii. + """ if self.vwii == 1: return True else: return False def get_tmd_version(self): - """Returns the version of the TMD.""" + """Gets the version of the TMD. + + Returns + ------- + int + The version of the TMD. + """ return self.version def get_required_ios_tid(self): - """Returns the TID of the required IOS for the title.""" + """Gets the TID of the required IOS for the title. + + Returns + ------- + str + The Title ID of the required IOS version. + """ return self.ios_tid def get_required_ios(self): - """Returns the required IOS version for the title.""" + """Gets the required IOS version for the title. + + Returns + ------- + int + The required IOS version. + """ return self.ios_version def get_title_type(self): - """Returns the type of the TMD's associated title.""" + """Gets the type of the TMD's associated title. + + Can be one of several possible values: + 'System', 'Game', 'Channel', 'SystemChannel', 'GameWithChannel', or 'HiddenChannel' + + Returns + ------- + str + The type of the title. + """ title_id_high = self.title_id[:8] match title_id_high: case '00000001': @@ -177,7 +253,16 @@ class TMD: return "Unknown" def get_content_type(self): - """Returns the type of content contained in the TMD's associated title.""" + """Gets the type of content contained in the TMD's associated title. + + Can be one of several possible values: + 'Normal', 'Development/Unknown', 'Hash Tree', 'DLC', or 'Shared' + + Returns + ------- + str + The type of content. + """ match self.content_type: case '00000001': return "Normal" @@ -193,11 +278,23 @@ class TMD: return "Unknown" def get_num_contents(self): - """Returns the number of contents listed in the TMD.""" + """Gets the number of contents listed in the TMD. + + Returns + ------- + int + The number of contents. + """ return self.num_contents def get_content_record(self, record): - """Returns the content record at the specified index.""" + """Gets the content record at the specified index. + + Returns + ------- + ContentRecord + A ContentRecord object containing the data in the content record. + """ if record < self.num_contents: return self.content_records[record] else: From 8eb54ab96153dd9b8f15ffe30b7560d80d59254e Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:13:24 -0500 Subject: [PATCH 3/3] Fully documented remaining files --- src/libWiiPy/commonkeys.py | 14 +++++- src/libWiiPy/crypto.py | 18 ++++++- src/libWiiPy/ticket.py | 98 +++++++++++++++++++++++++++++++++----- src/libWiiPy/tmd.py | 5 ++ 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/src/libWiiPy/commonkeys.py b/src/libWiiPy/commonkeys.py index c0a6048..b1fd7d2 100644 --- a/src/libWiiPy/commonkeys.py +++ b/src/libWiiPy/commonkeys.py @@ -9,9 +9,19 @@ vwii_key = '30bfc76e7c19afbb23163330ced7c28d' def get_common_key(common_key_index): - """ - Returns the specified Wii Common Key based on the index provided. + """Gets the specified Wii Common Key based on the index provided. + Possible values for common_key_index: 0: Common Key, 1: Korean Key, 2: vWii Key + + Parameters + ---------- + common_key_index : int + The index of the common key to be returned. + + Returns + ------- + bytes + The specified common key, in binary format. """ match common_key_index: case 0: diff --git a/src/libWiiPy/crypto.py b/src/libWiiPy/crypto.py index d8a7451..44a4a8f 100644 --- a/src/libWiiPy/crypto.py +++ b/src/libWiiPy/crypto.py @@ -8,9 +8,23 @@ from Crypto.Cipher import AES def decrypt_title_key(title_key_enc, common_key_index, title_id): - """ - Returns the decrypted version of the encrypted Title Key provided. + """Gets the decrypted version of the encrypted Title Key provided. + Requires the index of the common key to use, and the Title ID of the title that the Title Key is for. + + Parameters + ---------- + title_key_enc : bytes + The encrypted Title Key. + common_key_index : int + The index of the common key to be returned. + title_id : bytes + The title ID of the tite that the key is for. + + Returns + ------- + bytes + The decrypted Title Key. """ # Load the correct common key for the title. common_key = get_common_key(common_key_index) diff --git a/src/libWiiPy/ticket.py b/src/libWiiPy/ticket.py index cf31c8f..637352f 100644 --- a/src/libWiiPy/ticket.py +++ b/src/libWiiPy/ticket.py @@ -11,7 +11,15 @@ from typing import List @dataclass class TitleLimit: - """Creates a TitleLimit object that contains the type of restriction and the limit.""" + """Creates a TitleLimit object that contains the type of restriction and the limit. + + Attributes: + ---------- + limit_type : int + The type of play limit applied. + maximum_usage : int + The maximum value for the type of play limit applied. + """ # The type of play limit applied. The following types exist: # 0 = None, 1 = Time Limit, 3 = None, 4 = Launch Count limit_type: int @@ -105,40 +113,102 @@ class Ticket: self.title_limits_list.append(TitleLimit(limit_type, limit_value)) def get_signature(self): - """Returns the signature of the ticket.""" + """Gets the signature of the ticket. + + Returns + ------- + bytes + The signature. + """ return self.signature def get_ticket_version(self): - """Returns the version of the ticket.""" + """Gets the version of the ticket. + + Returns + ------- + int + The version. + """ return self.ticket_version def get_title_key_enc(self): - """Returns the title key contained in the ticket, in encrypted form.""" + """Gets the Title Key contained in the ticket, in encrypted form. + + Returns + ------- + bytes + The encrypted Title Key. + """ return self.title_key_enc def get_ticket_id(self): - """Returns the ID of the ticket.""" + """Gets the ID of the ticket. + + Returns + ------- + bytes + The ID of the ticket. + """ return self.ticket_id def get_console_id(self): - """Returns the ID of the console this ticket is designed for, if the ticket is console-specific.""" + """Gets the ID of the console this ticket is designed for, if the ticket is console-specific. + + Returns + ------- + bytes + The ID of the console. + """ return self.console_id def get_title_id(self): - """Returns the Title ID of the ticket's associated title.""" + """Gets the Title ID of the ticket's associated title. + + Returns + ------- + str + The Title ID of the title. + """ title_id_str = str(self.title_id.decode()) return title_id_str def get_title_version(self): - """Returns the version of the ticket's associated title that this ticket is designed for.""" + """Gets the version of the ticket's associated title that this ticket is designed for. + + Returns + ------- + int + The version of the title. + """ return self.title_version def get_common_key_index(self): - """Returns the index of the common key used to encrypt the Title Key contained in the ticket.""" + """Gets the index of the common key used to encrypt the Title Key contained in the ticket. + + Returns + ------- + int + The index of the common key required. + + See Also + -------- + commonkeys.get_common_key + """ return self.common_key_index def get_common_key_type(self): - """Returns the name of the common key used to encrypt the Title Key contained in the ticket.""" + """Gets the name of the common key used to encrypt the Title Key contained in the ticket. + + Returns + ------- + str + The name of the common key required. + + See Also + -------- + commonkeys.get_common_key + """ match self.common_key_index: case 0: return "Common" @@ -148,7 +218,13 @@ class Ticket: return "vWii" def get_title_key(self): - """Returns the decrypted title key contained in the ticket.""" + """Gets the decrypted title key contained in the ticket. + + Returns + ------- + bytes + The decrypted title key. + """ title_key = decrypt_title_key(self.title_key_enc, self.common_key_index, self.title_id) return title_key diff --git a/src/libWiiPy/tmd.py b/src/libWiiPy/tmd.py index 4e577bb..d9e62df 100644 --- a/src/libWiiPy/tmd.py +++ b/src/libWiiPy/tmd.py @@ -290,6 +290,11 @@ class TMD: def get_content_record(self, record): """Gets the content record at the specified index. + Parameters + ---------- + record : int + The content record to be retrieved. + Returns ------- ContentRecord