mirror of
				https://github.com/NinjaCheetah/libWiiPy.git
				synced 2025-10-31 07:36:18 -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 | :maxdepth: 4 | ||||||
| 
 | 
 | ||||||
| libWiiPy.archive | libWiiPy.archive | ||||||
|  | libWiiPy.nand | ||||||
| libWiiPy.title | 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: |    :show-inheritance: | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### libWiiPy.title.emunand module |  | ||||||
| ```{eval-rst} |  | ||||||
| .. automodule:: libWiiPy.title.emunand |  | ||||||
|    :members: |  | ||||||
|    :undoc-members: |  | ||||||
|    :show-inheritance: |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ### libWiipy.title.iospatcher module | ### libWiipy.title.iospatcher module | ||||||
| ```{eval-rst} | ```{eval-rst} | ||||||
| .. automodule:: libWiiPy.title.iospatcher | .. automodule:: libWiiPy.title.iospatcher | ||||||
| @ -50,14 +42,6 @@ | |||||||
|    :show-inheritance: |    :show-inheritance: | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### libWiiPy.title.sys module |  | ||||||
| ```{eval-rst} |  | ||||||
| .. automodule:: libWiiPy.title.sys |  | ||||||
|    :members: |  | ||||||
|    :undoc-members: |  | ||||||
|    :show-inheritance: |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ### libWiiPy.title.ticket module | ### libWiiPy.title.ticket module | ||||||
| ```{eval-rst} | ```{eval-rst} | ||||||
| .. automodule:: libWiiPy.title.ticket | .. automodule:: libWiiPy.title.ticket | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [project] | [project] | ||||||
| name = "libWiiPy" | name = "libWiiPy" | ||||||
| version = "0.5.0" | version = "0.5.1" | ||||||
| authors = [ | authors = [ | ||||||
|     { name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" }, |     { name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" }, | ||||||
|     { name="Lillian Skinner", email="lillian@randommeaninglesscharacters.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. | # 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 archive | ||||||
|  | from . import nand | ||||||
| from . import title | 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 | # https://github.com/NinjaCheetah/libWiiPy | ||||||
| # | # | ||||||
| # Code for handling setting up and modifying a Wii EmuNAND. | # Code for handling setting up and modifying a Wii EmuNAND. | ||||||
| @ -6,8 +6,8 @@ | |||||||
| import os | import os | ||||||
| import pathlib | import pathlib | ||||||
| import shutil | import shutil | ||||||
| from .title import Title | from ..title.title import Title | ||||||
| from .content import SharedContentMap as _SharedContentMap | from ..title.content import SharedContentMap as _SharedContentMap | ||||||
| from .sys import UidSys as _UidSys | 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 | # https://github.com/NinjaCheetah/libWiiPy | ||||||
| # | # | ||||||
| # See https://wiibrew.org/wiki//sys/uid.sys for information about uid.sys. | # See https://wiibrew.org/wiki//sys/uid.sys for information about uid.sys. | ||||||
| @ -28,7 +28,7 @@ class _UidSysEntry: | |||||||
| class UidSys: | 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 |     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 |     Attributes | ||||||
|     ---------- |     ---------- | ||||||
| @ -3,10 +3,8 @@ | |||||||
| 
 | 
 | ||||||
| from .content import * | from .content import * | ||||||
| from .crypto import * | from .crypto import * | ||||||
| from .emunand import * |  | ||||||
| from .iospatcher import * | from .iospatcher import * | ||||||
| from .nus import * | from .nus import * | ||||||
| from .sys import * |  | ||||||
| from .ticket import * | from .ticket import * | ||||||
| from .title import * | from .title import * | ||||||
| from .tmd import * | from .tmd import * | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ class TMD: | |||||||
|         self.blob_header: bytes = b'' |         self.blob_header: bytes = b'' | ||||||
|         self.signature_type: int = 0 |         self.signature_type: int = 0 | ||||||
|         self.signature: bytes = b'' |         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.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.ca_crl_version: int = 0  # Certificate Authority Certificate Revocation List version | ||||||
|         self.signer_crl_version: int = 0  # Certificate Policy 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) |             self.signature = tmd_data.read(256) | ||||||
|             # Signing certificate issuer. |             # Signing certificate issuer. | ||||||
|             tmd_data.seek(0x140) |             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 version, seems to usually be 0, but I've seen references to other numbers. | ||||||
|             tmd_data.seek(0x180) |             tmd_data.seek(0x180) | ||||||
|             self.tmd_version = int.from_bytes(tmd_data.read(1)) |             self.tmd_version = int.from_bytes(tmd_data.read(1)) | ||||||
| @ -175,7 +175,7 @@ class TMD: | |||||||
|         # Padding to 64 bytes. |         # Padding to 64 bytes. | ||||||
|         tmd_data += b'\x00' * 60 |         tmd_data += b'\x00' * 60 | ||||||
|         # Signing certificate issuer. |         # Signing certificate issuer. | ||||||
|         tmd_data += self.issuer |         tmd_data += str.encode(self.signature_issuer) | ||||||
|         # TMD version. |         # TMD version. | ||||||
|         tmd_data += int.to_bytes(self.tmd_version, 1) |         tmd_data += int.to_bytes(self.tmd_version, 1) | ||||||
|         # Certificate Authority CRL version. |         # Certificate Authority CRL version. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user