diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/libWiiPy/__init__.py b/src/libWiiPy/__init__.py index f381d5b..0d3aa40 100644 --- a/src/libWiiPy/__init__.py +++ b/src/libWiiPy/__init__.py @@ -1,14 +1,9 @@ # "__init__.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # -# These are the essential modules from libWiiPy that you'd probably want imported by default. +# These are the essential submodules from libWiiPy that you'd probably want imported by default. -from .commonkeys import * -from .content import * -from .ticket import * -from .crypto import * -from .title import * -from .tmd import * -from .wad import * -from .nus import * -from .u8 import * +__all__ = ["archive", "title"] + +from . import archive +from . import title diff --git a/src/libWiiPy/archive/__init__.py b/src/libWiiPy/archive/__init__.py new file mode 100644 index 0000000..39144ba --- /dev/null +++ b/src/libWiiPy/archive/__init__.py @@ -0,0 +1,4 @@ +# "archive/__init__.py" from libWiiPy by NinjaCheetah & Contributors +# https://github.com/NinjaCheetah/libWiiPy + +from .u8 import * diff --git a/src/libWiiPy/u8.py b/src/libWiiPy/archive/u8.py similarity index 54% rename from src/libWiiPy/u8.py rename to src/libWiiPy/archive/u8.py index 69a5ad4..0dd9847 100644 --- a/src/libWiiPy/u8.py +++ b/src/libWiiPy/archive/u8.py @@ -1,4 +1,4 @@ -# "u8.py" from libWiiPy by NinjaCheetah & Contributors +# "archive/u8.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/U8_archive for details about the U8 archive format @@ -6,14 +6,38 @@ import io import binascii import os +from dataclasses import dataclass from typing import List -from .types import U8Node + + +@dataclass +class U8Node: + """ + A U8Node object that contains the data of a single node in a U8 file header. Each node keeps track of whether this + node is for a file or directory, the offset of the name of the file/directory, the offset of the data for the file/ + directory, and the size of the data. + + Attributes + ---------- + type : int + Whether this node refers to a file or a directory. Either 0x0000 for files, or 0x0100 for directories. + name_offset : int + The offset of the name of the file/directory this node refers to. + data_offset : int + The offset of the data for the file/directory this node refers to. + size : int + The size of the data for this node. + """ + type: int + name_offset: int + data_offset: int + size: int class U8Archive: def __init__(self): """ - A U8 object that allows for extracting and packing U8 archives. + A U8 object that allows for managing the contents of a U8 archive. Attributes ---------- @@ -77,33 +101,54 @@ class U8Archive: else: self.u8_file_data_list.append(b'') - def extract_to_folder(self, output_folder) -> None: - if os.path.isdir(output_folder): - raise ValueError("Output folder already exists!") - if self.u8_node_list is []: - raise ValueError("No U8 file is loaded!") + def dump(self) -> None: + """ + Dumps the U8Archive object into a U8 file. + """ + u8_data = b'' + # Magic number. + u8_data += b'\x55\xAA\x38\x2D' + # Root node offset (this is always 0x20). + u8_data += int.to_bytes(0x20, 4) - os.mkdir(output_folder) - current_dir = "" - for node in range(len(self.u8_node_list)): - if self.u8_node_list[node].name_offset != 0: - if self.u8_node_list[node].type == 256: - if self.u8_node_list[node].data_offset == 0: - os.mkdir(os.path.join(output_folder, self.file_name_list[node])) - current_dir = self.file_name_list[node] - elif self.u8_node_list[node].data_offset < node: - lower_path = os.path.join(output_folder, current_dir) - os.mkdir(os.path.join(lower_path, self.file_name_list[node])) - current_dir = os.path.join(current_dir, self.file_name_list[node]) - elif self.u8_node_list[node].type == 0: +def extract_u8(u8_data, output_folder) -> None: + if os.path.isdir(output_folder): + raise ValueError("Output folder already exists!") + + os.mkdir(output_folder) + + u8_archive = U8Archive() + u8_archive.load(u8_data) + + current_dir = "" + for node in range(len(u8_archive.u8_node_list)): + if u8_archive.u8_node_list[node].name_offset != 0: + if u8_archive.u8_node_list[node].type == 256: + if u8_archive.u8_node_list[node].data_offset == 0: + os.mkdir(os.path.join(output_folder, u8_archive.file_name_list[node])) + current_dir = u8_archive.file_name_list[node] + elif u8_archive.u8_node_list[node].data_offset < node: lower_path = os.path.join(output_folder, current_dir) - output_file = open(os.path.join(lower_path, self.file_name_list[node]), "wb") - output_file.write(self.u8_file_data_list[node]) - output_file.close() - - def pack_from_folder(self, input_folder) -> None: - if not os.path.isdir(input_folder): - raise ValueError("Input folder does not exist!") + os.mkdir(os.path.join(lower_path, u8_archive.file_name_list[node])) + current_dir = os.path.join(current_dir, u8_archive.file_name_list[node]) + elif u8_archive.u8_node_list[node].type == 0: + lower_path = os.path.join(output_folder, current_dir) + output_file = open(os.path.join(lower_path, u8_archive.file_name_list[node]), "wb") + output_file.write(u8_archive.u8_file_data_list[node]) + output_file.close() + + +def pack_u8(input_data) -> None: + if os.path.isdir(input_data): + raise ValueError("Only single-file packing is currently supported!") + elif os.path.isfile(input_data): + with open(input_data, "rb") as f: + u8_archive = U8Archive() + + file_name = os.path.basename(input_data) + + u8_archive.file_name_list.append(file_name) + u8_archive.u8_file_data_list.append(f.read()) diff --git a/src/libWiiPy/title/__init__.py b/src/libWiiPy/title/__init__.py new file mode 100644 index 0000000..a54b791 --- /dev/null +++ b/src/libWiiPy/title/__init__.py @@ -0,0 +1,8 @@ +# "title/__init__.py" from libWiiPy by NinjaCheetah & Contributors +# https://github.com/NinjaCheetah/libWiiPy + +from .content import * +from .ticket import * +from .title import * +from .tmd import * +from .wad import * diff --git a/src/libWiiPy/commonkeys.py b/src/libWiiPy/title/commonkeys.py similarity index 93% rename from src/libWiiPy/commonkeys.py rename to src/libWiiPy/title/commonkeys.py index 04755e2..9875bff 100644 --- a/src/libWiiPy/commonkeys.py +++ b/src/libWiiPy/title/commonkeys.py @@ -1,4 +1,4 @@ -# "commonkeys.py" from libWiiPy by NinjaCheetah & Contributors +# "title/commonkeys.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy import binascii diff --git a/src/libWiiPy/content.py b/src/libWiiPy/title/content.py similarity index 98% rename from src/libWiiPy/content.py rename to src/libWiiPy/title/content.py index 0029759..c2a3392 100644 --- a/src/libWiiPy/content.py +++ b/src/libWiiPy/title/content.py @@ -1,4 +1,4 @@ -# "content.py" from libWiiPy by NinjaCheetah & Contributors +# "title/content.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/Title for details about how titles are formatted @@ -7,8 +7,8 @@ import io import sys import hashlib from typing import List -from .types import ContentRecord -from .crypto import decrypt_content, encrypt_content +from src.libWiiPy.types import ContentRecord +from src.libWiiPy.title.crypto import decrypt_content, encrypt_content class ContentRegion: diff --git a/src/libWiiPy/crypto.py b/src/libWiiPy/title/crypto.py similarity index 96% rename from src/libWiiPy/crypto.py rename to src/libWiiPy/title/crypto.py index 734f0bf..5f7d88c 100644 --- a/src/libWiiPy/crypto.py +++ b/src/libWiiPy/title/crypto.py @@ -1,9 +1,9 @@ -# "crypto.py" from libWiiPy by NinjaCheetah & Contributors +# "title/crypto.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy import struct -from .commonkeys import get_common_key -from .shared import convert_tid_to_iv +from src.libWiiPy.title.commonkeys import get_common_key +from src.libWiiPy.shared import convert_tid_to_iv from Crypto.Cipher import AES diff --git a/src/libWiiPy/nus.py b/src/libWiiPy/title/nus.py similarity index 97% rename from src/libWiiPy/nus.py rename to src/libWiiPy/title/nus.py index 4282860..d9085c3 100644 --- a/src/libWiiPy/nus.py +++ b/src/libWiiPy/title/nus.py @@ -1,4 +1,4 @@ -# "nus.py" from libWiiPy by NinjaCheetah & Contributors +# "title/nus.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/NUS for details about the NUS @@ -6,9 +6,9 @@ import requests import hashlib from typing import List -from .title import Title -from .tmd import TMD -from .ticket import Ticket +from src.libWiiPy.title.title import Title +from src.libWiiPy.title.tmd import TMD +from src.libWiiPy.title.ticket import Ticket nus_endpoint = ["http://nus.cdn.shop.wii.com/ccs/download/", "http://ccs.cdn.wup.shop.nintendo.net/ccs/download/"] diff --git a/src/libWiiPy/ticket.py b/src/libWiiPy/title/ticket.py similarity index 98% rename from src/libWiiPy/ticket.py rename to src/libWiiPy/title/ticket.py index 8f6a0aa..6e433b0 100644 --- a/src/libWiiPy/ticket.py +++ b/src/libWiiPy/title/ticket.py @@ -1,12 +1,12 @@ -# "ticket.py" from libWiiPy by NinjaCheetah & Contributors +# "title/ticket.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/Ticket for details about the ticket format import io import binascii -from .crypto import decrypt_title_key -from .types import TitleLimit +from src.libWiiPy.title.crypto import decrypt_title_key +from src.libWiiPy.types import TitleLimit from typing import List diff --git a/src/libWiiPy/title.py b/src/libWiiPy/title/title.py similarity index 97% rename from src/libWiiPy/title.py rename to src/libWiiPy/title/title.py index 4478a62..d0d6690 100644 --- a/src/libWiiPy/title.py +++ b/src/libWiiPy/title/title.py @@ -1,12 +1,12 @@ -# "title.py" from libWiiPy by NinjaCheetah & Contributors +# "title/title.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/Title for details about how titles are formatted -from .content import ContentRegion -from .ticket import Ticket -from .tmd import TMD -from .wad import WAD +from src.libWiiPy.title.content import ContentRegion +from src.libWiiPy.title.ticket import Ticket +from src.libWiiPy.title.tmd import TMD +from src.libWiiPy.title.wad import WAD class Title: diff --git a/src/libWiiPy/tmd.py b/src/libWiiPy/title/tmd.py similarity index 99% rename from src/libWiiPy/tmd.py rename to src/libWiiPy/title/tmd.py index 4510cc4..fd3c5cf 100644 --- a/src/libWiiPy/tmd.py +++ b/src/libWiiPy/title/tmd.py @@ -1,4 +1,4 @@ -# "tmd.py" from libWiiPy by NinjaCheetah & Contributors +# "title/tmd.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/Title_metadata for details about the TMD format @@ -7,7 +7,7 @@ import io import binascii import struct from typing import List -from .types import ContentRecord +from src.libWiiPy.types import ContentRecord class TMD: diff --git a/src/libWiiPy/wad.py b/src/libWiiPy/title/wad.py similarity index 99% rename from src/libWiiPy/wad.py rename to src/libWiiPy/title/wad.py index 19d1700..12ba493 100644 --- a/src/libWiiPy/wad.py +++ b/src/libWiiPy/title/wad.py @@ -1,11 +1,11 @@ -# "wad.py" from libWiiPy by NinjaCheetah & Contributors +# "title/wad.py" from libWiiPy by NinjaCheetah & Contributors # https://github.com/NinjaCheetah/libWiiPy # # See https://wiibrew.org/wiki/WAD_files for details about the WAD format import io import binascii -from .shared import align_value, pad_bytes +from src.libWiiPy.shared import align_value, pad_bytes class WAD: diff --git a/src/libWiiPy/types.py b/src/libWiiPy/types.py index 9dd6813..155090e 100644 --- a/src/libWiiPy/types.py +++ b/src/libWiiPy/types.py @@ -49,27 +49,3 @@ class TitleLimit: limit_type: int # The maximum value of the limit applied. maximum_usage: int - - -@dataclass -class U8Node: - """ - A U8Node object that contains the data of a single node in a U8 file header. Each node keeps track of whether this - node is for a file or directory, the offset of the name of the file/directory, the offset of the data for the file/ - directory, and the size of the data. - - Attributes - ---------- - type : int - Whether this node refers to a file or a directory. Either 0x0000 for files, or 0x0100 for directories. - name_offset : int - The offset of the name of the file/directory this node refers to. - data_offset : int - The offset of the data for the file/directory this node refers to. - size : int - The size of the data for this node. - """ - type: int - name_offset: int - data_offset: int - size: int