implements PR-011c assets.pa payload source and open slice
This commit is contained in:
parent
d3321c4400
commit
077378a3b5
@ -5,6 +5,7 @@ use prometeu_hal::asset::{
|
||||
AssetEntry, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
||||
PreloadEntry, SlotRef, SlotStats,
|
||||
};
|
||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||
use prometeu_hal::color::Color;
|
||||
use prometeu_hal::sample::Sample;
|
||||
use prometeu_hal::sound_bank::SoundBank;
|
||||
@ -106,7 +107,7 @@ pub struct AssetManager {
|
||||
name_to_id: Arc<RwLock<HashMap<String, u32>>>,
|
||||
handles: Arc<RwLock<HashMap<HandleId, LoadHandleInfo>>>,
|
||||
next_handle_id: Mutex<HandleId>,
|
||||
assets_data: Arc<RwLock<Vec<u8>>>,
|
||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||
|
||||
/// Narrow hardware interfaces
|
||||
gfx_installer: Arc<dyn TileBankPoolInstaller>,
|
||||
@ -136,7 +137,7 @@ impl AssetBridge for AssetManager {
|
||||
&self,
|
||||
assets: Vec<AssetEntry>,
|
||||
preload: Vec<PreloadEntry>,
|
||||
assets_data: Vec<u8>,
|
||||
assets_data: AssetsPayloadSource,
|
||||
) {
|
||||
self.initialize_for_cartridge(assets, preload, assets_data)
|
||||
}
|
||||
@ -172,7 +173,7 @@ impl AssetBridge for AssetManager {
|
||||
impl AssetManager {
|
||||
pub fn new(
|
||||
assets: Vec<AssetEntry>,
|
||||
assets_data: Vec<u8>,
|
||||
assets_data: AssetsPayloadSource,
|
||||
gfx_installer: Arc<dyn TileBankPoolInstaller>,
|
||||
sound_installer: Arc<dyn SoundBankPoolInstaller>,
|
||||
) -> Self {
|
||||
@ -203,7 +204,7 @@ impl AssetManager {
|
||||
&self,
|
||||
assets: Vec<AssetEntry>,
|
||||
preload: Vec<PreloadEntry>,
|
||||
assets_data: Vec<u8>,
|
||||
assets_data: AssetsPayloadSource,
|
||||
) {
|
||||
self.shutdown();
|
||||
{
|
||||
@ -450,22 +451,20 @@ impl AssetManager {
|
||||
|
||||
fn perform_load_tile_bank(
|
||||
entry: &AssetEntry,
|
||||
assets_data: Arc<RwLock<Vec<u8>>>,
|
||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||
) -> Result<TileBank, String> {
|
||||
if entry.codec != "RAW" {
|
||||
return Err(format!("Unsupported codec: {}", entry.codec));
|
||||
}
|
||||
|
||||
let assets_data = assets_data.read().unwrap();
|
||||
|
||||
let start = entry.offset as usize;
|
||||
let end = start + entry.size as usize;
|
||||
|
||||
if end > assets_data.len() {
|
||||
return Err("Asset offset/size out of bounds".to_string());
|
||||
}
|
||||
|
||||
let buffer = &assets_data[start..end];
|
||||
let buffer = {
|
||||
let assets_data = assets_data.read().unwrap();
|
||||
assets_data
|
||||
.open_slice(entry.offset, entry.size)
|
||||
.map_err(|_| "Asset offset/size out of bounds".to_string())?
|
||||
.read_all()
|
||||
.map_err(|_| "Asset payload read failed".to_string())?
|
||||
};
|
||||
|
||||
// Decode TILEBANK metadata
|
||||
let tile_size_val =
|
||||
@ -505,22 +504,20 @@ impl AssetManager {
|
||||
|
||||
fn perform_load_sound_bank(
|
||||
entry: &AssetEntry,
|
||||
assets_data: Arc<RwLock<Vec<u8>>>,
|
||||
assets_data: Arc<RwLock<AssetsPayloadSource>>,
|
||||
) -> Result<SoundBank, String> {
|
||||
if entry.codec != "RAW" {
|
||||
return Err(format!("Unsupported codec: {}", entry.codec));
|
||||
}
|
||||
|
||||
let assets_data = assets_data.read().unwrap();
|
||||
|
||||
let start = entry.offset as usize;
|
||||
let end = start + entry.size as usize;
|
||||
|
||||
if end > assets_data.len() {
|
||||
return Err("Asset offset/size out of bounds".to_string());
|
||||
}
|
||||
|
||||
let buffer = &assets_data[start..end];
|
||||
let buffer = {
|
||||
let assets_data = assets_data.read().unwrap();
|
||||
assets_data
|
||||
.open_slice(entry.offset, entry.size)
|
||||
.map_err(|_| "Asset offset/size out of bounds".to_string())?
|
||||
.read_all()
|
||||
.map_err(|_| "Asset payload read failed".to_string())?
|
||||
};
|
||||
|
||||
let sample_rate =
|
||||
entry.metadata.get("sample_rate").and_then(|v| v.as_u64()).unwrap_or(44100) as u32;
|
||||
@ -818,7 +815,12 @@ mod tests {
|
||||
let data = test_tile_asset_data();
|
||||
let asset_entry = test_tile_asset_entry("test_tiles", data.len());
|
||||
|
||||
let am = AssetManager::new(vec![asset_entry], data, gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![asset_entry],
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
let slot = SlotRef::gfx(0);
|
||||
|
||||
let handle = am.load("test_tiles", slot).expect("Should start loading");
|
||||
@ -853,7 +855,12 @@ mod tests {
|
||||
let data = test_tile_asset_data();
|
||||
let asset_entry = test_tile_asset_entry("test_tiles", data.len());
|
||||
|
||||
let am = AssetManager::new(vec![asset_entry], data, gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![asset_entry],
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
|
||||
let handle1 = am.load("test_tiles", SlotRef::gfx(0)).unwrap();
|
||||
let start = Instant::now();
|
||||
@ -892,7 +899,12 @@ mod tests {
|
||||
}),
|
||||
};
|
||||
|
||||
let am = AssetManager::new(vec![asset_entry], data, gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![asset_entry],
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
let slot = SlotRef::audio(0);
|
||||
|
||||
let handle = am.load("test_sound", slot).expect("Should start loading");
|
||||
@ -934,12 +946,17 @@ mod tests {
|
||||
|
||||
let preload = vec![PreloadEntry { asset_name: "preload_sound".to_string(), slot: 5 }];
|
||||
|
||||
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
|
||||
// Before init, slot 5 is empty
|
||||
assert!(banks.sound_bank_slot(5).is_none());
|
||||
|
||||
am.initialize_for_cartridge(vec![asset_entry], preload, data);
|
||||
am.initialize_for_cartridge(vec![asset_entry], preload, AssetsPayloadSource::from_bytes(data));
|
||||
|
||||
// After init, slot 5 should be occupied because of preload
|
||||
assert!(banks.sound_bank_slot(5).is_some());
|
||||
@ -958,8 +975,13 @@ mod tests {
|
||||
|
||||
let preload = vec![PreloadEntry { asset_name: "my_tiles".to_string(), slot: 3 }];
|
||||
|
||||
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
||||
am.initialize_for_cartridge(vec![asset_entry], preload, data);
|
||||
let am = AssetManager::new(
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
am.initialize_for_cartridge(vec![asset_entry], preload, AssetsPayloadSource::from_bytes(data));
|
||||
|
||||
assert_eq!(am.find_slot_by_name("my_tiles", BankType::TILES), Some(3));
|
||||
assert_eq!(am.find_slot_by_name("unknown", BankType::TILES), None);
|
||||
@ -971,7 +993,12 @@ mod tests {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
|
||||
let result = am.load("missing", SlotRef::gfx(0));
|
||||
|
||||
@ -986,7 +1013,7 @@ mod tests {
|
||||
let data = test_tile_asset_data();
|
||||
let am = AssetManager::new(
|
||||
vec![test_tile_asset_entry("test_tiles", data.len())],
|
||||
data,
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
@ -1004,7 +1031,7 @@ mod tests {
|
||||
let data = test_tile_asset_data();
|
||||
let am = AssetManager::new(
|
||||
vec![test_tile_asset_entry("test_tiles", data.len())],
|
||||
data,
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
@ -1019,7 +1046,12 @@ mod tests {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let gfx_installer = Arc::clone(&banks) as Arc<dyn TileBankPoolInstaller>;
|
||||
let sound_installer = Arc::clone(&banks) as Arc<dyn SoundBankPoolInstaller>;
|
||||
let am = AssetManager::new(vec![], vec![], gfx_installer, sound_installer);
|
||||
let am = AssetManager::new(
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
|
||||
assert_eq!(am.status(999), LoadStatus::UnknownHandle);
|
||||
}
|
||||
@ -1032,7 +1064,7 @@ mod tests {
|
||||
let data = test_tile_asset_data();
|
||||
let am = AssetManager::new(
|
||||
vec![test_tile_asset_entry("test_tiles", data.len())],
|
||||
data,
|
||||
AssetsPayloadSource::from_bytes(data),
|
||||
gfx_installer,
|
||||
sound_installer,
|
||||
);
|
||||
|
||||
@ -7,6 +7,7 @@ use crate::memory_banks::{
|
||||
};
|
||||
use crate::pad::Pad;
|
||||
use crate::touch::Touch;
|
||||
use prometeu_hal::cartridge::AssetsPayloadSource;
|
||||
use prometeu_hal::{AssetBridge, AudioBridge, GfxBridge, HardwareBridge, PadBridge, TouchBridge};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -98,7 +99,7 @@ impl Hardware {
|
||||
touch: Touch::default(),
|
||||
assets: AssetManager::new(
|
||||
vec![],
|
||||
vec![],
|
||||
AssetsPayloadSource::empty(),
|
||||
Arc::clone(&memory_banks) as Arc<dyn TileBankPoolInstaller>,
|
||||
Arc::clone(&memory_banks) as Arc<dyn SoundBankPoolInstaller>,
|
||||
),
|
||||
|
||||
@ -171,7 +171,7 @@ mod tests {
|
||||
use prometeu_bytecode::assembler::assemble;
|
||||
use prometeu_bytecode::model::{BytecodeModule, FunctionMeta, SyscallDecl};
|
||||
use prometeu_drivers::hardware::Hardware;
|
||||
use prometeu_hal::cartridge::AppMode;
|
||||
use prometeu_hal::cartridge::{AppMode, AssetsPayloadSource};
|
||||
use prometeu_hal::syscalls::caps;
|
||||
use prometeu_system::CrashReport;
|
||||
|
||||
@ -184,7 +184,7 @@ mod tests {
|
||||
entrypoint: "".into(),
|
||||
capabilities: 0,
|
||||
program: vec![0, 0, 0, 0],
|
||||
assets: vec![],
|
||||
assets: AssetsPayloadSource::empty(),
|
||||
asset_table: vec![],
|
||||
preload: vec![],
|
||||
}
|
||||
@ -221,7 +221,7 @@ mod tests {
|
||||
entrypoint: "".into(),
|
||||
capabilities: caps::GFX,
|
||||
program,
|
||||
assets: vec![],
|
||||
assets: AssetsPayloadSource::empty(),
|
||||
asset_table: vec![],
|
||||
preload: vec![],
|
||||
}
|
||||
|
||||
@ -2,13 +2,14 @@ use crate::asset::{
|
||||
AssetEntry, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus,
|
||||
PreloadEntry, SlotRef, SlotStats,
|
||||
};
|
||||
use crate::cartridge::AssetsPayloadSource;
|
||||
|
||||
pub trait AssetBridge {
|
||||
fn initialize_for_cartridge(
|
||||
&self,
|
||||
assets: Vec<AssetEntry>,
|
||||
preload: Vec<PreloadEntry>,
|
||||
assets_data: Vec<u8>,
|
||||
assets_data: AssetsPayloadSource,
|
||||
);
|
||||
fn load(&self, asset_name: &str, slot: SlotRef) -> Result<HandleId, AssetLoadError>;
|
||||
fn status(&self, handle: HandleId) -> LoadStatus;
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
use crate::asset::{AssetEntry, PreloadEntry};
|
||||
use crate::syscalls::CapFlags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, SeekFrom};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub const ASSETS_PA_MAGIC: [u8; 4] = *b"ASPA";
|
||||
pub const ASSETS_PA_SCHEMA_VERSION: u32 = 1;
|
||||
@ -21,12 +25,12 @@ pub struct Cartridge {
|
||||
pub entrypoint: String,
|
||||
pub capabilities: CapFlags,
|
||||
pub program: Vec<u8>,
|
||||
pub assets: Vec<u8>,
|
||||
pub assets: AssetsPayloadSource,
|
||||
pub asset_table: Vec<AssetEntry>,
|
||||
pub preload: Vec<PreloadEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CartridgeDTO {
|
||||
pub app_id: u32,
|
||||
pub title: String,
|
||||
@ -35,10 +39,8 @@ pub struct CartridgeDTO {
|
||||
pub entrypoint: String,
|
||||
pub capabilities: CapFlags,
|
||||
pub program: Vec<u8>,
|
||||
pub assets: Vec<u8>,
|
||||
#[serde(default)]
|
||||
pub assets: AssetsPayloadSource,
|
||||
pub asset_table: Vec<AssetEntry>,
|
||||
#[serde(default)]
|
||||
pub preload: Vec<PreloadEntry>,
|
||||
}
|
||||
|
||||
@ -59,6 +61,88 @@ impl From<CartridgeDTO> for Cartridge {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AssetsPayloadSource {
|
||||
Memory(Arc<[u8]>),
|
||||
File(Arc<FileAssetsPayloadSource>),
|
||||
}
|
||||
|
||||
impl AssetsPayloadSource {
|
||||
pub fn empty() -> Self {
|
||||
Self::Memory(Arc::<[u8]>::from(Vec::<u8>::new()))
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Self {
|
||||
Self::Memory(Arc::<[u8]>::from(bytes))
|
||||
}
|
||||
|
||||
pub fn from_file(path: PathBuf, payload_offset: u64, payload_len: u64) -> Self {
|
||||
Self::File(Arc::new(FileAssetsPayloadSource { path, payload_offset, payload_len }))
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Memory(bytes) => bytes.is_empty(),
|
||||
Self::File(source) => source.payload_len == 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_slice(&self, offset: u64, size: u64) -> io::Result<AssetsPayloadSlice> {
|
||||
match self {
|
||||
Self::Memory(bytes) => {
|
||||
let start = usize::try_from(offset).map_err(|_| invalid_input("asset offset overflow"))?;
|
||||
let len = usize::try_from(size).map_err(|_| invalid_input("asset size overflow"))?;
|
||||
let end = start.checked_add(len).ok_or_else(|| invalid_input("asset range overflow"))?;
|
||||
if end > bytes.len() {
|
||||
return Err(invalid_input("asset range out of bounds"));
|
||||
}
|
||||
|
||||
Ok(AssetsPayloadSlice::Memory { bytes: Arc::clone(bytes), start, len })
|
||||
}
|
||||
Self::File(source) => {
|
||||
let end = offset.checked_add(size).ok_or_else(|| invalid_input("asset range overflow"))?;
|
||||
if end > source.payload_len {
|
||||
return Err(invalid_input("asset range out of bounds"));
|
||||
}
|
||||
|
||||
Ok(AssetsPayloadSlice::File { source: Arc::clone(source), offset, size })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileAssetsPayloadSource {
|
||||
pub path: PathBuf,
|
||||
pub payload_offset: u64,
|
||||
pub payload_len: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AssetsPayloadSlice {
|
||||
Memory { bytes: Arc<[u8]>, start: usize, len: usize },
|
||||
File { source: Arc<FileAssetsPayloadSource>, offset: u64, size: u64 },
|
||||
}
|
||||
|
||||
impl AssetsPayloadSlice {
|
||||
pub fn read_all(&self) -> io::Result<Vec<u8>> {
|
||||
match self {
|
||||
Self::Memory { bytes, start, len } => Ok(bytes[*start..*start + *len].to_vec()),
|
||||
Self::File { source, offset, size } => {
|
||||
let mut file = File::open(&source.path)?;
|
||||
file.seek(SeekFrom::Start(source.payload_offset + offset))?;
|
||||
let mut buffer = vec![0_u8; usize::try_from(*size).map_err(|_| invalid_input("asset size overflow"))?];
|
||||
file.read_exact(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_input(message: &'static str) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidInput, message)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct AssetsPackPrelude {
|
||||
pub magic: [u8; 4],
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use crate::cartridge::{
|
||||
ASSETS_PA_MAGIC, ASSETS_PA_PRELUDE_SIZE, ASSETS_PA_SCHEMA_VERSION, AssetsPackHeader,
|
||||
AssetsPackPrelude, Capability, Cartridge, CartridgeDTO, CartridgeError, CartridgeManifest,
|
||||
AssetsPackPrelude, AssetsPayloadSource, Capability, Cartridge, CartridgeDTO, CartridgeError,
|
||||
CartridgeManifest,
|
||||
};
|
||||
use crate::syscalls::{CapFlags, caps};
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
pub struct CartridgeLoader;
|
||||
@ -59,15 +61,22 @@ impl DirectoryCartridgeLoader {
|
||||
|
||||
let assets_pa_path = path.join("assets.pa");
|
||||
let (assets, asset_table, preload) = if assets_pa_path.exists() {
|
||||
let assets_pa = fs::read(assets_pa_path).map_err(|_| CartridgeError::IoError)?;
|
||||
let parsed = parse_assets_pack(&assets_pa)?;
|
||||
(parsed.payload.to_vec(), parsed.header.asset_table, parsed.header.preload)
|
||||
let parsed = parse_assets_pack(&assets_pa_path)?;
|
||||
(
|
||||
AssetsPayloadSource::from_file(
|
||||
assets_pa_path.clone(),
|
||||
parsed.payload_offset,
|
||||
parsed.payload_len,
|
||||
),
|
||||
parsed.header.asset_table,
|
||||
parsed.header.preload,
|
||||
)
|
||||
} else {
|
||||
if capabilities & caps::ASSET != 0 {
|
||||
return Err(CartridgeError::MissingAssets);
|
||||
}
|
||||
|
||||
(Vec::new(), Vec::new(), Vec::new())
|
||||
(AssetsPayloadSource::empty(), Vec::new(), Vec::new())
|
||||
};
|
||||
|
||||
let dto = CartridgeDTO {
|
||||
@ -120,37 +129,48 @@ fn normalize_capabilities(capabilities: &[Capability]) -> Result<CapFlags, Cartr
|
||||
Ok(normalized)
|
||||
}
|
||||
|
||||
struct ParsedAssetsPack<'a> {
|
||||
struct ParsedAssetsPack {
|
||||
header: AssetsPackHeader,
|
||||
payload: &'a [u8],
|
||||
payload_offset: u64,
|
||||
payload_len: u64,
|
||||
}
|
||||
|
||||
fn parse_assets_pack(bytes: &[u8]) -> Result<ParsedAssetsPack<'_>, CartridgeError> {
|
||||
fn parse_assets_pack(path: &Path) -> Result<ParsedAssetsPack, CartridgeError> {
|
||||
let mut file = fs::File::open(path).map_err(|_| CartridgeError::IoError)?;
|
||||
let mut prelude_bytes = [0_u8; ASSETS_PA_PRELUDE_SIZE];
|
||||
file.read_exact(&mut prelude_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
|
||||
let prelude =
|
||||
AssetsPackPrelude::from_bytes(bytes).ok_or(CartridgeError::InvalidFormat)?;
|
||||
AssetsPackPrelude::from_bytes(&prelude_bytes).ok_or(CartridgeError::InvalidFormat)?;
|
||||
|
||||
if prelude.magic != ASSETS_PA_MAGIC || prelude.schema_version != ASSETS_PA_SCHEMA_VERSION {
|
||||
return Err(CartridgeError::InvalidFormat);
|
||||
}
|
||||
|
||||
let header_start = ASSETS_PA_PRELUDE_SIZE;
|
||||
let header_len = usize::try_from(prelude.header_len).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
let header_end = header_start
|
||||
.checked_add(prelude.header_len as usize)
|
||||
.checked_add(header_len)
|
||||
.ok_or(CartridgeError::InvalidFormat)?;
|
||||
let payload_offset = usize::try_from(prelude.payload_offset).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
let file_len = usize::try_from(file.metadata().map_err(|_| CartridgeError::IoError)?.len())
|
||||
.map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
|
||||
if payload_offset < header_start || header_end > bytes.len() || payload_offset > bytes.len() {
|
||||
if payload_offset < header_start || header_end > file_len || payload_offset > file_len {
|
||||
return Err(CartridgeError::InvalidFormat);
|
||||
}
|
||||
if header_end != payload_offset {
|
||||
return Err(CartridgeError::InvalidFormat);
|
||||
}
|
||||
|
||||
let header_bytes = &bytes[header_start..header_end];
|
||||
file.seek(SeekFrom::Start(header_start as u64)).map_err(|_| CartridgeError::IoError)?;
|
||||
let mut header_bytes = vec![0_u8; header_len];
|
||||
file.read_exact(&mut header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
let header: AssetsPackHeader =
|
||||
serde_json::from_slice(header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
serde_json::from_slice(&header_bytes).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
let payload_len = u64::try_from(file_len - payload_offset).map_err(|_| CartridgeError::InvalidFormat)?;
|
||||
|
||||
Ok(ParsedAssetsPack { header, payload: &bytes[payload_offset..] })
|
||||
Ok(ParsedAssetsPack { header, payload_offset: prelude.payload_offset, payload_len })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -366,7 +386,13 @@ mod tests {
|
||||
|
||||
let cartridge = DirectoryCartridgeLoader::load(dir.path()).expect("cartridge must load");
|
||||
|
||||
assert_eq!(cartridge.assets, payload);
|
||||
let slice = cartridge
|
||||
.assets
|
||||
.open_slice(0, payload.len() as u64)
|
||||
.expect("payload slice must open")
|
||||
.read_all()
|
||||
.expect("payload slice must read");
|
||||
assert_eq!(slice, payload);
|
||||
assert_eq!(cartridge.asset_table.len(), 1);
|
||||
assert_eq!(cartridge.asset_table[0].asset_name, "tiles");
|
||||
assert_eq!(cartridge.preload.len(), 1);
|
||||
|
||||
@ -9,7 +9,7 @@ use prometeu_hal::AudioOpStatus;
|
||||
use prometeu_hal::asset::{AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus};
|
||||
use prometeu_hal::GfxOpStatus;
|
||||
use prometeu_hal::InputSignals;
|
||||
use prometeu_hal::cartridge::Cartridge;
|
||||
use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge};
|
||||
use prometeu_hal::syscalls::caps;
|
||||
use prometeu_vm::VmInitError;
|
||||
use std::collections::HashMap;
|
||||
@ -61,7 +61,7 @@ fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
|
||||
entrypoint: "".into(),
|
||||
capabilities,
|
||||
program,
|
||||
assets: vec![],
|
||||
assets: AssetsPayloadSource::empty(),
|
||||
asset_table: vec![],
|
||||
preload: vec![],
|
||||
}
|
||||
@ -632,7 +632,7 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() {
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
asset_data,
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
let code = assemble("PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 16\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module_with_consts(
|
||||
@ -668,7 +668,7 @@ fn tick_asset_load_kind_mismatch_returns_status_and_zero_handle() {
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
asset_data,
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
let code = assemble("PUSH_CONST 0\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
|
||||
let program = serialized_single_function_module_with_consts(
|
||||
@ -755,7 +755,7 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
asset_data,
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
let handle = hardware
|
||||
.assets
|
||||
@ -819,7 +819,7 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() {
|
||||
hardware.assets.initialize_for_cartridge(
|
||||
vec![test_tile_asset_entry("tile_asset", asset_data.len())],
|
||||
vec![],
|
||||
asset_data,
|
||||
AssetsPayloadSource::from_bytes(asset_data),
|
||||
);
|
||||
let handle = hardware
|
||||
.assets
|
||||
@ -1025,7 +1025,7 @@ fn tick_memcard_slot_roundtrip_for_game_profile() {
|
||||
entrypoint: "".into(),
|
||||
capabilities: caps::FS,
|
||||
program,
|
||||
assets: vec![],
|
||||
assets: AssetsPayloadSource::empty(),
|
||||
asset_table: vec![],
|
||||
preload: vec![],
|
||||
};
|
||||
@ -1065,7 +1065,7 @@ fn tick_memcard_access_is_denied_for_non_game_profile() {
|
||||
entrypoint: "".into(),
|
||||
capabilities: caps::FS,
|
||||
program,
|
||||
assets: vec![],
|
||||
assets: AssetsPayloadSource::empty(),
|
||||
asset_table: vec![],
|
||||
preload: vec![],
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user