From 982f55fde637bfe31822b484a4994a68db74c98b Mon Sep 17 00:00:00 2001 From: G2-Games Date: Sat, 8 Mar 2025 23:36:31 -0600 Subject: [PATCH] Added rather unfortunate audio playback to pak explorer --- luca_pak/src/entry.rs | 29 ++++++++++++++ luca_pak/src/lib.rs | 2 +- pak_explorer/Cargo.toml | 6 ++- pak_explorer/src/main.rs | 87 +++++++++++++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 18 deletions(-) diff --git a/luca_pak/src/entry.rs b/luca_pak/src/entry.rs index 1ba23f6..0fe1e5a 100644 --- a/luca_pak/src/entry.rs +++ b/luca_pak/src/entry.rs @@ -67,6 +67,17 @@ impl Entry { &self.data } + /// Get the byte data of an entry, but fixed to be compatible with normal things + pub fn cloned_bytes_fixed(&self) -> Vec { + match self.file_type() { + EntryType::OGGPAK => { + dbg!(self.data[15]); + self.data[15..].to_vec() + }, + _ => self.data.clone() + } + } + pub fn display_name(&self) -> String { let mut name = self.name().clone().unwrap_or(self.id().to_string()); let entry_type = self.file_type(); @@ -88,6 +99,12 @@ impl Entry { } } else if self.data[0..3] == [b'M', b'V', b'T'] { EntryType::MVT + } else if self.data[0..4] == [b'R', b'I', b'F', b'F'] { + EntryType::WAV + } else if self.data[0..4] == [b'O', b'g', b'g', b'S'] { + EntryType::OGG + } else if self.data[0..6] == [b'O', b'G', b'G', b'P', b'A', b'K'] { + EntryType::OGGPAK } else { EntryType::Unknown } @@ -96,6 +113,7 @@ impl Entry { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EntryType { + // CZ image files CZ0, CZ1, CZ2, @@ -106,6 +124,14 @@ pub enum EntryType { /// An MVT video file MVT, + /// OGG Audio file + OGG, + /// OGGPAK Audio file + OGGPAK, + + /// Wav Audio file + WAV, + /// Who knows! Unknown, } @@ -121,6 +147,9 @@ impl EntryType { Self::CZ4 => ".cz4", Self::CZ5 => ".cz5", Self::MVT => ".mvt", + Self::OGG => ".ogg", + Self::OGGPAK => ".oggpak", + Self::WAV => ".wav", Self::Unknown => "", } } diff --git a/luca_pak/src/lib.rs b/luca_pak/src/lib.rs index 5ac0e5b..acd84b1 100644 --- a/luca_pak/src/lib.rs +++ b/luca_pak/src/lib.rs @@ -64,7 +64,7 @@ pub struct PakLimits { impl Default for PakLimits { fn default() -> Self { Self { - entry_limit: 10_000, // 10,000 entries + entry_limit: 100_000, // 100,000 entries size_limit: u32::MAX as usize, // 10 gb } } diff --git a/pak_explorer/Cargo.toml b/pak_explorer/Cargo.toml index f312381..c500d96 100644 --- a/pak_explorer/Cargo.toml +++ b/pak_explorer/Cargo.toml @@ -10,14 +10,16 @@ authors.workspace = true publish = false [dependencies] -colog = "1.3.0" +colog = "1.3" cz = { path = "../cz/" } eframe = { version = "0.29", default-features = false, features = ["wayland", "x11", "accesskit", "default_fonts", "wgpu"] } egui_extras = "0.29" image = { version = "0.25", default-features = false, features = ["png"] } -log = "0.4.22" +kira = "0.10" +log = "0.4" luca_pak = { path = "../luca_pak/" } rfd = "0.15" +symphonia = "0.5.4" [lints] workspace = true diff --git a/pak_explorer/src/main.rs b/pak_explorer/src/main.rs index 900a4d8..aa65d19 100644 --- a/pak_explorer/src/main.rs +++ b/pak_explorer/src/main.rs @@ -2,11 +2,12 @@ use colog; use eframe::egui::{ - self, ColorImage, Image, TextureFilter, TextureHandle, TextureOptions, ThemePreference, + self, ColorImage, Image, ProgressBar, TextureFilter, TextureHandle, TextureOptions, ThemePreference }; +use kira::{backend::Backend, sound::static_sound::{StaticSoundData, StaticSoundHandle}, AudioManager, AudioManagerSettings, DefaultBackend, Tween}; use log::error; use luca_pak::{entry::EntryType, Pak}; -use std::fs; +use std::{fs, io::Cursor, time::Duration}; fn main() -> eframe::Result { colog::default_builder() @@ -18,13 +19,23 @@ fn main() -> eframe::Result { ..Default::default() }; + let manager = AudioManager::::new(AudioManagerSettings::default()).unwrap(); + eframe::run_native( "LUCA PAK Explorer", options, Box::new(|ctx| { let ppp = ctx.egui_ctx.pixels_per_point() * 1.5; ctx.egui_ctx.set_pixels_per_point(ppp); - Ok(Box::::default()) + Ok(Box::new(PakExplorer { + open_file: None, + selected_entry: None, + image_texture: None, + hex_string: None, + audio_player: manager, + audio_handle: None, + audio_duration: None, + })) }), ) } @@ -34,17 +45,9 @@ struct PakExplorer { selected_entry: Option, image_texture: Option, hex_string: Option>, -} - -impl Default for PakExplorer { - fn default() -> Self { - Self { - open_file: None, - selected_entry: None, - image_texture: None, - hex_string: None, - } - } + audio_player: AudioManager, + audio_handle: Option, + audio_duration: Option, } impl eframe::App for PakExplorer { @@ -67,6 +70,13 @@ impl eframe::App for PakExplorer { self.selected_entry = None; self.image_texture = None; self.hex_string = None; + + if let Some(a) = self.audio_handle.as_mut() { + a.stop(Tween::default()); + } + + self.audio_handle = None; + self.audio_duration = None; } } if let Some(pak) = &self.open_file { @@ -97,7 +107,7 @@ impl eframe::App for PakExplorer { }; ui.horizontal(|ui| { - egui::ComboBox::from_id_source("my-combobox") + egui::ComboBox::from_id_salt("my-combobox") .selected_text(selection.clone()) .truncate() .show_ui(ui, |ui| { @@ -112,6 +122,13 @@ impl eframe::App for PakExplorer { .clicked() { self.image_texture = None; + + if let Some(a) = self.audio_handle.as_mut() { + a.stop(Tween::default()); + } + + self.audio_handle = None; + self.audio_duration = None; }; } }); @@ -203,6 +220,46 @@ impl eframe::App for PakExplorer { ) }); } + EntryType::OGG + | EntryType::OGGPAK + | EntryType::WAV => { + ui.separator(); + + ui.horizontal(|ui| { + if ui.button("▶").clicked() && self.audio_handle.is_none() { + let sound_data = StaticSoundData::from_cursor( + Cursor::new(entry.cloned_bytes_fixed()) + ) + .unwrap() + .volume(-8.0); + + self.audio_duration = Some(sound_data.duration()); + self.audio_handle = Some(self.audio_player.play(sound_data.clone()).unwrap()); + } + + if ui.button("⏹").clicked() && self.audio_handle.is_some() { + self.audio_handle.as_mut().unwrap().stop(Tween::default()); + self.audio_handle = None; + self.audio_duration = None; + } + + if let Some(a) = &self.audio_handle { + let pos = a.position() as f32; + + ui.add(ProgressBar::new( + pos / self.audio_duration.as_ref().unwrap().as_secs_f32() + ).rounding(1.0).text(format!("{:02.0}:{:02.0}", pos / 60.0, pos % 60.0))); + + if pos / self.audio_duration.as_ref().unwrap().as_secs_f32() > 0.99 { + self.audio_handle.as_mut().unwrap().stop(Tween::default()); + self.audio_handle = None; + self.audio_duration = None; + } + } + + ctx.request_repaint_after(Duration::from_millis(50)); + }); + } _ => { ui.centered_and_justified(|ui| ui.label("No Preview Available")); }