From 6ab9993dd97695cf8f47a47531850be3d023117f Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Tue, 18 Mar 2025 20:39:38 -0400 Subject: [PATCH] Fixed content decryption, created base for real rustii CLI rustii currently only supports unpacking WADs, with packing support being a work-in-progress. --- Cargo.lock | 235 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 ++ src/bin/playground/main.rs | 37 ++++++ src/bin/rustii/main.rs | 74 +++++++----- src/bin/rustii/title/mod.rs | 4 + src/bin/rustii/title/wad.rs | 59 +++++++++ src/lib.rs | 4 +- src/title/commonkeys.rs | 2 +- src/title/content.rs | 7 +- src/title/crypto.rs | 4 +- src/title/mod.rs | 4 +- src/title/ticket.rs | 4 +- src/title/tmd.rs | 4 +- src/title/wad.rs | 6 +- 14 files changed, 406 insertions(+), 50 deletions(-) create mode 100644 src/bin/playground/main.rs create mode 100644 src/bin/rustii/title/mod.rs create mode 100644 src/bin/rustii/title/wad.rs diff --git a/Cargo.lock b/Cargo.lock index d61c663..74cb54e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,56 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -62,6 +112,52 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -101,6 +197,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -117,12 +219,42 @@ dependencies = [ "generic-array", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rustii" version = "0.1.0" @@ -130,6 +262,7 @@ dependencies = [ "aes", "byteorder", "cbc", + "clap", "hex", "sha1", ] @@ -145,14 +278,116 @@ dependencies = [ "digest", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index ca31a02..06908c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,23 @@ [package] name = "rustii" +authors = ["NinjaCheetah "] +license = "MIT" +description = "A Rust library and CLI for handling files and formats used by the Wii" version = "0.1.0" +readme = "README.md" +homepage = "https://github.com/NinjaCheetah/rustii" +repository = "https://github.com/NinjaCheetah/rustii" edition = "2024" +default-run = "rustii" [[bin]] name = "rustii" path = "src/bin/rustii/main.rs" +[[bin]] +name = "playground" +path = "src/bin/playground/main.rs" + [lib] path = "src/lib.rs" test = true @@ -18,3 +29,4 @@ cbc = "0" aes = "0" hex = "0" sha1 = "0" +clap = { version = "4", features = ["derive"] } diff --git a/src/bin/playground/main.rs b/src/bin/playground/main.rs new file mode 100644 index 0000000..ee10f39 --- /dev/null +++ b/src/bin/playground/main.rs @@ -0,0 +1,37 @@ +// Sample file for testing rustii library stuff. + +use std::fs; +use rustii::title::{tmd, ticket, content, crypto, wad}; + +fn main() { + let data = fs::read("sm.wad").unwrap(); + let wad = wad::WAD::from_bytes(&data).unwrap(); + println!("size of tmd: {:?}", wad.tmd().len()); + let tmd = tmd::TMD::from_bytes(&wad.tmd()).unwrap(); + println!("num content records: {:?}", tmd.content_records.len()); + println!("first record data: {:?}", tmd.content_records.first().unwrap()); + assert_eq!(wad.tmd(), tmd.to_bytes().unwrap()); + + let tik = ticket::Ticket::from_bytes(&wad.ticket()).unwrap(); + println!("title version from ticket is: {:?}", tik.title_version); + println!("title key (enc): {:?}", tik.title_key); + println!("title key (dec): {:?}", tik.dec_title_key()); + assert_eq!(wad.ticket(), tik.to_bytes().unwrap()); + + let content_region = content::ContentRegion::from_bytes(&wad.content(), tmd.content_records).unwrap(); + assert_eq!(wad.content(), content_region.to_bytes().unwrap()); + println!("content OK"); + + let content_dec = content_region.get_content_by_index(0, tik.dec_title_key()).unwrap(); + println!("content dec from index: {:?}", content_dec); + + let content = content_region.get_enc_content_by_index(0).unwrap(); + assert_eq!(content, crypto::encrypt_content(&content_dec, tik.dec_title_key(), 0, content_region.content_records[0].content_size)); + println!("content re-encrypted OK"); + + println!("wad header: {:?}", wad.header); + + let repacked = wad.to_bytes().unwrap(); + assert_eq!(repacked, data); + println!("wad packed OK"); +} diff --git a/src/bin/rustii/main.rs b/src/bin/rustii/main.rs index 45b3c96..4a39bec 100644 --- a/src/bin/rustii/main.rs +++ b/src/bin/rustii/main.rs @@ -1,37 +1,45 @@ -// Sample file for testing rustii library stuff. +// main.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii +// +// Base for the rustii CLI that handles argument parsing and directs execution to the proper module. -use std::fs; -use rustii::title::{tmd, ticket, content, crypto, wad}; +mod title; +use clap::{Subcommand, Parser}; +use title::wad; + + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +#[command(arg_required_else_help = true)] +enum Commands { + /// Pack/unpack/edit a WAD file + Wad { + #[command(subcommand)] + command: Option, + }, +} fn main() { - let data = fs::read("sm.wad").unwrap(); - let wad = wad::WAD::from_bytes(&data).unwrap(); - println!("size of tmd: {:?}", wad.tmd().len()); - let tmd = tmd::TMD::from_bytes(&wad.tmd()).unwrap(); - println!("num content records: {:?}", tmd.content_records.len()); - println!("first record data: {:?}", tmd.content_records.first().unwrap()); - assert_eq!(wad.tmd(), tmd.to_bytes().unwrap()); + let cli = Cli::parse(); - let tik = ticket::Ticket::from_bytes(&wad.ticket()).unwrap(); - println!("title version from ticket is: {:?}", tik.title_version); - println!("title key (enc): {:?}", tik.title_key); - println!("title key (dec): {:?}", tik.dec_title_key()); - assert_eq!(wad.ticket(), tik.to_bytes().unwrap()); - - let content_region = content::ContentRegion::from_bytes(&wad.content(), tmd.content_records).unwrap(); - assert_eq!(wad.content(), content_region.to_bytes().unwrap()); - println!("content OK"); - - let content_dec = content_region.get_content_by_index(0, tik.dec_title_key()).unwrap(); - println!("content dec from index: {:?}", content_dec); - - let content = content_region.get_enc_content_by_index(0).unwrap(); - assert_eq!(content, crypto::encrypt_content(&content_dec, tik.dec_title_key(), 0, content_region.content_records[0].content_size)); - println!("content re-encrypted OK"); - - println!("wad header: {:?}", wad.header); - - let repacked = wad.to_bytes().unwrap(); - assert_eq!(repacked, data); - println!("wad packed OK"); -} + match &cli.command { + Some(Commands::Wad { command }) => { + match command { + Some(wad::Commands::Pack { input, output}) => { + wad::pack_wad(input, output) + }, + Some(wad::Commands::Unpack { input, output }) => { + wad::unpack_wad(input, output) + }, + &None => { /* This is handled by clap */} + } + } + None => {} + } +} \ No newline at end of file diff --git a/src/bin/rustii/title/mod.rs b/src/bin/rustii/title/mod.rs new file mode 100644 index 0000000..99a2536 --- /dev/null +++ b/src/bin/rustii/title/mod.rs @@ -0,0 +1,4 @@ +// title/mod.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii + +pub mod wad; diff --git a/src/bin/rustii/title/wad.rs b/src/bin/rustii/title/wad.rs new file mode 100644 index 0000000..2863675 --- /dev/null +++ b/src/bin/rustii/title/wad.rs @@ -0,0 +1,59 @@ +// title/wad.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii +// +// Code for WAD-related commands in the rustii CLI. + +use clap::Subcommand; +use std::{str, fs}; +use std::path::Path; +use rustii::title::{tmd, ticket, wad, content}; + +#[derive(Subcommand)] +#[command(arg_required_else_help = true)] +pub enum Commands { + /// Pack a directory into a WAD file + Pack { + input: String, + output: String + }, + /// Unpack a WAD file into a directory + Unpack { + input: String, + output: String + } +} + +pub fn pack_wad(input: &str, output: &str) { + print!("packing"); +} + +pub fn unpack_wad(input: &str, output: &str) { + let wad_file = fs::read(input).expect("could not read WAD"); + let wad = wad::WAD::from_bytes(&wad_file).expect("could not parse WAD"); + let tmd = tmd::TMD::from_bytes(&wad.tmd()).expect("could not parse TMD"); + let tik = ticket::Ticket::from_bytes(&wad.ticket()).expect("could not parse Ticket"); + let cert_data = &wad.cert_chain(); + let meta_data = &wad.meta(); + // Create output directory if it doesn't exist. + if !Path::new(output).exists() { + fs::create_dir(output).expect("could not create output directory"); + } + let out_path = Path::new(output); + // Write out all WAD components. + let tmd_file_name = format!("{}.tmd", hex::encode(tmd.title_id)); + fs::write(Path::join(out_path, tmd_file_name), tmd.to_bytes().unwrap()).expect("could not write TMD file"); + let ticket_file_name = format!("{}.tik", hex::encode(tmd.title_id)); + fs::write(Path::join(out_path, ticket_file_name), tik.to_bytes().unwrap()).expect("could not write Ticket file"); + let cert_file_name = format!("{}.cert", hex::encode(tmd.title_id)); + fs::write(Path::join(out_path, cert_file_name), cert_data).expect("could not write Cert file"); + let meta_file_name = format!("{}.footer", hex::encode(tmd.title_id)); + fs::write(Path::join(out_path, meta_file_name), meta_data).expect("could not write footer file"); + // Iterate over contents, decrypt them, and write them out. + let content_region = content::ContentRegion::from_bytes(&wad.content(), tmd.content_records).unwrap(); + for i in 0..tmd.num_contents { + let content_file_name = format!("{:08X}.app", content_region.content_records[i as usize].index); + let dec_content = content_region.get_content_by_index(i as usize, tik.dec_title_key()).unwrap(); + fs::write(Path::join(out_path, content_file_name), dec_content).unwrap(); + } + println!("WAD file unpacked!"); +} diff --git a/src/lib.rs b/src/lib.rs index e06eff8..93c65c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ -// lib.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// lib.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii pub mod title; diff --git a/src/title/commonkeys.rs b/src/title/commonkeys.rs index 3a22078..2037a24 100644 --- a/src/title/commonkeys.rs +++ b/src/title/commonkeys.rs @@ -1,5 +1,5 @@ // title/commonkeys.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// https://github.com/NinjaCheetah/rustii const COMMON_KEY: &str = "ebe42a225e8593e448d9c5457381aaf7"; const KOREAN_KEY: &str = "63b82bb4f4614e2e13f2fefbba4c9b7e"; diff --git a/src/title/content.rs b/src/title/content.rs index 4b91754..27bd368 100644 --- a/src/title/content.rs +++ b/src/title/content.rs @@ -1,5 +1,5 @@ -// title/content.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/content.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii // // Implements content parsing and editing. @@ -95,7 +95,8 @@ impl ContentRegion { pub fn get_content_by_index(&self, index: usize, title_key: [u8; 16]) -> Result, ContentError> { let content = self.get_enc_content_by_index(index)?; // Verify the hash of the decrypted content against its record. - let content_dec = decrypt_content(&content, title_key, self.content_records[index].index); + let mut content_dec = decrypt_content(&content, title_key, self.content_records[index].index); + content_dec.resize(self.content_records[index].content_size as usize, 0); let mut hasher = Sha1::new(); hasher.update(content_dec.clone()); let result = hasher.finalize(); diff --git a/src/title/crypto.rs b/src/title/crypto.rs index 729a020..da95843 100644 --- a/src/title/crypto.rs +++ b/src/title/crypto.rs @@ -1,5 +1,5 @@ -// title/crypto.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/crypto.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii // // Implements the common crypto functions required to handle Wii content encryption. diff --git a/src/title/mod.rs b/src/title/mod.rs index 81dbf4a..9dbccc7 100644 --- a/src/title/mod.rs +++ b/src/title/mod.rs @@ -1,5 +1,5 @@ -// title/mod.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/mod.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii pub mod commonkeys; pub mod content; diff --git a/src/title/ticket.rs b/src/title/ticket.rs index f997f3e..b31cec1 100644 --- a/src/title/ticket.rs +++ b/src/title/ticket.rs @@ -1,5 +1,5 @@ -// title/tik.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/tik.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii // // Implements the structures and methods required for Ticket parsing and editing. diff --git a/src/title/tmd.rs b/src/title/tmd.rs index 91cd4c0..6500ed3 100644 --- a/src/title/tmd.rs +++ b/src/title/tmd.rs @@ -1,5 +1,5 @@ -// title/tmd.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/tmd.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii // // Implements the structures and methods required for TMD parsing and editing. diff --git a/src/title/wad.rs b/src/title/wad.rs index 6626a35..feca4e8 100644 --- a/src/title/wad.rs +++ b/src/title/wad.rs @@ -1,5 +1,5 @@ -// title/wad.rs from rustii-lib (c) 2025 NinjaCheetah & Contributors -// https://github.com/NinjaCheetah/rustii-lib +// title/wad.rs from rustii (c) 2025 NinjaCheetah & Contributors +// https://github.com/NinjaCheetah/rustii // // Implements the structures and methods required for WAD parsing and editing. @@ -141,7 +141,7 @@ impl WAD { }; Ok(wad) } - + pub fn to_bytes(&self) -> Result, WADError> { let mut buf = Vec::new(); buf.write_u32::(self.header.header_size).map_err(WADError::IOError)?;