wiithon.disc.patcher module
Modifying a Wii disck image and rebuilding it
This module exposes WiiIsoPatcher. To inspect it without modifying it, see wiithon.disc.reader
- class wiithon.disc.patcher.WiiIsoPatcher(src_path)[source]
Bases:
objectCollects modifications to a wii ISO and writes a new one
Nothing is applied as you go. Every call records an intent and the whole set is replayed when
build()is called The source ISO is opened read only and never written toWarning
Only the DATA partition is patched. Other partition are copied to the output byte by byte
Example
>>> with WiiIsoPatcher("path/to/iso") as patcher: ... patcher.replace_file("opening.bnr", data) ... patcher.modify_title("My super game") ... patcher.build("output/iso")
- Parameters:
src_path (
str)
- reader: WiiIsoReader | None
WiiIsoReader. Used internally
- data_partition: WiiPartitionInfo | None
The opened data partition
- dol_modifier: Callable[[DOL], None] | None
A callback function that runs when build is called. Used for modifying the DOL
- fst_modifier: Callable[[FST], None] | None
Callback applied to the FST at build time, set by
modify_fst()
- modify_fst(fn)[source]
Register a callback that edits the file system table directly
The callback runs during
build(), before the additions and removals queued byadd_file()andremove_file()are applied- Parameters:
fn (
Callable[[FST],None]) – Called with the FST of the DATA partition. Its return value is ignored, modify the tree in place- Return type:
Note
Only one callback is kept. Calling this twice replaces the first
Example
>>> with WiiIsoPatcher("path/to/iso") as patcher: ... patcher.modify_fst(lambda x: print(x.count_files())) ... patcher.build("output")
- add_file(path, data)[source]
Queue a new file for insertion
The file is added to the file system table and its data is written at build time. Parent directories must exists
- remove_file(path)[source]
Queue a new file for deletion
If
pathwas queued byadd_file()earlier that pending addition is cancelled instead of scheduling a removal- Parameters:
path (
str) – Destination path inside the DATA partition. Leading and trailing slashes are stripped- Return type:
Note
Removing a path that does not exist just do nothing, since the FST finds nothing
- replace_file(path, data)[source]
Queue new contents for an existing file
Unlike
add_file(), this does not touch the file system table, so the file must already exist on the disc. The new data may be of any size
- list_files()[source]
List every file of the DATA partition
Warning
This reflects the source disc. Files queued by
add_file()orremove_file()do not appear or disappear untilbuild()
- read_file(path)[source]
Read a file from the source disc
- Parameters:
path (
str) – Path inside the DATA partition- Return type:
- Returns:
The contents as stored in the source ISO. Pending replacements are not applied, so reading a file you just replaced returns the original data
- Raises:
FstFileNotFoundError – If no such file exists
FstIsADirectoryError – If the path is a directory
- edit_as(path, cls, **kwargs)[source]
Edit a file in place parsed as a given format
Reads the file, parses it with
cls.read(), hands you the object, then serialises it back withobj.write()and queues the result as a replacement when the block exitsThe path may cross archive boundaries Given
"Stage.arc/scenariodata.bcsv", the RARC archive is opened, the inner file is extracted, and the archive is re-serialised around your changes Yaz0 compression is handled transparently. Everything is transparent. You want the object, you have the object- Parameters:
- Yields:
The parsed object, ready to modify
- Return type:
Iterator[T]
Warning
Each call re-reads from the source disc. Editing two files inside the same archive with two successive calls loses the first edit, because the second call reopens the original archive Do both edits in a single block, opening the archive itself as
RarcNote
If the block raises, nothing is written back
Example
>>> with patcher.edit_as("AstroDome/AstroDome.arc/stageinfo/layera", BCSV, str_fmt="shift_jis") as bcsv: ... for entry in bcsv.entries: ... entry["Timer"] = 0
- patch_dol(fn)[source]
Register a callback that patches the main executable
The callback runs during
build(), on the DOL of the DATA partition- Parameters:
fn (
Callable[[DOL],None]) – Called with the parsedDOL. Modify it in place- Return type:
Note
Only one callback is kept. Calling this twice replaces the first
See also
Patching a Wii ISO for code injection above the arena
- read_dol()[source]
Read the main executable of the source disc
- Return type:
- Returns:
The parsed DOL, without any pending patch applied
- get_infos()[source]
Summarise the source disc
- Return type:
- Returns:
A dict with keys
game_id,title,disc_numberandversiongame_idis decoded tostrand stripped of padding
- modify_banner_title(new_title, language='English')[source]
Change the title shown in the Wii menu, for one language
Reads
opening.bnr, rewrites its IMET header and queues the result as a replacement- Parameters:
- Raises:
ValueError – If
languageis not one of the values above- Return type:
- modify_title(new_title)[source]
Change the game title stored in the disc header
- Parameters:
new_title (
str) – New title. It is truncated to the field size when written- Return type:
Note
This changes the disc header only. The name shown in the Wii menu comes from the banner, see
modify_banner_title()
- modify_title_id(new_id)[source]
Change the game ID of the disc and of the ticket
- Parameters:
new_id (
str) – Exactly 6 ASCII characters, such as"FEUR69"- Raises:
RuntimeError – If
new_idis not 6 bytes once encodedUnicodeEncodeError – If
new_idcontains non-ASCII characters
- Return type:
Note
The ticket title ID is rebuilt as
0x00010000followed by the first four characters of the new ID
- build(output_path, progress_cb=None)[source]
Write the patched ISO
Every partition of the source disc is copied to the output The DATA partition additionally receives the queued file changes, the FST callback and the DOL callback Hashes and encryption are recomputed as required
- Parameters:
output_path (
str) – Path of the ISO to create. It is overwritten if it existsprogress_cb (default:
None) – Called with an integer percentage from 0 to 100. It is invoked once per partition, so the value restarts at 0 for each
- Return type:
Note
This is where all the work happens. Expect it to take a while and to need free disc space of roughly the size of the source ISO