Skip to content

Commit

Permalink
Made experimental UI
Browse files Browse the repository at this point in the history
  • Loading branch information
G2-Games committed Jul 9, 2024
1 parent 3cb07bc commit f6374ee
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 26 deletions.
4 changes: 4 additions & 0 deletions experimental/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ publish = false

[dependencies]
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"
luca_pak = { path = "../luca_pak/" }
rfd = "0.14.1"

[lints]
workspace = true
159 changes: 153 additions & 6 deletions experimental/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,157 @@
use std::time::Instant;
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release

fn main() {
let mut cz_file = cz::open("test_file.cz3").unwrap();
//cz_file.save_as_png("test.png").unwrap();
use std::path::PathBuf;

cz_file.header_mut().set_version(4).unwrap();
use eframe::{egui::{self, text::{LayoutJob, TextWrapping}, ColorImage, Image, Rgba, TextBuffer, TextureFilter, TextureHandle, TextureOptions}, epaint::Fonts};
use luca_pak::{entry::EntryType, header, Pak};

cz_file.save_as_cz("test_file.cz4").unwrap();
fn main() -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 800.0]),
follow_system_theme: true,
..Default::default()
};
eframe::run_native(
"LUCA PAK Explorer",
options,
Box::new(|cc| {
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);

Ok(Box::<PakExplorer>::default())
}),
)
}

struct PakExplorer {
open_file: Option<Pak>,
selected_entry: Option<luca_pak::entry::Entry>,
image_texture: Option<egui::TextureHandle>,
hex_string: Option<Vec<String>>,
}

impl Default for PakExplorer {
fn default() -> Self {
Self {
open_file: None,
selected_entry: None,
image_texture: None,
hex_string: None,
}
}
}

impl eframe::App for PakExplorer {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ctx.set_pixels_per_point(1.5);
ui.heading("PAK File Explorer");

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);
self.selected_entry = None;
self.image_texture = None;
self.hex_string = None;
}
}

ui.separator();

if let Some(pak) = &self.open_file {
ui.label(format!("Opened {}", pak.path().file_name().unwrap().to_string_lossy()));
ui.label(format!("Contains {} Entries", pak.entries().len()));

let selection = if let Some(entry) = &self.selected_entry {
entry.display_name()
} else {
"None".to_string()
};

egui::ComboBox::from_id_source("my-combobox")
.selected_text(selection)
.truncate()
.show_ui(ui, |ui|
{
ui.selectable_value(&mut self.selected_entry, None, "");
for entry in pak.entries() {
if ui.selectable_value(
&mut self.selected_entry,
Some(entry.clone()),
entry.display_name(),
).clicked() {
self.image_texture = None;
};
}
});
} else {
ui.centered_and_justified(|ui|
ui.label("No File Opened")
);
}

if let Some(entry) = &self.selected_entry {
if ui.button("Save entry…").clicked() {
if let Some(path) = rfd::FileDialog::new()
.set_file_name(entry.display_name())
.save_file()
{
entry.save(&path).unwrap();
}
}
match entry.file_type() {
EntryType::CZ0 | EntryType::CZ1
| EntryType::CZ2 | EntryType::CZ3
| EntryType::CZ4 | EntryType::CZ5 =>
{
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()
.set_file_name(display_name)
.save_file()
{
let cz = cz::DynamicCz::decode(&mut std::io::Cursor::new(entry.as_bytes())).unwrap();
cz.save_as_png(&path).unwrap();
}
}

ui.separator();

let texture: &TextureHandle = self.image_texture.get_or_insert_with(|| {
let cz = cz::DynamicCz::decode(&mut std::io::Cursor::new(entry.as_bytes())).unwrap();
let image = ColorImage::from_rgba_unmultiplied(
[cz.header().width() as usize, cz.header().height() as usize],
cz.as_raw()
);
ui.ctx().load_texture("eventframe", image, TextureOptions {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Linear,
..Default::default()
})
});

ui.centered_and_justified(|ui|
ui.add(
Image::from_texture(texture)
.show_loading_spinner(true)
.shrink_to_fit()
.rounding(2.0)
)
);
}
_ => {
ui.centered_and_justified(|ui|
ui.label("No Preview Available")
);
},
}
} else {
ui.centered_and_justified(|ui|
ui.label("Select an Entry")
);
}
});
}
}
78 changes: 61 additions & 17 deletions luca_pak/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::{
error::Error,
fs::File,
io::{BufWriter, Write},
path::Path,
error::Error, fmt, fs::File, io::{BufWriter, Write}, path::Path
};

/// A single file entry in a PAK file
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Entry {
pub(super) index: usize,

Expand Down Expand Up @@ -42,18 +39,6 @@ impl Entry {

/// Save an [`Entry`] as its underlying data to a file
pub fn save<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), Box<dyn Error>> {
let mut path = path.as_ref().to_path_buf();
if !path.is_dir() {
return Err("Path must be a directory".into());
}

// Save the file to <folder> + <file name>
if let Some(name) = &self.name {
path.push(name);
} else {
path.push(&self.id.to_string())
}

let mut out_file = BufWriter::new(File::create(path)?);

out_file.write_all(&self.data)?;
Expand All @@ -70,4 +55,63 @@ impl Entry {
pub fn as_bytes(&self) -> &Vec<u8> {
&self.data
}

pub fn display_name(&self) -> String {
let mut name = self.name().clone().unwrap_or(self.id().to_string());
let entry_type = self.file_type();
name.push_str(&entry_type.extension());

name
}

pub fn file_type(&self) -> EntryType {
if self.data[0..2] == [b'C', b'Z'] {
match self.data[2] {
b'0' => EntryType::CZ0,
b'1' => EntryType::CZ1,
b'2' => EntryType::CZ2,
b'3' => EntryType::CZ3,
b'4' => EntryType::CZ4,
b'5' => EntryType::CZ5,
_ => EntryType::Unknown,
}
} else if self.data[0..3] == [b'M', b'V', b'T'] {
EntryType::MVT
} else {
EntryType::Unknown
}
}
}


#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntryType {
CZ0,
CZ1,
CZ2,
CZ3,
CZ4,
CZ5,

/// An MVT video file
MVT,

/// Who knows!
Unknown,
}

impl EntryType {
/// Get the file extension for the file
pub fn extension(&self) -> &'static str {
match self {
Self::CZ0 => ".cz0",
Self::CZ1 => ".cz1",
Self::CZ2 => ".cz2",
Self::CZ3 => ".cz3",
Self::CZ4 => ".cz4",
Self::CZ5 => ".cz5",
Self::MVT => ".mvt",
Self::Unknown => "",
}
}
}
4 changes: 2 additions & 2 deletions luca_pak/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod entry;
mod header;
pub mod entry;
pub mod header;

use byteorder::{LittleEndian, ReadBytesExt};
use header::Header;
Expand Down
4 changes: 3 additions & 1 deletion luca_pak/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ fn main() {
}

for entry in pak.entries() {
entry.save(&output).unwrap();
let mut outpath = output.clone();
outpath.push(entry.display_name());
entry.save(&outpath).unwrap();
}
},
Commands::Replace { batch, name, id, replacement, output } => {
Expand Down

0 comments on commit f6374ee

Please sign in to comment.