File Formats
Wiithon can parse several Nintendo formats directly, whether they come from an ISO or from a standalone file on disk.
Note
Every format class follows the same shape: read(stream) to parse,
write(stream) or get_bytes() to serialise back. All of them are
importable from the top-level package, for example from wiithon import Rarc.
Tip
To edit a file that lives inside an ISO. possibly nested in an archive.
you usually want patcher.edit_as() rather than these classes directly.
See Patching a Wii ISO.
RARC
RARC is Nintendo’s archive format, used to bundle multiple files into one.
from io import BytesIO
from wiithon import Rarc
with open("archive.arc", "rb") as f:
rarc = Rarc.read(f)
# Listing
for entry in rarc.entries:
print(entry.name, entry.data_size)
# Reading and replacing, by name
data = rarc.get_file("model.bmd")
rarc.replace_file("model.bmd", new_data)
# ...or by full path inside the archive
data = rarc.get_file_by_path("scene/model.bmd")
rarc.replace_file_by_path("scene/model.bmd", new_data)
# Serialising back
result = rarc.get_bytes()
# Extracting to disk
rarc.extract_to("output/")
See also
RARC Format for the on-disc structure and the hashing scheme.
Yaz0
Yaz0 is Nintendo’s compression format. Many RARC archives are Yaz0-compressed.
look for the Yaz0 magic bytes at offset 0.
from io import BytesIO
from wiithon import Yaz0
# Decompressing
with open("file.szs", "rb") as f:
yaz0 = Yaz0.read(f)
raw = yaz0.data
# Compressing
compressed = Yaz0.from_data(raw)
result = compressed.get_bytes()
Important
Yaz0.read() decompresses immediately: yaz0.data is the uncompressed
payload, not the compressed stream. Reading a non-Yaz0 file raises
InvalidFormatError.
See also
Yaz0 Format for the block and pointer encoding.
U8
U8 is the archive format used for banners and channel content.
from wiithon import U8
with open("banner.bin", "rb") as f:
u8 = U8.read(f)
data = u8.get_file("meta/banner.tpl")
u8.replace_file("meta/banner.tpl", new_data)
u8.extract_to("output/")
result = u8.get_bytes()
BNR
opening.bnr holds the banner shown in the Wii menu: the localised titles
(IMET header) plus an embedded U8 archive with the icon, banner and sound.
from io import BytesIO
from wiithon import BNR
bnr = BNR.read(BytesIO(patcher.read_file("opening.bnr")))
print(bnr.title)
bnr.imet.set_title("Modded game", language="English")
icon = bnr.get_icon() # meta/icon.bin
banner = bnr.get_banner() # meta/banner.bin
sound = bnr.get_sound() # meta/sound.bin
bnr.replace_sound(custom_sound)
patcher.replace_file("opening.bnr", bnr.get_bytes())
Note
For a simple title change, patcher.modify_banner_title() does all of the
above in one call.
DOL
The DOL is the main executable format for GameCube and Wii games.
from wiithon import DOL
with open("main.dol", "rb") as f:
dol = DOL.read(f)
value = dol.read_at(0x80123456, 4)
dol.write_at(0x80123456, b"\\xde\\xad\\xbe\\xef")
result = dol.to_bytes()
See also
Patching a Wii ISO for injecting new code, and PowerPC Instruction Reference for the PowerPC instruction encodings.
BCSV
BCSV is a table format. Think of a CSV with a typed, hashed header. Field names are stored as hashes, so you have to supply the names you expect.
from wiithon import BCSV
from wiithon.formats.bcsv import calculate_field_hash
field_names = {
calculate_field_hash("ScenarioNo"): "ScenarioNo",
}
bcsv = BCSV.read(stream, field_names=field_names, str_fmt="shift_jis")
for entry in bcsv.entries:
print(entry["ScenarioNo"])
Warning
The hash-to-name function is one-way: a field whose name you did not supply
shows up under its stringified hash. Passing the wrong str_fmt silently
produces some issues rather than an error. shift_jis is the usual choice
for Japanese-developed titles.
LZ77
LZ77 compression, used inside banner assets.
from wiithon import Lz77
compressed = Lz77.compress(raw_data)
raw_data = Lz77.uncompress(compressed, len(raw_data))