Entirely restructured package to make it more of a proper Python package

THIS IS A BREAKING COMMIT! ALL v0.2.X BASED PROJECTS WILL NEED TO BE UPDATED TO SUPPORT v0.3.X!
This commit is contained in:
Campbell 2024-06-02 22:18:05 -04:00
parent bc9224e40b
commit ade4b68394
Signed by: NinjaCheetah
GPG Key ID: B547958AF96ED344
14 changed files with 113 additions and 85 deletions

0
src/__init__.py Normal file
View File

View File

@ -1,14 +1,9 @@
# "__init__.py" from libWiiPy by NinjaCheetah & Contributors # "__init__.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # 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 * __all__ = ["archive", "title"]
from .content import *
from .ticket import * from . import archive
from .crypto import * from . import title
from .title import *
from .tmd import *
from .wad import *
from .nus import *
from .u8 import *

View File

@ -0,0 +1,4 @@
# "archive/__init__.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy
from .u8 import *

View File

@ -1,4 +1,4 @@
# "u8.py" from libWiiPy by NinjaCheetah & Contributors # "archive/u8.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/U8_archive for details about the U8 archive format # See https://wiibrew.org/wiki/U8_archive for details about the U8 archive format
@ -6,14 +6,38 @@
import io import io
import binascii import binascii
import os import os
from dataclasses import dataclass
from typing import List 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: class U8Archive:
def __init__(self): 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 Attributes
---------- ----------
@ -77,33 +101,54 @@ class U8Archive:
else: else:
self.u8_file_data_list.append(b'') self.u8_file_data_list.append(b'')
def extract_to_folder(self, output_folder) -> None: def dump(self) -> None:
if os.path.isdir(output_folder): """
raise ValueError("Output folder already exists!") Dumps the U8Archive object into a U8 file.
if self.u8_node_list is []: """
raise ValueError("No U8 file is loaded!") 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 = "" def extract_u8(u8_data, output_folder) -> None:
for node in range(len(self.u8_node_list)): if os.path.isdir(output_folder):
if self.u8_node_list[node].name_offset != 0: raise ValueError("Output folder already exists!")
if self.u8_node_list[node].type == 256:
if self.u8_node_list[node].data_offset == 0: os.mkdir(output_folder)
os.mkdir(os.path.join(output_folder, self.file_name_list[node]))
current_dir = self.file_name_list[node] u8_archive = U8Archive()
elif self.u8_node_list[node].data_offset < node: u8_archive.load(u8_data)
lower_path = os.path.join(output_folder, current_dir)
os.mkdir(os.path.join(lower_path, self.file_name_list[node])) current_dir = ""
current_dir = os.path.join(current_dir, self.file_name_list[node]) for node in range(len(u8_archive.u8_node_list)):
elif self.u8_node_list[node].type == 0: 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) lower_path = os.path.join(output_folder, current_dir)
output_file = open(os.path.join(lower_path, self.file_name_list[node]), "wb") os.mkdir(os.path.join(lower_path, u8_archive.file_name_list[node]))
output_file.write(self.u8_file_data_list[node]) current_dir = os.path.join(current_dir, u8_archive.file_name_list[node])
output_file.close() elif u8_archive.u8_node_list[node].type == 0:
lower_path = os.path.join(output_folder, current_dir)
def pack_from_folder(self, input_folder) -> None: output_file = open(os.path.join(lower_path, u8_archive.file_name_list[node]), "wb")
if not os.path.isdir(input_folder): output_file.write(u8_archive.u8_file_data_list[node])
raise ValueError("Input folder does not exist!") 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())

View File

@ -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 *

View File

@ -1,4 +1,4 @@
# "commonkeys.py" from libWiiPy by NinjaCheetah & Contributors # "title/commonkeys.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
import binascii import binascii

View File

@ -1,4 +1,4 @@
# "content.py" from libWiiPy by NinjaCheetah & Contributors # "title/content.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/Title for details about how titles are formatted # See https://wiibrew.org/wiki/Title for details about how titles are formatted
@ -7,8 +7,8 @@ import io
import sys import sys
import hashlib import hashlib
from typing import List from typing import List
from .types import ContentRecord from src.libWiiPy.types import ContentRecord
from .crypto import decrypt_content, encrypt_content from src.libWiiPy.title.crypto import decrypt_content, encrypt_content
class ContentRegion: class ContentRegion:

View File

@ -1,9 +1,9 @@
# "crypto.py" from libWiiPy by NinjaCheetah & Contributors # "title/crypto.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
import struct import struct
from .commonkeys import get_common_key from src.libWiiPy.title.commonkeys import get_common_key
from .shared import convert_tid_to_iv from src.libWiiPy.shared import convert_tid_to_iv
from Crypto.Cipher import AES from Crypto.Cipher import AES

View File

@ -1,4 +1,4 @@
# "nus.py" from libWiiPy by NinjaCheetah & Contributors # "title/nus.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/NUS for details about the NUS # See https://wiibrew.org/wiki/NUS for details about the NUS
@ -6,9 +6,9 @@
import requests import requests
import hashlib import hashlib
from typing import List from typing import List
from .title import Title from src.libWiiPy.title.title import Title
from .tmd import TMD from src.libWiiPy.title.tmd import TMD
from .ticket import Ticket 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/"] nus_endpoint = ["http://nus.cdn.shop.wii.com/ccs/download/", "http://ccs.cdn.wup.shop.nintendo.net/ccs/download/"]

View File

@ -1,12 +1,12 @@
# "ticket.py" from libWiiPy by NinjaCheetah & Contributors # "title/ticket.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/Ticket for details about the ticket format # See https://wiibrew.org/wiki/Ticket for details about the ticket format
import io import io
import binascii import binascii
from .crypto import decrypt_title_key from src.libWiiPy.title.crypto import decrypt_title_key
from .types import TitleLimit from src.libWiiPy.types import TitleLimit
from typing import List from typing import List

View File

@ -1,12 +1,12 @@
# "title.py" from libWiiPy by NinjaCheetah & Contributors # "title/title.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/Title for details about how titles are formatted # See https://wiibrew.org/wiki/Title for details about how titles are formatted
from .content import ContentRegion from src.libWiiPy.title.content import ContentRegion
from .ticket import Ticket from src.libWiiPy.title.ticket import Ticket
from .tmd import TMD from src.libWiiPy.title.tmd import TMD
from .wad import WAD from src.libWiiPy.title.wad import WAD
class Title: class Title:

View File

@ -1,4 +1,4 @@
# "tmd.py" from libWiiPy by NinjaCheetah & Contributors # "title/tmd.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/Title_metadata for details about the TMD format # See https://wiibrew.org/wiki/Title_metadata for details about the TMD format
@ -7,7 +7,7 @@ import io
import binascii import binascii
import struct import struct
from typing import List from typing import List
from .types import ContentRecord from src.libWiiPy.types import ContentRecord
class TMD: class TMD:

View File

@ -1,11 +1,11 @@
# "wad.py" from libWiiPy by NinjaCheetah & Contributors # "title/wad.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy # https://github.com/NinjaCheetah/libWiiPy
# #
# See https://wiibrew.org/wiki/WAD_files for details about the WAD format # See https://wiibrew.org/wiki/WAD_files for details about the WAD format
import io import io
import binascii import binascii
from .shared import align_value, pad_bytes from src.libWiiPy.shared import align_value, pad_bytes
class WAD: class WAD:

View File

@ -49,27 +49,3 @@ class TitleLimit:
limit_type: int limit_type: int
# The maximum value of the limit applied. # The maximum value of the limit applied.
maximum_usage: int 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