diff --git a/Cargo.lock b/Cargo.lock index 7540d36..c72f553 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -620,6 +620,15 @@ dependencies = [ "libc", ] +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "num-traits", +] + [[package]] name = "clipboard-win" version = "5.4.0" @@ -1565,6 +1574,7 @@ name = "ireko" version = "0.1.0" dependencies = [ "binrw", + "chrono", "eframe", "flate2", "paramacro", diff --git a/Cargo.toml b/Cargo.toml index 0b446ba..27821f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ flate2 = { version = "1.0", features = ["zlib-ng"], default-features = false } binrw = { version = "0.14", features = ["std"], default-features = false } paramacro = { path = "paramacro" } eframe = { version= "0.31" } +chrono = { version = "0.4", default-features = false } diff --git a/src/bin/gui.rs b/src/bin/gui.rs index 95db0e1..e640483 100644 --- a/src/bin/gui.rs +++ b/src/bin/gui.rs @@ -3,7 +3,18 @@ use std::{env, io::Cursor}; use binrw::BinRead; use eframe::egui; use ireko::{ - property::{array_property::ArrayValue, map_property::{MabSubProperty, MapKeyProperty}}, save_object::{generic::Property, LocalProfileObject, PersistentObject}, structure::{DAAssembleIdDataStruct, DABuildDataStruct, DACustomizeAssetIdDataStruct, DAHumanoidColoringDataStruct, DAHumanoidFigureData, DAMachineColoringDataStruct, DAModuleColorStruct, DATriggerDataStruct, DATuningDataStruct, Guid, LinearColorStruct, PrimaryAssetIdStruct, Struct}, CompressedSaveFile + CompressedSaveFile, + property::{ + array_property::ArrayValue, + map_property::{MabSubProperty, MapKeyProperty}, + }, + save_object::{LocalProfileObject, PersistentObject, SlotObject, generic::Property}, + structure::{ + DAAssembleIdDataStruct, DABuildDataStruct, DACustomizeAssetIdDataStruct, + DAHumanoidColoringDataStruct, DAHumanoidFigureData, DAMachineColoringDataStruct, + DAModuleColorStruct, DATriggerDataStruct, DATuningDataStruct, Guid, LinearColorStruct, + PrimaryAssetIdStruct, Struct, + }, }; fn main() -> eframe::Result { @@ -39,7 +50,7 @@ fn main() -> eframe::Result { ModuleInventory, PartsInventory, Build, - Presets + Presets, }; let mut tab = Tab::General; @@ -67,7 +78,29 @@ fn main() -> eframe::Result { panic!("Expected a stirng!"); }; - if ui.button(&name.value).clicked() { + let slot_path = format!("{save_dir}/{}/Slot.sav", id.value); + + let mut slot_data = Cursor::new(std::fs::read(&slot_path).unwrap()); + + let slot = CompressedSaveFile::::read_le(&mut slot_data).unwrap(); + + let playtime = chrono::NaiveTime::from_num_seconds_from_midnight_opt( + slot.value.objs.playtime.value.round() as u32, + 0, + ) + .unwrap(); + let last_checkpoint = slot.value.objs.district_tag.value; + + if ui + .button(&format!( + "{} {} {} {}", + name.value, + slot.value.objs.slot_info.timestamp.to_datetime(), + playtime, + last_checkpoint + )) + .clicked() + { let persistent_path = format!("{save_dir}/{}/Persistent.sav", id.value); let mut persistent_data = @@ -116,15 +149,20 @@ fn main() -> eframe::Result { ui.checkbox(&mut persistent.demo_version.value, "Demo Version"); ui.horizontal(|ui| { - ui.add(egui::DragValue::new(&mut persistent.money.value).suffix(" cell")); + ui.add( + egui::DragValue::new(&mut persistent.money.value).suffix(" cell"), + ); ui.label("Money") }); ui.horizontal(|ui| { - ui.add(egui::DragValue::new(&mut persistent.current_item_slot.value).range(0..=5)); + ui.add( + egui::DragValue::new(&mut persistent.current_item_slot.value) + .range(0..=5), + ); ui.label("Selected Item"); }); - }, + } Tab::ItemWheel => { for (i, item) in persistent.item_slots.entries.iter_mut().enumerate() { let ArrayValue::Struct { ref mut r#struct } = item.key else { @@ -140,14 +178,16 @@ fn main() -> eframe::Result { ui.label(format!("{i:02}")); }); } - }, + } Tab::ItemInventory => { for item in &mut persistent.normal_item_inventory.entries { let MapKeyProperty::StructMaybe(ref key) = item.key else { panic!("Expecting a struct!"); }; - let Property::Name(ref name) = **(key.extra_fields[0].key.as_ref().unwrap()) else { + let Property::Name(ref name) = + **(key.extra_fields[0].key.as_ref().unwrap()) + else { panic!("Expecting a name property!"); }; @@ -155,11 +195,15 @@ fn main() -> eframe::Result { panic!("Expecting a struct!"); }; - let Property::Int(ref carry_count) = **(value.fields[0].key.as_ref().unwrap()) else { + let Property::Int(ref carry_count) = + **(value.fields[0].key.as_ref().unwrap()) + else { panic!("Expecting an int!"); }; - let Property::Int(ref store_count) = **(value.fields[1].key.as_ref().unwrap()) else { + let Property::Int(ref store_count) = + **(value.fields[1].key.as_ref().unwrap()) + else { panic!("Expecting an int!"); }; @@ -169,7 +213,7 @@ fn main() -> eframe::Result { ui.label(format!("store: {}", store_count.value)); }); } - }, + } Tab::ModuleInventory => { ui.horizontal(|ui| { if ui.button("Mobility").clicked() { @@ -208,8 +252,12 @@ fn main() -> eframe::Result { let matches = match module { ModuleType::Mobility => key.value == "EDAModuleItemType::Mobility", - ModuleType::FrontWeapon => key.value == "EDAModuleItemType::FrontWeapon", - ModuleType::RearWeapon => key.value == "EDAModuleItemType::RearWeapon", + ModuleType::FrontWeapon => { + key.value == "EDAModuleItemType::FrontWeapon" + } + ModuleType::RearWeapon => { + key.value == "EDAModuleItemType::RearWeapon" + } ModuleType::Hanger => key.value == "EDAModuleItemType::Hanger", ModuleType::Utility => key.value == "EDAModuleItemType::Utility", ModuleType::Thruster => key.value == "EDAModuleItemType::Thruster", @@ -221,7 +269,9 @@ fn main() -> eframe::Result { panic!("Expecting struct value!"); }; - let Property::Map(ref mut map) = **value.fields[0].key.as_mut().unwrap() else { + let Property::Map(ref mut map) = + **value.fields[0].key.as_mut().unwrap() + else { panic!("Expecting map!"); }; @@ -236,11 +286,14 @@ fn main() -> eframe::Result { panic!("Expecting struct value!"); }; - let Property::Struct(ref asset_id) = **value.fields[0].key.as_mut().unwrap() else { + let Property::Struct(ref asset_id) = + **value.fields[0].key.as_mut().unwrap() + else { panic!("Expecting struct!"); }; - let Struct::PrimaryAssetId(ref asset_id) = asset_id.r#struct else { + let Struct::PrimaryAssetId(ref asset_id) = asset_id.r#struct + else { panic!("Expecting primary asset id!"); }; @@ -253,23 +306,24 @@ fn main() -> eframe::Result { };*/ ui.horizontal(|ui| { - ui.label(format!("{:#?} {} level", key, asset_id.primary_asset_name.value)); + ui.label(format!( + "{:#?} {} level", + key, asset_id.primary_asset_name.value + )); }); } } } //println!("{:#?}", persistent.module_inventory); - }, + } Tab::PartsInventory => { println!("{:#?}", persistent.parts_inventory); - }, + } Tab::Build => { edit_build_data(ui, &mut persistent.current_build_data); - }, - Tab::Presets => { - - }, + } + Tab::Presets => {} } } }); @@ -322,8 +376,14 @@ fn edit_customize_data(ui: &mut egui::Ui, customize: &mut DACustomizeAssetIdData edit_humanoid_figure(ui, &mut customize.figure_data); ui.checkbox(&mut customize.inverse_face_mesh.value, "Inverse Face"); - ui.checkbox(&mut customize.inverse_front_hair_mesh.value, "Inverse Front Hair"); - ui.checkbox(&mut customize.inverse_back_hair_mesh.value, "Inverse Back Hair"); + ui.checkbox( + &mut customize.inverse_front_hair_mesh.value, + "Inverse Front Hair", + ); + ui.checkbox( + &mut customize.inverse_back_hair_mesh.value, + "Inverse Back Hair", + ); } fn edit_humanoid_figure(ui: &mut egui::Ui, figure_data: &mut DAHumanoidFigureData) { @@ -348,7 +408,10 @@ fn edit_humanoid_figure(ui: &mut egui::Ui, figure_data: &mut DAHumanoidFigureDat }); ui.horizontal(|ui| { - ui.add(egui::Slider::new(&mut figure_data.waist_up.value, 0.0..=1.0)); + ui.add(egui::Slider::new( + &mut figure_data.waist_up.value, + 0.0..=1.0, + )); ui.label("Waist"); }); } diff --git a/src/save_object/slot.rs b/src/save_object/slot.rs index 019dacd..b05dcec 100644 --- a/src/save_object/slot.rs +++ b/src/save_object/slot.rs @@ -8,29 +8,29 @@ use crate::{ #[derive(Debug)] pub struct SlotObject { #[paramacro::serialized_field = "SavedDataVersion"] - version: IntProperty, + pub version: IntProperty, #[paramacro::serialized_field = "bDemoVersion"] - demo: BoolProperty, + pub demo: BoolProperty, #[paramacro::serialized_field = "CreatedTimeStamp"] - created_timestamp: DateTimeStruct, + pub created_timestamp: DateTimeStruct, #[paramacro::serialized_field = "PlayTime"] - playtime: FloatProperty, + pub playtime: FloatProperty, #[paramacro::serialized_field = "RegisteredName"] - name: StrProperty, + pub name: StrProperty, #[paramacro::serialized_field = "LoadOption"] - load_option: DALoadOptionStruct, + pub load_option: DALoadOptionStruct, #[paramacro::serialized_field = "DistrictTag"] - district_tag: NameProperty, + pub district_tag: NameProperty, #[paramacro::serialized_field = "CycleCount"] - cycle_count: IntProperty, + pub cycle_count: IntProperty, #[paramacro::serialized_field = "SlotInfo"] - slot_info: SaveSlotInfoStruct, + pub slot_info: SaveSlotInfoStruct, } diff --git a/src/structure/datetime.rs b/src/structure/datetime.rs index 0764e94..c6409aa 100644 --- a/src/structure/datetime.rs +++ b/src/structure/datetime.rs @@ -1,4 +1,5 @@ use binrw::binrw; +use chrono::Datelike; use crate::property::PropertyBase; @@ -26,3 +27,13 @@ impl PropertyBase for DateTimeStruct { 8 } } + +impl DateTimeStruct { + pub fn to_datetime(&self) -> chrono::NaiveDateTime { + let datetime = chrono::NaiveDate::from_ymd_opt(1, 1, 1).unwrap(); + let datetime = chrono::NaiveDateTime::from(datetime); + datetime + .checked_add_signed(chrono::TimeDelta::microseconds(self.ticks / 10)) + .unwrap() + } +}