feat(assets): implement exported AAssetManager C-ABI functions and handle table

This commit is contained in:
katR 2026-10-01 19:35:28 +05:30
parent 1969f7aae9
commit 4850bade6b
3 changed files with 252 additions and 0 deletions

View file

@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#![allow(non_snake_case)]
use super::asset::AssetFile;
use super::dir::AssetDir;
use super::manager::AssetManager;
use std::collections::HashMap;
use std::ffi::{c_void, CStr};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
static NEXT_ASSET_ID: AtomicUsize = AtomicUsize::new(3001);
lazy_static::lazy_static! {
static ref ASSET_TABLE: RwLock<HashMap<usize, Mutex<AssetFile>>> = RwLock::new(HashMap::new());
static ref DIR_TABLE: RwLock<HashMap<usize, Mutex<AssetDir>>> = RwLock::new(HashMap::new());
pub static ref GLOBAL_ASSET_MANAGER: Arc<RwLock<AssetManager>> = Arc::new(RwLock::new(AssetManager::new()));
}
pub fn register_test_asset(path: &str, data: Vec<u8>) -> *mut () {
if let Ok(mut mgr) = GLOBAL_ASSET_MANAGER.write() {
mgr.add_asset(path, data);
}
1001 as *mut ()
}
#[no_mangle]
pub unsafe extern "C" fn AAssetManager_open(
_mgr: *mut (),
filename: *const libc::c_char,
_mode: i32,
) -> *mut () {
if filename.is_null() {
return std::ptr::null_mut();
}
let name_str = match CStr::from_ptr(filename).to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
let mgr = match GLOBAL_ASSET_MANAGER.read() {
Ok(m) => m,
Err(_) => return std::ptr::null_mut(),
};
if let Some(file) = mgr.open(name_str) {
let id = NEXT_ASSET_ID.fetch_add(1, Ordering::Relaxed);
if let Ok(mut table) = ASSET_TABLE.write() {
table.insert(id, Mutex::new(file));
id as *mut ()
} else {
std::ptr::null_mut()
}
} else {
std::ptr::null_mut()
}
}
#[no_mangle]
pub unsafe extern "C" fn AAssetManager_openDir(_mgr: *mut (), dirName: *const libc::c_char) -> *mut () {
let name_str = if !dirName.is_null() {
CStr::from_ptr(dirName).to_str().unwrap_or("")
} else {
""
};
let mgr = match GLOBAL_ASSET_MANAGER.read() {
Ok(m) => m,
Err(_) => return std::ptr::null_mut(),
};
let dir = mgr.open_dir(name_str);
let id = NEXT_ASSET_ID.fetch_add(1, Ordering::Relaxed);
if let Ok(mut table) = DIR_TABLE.write() {
table.insert(id, Mutex::new(dir));
id as *mut ()
} else {
std::ptr::null_mut()
}
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_read(asset: *mut (), buf: *mut c_void, count: usize) -> i32 {
if asset.is_null() || buf.is_null() || count == 0 {
return 0;
}
let id = asset as usize;
if let Ok(table) = ASSET_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(mut file) = mutex.lock() {
let slice = std::slice::from_raw_parts_mut(buf as *mut u8, count);
return file.read(slice) as i32;
}
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_seek(asset: *mut (), offset: i64, whence: i32) -> i64 {
if asset.is_null() {
return -1;
}
let id = asset as usize;
if let Ok(table) = ASSET_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(mut file) = mutex.lock() {
return file.seek(offset, whence);
}
}
}
-1
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_seek64(asset: *mut (), offset: i64, whence: i32) -> i64 {
AAsset_seek(asset, offset, whence)
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_getLength(asset: *mut ()) -> i64 {
if asset.is_null() {
return 0;
}
let id = asset as usize;
if let Ok(table) = ASSET_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(file) = mutex.lock() {
return file.length();
}
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_getLength64(asset: *mut ()) -> i64 {
AAsset_getLength(asset)
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_getRemainingLength(asset: *mut ()) -> i64 {
if asset.is_null() {
return 0;
}
let id = asset as usize;
if let Ok(table) = ASSET_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(file) = mutex.lock() {
return file.remaining_length();
}
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_getRemainingLength64(asset: *mut ()) -> i64 {
AAsset_getRemainingLength(asset)
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_getBuffer(asset: *mut ()) -> *const c_void {
if asset.is_null() {
return std::ptr::null();
}
let id = asset as usize;
if let Ok(table) = ASSET_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(file) = mutex.lock() {
return file.get_buffer();
}
}
}
std::ptr::null()
}
#[no_mangle]
pub unsafe extern "C" fn AAsset_close(asset: *mut ()) {
if !asset.is_null() {
let id = asset as usize;
if let Ok(mut table) = ASSET_TABLE.write() {
table.remove(&id);
}
}
}
#[no_mangle]
pub unsafe extern "C" fn AAssetDir_getNextFileName(assetDir: *mut ()) -> *const libc::c_char {
if assetDir.is_null() {
return std::ptr::null();
}
let id = assetDir as usize;
if let Ok(table) = DIR_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(mut dir) = mutex.lock() {
return dir.get_next_filename();
}
}
}
std::ptr::null()
}
#[no_mangle]
pub unsafe extern "C" fn AAssetDir_rewind(assetDir: *mut ()) {
if !assetDir.is_null() {
let id = assetDir as usize;
if let Ok(table) = DIR_TABLE.read() {
if let Some(mutex) = table.get(&id) {
if let Ok(mut dir) = mutex.lock() {
dir.rewind();
}
}
}
}
}
#[no_mangle]
pub unsafe extern "C" fn AAssetDir_close(assetDir: *mut ()) {
if !assetDir.is_null() {
let id = assetDir as usize;
if let Ok(mut table) = DIR_TABLE.write() {
table.remove(&id);
}
}
}

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod asset;
pub mod c_api;
pub mod dir;
pub mod manager;
pub mod types;

View file

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_core::assets::c_api::*;
use std::ffi::CString;
#[test]
fn test_aasset_c_api_lifecycle() {
let mgr_handle = register_test_asset("sounds/click.ogg", b"TEST_CLICK_AUDIO".to_vec());
unsafe {
let filename = CString::new("sounds/click.ogg").unwrap();
let asset = AAssetManager_open(mgr_handle, filename.as_ptr(), 3);
assert!(!asset.is_null());
assert_eq!(AAsset_getLength(asset), 16);
assert_eq!(AAsset_getRemainingLength(asset), 16);
let mut buf = [0u8; 16];
let read_bytes = AAsset_read(asset, buf.as_mut_ptr() as *mut _, 16);
assert_eq!(read_bytes, 16);
assert_eq!(&buf, b"TEST_CLICK_AUDIO");
AAsset_close(asset);
// Double-close must not panic or abort
AAsset_close(asset);
}
}