Added replace by name for PAK

This commit is contained in:
G2-Games 2024-07-06 21:30:05 -05:00
parent 84c02a1347
commit 8c9c33b670
3 changed files with 71 additions and 71 deletions

View file

@ -8,6 +8,8 @@ use std::{
/// A single file entry in a PAK file /// A single file entry in a PAK file
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Entry { pub struct Entry {
pub(super) index: usize,
/// The location within the PAK file, this number is multiplied by the /// The location within the PAK file, this number is multiplied by the
/// block size /// block size
pub(super) offset: u32, pub(super) offset: u32,
@ -34,6 +36,10 @@ impl Entry {
&self.name &self.name
} }
pub fn id(&self) -> u32 {
self.id
}
/// Save an [`Entry`] as its underlying data to a file /// Save an [`Entry`] as its underlying data to a file
pub fn save<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), Box<dyn Error>> { pub fn save<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), Box<dyn Error>> {
let mut path = path.as_ref().to_path_buf(); let mut path = path.as_ref().to_path_buf();

View file

@ -38,11 +38,9 @@ pub struct Pak {
path: PathBuf, path: PathBuf,
header: Header, header: Header,
pub unknown_pre_data: Vec<u32>, unknown_pre_data: Vec<u32>,
unknown_post_header: Vec<u8>, unknown_post_header: Vec<u8>,
rebuild: bool, // TODO: Look into a better way to indicate this, or if it's needed at all
entries: Vec<Entry>, entries: Vec<Entry>,
} }
@ -59,7 +57,7 @@ impl Pak {
Pak::decode(&mut file, path.as_ref().to_path_buf()) Pak::decode(&mut file, path.as_ref().to_path_buf())
} }
/// Decode a PAK file from a byte stream /// Decode a PAK file from a byte stream.
pub fn decode<T: Seek + Read>( pub fn decode<T: Seek + Read>(
input: &mut T, input: &mut T,
path: PathBuf, path: PathBuf,
@ -68,7 +66,7 @@ impl Pak {
let mut input = BufReader::new(input); let mut input = BufReader::new(input);
// Read in all the header bytes // Read in all the header bytes
info!("READING: header"); debug!("READING: header");
let header = Header { let header = Header {
data_offset: input.read_u32::<LE>()?, data_offset: input.read_u32::<LE>()?,
entry_count: input.read_u32::<LE>()?, entry_count: input.read_u32::<LE>()?,
@ -81,8 +79,8 @@ impl Pak {
flags: PakFlags(input.read_u32::<LE>()?), flags: PakFlags(input.read_u32::<LE>()?),
}; };
info!("{} entries detected", header.entry_count); info!("{} entries detected", header.entry_count);
info!("Block size is {} bytes", header.block_size); debug!("Block size is {} bytes", header.block_size);
info!("Flag bits {:#032b}", header.flags().0); debug!("Flag bits {:#032b}", header.flags().0);
let first_offset = header.data_offset() / header.block_size(); let first_offset = header.data_offset() / header.block_size();
@ -97,7 +95,7 @@ impl Pak {
unknown_pre_data.push(unknown); unknown_pre_data.push(unknown);
} }
info!("Pre-position bytes: {}", unknown_pre_data.len()); debug!("Pre-position bytes: {}", unknown_pre_data.len());
if input.stream_position()? == header.data_offset() as u64 { if input.stream_position()? == header.data_offset() as u64 {
log::error!("Header length exceeded first data block"); log::error!("Header length exceeded first data block");
@ -106,7 +104,7 @@ impl Pak {
// Read all the offsets and lengths // Read all the offsets and lengths
// TODO: I think a flag controls this // TODO: I think a flag controls this
info!("READING: offsets"); debug!("READING: offsets");
let mut offsets = Vec::new(); let mut offsets = Vec::new();
for _ in 0..header.entry_count() { for _ in 0..header.entry_count() {
let offset = input.read_u32::<LE>().unwrap(); let offset = input.read_u32::<LE>().unwrap();
@ -120,7 +118,7 @@ impl Pak {
// Read all unknown_data1 // Read all unknown_data1
let mut unknown_data1 = None; let mut unknown_data1 = None;
if header.flags.has_unknown_data1() { if header.flags.has_unknown_data1() {
info!("READING: unknown_data1"); debug!("READING: unknown_data1");
unknown_data1 = Some(Vec::new()); unknown_data1 = Some(Vec::new());
let mut buf = [0u8; 12]; let mut buf = [0u8; 12];
for _ in 0..header.entry_count() { for _ in 0..header.entry_count() {
@ -133,7 +131,7 @@ impl Pak {
// Read all the file names // Read all the file names
let mut file_names = None; let mut file_names = None;
if header.flags.has_names() { if header.flags.has_names() {
info!("READING: file_names"); debug!("READING: file_names");
let mut string_buf = Vec::new(); let mut string_buf = Vec::new();
file_names = Some(Vec::new()); file_names = Some(Vec::new());
for _ in 0..header.entry_count() { for _ in 0..header.entry_count() {
@ -151,7 +149,7 @@ impl Pak {
input.read_exact(&mut unknown_post_header)?; input.read_exact(&mut unknown_post_header)?;
// Read all entry data // Read all entry data
info!("Creating entry list"); debug!("Creating entry list");
let mut entries: Vec<Entry> = Vec::new(); let mut entries: Vec<Entry> = Vec::new();
for (i, offset_info) in offsets.iter().enumerate().take(header.entry_count() as usize) { for (i, offset_info) in offsets.iter().enumerate().take(header.entry_count() as usize) {
debug!("Seeking to block {}", offset_info.offset); debug!("Seeking to block {}", offset_info.offset);
@ -178,6 +176,7 @@ impl Pak {
// Build the entry from the data we now know // Build the entry from the data we now know
let entry = Entry { let entry = Entry {
index: i,
offset: offset_info.offset, offset: offset_info.offset,
length: offset_info.length, length: offset_info.length,
unknown1, unknown1,
@ -187,7 +186,7 @@ impl Pak {
}; };
entries.push(entry); entries.push(entry);
} }
info!("Entry list contains {} entries", entries.len()); debug!("Entry list contains {} entries", entries.len());
Ok(Pak { Ok(Pak {
header, header,
@ -195,10 +194,10 @@ impl Pak {
entries, entries,
unknown_post_header, unknown_post_header,
path, path,
rebuild: false,
}) })
} }
/// Encode a PAK file into a byte stream.
pub fn encode<T: Write + Seek>( pub fn encode<T: Write + Seek>(
&self, &self,
mut output: &mut T mut output: &mut T
@ -256,31 +255,70 @@ impl Pak {
Ok(()) Ok(())
} }
/// Replace the data of an entry with some other bytes.
///
/// This function updates the offsets of all entries to fit within the
/// chunk size specified in the header.
pub fn replace( pub fn replace(
&mut self, &mut self,
index: usize, index: usize,
replacement_bytes: &[u8] replacement_bytes: &[u8],
) -> Result<(), PakError> { ) -> Result<(), PakError> {
let block_size = self.header().block_size(); let block_size = self.header().block_size();
let replaced_entry = if let Some(entry) = self.entries.get_mut(index) { let replaced_entry;
entry if let Some(entry) = self.entries.get_mut(index) {
replaced_entry = entry
} else { } else {
log::error!("Entry {} not found!", index);
return Err(PakError::IndexError) return Err(PakError::IndexError)
}; };
if let Some(name) = replaced_entry.name() {
info!("Replacing entry {}: {}", index, name);
} else {
info!("Replacing entry {}: {}", index, replaced_entry.id());
}
// Replace the entry data // Replace the entry data
replaced_entry.data = replacement_bytes.to_vec(); replaced_entry.data = replacement_bytes.to_vec();
replaced_entry.length = replaced_entry.data.len() as u32; replaced_entry.length = replaced_entry.data.len() as u32;
let mut next_offset = replaced_entry.offset + replaced_entry.length.div_ceil(block_size); // Get the offset of the next entry based on the current one
let mut next_offset =
replaced_entry.offset + replaced_entry.length.div_ceil(block_size);
// Update the position of all subsequent entries
let mut i = 0;
for entry in self.entries.iter_mut().skip(index + 1) { for entry in self.entries.iter_mut().skip(index + 1) {
entry.offset = next_offset; entry.offset = next_offset;
next_offset = entry.offset + entry.length.div_ceil(block_size); next_offset = entry.offset + entry.length.div_ceil(block_size);
i += 1;
} }
info!("Aligned {} subsequent entries", i);
Ok(())
}
/// Replace the data of an entry with some other bytes, indexing by name.
///
/// Read more in [`Pak::replace()`]
pub fn replace_by_name(
&mut self,
name: String,
replacement_bytes: &[u8],
) -> Result<(), PakError> {
let entry = self.get_entry_by_name(&name);
let index = if let Some(entry) = entry {
entry.index
} else {
return Err(PakError::IndexError)
};
self.replace(index, replacement_bytes)?;
Ok(()) Ok(())
} }
@ -293,21 +331,18 @@ impl Pak {
&self.path &self.path
} }
/// Get an individual entry from the PAK by its index
pub fn get_entry(&self, index: u32) -> Option<&Entry> {
self.entries.get(index as usize)
}
/// Get an individual entry from the PAK by its ID /// Get an individual entry from the PAK by its ID
pub fn get_entry_by_id(&self, id: u32) -> Option<&Entry> { pub fn get_entry_by_id(&mut self, id: u32) -> Option<&mut Entry> {
self.entries.get((id - self.header.id_start) as usize) self.entries
.get_mut((id - self.header.id_start) as usize)
} }
pub fn get_entry_by_name(&self, name: &str) -> Option<&Entry> { pub fn get_entry_by_name(&mut self, name: &str) -> Option<&mut Entry> {
self.entries self.entries
.iter() .iter_mut()
.find(|e| e.name.as_ref() .find(|e|
.is_some_and(|n| n == &name)) e.name.as_ref().is_some_and(|n| n == &name)
)
} }
/// Get a list of all entries from the PAK /// Get a list of all entries from the PAK

View file

@ -6,50 +6,9 @@ fn main() {
clog.filter(None, log::LevelFilter::Info); clog.filter(None, log::LevelFilter::Info);
clog.init(); clog.init();
/*
let paths = std::fs::read_dir(".")
.unwrap()
.filter_map(|res| res.ok())
.map(|dir_entry| dir_entry.path())
.filter_map(|path| {
if path.extension().map_or(false, |ext| ext.to_ascii_lowercase() == "pak") {
Some(path)
} else {
None
}
})
.collect::<Vec<_>>();
let mut pak_files = vec![];
for path in paths {
let pak = Pak::open(&path).unwrap();
pak_files.push(pak)
}
pak_files.sort_by_key(|x| x.header().flags().0 & 0xF);
for pak in pak_files {
println!(
"{:#032b} - {} - {:?}",
pak.header().flags().0,
pak.unknown_pre_data.len(),
pak.path(),
);
}
*/
let mut pak = Pak::open("MANUAL.PAK").unwrap(); let mut pak = Pak::open("MANUAL.PAK").unwrap();
//println!("{:#?}", pak.header());
//println!("{:#032b}", pak.header().flags().0);
/* let rep_cz_data: Vec<u8> = std::fs::read("en_manual01_Linkto_2_6.cz1").unwrap();
for (i, entry) in pak.entries().iter().enumerate() {
println!("{i:03}: {:06.2} kB - {}", entry.len() as f32 / 1_000.0, entry.name().as_ref().unwrap());
entry.save("./output/").unwrap();
}
*/
let rep_cz_data: Vec<u8> = std::fs::read("en_manual01_Linkto_2_6").unwrap();
pak.replace(4, &rep_cz_data).unwrap(); pak.replace(4, &rep_cz_data).unwrap();
let mut output = BufWriter::new(File::create("MANUAL-modified.PAK").unwrap()); let mut output = BufWriter::new(File::create("MANUAL-modified.PAK").unwrap());