assets loaded by bank id instead of name (no more traps)

This commit is contained in:
bQUARKz 2026-03-16 17:23:35 +00:00
parent 17878e481c
commit d5eb5cf033
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
9 changed files with 27 additions and 10 deletions

View File

@ -183,10 +183,12 @@ impl Audio {
} }
// Resolve the sample from the hardware pools // Resolve the sample from the hardware pools
let sample = self let bank_slot = self.sound_banks.sound_bank_slot(bank_id as usize);
.sound_banks if bank_slot.is_none() {
.sound_bank_slot(bank_id as usize) return AudioOpStatus::BankInvalid;
.and_then(|bank| bank.samples.get(sample_id as usize).map(Arc::clone)); }
let sample = bank_slot.and_then(|bank| bank.samples.get(sample_id as usize).map(Arc::clone));
if let Some(s) = sample { if let Some(s) = sample {
// println!( // println!(

View File

@ -16,6 +16,7 @@ pub enum AudioOpStatus {
ArgRangeInvalid = 3, ArgRangeInvalid = 3,
AssetNotFound = 4, AssetNotFound = 4,
NoEffect = 5, NoEffect = 5,
BankInvalid = 6,
} }
pub trait AudioBridge { pub trait AudioBridge {

View File

@ -18,6 +18,7 @@ pub enum GfxOpStatus {
AssetNotFound = 1, AssetNotFound = 1,
InvalidSpriteIndex = 2, InvalidSpriteIndex = 2,
ArgRangeInvalid = 3, ArgRangeInvalid = 3,
BankInvalid = 4,
} }
pub trait GfxBridge { pub trait GfxBridge {

View File

@ -150,6 +150,12 @@ impl NativeInterface for VirtualMachineRuntime {
ret.push_int(GfxOpStatus::InvalidSpriteIndex as i64); ret.push_int(GfxOpStatus::InvalidSpriteIndex as i64);
return Ok(()); return Ok(());
} }
if hw.assets().slot_info(SlotRef::gfx(bank_id as usize)).asset_id.is_none() {
ret.push_int(GfxOpStatus::BankInvalid as i64);
return Ok(());
}
if palette_id >= 64 || priority >= 5 { if palette_id >= 64 || priority >= 5 {
ret.push_int(GfxOpStatus::ArgRangeInvalid as i64); ret.push_int(GfxOpStatus::ArgRangeInvalid as i64);
return Ok(()); return Ok(());
@ -237,6 +243,11 @@ impl NativeInterface for VirtualMachineRuntime {
return Ok(()); return Ok(());
} }
if hw.assets().slot_info(SlotRef::audio(bank_id as usize)).asset_id.is_none() {
ret.push_int(AudioOpStatus::BankInvalid as i64);
return Ok(());
}
if sample_id_raw < 0 if sample_id_raw < 0
|| sample_id_raw > u16::MAX as i64 || sample_id_raw > u16::MAX as i64
|| !(0..=255).contains(&volume_raw) || !(0..=255).contains(&volume_raw)

View File

@ -376,7 +376,7 @@ fn tick_gfx_set_sprite_operational_error_returns_status_not_crash() {
let report = runtime.tick(&mut vm, &signals, &mut hardware); let report = runtime.tick(&mut vm, &signals, &mut hardware);
assert!(report.is_none(), "operational error must not crash"); assert!(report.is_none(), "operational error must not crash");
assert!(vm.is_halted()); assert!(vm.is_halted());
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::Ok as i64)]); assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::BankInvalid as i64)]);
} }
#[test] #[test]
@ -437,7 +437,7 @@ fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() {
let report = runtime.tick(&mut vm, &signals, &mut hardware); let report = runtime.tick(&mut vm, &signals, &mut hardware);
assert!(report.is_none(), "invalid gfx parameter range must not crash"); assert!(report.is_none(), "invalid gfx parameter range must not crash");
assert!(vm.is_halted()); assert!(vm.is_halted());
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::ArgRangeInvalid as i64)]); assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::BankInvalid as i64)]);
} }
#[test] #[test]
@ -522,7 +522,7 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() {
let report = runtime.tick(&mut vm, &signals, &mut hardware); let report = runtime.tick(&mut vm, &signals, &mut hardware);
assert!(report.is_none(), "missing audio asset must not crash"); assert!(report.is_none(), "missing audio asset must not crash");
assert!(vm.is_halted()); assert!(vm.is_halted());
assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::SampleNotFound as i64)]); assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::BankInvalid as i64)]);
} }
#[test] #[test]
@ -930,8 +930,8 @@ fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() {
vec![ vec![
Value::Int64(0), Value::Int64(0),
Value::Int64(AssetLoadError::AssetNotFound as i64), Value::Int64(AssetLoadError::AssetNotFound as i64),
Value::Int64(AudioOpStatus::SampleNotFound as i64), Value::Int64(AudioOpStatus::BankInvalid as i64),
Value::Int64(GfxOpStatus::Ok as i64), Value::Int64(GfxOpStatus::BankInvalid as i64),
] ]
); );
} }

View File

@ -126,7 +126,7 @@ fn heavy_load(rom: &mut Vec<u8>) {
rom.extend(asm("PUSH_I32 0\nHOSTCALL 0")); rom.extend(asm("PUSH_I32 0\nHOSTCALL 0"));
// --- call status-first syscall path once per frame and drop status --- // --- call status-first syscall path once per frame and drop status ---
rom.extend(asm( rom.extend(asm(
"PUSH_CONST 2\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 4\nPOP_N 1", "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 4\nPOP_N 1",
)); ));
// --- draw 500 discs --- // --- draw 500 discs ---

View File

@ -564,6 +564,7 @@ Minimum status table:
- `0` = `OK` - `0` = `OK`
- `2` = `INVALID_SPRITE_INDEX` - `2` = `INVALID_SPRITE_INDEX`
- `3` = `INVALID_ARG_RANGE` - `3` = `INVALID_ARG_RANGE`
- `4` = `BANK_INVALID`
Operational notes: Operational notes:

View File

@ -215,6 +215,7 @@ Return-shape matrix in v1 syscall surface:
- `2` = `SAMPLE_NOT_FOUND` - `2` = `SAMPLE_NOT_FOUND`
- `3` = `ARG_RANGE_INVALID` - `3` = `ARG_RANGE_INVALID`
- `5` = `NO_EFFECT` - `5` = `NO_EFFECT`
- `6` = `BANK_INVALID`
Operational rules: Operational rules: