Skip to content
4 changes: 3 additions & 1 deletion crates/ps2-filetypes/src/common/sjis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pub fn encode_sjis(input: &str) -> Vec<u8> {
.as_bytes()
.iter()
.flat_map(|b| match *b {
b' ' => [0x80, 0x3F],
b' ' => [0x81, 0x40],
b'.' => [0x81, 0x44],
b':' => [0x81, 0x46],
b'/' => [0x81, 0x5E],
b'(' => [0x81, 0x69],
Expand Down Expand Up @@ -42,6 +43,7 @@ pub fn decode_sjis(input: &[u8]) -> String {
0x81 => match pair[1] {
0x40 => b' ',
0x46 => b':',
0x44 => b'.',
0x5E => b'/',
0x69 => b'(',
0x6A => b')',
Expand Down
38 changes: 28 additions & 10 deletions crates/ps2-filetypes/src/parser/icon_sys.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::io::{Cursor, Read, Result};
use crate::color::Color;
use crate::sjis::{decode_sjis, encode_sjis};
use crate::util::parse_cstring;
use byteorder::{ReadBytesExt, LE};
use std::io::{Cursor, Read, Result};

#[derive(Clone, Copy, Debug)]
pub struct ColorF {
Expand Down Expand Up @@ -75,6 +75,12 @@ pub struct IconSys {
}

impl IconSys {
// Maximum length of a title the icon.sys format can hold in bytes (2 bytes per character)
pub const MAXIMUM_TITLE_BYTE_LENGTH: usize = 68;

// Maximum length of a filename (for the .icn files) the icon.sys format can hold in bytes (2 bytes per character)
pub const MAXIMUM_FILENAME_BYTE_LENGTH: usize = 64;

pub fn new(bytes: Vec<u8>) -> Self {
parse_icon_sys(bytes).unwrap()
}
Expand Down Expand Up @@ -104,34 +110,46 @@ impl IconSys {

let title_bytes = encode_sjis(&self.title);
let title_len = title_bytes.len();
if title_len > 68 {
if title_len > Self::MAXIMUM_TITLE_BYTE_LENGTH {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Title length exceeds 68 bytes",
));
}

bytes.extend_from_slice(&title_bytes);
if title_len < 68 {
bytes.extend(vec![0; 68 - title_len]);
if title_len < Self::MAXIMUM_TITLE_BYTE_LENGTH {
bytes.extend(vec![0; Self::MAXIMUM_TITLE_BYTE_LENGTH - title_len]);
}

bytes.extend_from_slice(self.icon_file.as_bytes());

if self.icon_file.len() < 64 {
bytes.extend(vec![0; 64 - self.icon_file.len()]);
if self.icon_file.len() < Self::MAXIMUM_FILENAME_BYTE_LENGTH {
bytes.extend(vec![
0;
Self::MAXIMUM_FILENAME_BYTE_LENGTH
- self.icon_file.len()
]);
}

bytes.extend_from_slice(self.icon_copy_file.as_bytes());

if self.icon_copy_file.len() < 64 {
bytes.extend(vec![0; 64 - self.icon_copy_file.len()]);
if self.icon_copy_file.len() < Self::MAXIMUM_FILENAME_BYTE_LENGTH {
bytes.extend(vec![
0;
Self::MAXIMUM_FILENAME_BYTE_LENGTH
- self.icon_copy_file.len()
]);
}

bytes.extend_from_slice(self.icon_delete_file.as_bytes());

if self.icon_delete_file.len() < 64 {
bytes.extend(vec![0; 64 - self.icon_delete_file.len()]);
if self.icon_delete_file.len() < Self::MAXIMUM_FILENAME_BYTE_LENGTH {
bytes.extend(vec![
0;
Self::MAXIMUM_FILENAME_BYTE_LENGTH
- self.icon_delete_file.len()
]);
}

bytes.extend(vec![0; 512]);
Expand Down
5 changes: 3 additions & 2 deletions crates/suitcase/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
pub mod bottom_bar;
pub mod buttons;
pub mod dialogs;
pub(crate) mod file_picker;
pub mod file_tree;
pub mod greeting;
pub mod menu_bar;
pub mod menu_item;
pub mod tab_viewer;
pub mod toolbar;
pub mod greeting;
pub(crate) mod file_picker;
pub mod value_select;
67 changes: 67 additions & 0 deletions crates/suitcase/src/components/value_select.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use eframe::egui::{CornerRadius, Id, PopupCloseBehavior, Response, Ui};
use std::ops::Add;

pub fn value_select(
ui: &mut Ui,
name: impl Into<String>,
selected_value: &mut String,
values: &[String],
) -> Response {
let id = Id::from(name.into());
let mut layout_response = ui.horizontal(|ui| {
ui.style_mut().spacing.item_spacing.x = 1.0;

set_border_radius(
ui,
CornerRadius {
nw: 2,
sw: 2,
ne: 0,
se: 0,
},
);
let edit_response = ui.text_edit_singleline(selected_value);

set_border_radius(
ui,
CornerRadius {
nw: 0,
sw: 0,
ne: 2,
se: 2,
},
);
let button_response = ui.button("🔽");
button_response.clicked().then(|| {
ui.memory_mut(|mem| {
mem.toggle_popup(id);
});
});

(edit_response, button_response)
});

// Small hack to ensure the popup is positioned correctly
let res = Response {
rect: layout_response.response.rect,
..layout_response.inner.1
};

eframe::egui::popup_below_widget(ui, id, &res, PopupCloseBehavior::CloseOnClick, |ui| {
ui.set_min_width(200.0);
values.iter().for_each(|value| {
if ui.selectable_label(false, value.clone()).clicked() {
*selected_value = value.clone();
layout_response.inner.0.mark_changed();
}
});
});

layout_response.inner.0
}

fn set_border_radius(ui: &mut Ui, radius: CornerRadius) {
ui.style_mut().visuals.widgets.hovered.corner_radius = radius.add(CornerRadius::same(1));
ui.style_mut().visuals.widgets.inactive.corner_radius = radius;
ui.style_mut().visuals.widgets.active.corner_radius = radius;
}
74 changes: 55 additions & 19 deletions crates/suitcase/src/tabs/icn_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
};
use cgmath::Vector3;
use eframe::egui::load::SizedTexture;
use eframe::egui::{vec2, ColorImage, ComboBox, Grid, Id, ImageData, ImageSource, Stroke, TextureId, TextureOptions, WidgetText};
use eframe::egui::{
vec2, ColorImage, ComboBox, Grid, Id, ImageData, ImageSource, Stroke, TextureId,
TextureOptions, WidgetText,
};
use eframe::{
egui,
egui::{include_image, menu, Color32, Ui},
Expand Down Expand Up @@ -64,9 +67,11 @@ impl<'a> TabViewer for ICNTabViewer<'a> {
}
ui.end_row();
ui.label("Compression");
ComboBox::from_id_salt("compression").selected_text("On").show_ui(ui, |ui| {
ui.selectable_label(true, "On");
})
ComboBox::from_id_salt("compression")
.selected_text("On")
.show_ui(ui, |ui| {
ui.selectable_label(true, "On");
})
});
}
ICNTab::IconSysProperties => {
Expand Down Expand Up @@ -139,25 +144,45 @@ impl ICNViewer {
impl ICNViewer {
pub fn new(file: &VirtualFile, state: &AppState) -> Self {
let mut background_colors = [Color32::DARK_GRAY; 4];
let mut light_colors = [ColorF{r: 0.0, g: 0.0, b: 0.0, a: 0.0}; 3];
let mut light_positions = [Vector{x: 0.0, y: 0.0, z: 0.0, w: 0.0}; 3];
let mut ambient_color = ColorF{r: 0.1, g: 0.1, b: 0.1, a: 0.0};
let mut light_colors = [ColorF {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
}; 3];
let mut light_positions = [Vector {
x: 0.0,
y: 0.0,
z: 0.0,
w: 0.0,
}; 3];
let mut ambient_color = ColorF {
r: 0.1,
g: 0.1,
b: 0.1,
a: 0.0,
};

let buf = std::fs::read(&file.file_path).expect("File not found");
let icon_sys = file.file_path.clone().parent().unwrap().join("icon.sys");

if icon_sys.exists() {
let icon_sys = IconSys::new(std::fs::read(icon_sys).unwrap());
background_colors = icon_sys.background_colors.map(|c| PS2RgbaInterface::build_from_color(c).into());
background_colors = icon_sys
.background_colors
.map(|c| PS2RgbaInterface::build_from_color(c).into());
light_colors = icon_sys.light_colors;
light_positions = icon_sys.light_directions;
}

let icn = ps2_filetypes::ICNParser::read(&buf.clone()).unwrap();
let mut dock_state =
DockState::new(vec![ICNTab::IconProperties]);
let mut dock_state = DockState::new(vec![ICNTab::IconProperties]);

dock_state.main_surface_mut().split_below(NodeIndex::root(), 0.5, vec![ICNTab::IconSysProperties]);
dock_state.main_surface_mut().split_below(
NodeIndex::root(),
0.5,
vec![ICNTab::IconSysProperties],
);

Self {
dock_state,
Expand Down Expand Up @@ -218,8 +243,7 @@ impl ICNViewer {
let mut delta_pitch = 0.0;
let mut delta_zoom = 0.0;

if response.dragged()
{
if response.dragged() {
let delta = response.drag_delta();
delta_yaw -= delta.x * 0.01;
delta_pitch += delta.y * 0.01;
Expand Down Expand Up @@ -257,7 +281,15 @@ impl ICNViewer {
if closing {
renderer.drop(painter.gl())
} else {
renderer.paint(painter.gl(), aspect_ratio, camera, frame, light_colors, light_positions, ambient_color);
renderer.paint(
painter.gl(),
aspect_ratio,
camera,
frame,
light_colors,
light_positions,
ambient_color,
);
}
})),
};
Expand Down Expand Up @@ -300,7 +332,8 @@ impl ICNViewer {
});
ui.vertical(|ui| {
menu::bar(ui, |ui| {
ui.set_height(50.0);
ui.set_height(Self::TOOLBAR_HEIGHT);
ui.add_space(Self::TOOLBAR_LEFT_MARGIN);
if ui
.icon_text_button(
include_image!("../../assets/icons/file-arrow-right.svg"),
Expand Down Expand Up @@ -358,10 +391,13 @@ impl ICNViewer {
ui.vertical(|ui| {
ui.set_height(ui.available_size_before_wrap().y - 28.0);

egui::Frame::canvas(ui.style()).stroke(Stroke::NONE).corner_radius(0).show(ui, |ui| {
draw_background(ui, &self.background_colors);
self.custom_painting(ui);
});
egui::Frame::canvas(ui.style())
.stroke(Stroke::NONE)
.corner_radius(0)
.show(ui, |ui| {
draw_background(ui, &self.background_colors);
self.custom_painting(ui);
});
});

if self.icn.animation_header.frame_length > 1 {
Expand Down
Loading
Loading