Expose save slot info (like playtime) in GUI

This commit is contained in:
Joshua Goins 2025-04-20 01:02:00 -04:00
parent d1e9a8d57a
commit dc40526c33
5 changed files with 120 additions and 35 deletions

10
Cargo.lock generated
View file

@ -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",

View file

@ -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 }

View file

@ -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::<SlotObject>::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");
});
}

View file

@ -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,
}

View file

@ -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()
}
}