mirror of
https://github.com/NinjaCheetah/libWiiPy.git
synced 2025-04-25 12:51:01 -04:00
Added setting.txt parser, moved some modules under a new "nand" subpackage
This commit is contained in:
parent
1ae649afac
commit
9fb0fdbc17
@ -6,6 +6,7 @@
|
||||
:maxdepth: 4
|
||||
|
||||
libWiiPy.archive
|
||||
libWiiPy.nand
|
||||
libWiiPy.title
|
||||
```
|
||||
|
||||
|
27
docs/source/libWiiPy.nand.md
Normal file
27
docs/source/libWiiPy.nand.md
Normal file
@ -0,0 +1,27 @@
|
||||
# libWiiPy.nand package
|
||||
|
||||
## Submodules
|
||||
|
||||
### libWiiPy.nand.emunand module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.nand.emunand
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiiPy.nand.setting module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.nand.setting
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiiPy.nand.sys module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.nand.sys
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
@ -26,14 +26,6 @@
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiiPy.title.emunand module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.title.emunand
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiipy.title.iospatcher module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.title.iospatcher
|
||||
@ -50,14 +42,6 @@
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiiPy.title.sys module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.title.sys
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
### libWiiPy.title.ticket module
|
||||
```{eval-rst}
|
||||
.. automodule:: libWiiPy.title.ticket
|
||||
|
@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "libWiiPy"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
authors = [
|
||||
{ name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" },
|
||||
{ name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" }
|
||||
|
@ -3,7 +3,8 @@
|
||||
#
|
||||
# These are the essential submodules from libWiiPy that you'd probably want imported by default.
|
||||
|
||||
__all__ = ["archive", "title"]
|
||||
__all__ = ["archive", "nand", "title"]
|
||||
|
||||
from . import archive
|
||||
from . import nand
|
||||
from . import title
|
||||
|
6
src/libWiiPy/nand/__init__.py
Normal file
6
src/libWiiPy/nand/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
# "nand/__init__.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
|
||||
from .emunand import *
|
||||
from .setting import *
|
||||
from .sys import *
|
@ -1,4 +1,4 @@
|
||||
# "title/emunand.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# "nand/emunand.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
#
|
||||
# Code for handling setting up and modifying a Wii EmuNAND.
|
||||
@ -6,8 +6,8 @@
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
from .title import Title
|
||||
from .content import SharedContentMap as _SharedContentMap
|
||||
from ..title.title import Title
|
||||
from ..title.content import SharedContentMap as _SharedContentMap
|
||||
from .sys import UidSys as _UidSys
|
||||
|
||||
|
135
src/libWiiPy/nand/setting.py
Normal file
135
src/libWiiPy/nand/setting.py
Normal file
@ -0,0 +1,135 @@
|
||||
# "nand/setting.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
#
|
||||
# See https://wiibrew.org/wiki//title/00000001/00000002/data/setting.txt for information about setting.txt.
|
||||
|
||||
import io
|
||||
from ..shared import _pad_bytes
|
||||
|
||||
|
||||
_key = 0x73B5DBFA
|
||||
|
||||
class SettingTxt:
|
||||
"""
|
||||
A SettingTxt object that allows for decrypting and then parsing a setting.txt file from the Wii.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
area : str
|
||||
The region of the System Menu this file matches with.
|
||||
model : str
|
||||
The model of the console, usually RVL-001 or RVL-101.
|
||||
dvd : int
|
||||
Unknown, might have to do with indicating support for scrapped DVD playback capabilities.
|
||||
mpch : str
|
||||
Unknown, generally accepted value is "0x7FFE".
|
||||
code : str
|
||||
Unknown code, may match with manufacturer code in serial number?
|
||||
serial_number : str
|
||||
Serial number of the console.
|
||||
video : str
|
||||
Video mode, either NTSC or PAL.
|
||||
game : str
|
||||
Another region code, possibly set by the hidden region select channel.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.area: str = ""
|
||||
self.model: str = ""
|
||||
self.dvd: int = 0
|
||||
self.mpch: str = "" # What does this mean, Movie Player Channel? It's also a hex string, it seems.
|
||||
self.code: str = ""
|
||||
self.serial_number: str = ""
|
||||
self.video: str = ""
|
||||
self.game: str = ""
|
||||
|
||||
def load(self, setting_txt: bytes) -> None:
|
||||
"""
|
||||
Loads the raw data of an encrypted setting.txt file and decrypts it to parse its arguments
|
||||
|
||||
Parameters
|
||||
----------
|
||||
setting_txt : bytes
|
||||
The data of an encrypted setting.txt file.
|
||||
"""
|
||||
with io.BytesIO(setting_txt) as setting_data:
|
||||
global _key # I still don't actually know what *kind* of encryption this is.
|
||||
setting_txt_dec: [int] = []
|
||||
for i in range(0, 256):
|
||||
setting_txt_dec.append(int.from_bytes(setting_data.read(1)) ^ (_key & 0xff))
|
||||
_key = (_key << 1) | (_key >> 31)
|
||||
setting_txt_dec = bytes(setting_txt_dec)
|
||||
try:
|
||||
setting_str = setting_txt_dec.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
last_newline_pos = setting_txt_dec.rfind(b'\n') # This makes sure we don't try to decode any garbage data.
|
||||
setting_str = setting_txt_dec[:last_newline_pos + 1].decode('utf-8')
|
||||
self.load_decrypted(setting_str)
|
||||
|
||||
def load_decrypted(self, setting_txt: str) -> None:
|
||||
"""
|
||||
Loads the raw data of a decrypted setting.txt file and parses its arguments
|
||||
|
||||
Parameters
|
||||
----------
|
||||
setting_txt : str
|
||||
The data of a decrypted setting.txt file.
|
||||
"""
|
||||
setting_dict = {}
|
||||
print(setting_txt)
|
||||
# Iterate over every key in the file to create a dictionary.
|
||||
for line in setting_txt.splitlines():
|
||||
line = line.strip()
|
||||
if line is not None:
|
||||
key, value = line.split('=', 1)
|
||||
setting_dict[key.strip()] = value.strip()
|
||||
# Load the values from the dictionary into the object.
|
||||
self.area = setting_dict["AREA"]
|
||||
self.model = setting_dict["MODEL"]
|
||||
self.dvd = int(setting_dict["DVD"])
|
||||
self.mpch = setting_dict["MPCH"]
|
||||
self.code = setting_dict["CODE"]
|
||||
self.serial_number = setting_dict["SERNO"]
|
||||
self.video = setting_dict["VIDEO"]
|
||||
self.game = setting_dict["GAME"]
|
||||
|
||||
def dump(self) -> bytes:
|
||||
"""
|
||||
Dumps the SettingTxt object back into an encrypted bytes that the Wii can load.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bytes
|
||||
The setting.txt file as encrypted bytes.
|
||||
"""
|
||||
setting_str = self.dump_decrypted()
|
||||
setting_txt_dec = setting_str.encode()
|
||||
global _key
|
||||
# This could probably be made more efficient somehow.
|
||||
setting_txt_enc: [int] = []
|
||||
with io.BytesIO(setting_txt_dec) as setting_data:
|
||||
for i in range(0, len(setting_txt_dec)):
|
||||
setting_txt_enc.append(int.from_bytes(setting_data.read(1)) ^ (_key & 0xff))
|
||||
_key = (_key << 1) | (_key >> 31)
|
||||
setting_txt_enc = _pad_bytes(bytes(setting_txt_enc), 256)
|
||||
return setting_txt_enc
|
||||
|
||||
def dump_decrypted(self) -> str:
|
||||
"""
|
||||
Dumps the SettingTxt object into a decrypted string.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The setting.txt file as decrypted text.
|
||||
"""
|
||||
# Write the keys back into a text file that can then be manually edited or re-encrypted.
|
||||
setting_txt = ""
|
||||
setting_txt += f"AREA={self.area}\n"
|
||||
setting_txt += f"MODEL={self.model}\n"
|
||||
setting_txt += f"DVD={self.dvd}\n"
|
||||
setting_txt += f"MPCH={self.mpch}\n"
|
||||
setting_txt += f"CODE={self.code}\n"
|
||||
setting_txt += f"SERNO={self.serial_number}\n"
|
||||
setting_txt += f"VIDEO={self.video}\n"
|
||||
setting_txt += f"GAME={self.game}\n"
|
||||
return setting_txt
|
@ -1,4 +1,4 @@
|
||||
# "title/sys.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# "nand/sys.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
#
|
||||
# See https://wiibrew.org/wiki//sys/uid.sys for information about uid.sys.
|
||||
@ -28,7 +28,7 @@ class _UidSysEntry:
|
||||
class UidSys:
|
||||
"""
|
||||
A UidSys object to parse and edit the uid.sys file stored in /sys/ on the Wii's NAND. This file is used to track all
|
||||
the titles installed on the console.
|
||||
the titles that have been launched on a console.
|
||||
|
||||
Attributes
|
||||
----------
|
@ -3,10 +3,8 @@
|
||||
|
||||
from .content import *
|
||||
from .crypto import *
|
||||
from .emunand import *
|
||||
from .iospatcher import *
|
||||
from .nus import *
|
||||
from .sys import *
|
||||
from .ticket import *
|
||||
from .title import *
|
||||
from .tmd import *
|
||||
|
@ -37,7 +37,7 @@ class TMD:
|
||||
self.blob_header: bytes = b''
|
||||
self.signature_type: int = 0
|
||||
self.signature: bytes = b''
|
||||
self.issuer: bytes = b'' # Follows the format "Root-CA%08x-CP%08x"
|
||||
self.signature_issuer: str = "" # Follows the format "Root-CA%08x-CP%08x"
|
||||
self.tmd_version: int = 0 # This seems to always be 0 no matter what?
|
||||
self.ca_crl_version: int = 0 # Certificate Authority Certificate Revocation List version
|
||||
self.signer_crl_version: int = 0 # Certificate Policy Certificate Revocation List version
|
||||
@ -82,7 +82,7 @@ class TMD:
|
||||
self.signature = tmd_data.read(256)
|
||||
# Signing certificate issuer.
|
||||
tmd_data.seek(0x140)
|
||||
self.issuer = tmd_data.read(64)
|
||||
self.signature_issuer = str(tmd_data.read(64).decode())
|
||||
# TMD version, seems to usually be 0, but I've seen references to other numbers.
|
||||
tmd_data.seek(0x180)
|
||||
self.tmd_version = int.from_bytes(tmd_data.read(1))
|
||||
@ -175,7 +175,7 @@ class TMD:
|
||||
# Padding to 64 bytes.
|
||||
tmd_data += b'\x00' * 60
|
||||
# Signing certificate issuer.
|
||||
tmd_data += self.issuer
|
||||
tmd_data += str.encode(self.signature_issuer)
|
||||
# TMD version.
|
||||
tmd_data += int.to_bytes(self.tmd_version, 1)
|
||||
# Certificate Authority CRL version.
|
||||
|
Loading…
x
Reference in New Issue
Block a user