Support running/profiling ISX files
This commit is contained in:
		
							parent
							
								
									db482b5c21
								
							
						
					
					
						commit
						f7f112f7c1
					
				
							
								
								
									
										4
									
								
								build.rs
								
								
								
								
							
							
						
						
									
										4
									
								
								build.rs
								
								
								
								
							| 
						 | 
					@ -18,6 +18,7 @@ fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    builder
 | 
					    builder
 | 
				
			||||||
        .include(Path::new("shrooms-vb-core/core"))
 | 
					        .include(Path::new("shrooms-vb-core/core"))
 | 
				
			||||||
 | 
					        .include(Path::new("shrooms-vb-core/util"))
 | 
				
			||||||
        .opt_level(opt_level)
 | 
					        .opt_level(opt_level)
 | 
				
			||||||
        .flag_if_supported("-fno-strict-aliasing")
 | 
					        .flag_if_supported("-fno-strict-aliasing")
 | 
				
			||||||
        .define("VB_LITTLE_ENDIAN", None)
 | 
					        .define("VB_LITTLE_ENDIAN", None)
 | 
				
			||||||
| 
						 | 
					@ -29,7 +30,10 @@ fn main() -> Result<(), Box<dyn Error>> {
 | 
				
			||||||
        .define("VB_DIRECT_FRAME", "on_frame")
 | 
					        .define("VB_DIRECT_FRAME", "on_frame")
 | 
				
			||||||
        .define("VB_DIRECT_READ", "on_read")
 | 
					        .define("VB_DIRECT_READ", "on_read")
 | 
				
			||||||
        .define("VB_DIRECT_WRITE", "on_write")
 | 
					        .define("VB_DIRECT_WRITE", "on_write")
 | 
				
			||||||
 | 
					        .define("VBU_REALLOC", "vbu_realloc_shim")
 | 
				
			||||||
        .file(Path::new("shrooms-vb-core/core/vb.c"))
 | 
					        .file(Path::new("shrooms-vb-core/core/vb.c"))
 | 
				
			||||||
 | 
					        .file(Path::new("shrooms-vb-core/util/isx.c"))
 | 
				
			||||||
 | 
					        .file(Path::new("shrooms-vb-core/util/vbu.c"))
 | 
				
			||||||
        .compile("vb");
 | 
					        .compile("vb");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ mod cart;
 | 
				
			||||||
mod game_info;
 | 
					mod game_info;
 | 
				
			||||||
mod inline_stack_map;
 | 
					mod inline_stack_map;
 | 
				
			||||||
mod shrooms_vb_core;
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					mod shrooms_vb_util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
					#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
				
			||||||
pub enum SimId {
 | 
					pub enum SimId {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ use std::{
 | 
				
			||||||
    sync::Arc,
 | 
					    sync::Arc,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::{SimId, game_info::GameInfo};
 | 
					use crate::emulator::{SimId, game_info::GameInfo, shrooms_vb_util::rom_from_isx};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Cart {
 | 
					pub struct Cart {
 | 
				
			||||||
    pub file_path: PathBuf,
 | 
					    pub file_path: PathBuf,
 | 
				
			||||||
| 
						 | 
					@ -20,8 +20,9 @@ pub struct Cart {
 | 
				
			||||||
impl Cart {
 | 
					impl Cart {
 | 
				
			||||||
    pub fn load(file_path: &Path, sim_id: SimId) -> Result<Self> {
 | 
					    pub fn load(file_path: &Path, sim_id: SimId) -> Result<Self> {
 | 
				
			||||||
        let rom = fs::read(file_path)?;
 | 
					        let rom = fs::read(file_path)?;
 | 
				
			||||||
        let (rom, info) =
 | 
					        let (rom, info) = try_parse_isx(file_path, &rom)
 | 
				
			||||||
            try_parse_elf(file_path, &rom).unwrap_or_else(|| (rom, GameInfo::empty(file_path)));
 | 
					            .or_else(|| try_parse_elf(file_path, &rom))
 | 
				
			||||||
 | 
					            .unwrap_or_else(|| (rom, GameInfo::empty(file_path)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut sram_file = File::options()
 | 
					        let mut sram_file = File::options()
 | 
				
			||||||
            .read(true)
 | 
					            .read(true)
 | 
				
			||||||
| 
						 | 
					@ -60,6 +61,12 @@ impl Cart {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn try_parse_isx(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
 | 
				
			||||||
 | 
					    let rom = rom_from_isx(data)?;
 | 
				
			||||||
 | 
					    let info = GameInfo::from_isx(file_path, data);
 | 
				
			||||||
 | 
					    Some((rom, info))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn try_parse_elf(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
 | 
					fn try_parse_elf(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
 | 
				
			||||||
    use object::read::elf::FileHeader;
 | 
					    use object::read::elf::FileHeader;
 | 
				
			||||||
    let program = match object::FileKind::parse(data).ok()? {
 | 
					    let program = match object::FileKind::parse(data).ok()? {
 | 
				
			||||||
| 
						 | 
					@ -73,7 +80,7 @@ fn try_parse_elf(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        _ => return None,
 | 
					        _ => return None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let info = GameInfo::new(file_path, data).unwrap_or_else(|_| GameInfo::empty(file_path));
 | 
					    let info = GameInfo::from_elf(file_path, data).unwrap_or_else(|_| GameInfo::empty(file_path));
 | 
				
			||||||
    Some((program, info))
 | 
					    Some((program, info))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ pub struct GameInfo {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl GameInfo {
 | 
					impl GameInfo {
 | 
				
			||||||
    pub fn new(file_path: &Path, input: &[u8]) -> Result<Self> {
 | 
					    pub fn from_elf(file_path: &Path, input: &[u8]) -> Result<Self> {
 | 
				
			||||||
        let file = object::File::parse(input)?;
 | 
					        let file = object::File::parse(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let (name, path) = name_and_path(file_path);
 | 
					        let (name, path) = name_and_path(file_path);
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,26 @@ impl GameInfo {
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn from_isx(file_path: &Path, input: &[u8]) -> Self {
 | 
				
			||||||
 | 
					        let (name, path) = name_and_path(file_path);
 | 
				
			||||||
 | 
					        let symbols = extract_isx_symbols(input);
 | 
				
			||||||
 | 
					        let library_info = LibraryInfo {
 | 
				
			||||||
 | 
					            name: name.clone(),
 | 
				
			||||||
 | 
					            debug_name: name,
 | 
				
			||||||
 | 
					            path: path.clone(),
 | 
				
			||||||
 | 
					            debug_path: path,
 | 
				
			||||||
 | 
					            debug_id: DebugId::default(),
 | 
				
			||||||
 | 
					            code_id: None,
 | 
				
			||||||
 | 
					            arch: None,
 | 
				
			||||||
 | 
					            symbol_table: symbols.map(|syms| Arc::new(SymbolTable::new(syms))),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let inline_stack_map = InlineStackMap::empty();
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            library_info,
 | 
				
			||||||
 | 
					            inline_stack_map,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn empty(file_path: &Path) -> Self {
 | 
					    pub fn empty(file_path: &Path) -> Self {
 | 
				
			||||||
        let (name, path) = name_and_path(file_path);
 | 
					        let (name, path) = name_and_path(file_path);
 | 
				
			||||||
        let library_info = LibraryInfo {
 | 
					        let library_info = LibraryInfo {
 | 
				
			||||||
| 
						 | 
					@ -115,6 +135,66 @@ fn build_inline_stack_map(file: object::File) -> Result<InlineStackMap> {
 | 
				
			||||||
    Ok(frames.build())
 | 
					    Ok(frames.build())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn extract_isx_symbols(input: &[u8]) -> Option<Vec<Symbol>> {
 | 
				
			||||||
 | 
					    let mut syms = vec![];
 | 
				
			||||||
 | 
					    let (_, mut buf) = input.split_at_checked(32)?;
 | 
				
			||||||
 | 
					    while !buf.is_empty() {
 | 
				
			||||||
 | 
					        let typ;
 | 
				
			||||||
 | 
					        (typ, buf) = buf.split_first()?;
 | 
				
			||||||
 | 
					        match typ {
 | 
				
			||||||
 | 
					            0x11 => {
 | 
				
			||||||
 | 
					                // Code (Virtual Boy)
 | 
				
			||||||
 | 
					                (_, buf) = buf.split_at_checked(4)?;
 | 
				
			||||||
 | 
					                let len_bytes;
 | 
				
			||||||
 | 
					                (len_bytes, buf) = buf.split_first_chunk()?;
 | 
				
			||||||
 | 
					                let len = u32::from_le_bytes(*len_bytes);
 | 
				
			||||||
 | 
					                (_, buf) = buf.split_at_checked(len as usize)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            0x13 => {
 | 
				
			||||||
 | 
					                // Range (Virtual Boy)
 | 
				
			||||||
 | 
					                let count_bytes;
 | 
				
			||||||
 | 
					                (count_bytes, buf) = buf.split_first_chunk()?;
 | 
				
			||||||
 | 
					                let count = u16::from_le_bytes(*count_bytes) + 1;
 | 
				
			||||||
 | 
					                (_, buf) = buf.split_at_checked(count as usize * 9)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            0x14 => {
 | 
				
			||||||
 | 
					                // Symbol (Virtual Boy)
 | 
				
			||||||
 | 
					                let count_bytes;
 | 
				
			||||||
 | 
					                (count_bytes, buf) = buf.split_first_chunk()?;
 | 
				
			||||||
 | 
					                let count = u16::from_le_bytes(*count_bytes) + 1;
 | 
				
			||||||
 | 
					                for _ in 0..count {
 | 
				
			||||||
 | 
					                    let name_len;
 | 
				
			||||||
 | 
					                    (name_len, buf) = buf.split_first()?;
 | 
				
			||||||
 | 
					                    let name_bytes;
 | 
				
			||||||
 | 
					                    (name_bytes, buf) = buf.split_at_checked(*name_len as usize)?;
 | 
				
			||||||
 | 
					                    (_, buf) = buf.split_at_checked(2)?;
 | 
				
			||||||
 | 
					                    let address_bytes;
 | 
				
			||||||
 | 
					                    (address_bytes, buf) = buf.split_first_chunk()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let name_str = String::from_utf8_lossy(name_bytes);
 | 
				
			||||||
 | 
					                    let address = u32::from_le_bytes(*address_bytes);
 | 
				
			||||||
 | 
					                    syms.push(Symbol {
 | 
				
			||||||
 | 
					                        address,
 | 
				
			||||||
 | 
					                        size: Some(4),
 | 
				
			||||||
 | 
					                        name: demangle_any(&name_str),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            0x20 | 0x21 | 0x22 => {
 | 
				
			||||||
 | 
					                // System (undocumented)
 | 
				
			||||||
 | 
					                let length_bytes;
 | 
				
			||||||
 | 
					                (length_bytes, buf) = buf.split_first_chunk()?;
 | 
				
			||||||
 | 
					                let length = u32::from_le_bytes(*length_bytes);
 | 
				
			||||||
 | 
					                (_, buf) = buf.split_at_checked(length as usize)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => {
 | 
				
			||||||
 | 
					                return None;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Some(syms)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Reader<'a> = gimli::EndianSlice<'a, gimli::RunTimeEndian>;
 | 
					type Reader<'a> = gimli::EndianSlice<'a, gimli::RunTimeEndian>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ParseContext<'a> {
 | 
					struct ParseContext<'a> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					use std::ffi::c_void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[unsafe(no_mangle)]
 | 
				
			||||||
 | 
					unsafe fn vbu_realloc_shim(ptr: *mut c_void, new_size: usize) -> *mut c_void {
 | 
				
			||||||
 | 
					    if !ptr.is_null() {
 | 
				
			||||||
 | 
					        // not supporting proper realloc because it needs bookkeeping and it's unnecessary
 | 
				
			||||||
 | 
					        return std::ptr::null_mut();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let allocation = vec![0u8; new_size].into_boxed_slice();
 | 
				
			||||||
 | 
					    Box::into_raw(allocation).cast()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[link(name = "vb")]
 | 
				
			||||||
 | 
					unsafe extern "C" {
 | 
				
			||||||
 | 
					    #[link_name = "vbuFromISX"]
 | 
				
			||||||
 | 
					    fn vbu_from_isx(bytes: *const c_void, length: usize, rom_length: *mut usize) -> *mut c_void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn rom_from_isx(bytes: &[u8]) -> Option<Vec<u8>> {
 | 
				
			||||||
 | 
					    if !bytes.starts_with(b"ISX") {
 | 
				
			||||||
 | 
					        return None;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let mut rom_length = 0;
 | 
				
			||||||
 | 
					    let raw_rom =
 | 
				
			||||||
 | 
					        unsafe { vbu_from_isx(bytes.as_ptr().cast(), bytes.len(), &mut rom_length) };
 | 
				
			||||||
 | 
					    if raw_rom.is_null() {
 | 
				
			||||||
 | 
					        return None;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // SAFETY: the rom was allocated by vbu_realloc_shim, which created it from a Vec<u8>.
 | 
				
			||||||
 | 
					    let rom =
 | 
				
			||||||
 | 
					        unsafe { Vec::from_raw_parts(raw_rom.cast(), rom_length, rom_length) };
 | 
				
			||||||
 | 
					    Some(rom)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue