Added rather unfortunate audio playback to pak explorer

This commit is contained in:
G2-Games 2025-03-08 23:36:31 -06:00
parent 1ffc88d379
commit 982f55fde6
4 changed files with 106 additions and 18 deletions

View file

@ -67,6 +67,17 @@ impl Entry {
&self.data &self.data
} }
/// Get the byte data of an entry, but fixed to be compatible with normal things
pub fn cloned_bytes_fixed(&self) -> Vec<u8> {
match self.file_type() {
EntryType::OGGPAK => {
dbg!(self.data[15]);
self.data[15..].to_vec()
},
_ => self.data.clone()
}
}
pub fn display_name(&self) -> String { pub fn display_name(&self) -> String {
let mut name = self.name().clone().unwrap_or(self.id().to_string()); let mut name = self.name().clone().unwrap_or(self.id().to_string());
let entry_type = self.file_type(); let entry_type = self.file_type();
@ -88,6 +99,12 @@ impl Entry {
} }
} else if self.data[0..3] == [b'M', b'V', b'T'] { } else if self.data[0..3] == [b'M', b'V', b'T'] {
EntryType::MVT 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 { } else {
EntryType::Unknown EntryType::Unknown
} }
@ -96,6 +113,7 @@ impl Entry {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntryType { pub enum EntryType {
// CZ image files
CZ0, CZ0,
CZ1, CZ1,
CZ2, CZ2,
@ -106,6 +124,14 @@ pub enum EntryType {
/// An MVT video file /// An MVT video file
MVT, MVT,
/// OGG Audio file
OGG,
/// OGGPAK Audio file
OGGPAK,
/// Wav Audio file
WAV,
/// Who knows! /// Who knows!
Unknown, Unknown,
} }
@ -121,6 +147,9 @@ impl EntryType {
Self::CZ4 => ".cz4", Self::CZ4 => ".cz4",
Self::CZ5 => ".cz5", Self::CZ5 => ".cz5",
Self::MVT => ".mvt", Self::MVT => ".mvt",
Self::OGG => ".ogg",
Self::OGGPAK => ".oggpak",
Self::WAV => ".wav",
Self::Unknown => "", Self::Unknown => "",
} }
} }

View file

@ -64,7 +64,7 @@ pub struct PakLimits {
impl Default for PakLimits { impl Default for PakLimits {
fn default() -> Self { fn default() -> Self {
Self { Self {
entry_limit: 10_000, // 10,000 entries entry_limit: 100_000, // 100,000 entries
size_limit: u32::MAX as usize, // 10 gb size_limit: u32::MAX as usize, // 10 gb
} }
} }

View file

@ -10,14 +10,16 @@ authors.workspace = true
publish = false publish = false
[dependencies] [dependencies]
colog = "1.3.0" colog = "1.3"
cz = { path = "../cz/" } cz = { path = "../cz/" }
eframe = { version = "0.29", default-features = false, features = ["wayland", "x11", "accesskit", "default_fonts", "wgpu"] } eframe = { version = "0.29", default-features = false, features = ["wayland", "x11", "accesskit", "default_fonts", "wgpu"] }
egui_extras = "0.29" egui_extras = "0.29"
image = { version = "0.25", default-features = false, features = ["png"] } image = { version = "0.25", default-features = false, features = ["png"] }
log = "0.4.22" kira = "0.10"
log = "0.4"
luca_pak = { path = "../luca_pak/" } luca_pak = { path = "../luca_pak/" }
rfd = "0.15" rfd = "0.15"
symphonia = "0.5.4"
[lints] [lints]
workspace = true workspace = true

View file

@ -2,11 +2,12 @@
use colog; use colog;
use eframe::egui::{ 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 log::error;
use luca_pak::{entry::EntryType, Pak}; use luca_pak::{entry::EntryType, Pak};
use std::fs; use std::{fs, io::Cursor, time::Duration};
fn main() -> eframe::Result { fn main() -> eframe::Result {
colog::default_builder() colog::default_builder()
@ -18,13 +19,23 @@ fn main() -> eframe::Result {
..Default::default() ..Default::default()
}; };
let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default()).unwrap();
eframe::run_native( eframe::run_native(
"LUCA PAK Explorer", "LUCA PAK Explorer",
options, options,
Box::new(|ctx| { Box::new(|ctx| {
let ppp = ctx.egui_ctx.pixels_per_point() * 1.5; let ppp = ctx.egui_ctx.pixels_per_point() * 1.5;
ctx.egui_ctx.set_pixels_per_point(ppp); ctx.egui_ctx.set_pixels_per_point(ppp);
Ok(Box::<PakExplorer>::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<luca_pak::entry::Entry>, selected_entry: Option<luca_pak::entry::Entry>,
image_texture: Option<egui::TextureHandle>, image_texture: Option<egui::TextureHandle>,
hex_string: Option<Vec<String>>, hex_string: Option<Vec<String>>,
} audio_player: AudioManager,
audio_handle: Option<StaticSoundHandle>,
impl Default for PakExplorer { audio_duration: Option<Duration>,
fn default() -> Self {
Self {
open_file: None,
selected_entry: None,
image_texture: None,
hex_string: None,
}
}
} }
impl eframe::App for PakExplorer { impl eframe::App for PakExplorer {
@ -67,6 +70,13 @@ impl eframe::App for PakExplorer {
self.selected_entry = None; self.selected_entry = None;
self.image_texture = None; self.image_texture = None;
self.hex_string = 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 { if let Some(pak) = &self.open_file {
@ -97,7 +107,7 @@ impl eframe::App for PakExplorer {
}; };
ui.horizontal(|ui| { ui.horizontal(|ui| {
egui::ComboBox::from_id_source("my-combobox") egui::ComboBox::from_id_salt("my-combobox")
.selected_text(selection.clone()) .selected_text(selection.clone())
.truncate() .truncate()
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
@ -112,6 +122,13 @@ impl eframe::App for PakExplorer {
.clicked() .clicked()
{ {
self.image_texture = None; 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")); ui.centered_and_justified(|ui| ui.label("No Preview Available"));
} }