A Python library for reading, patching and rebuilding Nintendo Wii disc images.
pip install wiithonRequires Python 3.11 or later. Type annotations are shipped (PEP 561).
Read a disc image:
from wiithon import WiiIsoReader
with WiiIsoReader("game.iso") as reader:
print(reader.disc_header.game_title)
partition = reader.open_partition(reader.get_data_partition())
for path in partition.list_files():
print(path)
data = partition.read_file("opening.bnr")Patch it and write a new image:
from wiithon import WiiIsoPatcher
with WiiIsoPatcher("game.iso") as patcher:
patcher.modify_banner_title("My Hack")
patcher.replace_file("StageData/Foo.arc", new_bytes)
patcher.add_file("custom/data.bin", b"...")
patcher.build("patched.iso")Edit a file inside an archive, without unpacking anything by hand:
from wiithon import WiiIsoPatcher, BCSV
with WiiIsoPatcher("game.iso") as patcher:
path = "StageData/CannonFleetGalaxy/CannonFleetGalaxyScenario.arc/scenariodata.bcsv"
with patcher.edit_as(path, BCSV, str_fmt="shift_jis") as bcsv:
for entry in bcsv.entries:
entry["LuigiModeTimer"] = 0
patcher.build("patched.iso")Patch the executable:
from wiithon import WiiIsoPatcher, DOL
def patch(dol: DOL) -> None:
dol.write_at(0x80123456, b"\x60\x00\x00\x00") # nop
print(dol.read_until_null_at(0x805A1234).decode("shift_jis"))
with WiiIsoPatcher("game.iso") as patcher:
patcher.patch_dol(patch)
patcher.build("patched.iso")Discs parses the disc header, partition table and File System Table. Partition data is decrypted on the fly, so reading one file does not mean decrypting the whole partition. Rebuilding handles re-encryption, the full H0–H3 Merkle tree and TMD fakesigning, so the result boots in Dolphin.
Building master a new image by copying an existing one, or from an
extracted sys/ + files/ directory tree. The PartitionSource interface
lets you plug in your own.
File formats DOL, BCSV, RARC, U8, Yaz0, LZ77, BNR with IMET titles. Compressed archives are resolved transparently: a path may cross into an archive, and into a Yaz0 layer wrapping it.
DOL patching read and write at virtual addresses, add text or data
sections, find code caves, locate and patch the arenaLo setter, inject
code above the arena. A PowerPC instruction encoder is included.
wiithon iso info game.iso
wiithon iso list game.iso
wiithon iso extract game.iso ./out
wiithon iso cat game.iso opening.bnr
wiithon rarc info archive.arc
wiithon rarc extract archive.arc ./out
wiithon dol caves main.dolAnything raised because of malformed data derives from WiithonError:
from wiithon import WiiIsoReader, InvalidDiscError, FstFileNotFoundError
try:
with WiiIsoReader("maybe.iso") as reader:
...
except InvalidDiscError:
print("not a Wii disc image")The "not found" variants also inherit from the matching builtin, so
except FileNotFoundError keeps working. Invalid arguments still raise
the usual ValueError and TypeError.
MIT see LICENSE.md.
Some people can be concerned about AI in project. So for transparency, this section will explain when and what is AI-generated. Everything is reread by me.
- English is not my mother tongue, i used AI for documentation. I'm writing a first documentation in english/french locally and i'm using AI to translate it and fix my spell mistakes. I'm doing also that for long docstring (because it's written in the API documentation) ;
- I used generative AI for some mock helper function in unit tests, because it's faster and i tested the mocking stuff by hand before adding them. It's the only code generated by an AI. Everything else that i wrote is not generated by AI ;
- I used AI personally when i struggle to understand some documentation when 3 websites says 3 differents things for one format (like rarc files, omg).
It's impossible to know if some people used AI in their pull request. I'm not banning AI in pull request but for the base code, i'm suggering to not use it. I put a lot of time to understand everything and i probably forgot some stuff about the beginning but i love so much understanding such things. If you think it's to much AI, it's completely up to you and i understand your position, really !