jmrenamer: rewrite to use package.toc

The package.toc file contains all the file names, except for itself (but we know it always so this isn't a problem).

Therefore, we don't need to store a hardcoded string table! Yay!
This commit is contained in:
Lily Tsuru 2023-06-28 19:25:18 -04:00
parent 11abf7668a
commit 15d438f294
5 changed files with 101 additions and 57 deletions

View File

@ -16,23 +16,23 @@ pub struct PackageTocEntry {
}
impl PackageTocEntry {
fn file_name(&self) -> Option<String> {
pub fn file_name(&self) -> Option<String> {
make_c_string(&self.file_name)
}
fn file_name_hash(&self) -> u32 {
pub fn file_name_hash(&self) -> u32 {
self.file_name_hash
}
fn toc_start_offset(&self) -> u32 {
pub fn toc_start_offset(&self) -> u32 {
self.toc_start_offset
}
fn toc_size(&self) -> u32 {
pub fn toc_size(&self) -> u32 {
self.toc_size
}
fn toc_file_count(&self) -> u32 {
pub fn toc_file_count(&self) -> u32 {
self.toc_file_count
}
}

View File

@ -16,3 +16,9 @@ pub fn dat_filename(filename: &str) -> String {
pub fn met_filename(filename: &str) -> String {
format!("{:X}.MET", hash_string(String::from(filename)))
}
/// Make a .DAT filename from a pre-created hash.
/// This is notably used to re-use hashes from `package.toc`.
pub fn dat_filename_from_hash(hash: u32) -> String {
format!("{:X}.DAT", hash)
}

View File

@ -1 +1,5 @@
//! High-level readers
pub mod package_toc;
pub mod texture;

View File

@ -0,0 +1,47 @@
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::path::{PathBuf};
use crate::format::{
package_toc::*
};
use binext::BinaryRead;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("underlying I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("file too small to hold package toc entry")]
FileTooSmall,
/// Under-read of data
#[error("underread")]
ReadTooSmall,
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn read_package_toc(path: PathBuf) -> Result<Vec<PackageTocEntry>> {
let mut toc_file = File::open(path.clone())?;
let file_size = toc_file.seek(SeekFrom::End(0))?;
toc_file.seek(SeekFrom::Start(0))?;
let vec_size : usize = file_size as usize / std::mem::size_of::<PackageTocEntry>();
if vec_size == 0 {
return Err(Error::FileTooSmall);
}
let mut vec : Vec<PackageTocEntry> = Vec::with_capacity(vec_size);
for _ in 0..vec_size {
vec.push(toc_file.read_binary::<PackageTocEntry>()?);
}
drop(toc_file);
Ok(vec)
}

View File

@ -1,68 +1,55 @@
//! A reimplemntation of jmmt_renamer in Rust.
//! A reimplementation of jmmt_renamer in Rust.
//! This program should be run in the root directory
//! of an extracted (from image) copy of the game.
use std::{fs, io, path::Path};
use std::{fs, path::Path};
use jmmt::hash::filename::*;
use jmmt::read::package_toc::{read_package_toc};
const FILENAME_TABLE : [&str; 20] = [
// First loaded by the game
"package.toc",
// General packs
"config.pak",
// This file is referenced in the game files,
// but doesn't seem to exist anymore in the final build.
//"shell.pak",
"shell_character_select.pak",
"shell_main.pak",
"shell_title.pak",
"shell_venue.pak",
"shell_event.pak",
"shell_option.pak",
"win_screens.pak",
// Game levels
"SF_san_fran.pak",
"DC_washington.pak",
"MK_MT_KILI.pak",
"MP_MACHU_PIHU.pak",
"LV_Las_Vegas.pak",
"AN_ANTARTICA.pak",
"NP_Nepal.pak",
"TH_TAHOE.pak",
"VA_Valdez_alaska.pak",
"RV_Rome.pak",
"TR_training.pak"
];
fn main() -> io::Result<()> {
// TODO: A mode that will re-name everything back? This wouldn't be too hard to implement
fn main() {
// A relatively simple idiot-check. Later on utilities might have a shared library
// of code which validates game root stuff and can open it up/etc.
if !Path::new("DATA").is_dir() {
println!("This program should be run in the root of an extracted copy.");
std::process::exit(1);
return ();
}
// Go through the clearname table and rename files in the DATA directory
for clearname in FILENAME_TABLE.iter() {
let dat_filename = dat_filename(clearname);
let dat_src = format!("DATA/{}", dat_filename);
let dat_clearname = format!("DATA/{}", String::from(*clearname));
let package_toc_filename = format!("DATA/{}", dat_filename("package.toc"));
let src_path = Path::new(dat_src.as_str());
let dest_path = Path::new(dat_clearname.as_str());
match read_package_toc(Path::new(package_toc_filename.as_str()).to_path_buf()) {
Ok(toc) => {
for toc_entry in toc {
let dat_src = format!("DATA/{}", dat_filename_from_hash(toc_entry.file_name_hash()));
let src_path = Path::new(dat_src.as_str());
let dat_clearname = format!("DATA/{}", toc_entry.file_name().expect("How did invalid ASCII get here?"));
let dest_path = Path::new(dat_clearname.as_str());
if src_path.exists() {
fs::rename(src_path, dest_path)?;
println!("moved {} -> {}", src_path.display(), dest_path.display());
} else if dest_path.exists() {
println!("{} already exists", dest_path.display());
if src_path.exists() {
match fs::rename(src_path, dest_path) {
Ok(_) => {},
Err(error) => {
println!("Error renaming {}: {}", src_path.display(), error);
return ();
}
};
println!("moved {} -> {}", src_path.display(), dest_path.display());
}
}
match fs::rename(Path::new(package_toc_filename.as_str()), Path::new("DATA/package.toc")) {
Ok(_) => {},
Err(error) => {
println!("Error renaming TOC file: {}", error);
return ();
}
};
}
Err(error) => {
println!("Error reading package.toc file: {}", error);
return ();
}
}
Ok(())
}