Add logging to pak explorer, fix some error messages in pak library

This commit is contained in:
G2-Games 2024-09-08 02:39:21 -05:00
parent d9bd35f075
commit fddcf2f055
3 changed files with 79 additions and 18 deletions

View file

@ -1,14 +1,13 @@
pub mod entry;
pub mod header;
use byteorder::{LittleEndian, ReadBytesExt};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use header::Header;
use log::{debug, info};
use std::{
ffi::CString, fs::File, io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}, path::{Path, PathBuf}
};
use thiserror::Error;
use byteorder::WriteBytesExt;
type LE = LittleEndian;
@ -20,8 +19,11 @@ pub enum PakError {
#[error("Could not read/write file")]
IoError(#[from] io::Error),
#[error("Expected {} files, got {} in {}", 0, 1, 2)]
FileCountMismatch(usize, usize, &'static str),
#[error("Expected {0} files, got {1} in {2}")]
EntryCountMismatch(usize, usize, &'static str),
#[error("Number of entries in header ({0}) exceeds limit of {1}")]
EntryLimit(u32, usize),
#[error("Malformed header information")]
HeaderError,
@ -35,9 +37,11 @@ pub enum PakError {
pub struct Pak {
subdirectory: Option<String>,
/// The path of the PAK file, can serve as an identifier or name as the
/// The path to the PAK file, can serve as an identifier or name as the
/// header has no name for the file.
path: PathBuf,
/// Header information
header: Header,
unknown_pre_data: Vec<u32>,
@ -46,23 +50,42 @@ pub struct Pak {
entries: Vec<Entry>,
}
struct FileLocation {
struct EntryLocation {
offset: u32,
length: u32,
}
pub struct PakLimits {
pub entry_limit: usize,
pub size_limit: usize,
}
impl Default for PakLimits {
fn default() -> Self {
Self {
entry_limit: 10_000, // 10,000 entries
size_limit: 10_000_000_000, // 10 gb
}
}
}
impl Pak {
/// Convenience method to open a PAK file from a path and decode it
pub fn open<P: ?Sized + AsRef<Path>>(path: &P) -> Result<Self, PakError> {
let mut file = File::open(path)?;
Pak::decode(&mut file, path.as_ref().to_path_buf())
Pak::decode(
&mut file,
path.as_ref().to_path_buf(),
PakLimits::default()
)
}
/// Decode a PAK file from a byte stream.
pub fn decode<T: Seek + Read>(
input: &mut T,
path: PathBuf,
limits: PakLimits,
) -> Result<Self, PakError> {
info!("Reading pak from {:?}", path);
let mut input = BufReader::new(input);
@ -80,6 +103,10 @@ impl Pak {
unknown4: input.read_u32::<LE>()?,
flags: PakFlags(input.read_u32::<LE>()?),
};
if header.entry_count >= limits.entry_limit as u32 {
return Err(PakError::EntryLimit(header.entry_count, limits.entry_limit))
}
info!("{} entries detected", header.entry_count);
debug!("Block size is {} bytes", header.block_size);
debug!("Flag bits {:#032b}", header.flags().0);
@ -87,6 +114,7 @@ impl Pak {
let first_offset = header.data_offset() / header.block_size();
// Read some unknown data before the data we want
// TODO: This *must* be done differently for real, figure it out!
let mut unknown_pre_data = Vec::new();
while input.stream_position()? < header.data_offset() as u64 {
let unknown = input.read_u32::<LE>()?;
@ -111,7 +139,7 @@ impl Pak {
for _ in 0..header.entry_count() {
let offset = input.read_u32::<LE>().unwrap();
let length = input.read_u32::<LE>().unwrap();
offsets.push(FileLocation {
offsets.push(EntryLocation {
offset,
length,
});

View file

@ -10,9 +10,11 @@ authors.workspace = true
publish = false
[dependencies]
colog = "1.3.0"
cz = { path = "../cz/", features = ["png"] }
eframe = { version = "0.28.1", default-features = false, features = ["wayland", "x11", "accesskit", "default_fonts", "wgpu"] }
egui_extras = "0.28.1"
log = "0.4.22"
luca_pak = { path = "../luca_pak/" }
rfd = "0.14.1"

View file

@ -1,22 +1,47 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use std::fs;
use colog;
use eframe::egui::{self, ColorImage, Image, TextureFilter, TextureHandle, TextureOptions};
use log::error;
use luca_pak::{entry::EntryType, Pak};
fn main() -> eframe::Result {
colog::default_builder()
.filter(None, log::LevelFilter::Warn)
.init();
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 800.0]),
follow_system_theme: true,
..Default::default()
};
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"Noto Sans".to_owned(),
egui::FontData::from_static(include_bytes!("/home/g2/Downloads/Noto_Sans/static/NotoSans-Regular.ttf")),
);
fonts.font_data.insert(
"Noto Sans Japanese".to_owned(),
egui::FontData::from_static(include_bytes!("/home/g2/Downloads/Noto_Sans_JP/static/NotoSansJP-Regular.ttf")),
);
fonts
.families
.entry(egui::FontFamily::Proportional)
.or_default()
.insert(0, "Noto Sans".to_owned());
fonts
.families
.entry(egui::FontFamily::Proportional)
.or_default()
.insert(1, "Noto Sans Japanese".to_owned());
eframe::run_native(
"LUCA PAK Explorer",
options,
Box::new(|cc| {
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);
cc.egui_ctx.set_fonts(fonts);
Ok(Box::<PakExplorer>::default())
}),
@ -48,17 +73,23 @@ impl eframe::App for PakExplorer {
ui.heading("PAK File Explorer");
ui.horizontal(|ui| {
if ui.button("Open file").clicked() {
if ui.button("Open file").clicked() {
if let Some(path) = rfd::FileDialog::new().pick_file() {
let pak = Pak::open(&path).unwrap();
self.open_file = Some(pak);
let pak = match Pak::open(&path) {
Ok(pak) => Some(pak),
Err(e) => {
error!("Unable to read selected file as PAK: {}", e);
None
},
};
self.open_file = pak;
self.selected_entry = None;
self.image_texture = None;
self.hex_string = None;
}
}
if let Some(pak) = &self.open_file {
if ui.button("Save PAK").clicked() {
if ui.button("Save PAK").clicked() {
if let Some(path) = rfd::FileDialog::new()
.set_file_name(pak.path().file_name().unwrap().to_string_lossy())
.save_file()
@ -105,7 +136,7 @@ impl eframe::App for PakExplorer {
if let Some(entry) = &self.selected_entry {
ui.horizontal(|ui| {
if ui.button("Save entry").clicked() {
if ui.button("Save entry").clicked() {
if let Some(path) = rfd::FileDialog::new()
.set_file_name(entry.display_name())
.save_file()
@ -115,7 +146,7 @@ impl eframe::App for PakExplorer {
}
if let Some(pak) = &mut self.open_file.as_mut() {
if ui.button("Replace entry").clicked() {
if ui.button("Replace entry").clicked() {
if let Some(path) = rfd::FileDialog::new().pick_file() {
let file_bytes = fs::read(path).unwrap();
pak.replace(entry.index(), &file_bytes).unwrap();
@ -128,7 +159,7 @@ impl eframe::App for PakExplorer {
| EntryType::CZ2 | EntryType::CZ3
| EntryType::CZ4 | EntryType::CZ5 =>
{
if ui.button("Save as PNG").clicked() {
if ui.button("Save as PNG").clicked() {
let mut display_name = entry.display_name();
display_name.push_str(".png");
if let Some(path) = rfd::FileDialog::new()