diff --git a/crates/molotov-core/src/assets/manager.rs b/crates/molotov-core/src/assets/manager.rs new file mode 100644 index 0000000..178ccb0 --- /dev/null +++ b/crates/molotov-core/src/assets/manager.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use super::asset::AssetFile; +use super::dir::AssetDir; +use std::collections::HashMap; + +pub struct AssetManager { + base_assets: HashMap>, + mod_overrides: HashMap>, +} + +impl Default for AssetManager { + fn default() -> Self { + Self::new() + } +} + +impl AssetManager { + pub fn new() -> Self { + Self { + base_assets: HashMap::new(), + mod_overrides: HashMap::new(), + } + } + + pub fn add_asset(&mut self, path: &str, data: Vec) { + self.base_assets.insert(path.to_string(), data); + } + + pub fn add_mod_override(&mut self, path: &str, data: Vec) { + self.mod_overrides.insert(path.to_string(), data); + } + + pub fn open(&self, filename: &str) -> Option { + // 1. Check user mod overrides first (non-destructive mod engine) + if let Some(data) = self.mod_overrides.get(filename) { + tracing::info!("AssetManager: redirected '{}' to active user mod", filename); + return Some(AssetFile::new(data.clone())); + } + + // 2. Fallback to base asset + if let Some(data) = self.base_assets.get(filename) { + return Some(AssetFile::new(data.clone())); + } + + tracing::warn!("AssetManager: asset '{}' not found", filename); + None + } + + pub fn open_dir(&self, dir_name: &str) -> AssetDir { + let prefix = if dir_name.is_empty() || dir_name == "." { + "".to_string() + } else if !dir_name.ends_with('/') { + format!("{}/", dir_name) + } else { + dir_name.to_string() + }; + + let mut files = Vec::new(); + for key in self.base_assets.keys().chain(self.mod_overrides.keys()) { + if key.starts_with(&prefix) { + let rest = &key[prefix.len()..]; + if !rest.contains('/') { + files.push(rest.to_string()); + } + } + } + files.sort(); + files.dedup(); + AssetDir::new(files) + } +} diff --git a/crates/molotov-core/src/assets/mod.rs b/crates/molotov-core/src/assets/mod.rs index 897866a..02365ce 100644 --- a/crates/molotov-core/src/assets/mod.rs +++ b/crates/molotov-core/src/assets/mod.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later pub mod asset; pub mod dir; +pub mod manager; pub mod types; diff --git a/crates/molotov-core/tests/asset_manager_virtual_test.rs b/crates/molotov-core/tests/asset_manager_virtual_test.rs new file mode 100644 index 0000000..9a31790 --- /dev/null +++ b/crates/molotov-core/tests/asset_manager_virtual_test.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use molotov_core::assets::manager::AssetManager; + +#[test] +fn test_asset_manager_open_with_mod_override() { + let mut mgr = AssetManager::new(); + mgr.add_asset("content/sounds/ouch.ogg", b"BASE_SOUND".to_vec()); + mgr.add_mod_override("content/sounds/ouch.ogg", b"CUSTOM_MOD_SOUND".to_vec()); + + let mut asset = mgr.open("content/sounds/ouch.ogg").expect("Should open asset"); + let mut content = vec![0u8; asset.length() as usize]; + asset.read(&mut content); + + assert_eq!(&content, b"CUSTOM_MOD_SOUND", "Must redirect to user mod bytes"); +}