// SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later use std::fs::read; use std::io::Cursor; use binrw::BinRead; use binrw::binrw; use crate::gamedata::MemoryBuffer; use crate::sha1::Sha1; #[binrw] #[brw(magic = b"FileInfo")] #[derive(Debug)] #[brw(little)] pub struct FileInfo { #[brw(pad_before = 16)] #[bw(calc = 1024)] unknown: i32, #[br(temp)] #[bw(calc = (entries.len() * 96) as i32)] entries_size: i32, #[brw(pad_before = 992)] #[br(count = entries_size / 96)] pub entries: Vec, } #[binrw] #[derive(Debug)] pub struct FIINEntry { pub file_size: i32, #[brw(pad_before = 4)] #[br(count = 64)] #[bw(pad_size_to = 64)] #[bw(map = |x : &String | x.as_bytes())] #[br(map = | x: Vec | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())] pub file_name: String, #[br(count = 24)] #[bw(pad_size_to = 24)] pub sha1: Vec, } impl FileInfo { /// Parses an existing FIIN file. pub fn from_existing(buffer: &MemoryBuffer) -> Option { let mut cursor = Cursor::new(buffer); FileInfo::read(&mut cursor).ok() } /// Creates a new FileInfo structure from a list of filenames. These filenames must be present in /// the current working directory in order to be read properly, since it also generates SHA1 /// hashes. /// /// The new FileInfo structure can then be serialized back into retail-compatible form. pub fn new(file_names: &[&str]) -> Option { let mut entries = vec![]; for name in file_names { let file = &read(name).expect("Cannot read file."); entries.push(FIINEntry { file_size: file.len() as i32, file_name: name.to_string(), sha1: Sha1::from(file).digest().bytes().to_vec(), }); } Some(FileInfo { entries }) } } #[cfg(test)] mod tests { use std::fs::read; use std::path::PathBuf; use crate::fiin::FileInfo; fn common_setup() -> FileInfo { let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); d.push("resources/tests"); d.push("test.fiin"); FileInfo::from_existing(&read(d).unwrap()).unwrap() } #[test] fn basic_parsing() { let fiin = common_setup(); assert_eq!(fiin.entries[0].file_name, "test.txt"); assert_eq!(fiin.entries[1].file_name, "test.exl"); } }