use colored::Colorize;
use std::{
fmt::Display,
fs,
io::{BufWriter, Read, Write},
os::unix::prelude::FileExt,
path::Path,
process::exit,
};
use serde::{Deserialize, Serialize};
use RomType::*;
const ROM_LEN: u32 = 8388608;
#[derive(PartialEq, Eq, Debug, Deserialize, Serialize, Clone, Copy)]
pub enum RomType {
#[serde(rename = "rom_ByteSwapped")]
ByteSwapped,
#[serde(rename = "rom_LittleEndian")]
LittleEndian,
#[serde(rename = "rom_BigEndian")]
BigEndian,
}
impl RomType {
fn as_str(&self) -> &str {
match self {
ByteSwapped => "v64",
LittleEndian => "n64",
BigEndian => "z64",
}
}
pub fn from_string<S: AsRef<str>>(s: S) -> Result<RomType, Error> {
let result = match s.as_ref() {
"n64" => LittleEndian,
"z64" => BigEndian,
"v64" => ByteSwapped,
_ => return Err(Error("the type you entered was not valid!".into())),
};
Ok(result)
}
}
impl ToString for RomType {
fn to_string(&self) -> String {
self.as_str().to_owned()
}
}
#[derive(Debug)]
pub struct Error(pub String);
impl Error {
pub fn pretty_panic(&self) {
println!("{}{}", "error: ".bold().red(), self);
exit(1)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Error {
fn cause(&self) -> Option<&dyn std::error::Error> {
None
}
fn description(&self) -> &str {
&self.0
}
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
pub fn determine_format<P: AsRef<Path>>(file_path: P) -> Result<RomType, Error> {
let mut file = fs::File::open(file_path.as_ref())
.unwrap_or_else(|e| panic!("failed to open the file: {}", e));
let mut buf: [u8; 4] = [0, 0, 0, 0];
file.read_exact(&mut buf)
.unwrap_or_else(|e| panic!("failed to read the first 4 bytes of the ROM file: {}", e));
use RomType::*;
let result = match buf {
[0x37, 0x80, 0x40, 0x12] => ByteSwapped,
[0x40, 0x12, 0x37, 0x80] => LittleEndian,
[0x80, 0x37, 0x12, 0x40] => BigEndian,
_ => return Err(Error("Did not recognize a supported format!".to_owned())),
};
Ok(result)
}
pub fn endian_swap<P: AsRef<Path>>(input_file: P, output_file: P) {
let in_file = fs::File::open(input_file)
.unwrap_or_else(|e| panic!("failed to open the ROM as a file: {}", e));
let out_file = fs::File::create(output_file)
.unwrap_or_else(|e| panic!("failed to create the new ROM: {}", e));
let mut out_file_writer = BufWriter::new(out_file);
for idx in (0..ROM_LEN).step_by(65536) {
let mut buf: Vec<u8> = (0..65536).map(|_| 0).collect::<Vec<u8>>();
in_file.read_at(&mut buf, idx.into()).unwrap();
let new_bytes = buf
.chunks(4)
.flat_map(|chunk| [chunk[3], chunk[2], chunk[1], chunk[0]])
.collect::<Vec<u8>>();
out_file_writer.write_all(&new_bytes).unwrap();
}
}
pub fn byte_swap<P: AsRef<Path>>(input_file: P, output_file: P) {
let in_file = fs::File::open(input_file).unwrap();
let out_file = fs::File::create(output_file).unwrap();
let mut out_file_writer = BufWriter::new(out_file);
for idx in (0..ROM_LEN).step_by(65536) {
let mut buf = (0..65536).map(|_| 0).collect::<Vec<u8>>();
in_file.read_at(&mut buf, idx.into()).unwrap();
let swapped_bytes = buf
.chunks(2)
.flat_map(|chunk| [chunk[1], chunk[0]])
.collect::<Vec<u8>>();
out_file_writer.write_all(&swapped_bytes).unwrap();
}
}
pub fn byte_endian_swap<P: AsRef<Path>>(input_file: P, output_file: P) {
let in_file = fs::File::open(input_file).unwrap();
let out_file = fs::File::create(output_file).unwrap();
let mut out_file_writer = BufWriter::new(out_file);
for idx in (0..ROM_LEN).step_by(65536) {
let mut buf: Vec<u8> = (0..65536).map(|_| 0).collect::<Vec<u8>>();
in_file.read_at(&mut buf, idx.into()).unwrap();
let new_bytes = buf
.chunks(4)
.flat_map(|chunk| [chunk[2], chunk[3], chunk[0], chunk[1]])
.collect::<Vec<u8>>();
out_file_writer.write_all(&new_bytes).unwrap();
}
}