asset name and id

This commit is contained in:
Nilton Constantino 2026-01-22 14:46:23 +00:00
parent 8d97f6225b
commit 81f7813c7c
No known key found for this signature in database
5 changed files with 88 additions and 57 deletions

View File

@ -38,7 +38,7 @@ impl<T> ResidentEntry<T> {
/// This is internal to the AssetManager and not visible to peripherals. /// This is internal to the AssetManager and not visible to peripherals.
pub struct BankPolicy<T> { pub struct BankPolicy<T> {
/// Dedup table: asset_id -> resident entry (value + telemetry). /// Dedup table: asset_id -> resident entry (value + telemetry).
resident: Arc<RwLock<HashMap<String, ResidentEntry<T>>>>, resident: Arc<RwLock<HashMap<u32, ResidentEntry<T>>>>,
/// Staging area: handle -> value ready to commit. /// Staging area: handle -> value ready to commit.
staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>, staging: Arc<RwLock<HashMap<HandleId, Arc<T>>>>,
@ -53,15 +53,15 @@ impl<T> BankPolicy<T> {
} }
/// Try get a resident value by asset_id (dedupe path). /// Try get a resident value by asset_id (dedupe path).
pub fn get_resident(&self, asset_id: &str) -> Option<Arc<T>> { pub fn get_resident(&self, asset_id: u32) -> Option<Arc<T>> {
let mut map = self.resident.write().unwrap(); let mut map = self.resident.write().unwrap();
let entry = map.get_mut(asset_id)?; let entry = map.get_mut(&asset_id)?;
entry.last_used = Instant::now(); entry.last_used = Instant::now();
Some(Arc::clone(&entry.value)) Some(Arc::clone(&entry.value))
} }
/// Insert or reuse a resident entry. Returns the resident Arc<T>. /// Insert or reuse a resident entry. Returns the resident Arc<T>.
pub fn put_resident(&self, asset_id: String, value: Arc<T>, bytes: usize) -> Arc<T> { pub fn put_resident(&self, asset_id: u32, value: Arc<T>, bytes: usize) -> Arc<T> {
let mut map = self.resident.write().unwrap(); let mut map = self.resident.write().unwrap();
match map.get_mut(&asset_id) { match map.get_mut(&asset_id) {
Some(existing) => { Some(existing) => {
@ -94,7 +94,8 @@ impl<T> BankPolicy<T> {
} }
pub struct AssetManager { pub struct AssetManager {
assets: Arc<RwLock<HashMap<String, AssetEntry>>>, assets: Arc<RwLock<HashMap<u32, AssetEntry>>>,
name_to_id: Arc<RwLock<HashMap<String, u32>>>,
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>, handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
next_handle_id: Mutex<HandleId>, next_handle_id: Mutex<HandleId>,
assets_data: Arc<RwLock<Vec<u8>>>, assets_data: Arc<RwLock<Vec<u8>>>,
@ -104,8 +105,8 @@ pub struct AssetManager {
sound_installer: Arc<dyn SoundBankPoolInstaller>, sound_installer: Arc<dyn SoundBankPoolInstaller>,
/// Track what is installed in each hardware slot (for stats/info). /// Track what is installed in each hardware slot (for stats/info).
gfx_slots: Arc<RwLock<[Option<String>; 16]>>, gfx_slots: Arc<RwLock<[Option<u32>; 16]>>,
sound_slots: Arc<RwLock<[Option<String>; 16]>>, sound_slots: Arc<RwLock<[Option<u32>; 16]>>,
/// Residency policy for GFX tile banks. /// Residency policy for GFX tile banks.
gfx_policy: BankPolicy<TileBank>, gfx_policy: BankPolicy<TileBank>,
@ -117,7 +118,7 @@ pub struct AssetManager {
} }
struct LoadHandleInfo { struct LoadHandleInfo {
_asset_id: String, _asset_id: u32,
slot: SlotRef, slot: SlotRef,
status: LoadStatus, status: LoadStatus,
} }
@ -130,12 +131,15 @@ impl AssetManager {
sound_installer: Arc<dyn SoundBankPoolInstaller>, sound_installer: Arc<dyn SoundBankPoolInstaller>,
) -> Self { ) -> Self {
let mut asset_map = HashMap::new(); let mut asset_map = HashMap::new();
let mut name_to_id = HashMap::new();
for entry in assets { for entry in assets {
asset_map.insert(entry.asset_id.clone(), entry); name_to_id.insert(entry.asset_name.clone(), entry.asset_id);
asset_map.insert(entry.asset_id, entry);
} }
Self { Self {
assets: Arc::new(RwLock::new(asset_map)), assets: Arc::new(RwLock::new(asset_map)),
name_to_id: Arc::new(RwLock::new(name_to_id)),
gfx_installer, gfx_installer,
sound_installer, sound_installer,
gfx_slots: Arc::new(RwLock::new(std::array::from_fn(|_| None))), gfx_slots: Arc::new(RwLock::new(std::array::from_fn(|_| None))),
@ -153,9 +157,12 @@ impl AssetManager {
self.shutdown(); self.shutdown();
{ {
let mut asset_map = self.assets.write().unwrap(); let mut asset_map = self.assets.write().unwrap();
let mut name_to_id = self.name_to_id.write().unwrap();
asset_map.clear(); asset_map.clear();
name_to_id.clear();
for entry in assets.iter() { for entry in assets.iter() {
asset_map.insert(entry.asset_id.clone(), entry.clone()); name_to_id.insert(entry.asset_name.clone(), entry.asset_id);
asset_map.insert(entry.asset_id, entry.clone());
} }
} }
*self.assets_data.write().unwrap() = assets_data; *self.assets_data.write().unwrap() = assets_data;
@ -164,7 +171,10 @@ impl AssetManager {
for item in preload { for item in preload {
let entry_opt = { let entry_opt = {
let assets = self.assets.read().unwrap(); let assets = self.assets.read().unwrap();
assets.get(&item.asset_id).cloned() let name_to_id = self.name_to_id.read().unwrap();
name_to_id.get(&item.asset_name)
.and_then(|id| assets.get(id))
.cloned()
}; };
if let Some(entry) = entry_opt { if let Some(entry) = entry_opt {
@ -173,43 +183,46 @@ impl AssetManager {
BankType::TILES => { BankType::TILES => {
if let Ok(bank) = Self::perform_load_tile_bank(&entry, self.assets_data.clone()) { if let Ok(bank) = Self::perform_load_tile_bank(&entry, self.assets_data.clone()) {
let bank_arc = Arc::new(bank); let bank_arc = Arc::new(bank);
self.gfx_policy.put_resident(entry.asset_id.clone(), Arc::clone(&bank_arc), entry.decoded_size as usize); self.gfx_policy.put_resident(entry.asset_id, Arc::clone(&bank_arc), entry.decoded_size as usize);
self.gfx_installer.install_tile_bank(slot_index, bank_arc); self.gfx_installer.install_tile_bank(slot_index, bank_arc);
let mut slots = self.gfx_slots.write().unwrap(); let mut slots = self.gfx_slots.write().unwrap();
if slot_index < slots.len() { if slot_index < slots.len() {
slots[slot_index] = Some(entry.asset_id.clone()); slots[slot_index] = Some(entry.asset_id);
} }
println!("[AssetManager] Preloaded tile asset '{}' into slot {}", entry.asset_id, slot_index); println!("[AssetManager] Preloaded tile asset '{}' (id: {}) into slot {}", entry.asset_name, entry.asset_id, slot_index);
} else { } else {
eprintln!("[AssetManager] Failed to preload tile asset '{}'", entry.asset_id); eprintln!("[AssetManager] Failed to preload tile asset '{}'", entry.asset_name);
} }
} }
BankType::SOUNDS => { BankType::SOUNDS => {
if let Ok(bank) = Self::perform_load_sound_bank(&entry, self.assets_data.clone()) { if let Ok(bank) = Self::perform_load_sound_bank(&entry, self.assets_data.clone()) {
let bank_arc = Arc::new(bank); let bank_arc = Arc::new(bank);
self.sound_policy.put_resident(entry.asset_id.clone(), Arc::clone(&bank_arc), entry.decoded_size as usize); self.sound_policy.put_resident(entry.asset_id, Arc::clone(&bank_arc), entry.decoded_size as usize);
self.sound_installer.install_sound_bank(slot_index, bank_arc); self.sound_installer.install_sound_bank(slot_index, bank_arc);
let mut slots = self.sound_slots.write().unwrap(); let mut slots = self.sound_slots.write().unwrap();
if slot_index < slots.len() { if slot_index < slots.len() {
slots[slot_index] = Some(entry.asset_id.clone()); slots[slot_index] = Some(entry.asset_id);
} }
println!("[AssetManager] Preloaded sound asset '{}' into slot {}", entry.asset_id, slot_index); println!("[AssetManager] Preloaded sound asset '{}' (id: {}) into slot {}", entry.asset_name, entry.asset_id, slot_index);
} else { } else {
eprintln!("[AssetManager] Failed to preload sound asset '{}'", entry.asset_id); eprintln!("[AssetManager] Failed to preload sound asset '{}'", entry.asset_name);
} }
} }
} }
} else { } else {
eprintln!("[AssetManager] Preload failed: asset '{}' not found in table", item.asset_id); eprintln!("[AssetManager] Preload failed: asset '{}' not found in table", item.asset_name);
} }
} }
} }
pub fn load(&self, asset_id: &str, slot: SlotRef) -> Result<HandleId, String> { pub fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, String> {
let entry = { let entry = {
let assets = self.assets.read().unwrap(); let assets = self.assets.read().unwrap();
assets.get(asset_id).ok_or_else(|| format!("Asset not found: {}", asset_id))?.clone() let name_to_id = self.name_to_id.read().unwrap();
let id = name_to_id.get(asset_name).ok_or_else(|| format!("Asset not found: {}", asset_name))?;
assets.get(id).ok_or_else(|| format!("Asset ID {} not found in table", id))?.clone()
}; };
let asset_id = entry.asset_id;
if slot.asset_type != entry.bank_type { if slot.asset_type != entry.bank_type {
return Err("INCOMPATIBLE_SLOT_KIND".to_string()); return Err("INCOMPATIBLE_SLOT_KIND".to_string());
@ -237,7 +250,7 @@ impl AssetManager {
if already_resident { if already_resident {
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo { self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
_asset_id: asset_id.to_string(), _asset_id: asset_id,
slot, slot,
status: LoadStatus::READY, status: LoadStatus::READY,
}); });
@ -246,7 +259,7 @@ impl AssetManager {
// Not resident, start loading // Not resident, start loading
self.handles.write().unwrap().insert(handle_id, LoadHandleInfo { self.handles.write().unwrap().insert(handle_id, LoadHandleInfo {
_asset_id: asset_id.to_string(), _asset_id: asset_id,
slot, slot,
status: LoadStatus::PENDING, status: LoadStatus::PENDING,
}); });
@ -254,7 +267,6 @@ impl AssetManager {
let handles = self.handles.clone(); let handles = self.handles.clone();
let assets_data = self.assets_data.clone(); let assets_data = self.assets_data.clone();
let entry_clone = entry.clone(); let entry_clone = entry.clone();
let asset_id_clone = asset_id.to_string();
// Capture policies for the worker thread // Capture policies for the worker thread
let gfx_policy_resident = Arc::clone(&self.gfx_policy.resident); let gfx_policy_resident = Arc::clone(&self.gfx_policy.resident);
@ -284,13 +296,13 @@ impl AssetManager {
let bank_arc = Arc::new(tilebank); let bank_arc = Arc::new(tilebank);
let resident_arc = { let resident_arc = {
let mut map = gfx_policy_resident.write().unwrap(); let mut map = gfx_policy_resident.write().unwrap();
if let Some(existing) = map.get_mut(&asset_id_clone) { if let Some(existing) = map.get_mut(&asset_id) {
existing.last_used = Instant::now(); existing.last_used = Instant::now();
existing.loads += 1; existing.loads += 1;
Arc::clone(&existing.value) Arc::clone(&existing.value)
} else { } else {
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize); let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
map.insert(asset_id_clone, entry); map.insert(asset_id, entry);
bank_arc bank_arc
} }
}; };
@ -310,13 +322,13 @@ impl AssetManager {
let bank_arc = Arc::new(soundbank); let bank_arc = Arc::new(soundbank);
let resident_arc = { let resident_arc = {
let mut map = sound_policy_resident.write().unwrap(); let mut map = sound_policy_resident.write().unwrap();
if let Some(existing) = map.get_mut(&asset_id_clone) { if let Some(existing) = map.get_mut(&asset_id) {
existing.last_used = Instant::now(); existing.last_used = Instant::now();
existing.loads += 1; existing.loads += 1;
Arc::clone(&existing.value) Arc::clone(&existing.value)
} else { } else {
let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize); let entry = ResidentEntry::new(Arc::clone(&bank_arc), entry_clone.decoded_size as usize);
map.insert(asset_id_clone, entry); map.insert(asset_id, entry);
bank_arc bank_arc
} }
}; };
@ -459,7 +471,7 @@ impl AssetManager {
self.gfx_installer.install_tile_bank(h.slot.index, bank); self.gfx_installer.install_tile_bank(h.slot.index, bank);
let mut slots = self.gfx_slots.write().unwrap(); let mut slots = self.gfx_slots.write().unwrap();
if h.slot.index < slots.len() { if h.slot.index < slots.len() {
slots[h.slot.index] = Some(h._asset_id.clone()); slots[h.slot.index] = Some(h._asset_id);
} }
h.status = LoadStatus::COMMITTED; h.status = LoadStatus::COMMITTED;
} }
@ -469,7 +481,7 @@ impl AssetManager {
self.sound_installer.install_sound_bank(h.slot.index, bank); self.sound_installer.install_sound_bank(h.slot.index, bank);
let mut slots = self.sound_slots.write().unwrap(); let mut slots = self.sound_slots.write().unwrap();
if h.slot.index < slots.len() { if h.slot.index < slots.len() {
slots[h.slot.index] = Some(h._asset_id.clone()); slots[h.slot.index] = Some(h._asset_id);
} }
h.status = LoadStatus::COMMITTED; h.status = LoadStatus::COMMITTED;
} }
@ -573,17 +585,20 @@ impl AssetManager {
let slots = self.gfx_slots.read().unwrap(); let slots = self.gfx_slots.read().unwrap();
let asset_id = slots.get(slot.index).and_then(|s| s.clone()); let asset_id = slots.get(slot.index).and_then(|s| s.clone());
let bytes = if let Some(id) = &asset_id { let (bytes, asset_name) = if let Some(id) = &asset_id {
self.gfx_policy.resident.read().unwrap() let bytes = self.gfx_policy.resident.read().unwrap()
.get(id) .get(id)
.map(|entry| entry.bytes) .map(|entry| entry.bytes)
.unwrap_or(0) .unwrap_or(0);
let name = self.assets.read().unwrap().get(id).map(|e| e.asset_name.clone());
(bytes, name)
} else { } else {
0 (0, None)
}; };
SlotStats { SlotStats {
asset_id, asset_id,
asset_name,
generation: 0, generation: 0,
resident_bytes: bytes, resident_bytes: bytes,
} }
@ -592,17 +607,20 @@ impl AssetManager {
let slots = self.sound_slots.read().unwrap(); let slots = self.sound_slots.read().unwrap();
let asset_id = slots.get(slot.index).and_then(|s| s.clone()); let asset_id = slots.get(slot.index).and_then(|s| s.clone());
let bytes = if let Some(id) = &asset_id { let (bytes, asset_name) = if let Some(id) = &asset_id {
self.sound_policy.resident.read().unwrap() let bytes = self.sound_policy.resident.read().unwrap()
.get(id) .get(id)
.map(|entry| entry.bytes) .map(|entry| entry.bytes)
.unwrap_or(0) .unwrap_or(0);
let name = self.assets.read().unwrap().get(id).map(|e| e.asset_name.clone());
(bytes, name)
} else { } else {
0 (0, None)
}; };
SlotStats { SlotStats {
asset_id, asset_id,
asset_name,
generation: 0, generation: 0,
resident_bytes: bytes, resident_bytes: bytes,
} }
@ -635,7 +653,8 @@ mod tests {
data.extend_from_slice(&[0u8; 2048]); data.extend_from_slice(&[0u8; 2048]);
let asset_entry = AssetEntry { let asset_entry = AssetEntry {
asset_id: "test_tiles".to_string(), asset_id: 0,
asset_name: "test_tiles".to_string(),
bank_type: BankType::TILES, bank_type: BankType::TILES,
offset: 0, offset: 0,
size: data.len() as u64, size: data.len() as u64,
@ -684,7 +703,8 @@ mod tests {
data.extend_from_slice(&[0u8; 2048]); data.extend_from_slice(&[0u8; 2048]);
let asset_entry = AssetEntry { let asset_entry = AssetEntry {
asset_id: "test_tiles".to_string(), asset_id: 0,
asset_name: "test_tiles".to_string(),
bank_type: BankType::TILES, bank_type: BankType::TILES,
offset: 0, offset: 0,
size: data.len() as u64, size: data.len() as u64,
@ -724,7 +744,8 @@ mod tests {
let data = vec![0u8; 200]; let data = vec![0u8; 200];
let asset_entry = AssetEntry { let asset_entry = AssetEntry {
asset_id: "test_sound".to_string(), asset_id: 1,
asset_name: "test_sound".to_string(),
bank_type: BankType::SOUNDS, bank_type: BankType::SOUNDS,
offset: 0, offset: 0,
size: data.len() as u64, size: data.len() as u64,
@ -763,7 +784,8 @@ mod tests {
let data = vec![0u8; 200]; let data = vec![0u8; 200];
let asset_entry = AssetEntry { let asset_entry = AssetEntry {
asset_id: "preload_sound".to_string(), asset_id: 2,
asset_name: "preload_sound".to_string(),
bank_type: BankType::SOUNDS, bank_type: BankType::SOUNDS,
offset: 0, offset: 0,
size: data.len() as u64, size: data.len() as u64,
@ -775,7 +797,7 @@ mod tests {
}; };
let preload = vec![ let preload = vec![
PreloadEntry { asset_id: "preload_sound".to_string(), slot: 5 } PreloadEntry { asset_name: "preload_sound".to_string(), slot: 5 }
]; ];
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer); let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
@ -787,6 +809,6 @@ mod tests {
// After init, slot 5 should be occupied because of preload // After init, slot 5 should be occupied because of preload
assert!(banks.sound_bank_slot(5).is_some()); assert!(banks.sound_bank_slot(5).is_some());
assert_eq!(am.slot_info(SlotRef::audio(5)).asset_id, Some("preload_sound".to_string())); assert_eq!(am.slot_info(SlotRef::audio(5)).asset_id, Some(2));
} }
} }

View File

@ -13,7 +13,8 @@ pub enum BankType {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AssetEntry { pub struct AssetEntry {
pub asset_id: String, pub asset_id: u32,
pub asset_name: String,
pub bank_type: BankType, pub bank_type: BankType,
pub offset: u64, pub offset: u64,
pub size: u64, pub size: u64,
@ -24,7 +25,7 @@ pub struct AssetEntry {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PreloadEntry { pub struct PreloadEntry {
pub asset_id: String, pub asset_name: String,
pub slot: usize, pub slot: usize,
} }
@ -50,7 +51,8 @@ pub struct BankStats {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SlotStats { pub struct SlotStats {
pub asset_id: Option<String>, pub asset_id: Option<u32>,
pub asset_name: Option<String>,
pub generation: u32, pub generation: u32,
pub resident_bytes: usize, pub resident_bytes: usize,
} }

View File

@ -900,6 +900,7 @@ impl NativeInterface for PrometeuOS {
let asset_type = match asset_type_val { let asset_type = match asset_type_val {
0 => crate::model::BankType::TILES, 0 => crate::model::BankType::TILES,
1 => crate::model::BankType::SOUNDS,
_ => return Err("Invalid asset type".to_string()), _ => return Err("Invalid asset type".to_string()),
}; };
let slot = crate::model::SlotRef { asset_type, index: slot_index }; let slot = crate::model::SlotRef { asset_type, index: slot_index };
@ -942,6 +943,7 @@ impl NativeInterface for PrometeuOS {
let asset_type_val = vm.pop_integer()? as u32; let asset_type_val = vm.pop_integer()? as u32;
let asset_type = match asset_type_val { let asset_type = match asset_type_val {
0 => crate::model::BankType::TILES, 0 => crate::model::BankType::TILES,
1 => crate::model::BankType::SOUNDS,
_ => return Err("Invalid asset type".to_string()), _ => return Err("Invalid asset type".to_string()),
}; };
let info = hw.assets().bank_info(asset_type); let info = hw.assets().bank_info(asset_type);
@ -954,6 +956,7 @@ impl NativeInterface for PrometeuOS {
let asset_type_val = vm.pop_integer()? as u32; let asset_type_val = vm.pop_integer()? as u32;
let asset_type = match asset_type_val { let asset_type = match asset_type_val {
0 => crate::model::BankType::TILES, 0 => crate::model::BankType::TILES,
1 => crate::model::BankType::SOUNDS,
_ => return Err("Invalid asset type".to_string()), _ => return Err("Invalid asset type".to_string()),
}; };
let slot = crate::model::SlotRef { asset_type, index: slot_index }; let slot = crate::model::SlotRef { asset_type, index: slot_index };

View File

@ -44,7 +44,8 @@ It describes **content**, not residency.
### Required Fields (conceptual) ### Required Fields (conceptual)
* `asset_id` (string or hash) * `asset_id` (integer, internal identifier)
* `asset_name` (string, user-facing identifier)
* `asset_type` (TILEBANK, SOUNDBANK, BLOB, TILEMAP, ...) * `asset_type` (TILEBANK, SOUNDBANK, BLOB, TILEMAP, ...)
* `bank_kind` (mandatory, single) * `bank_kind` (mandatory, single)
* `offset` (byte offset in cartridge) * `offset` (byte offset in cartridge)
@ -139,12 +140,12 @@ Conceptually, each Bank is a **specialized allocator**.
### Conceptual API ### Conceptual API
```text ```text
handle = asset.load(asset_id, slotRef, flags) handle = asset.load(asset_name, slotRef, flags)
``` ```
Load flow: Load flow:
1. Resolve `asset_id` via Asset Table 1. Resolve `asset_name` via Asset Table to get its `asset_id`
2. Read `bank_kind` from asset entry 2. Read `bank_kind` from asset entry
3. Validate compatibility with `slotRef` 3. Validate compatibility with `slotRef`
4. Enqueue load request 4. Enqueue load request
@ -264,6 +265,7 @@ Each Bank must expose:
* inflight memory * inflight memory
* occupied slots * occupied slots
* `asset_id` per slot * `asset_id` per slot
* `asset_name` per slot
* generation per slot * generation per slot
This enables debuggers to visualize: This enables debuggers to visualize:
@ -279,7 +281,7 @@ This enables debuggers to visualize:
The following syscalls form the minimal hardware contract for asset management: The following syscalls form the minimal hardware contract for asset management:
```text ```text
asset.load(asset_id, slotRef, flags) -> handle asset.load(asset_name, slotRef, flags) -> handle
asset.status(handle) -> LoadStatus asset.status(handle) -> LoadStatus
asset.commit(handle) asset.commit(handle)
asset.cancel(handle) asset.cancel(handle)
@ -292,7 +294,7 @@ Where:
* `LoadStatus` ∈ { PENDING, LOADING, READY, COMMITTED, CANCELED, ERROR } * `LoadStatus` ∈ { PENDING, LOADING, READY, COMMITTED, CANCELED, ERROR }
* `BankStats` exposes memory usage and limits * `BankStats` exposes memory usage and limits
* `SlotStats` exposes current asset_id and generation * `SlotStats` exposes current asset_id, asset_name and generation
--- ---

View File

@ -8,7 +8,8 @@
"entrypoint": "0", "entrypoint": "0",
"asset_table": [ "asset_table": [
{ {
"asset_id": "bgm_music", "asset_id": 0,
"asset_name": "bgm_music",
"bank_type": "SOUNDS", "bank_type": "SOUNDS",
"offset": 0, "offset": 0,
"size": 88200, "size": 88200,
@ -19,7 +20,8 @@
} }
}, },
{ {
"asset_id": "mouse_cursor", "asset_id": 1,
"asset_name": "mouse_cursor",
"bank_type": "TILES", "bank_type": "TILES",
"offset": 88200, "offset": 88200,
"size": 2304, "size": 2304,
@ -33,7 +35,7 @@
} }
], ],
"preload": [ "preload": [
{ "asset_id": "bgm_music", "slot": 0 }, { "asset_name": "bgm_music", "slot": 0 },
{ "asset_id": "mouse_cursor", "slot": 1 } { "asset_name": "mouse_cursor", "slot": 1 }
] ]
} }