134 lines
3.8 KiB
Rust
134 lines
3.8 KiB
Rust
use super::super::{GBAError, GBAResult};
|
|
use std::collections::HashMap;
|
|
use std::io::prelude::*;
|
|
use std::io::Cursor;
|
|
use std::path::Path;
|
|
|
|
use crate::util::read_bin_file;
|
|
use zip::ZipArchive;
|
|
|
|
#[cfg(feature = "elf_support")]
|
|
use goblin;
|
|
|
|
pub enum LoadRom {
|
|
Elf {
|
|
data: Vec<u8>,
|
|
symbols: HashMap<String, u32>,
|
|
},
|
|
Raw(Vec<u8>),
|
|
}
|
|
type LoadRomResult = GBAResult<LoadRom>;
|
|
|
|
#[cfg(feature = "elf_support")]
|
|
impl From<goblin::error::Error> for GBAError {
|
|
fn from(err: goblin::error::Error) -> GBAError {
|
|
GBAError::CartridgeLoadError(format!("elf parsing error: {}", err))
|
|
}
|
|
}
|
|
|
|
fn try_load_zip(data: &[u8]) -> LoadRomResult {
|
|
let reader = Cursor::new(data);
|
|
let mut archive = ZipArchive::new(reader)?;
|
|
for i in 0..archive.len() {
|
|
let mut file = archive.by_index(i)?;
|
|
if file.name().ends_with(".gba") {
|
|
let mut buf = Vec::new();
|
|
file.read_to_end(&mut buf)?;
|
|
return Ok(LoadRom::Raw(buf));
|
|
}
|
|
}
|
|
Err(GBAError::CartridgeLoadError(
|
|
"no .gba files found within the zip archive".to_owned(),
|
|
))
|
|
}
|
|
|
|
#[cfg(feature = "elf_support")]
|
|
fn try_load_elf(elf_bytes: &[u8]) -> LoadRomResult {
|
|
const CART_BASE: usize = 0x0800_0000;
|
|
|
|
let elf = goblin::elf::Elf::parse(&elf_bytes)?;
|
|
|
|
let entry = elf.entry;
|
|
if entry != (CART_BASE as u64) {
|
|
return Err(GBAError::CartridgeLoadError(
|
|
"bad elf entry point, maybe multiboot rom ?".to_owned(),
|
|
));
|
|
}
|
|
|
|
let mut rom = vec![0; 0x200_0000];
|
|
for phdr in &elf.program_headers {
|
|
if phdr.p_type == goblin::elf::program_header::PT_LOAD {
|
|
let file_range = phdr.file_range();
|
|
let phys_range =
|
|
(phdr.p_paddr as usize)..(phdr.p_paddr as usize + phdr.p_memsz as usize);
|
|
let phys_range_adjusted = (phdr.p_paddr as usize - CART_BASE)
|
|
..(phdr.p_paddr as usize + phdr.p_memsz as usize - CART_BASE);
|
|
|
|
if phys_range_adjusted.start + (phdr.p_filesz as usize) >= rom.len() {
|
|
warn!("ELF: skipping program header {:?}", phdr);
|
|
continue;
|
|
}
|
|
|
|
info!(
|
|
"ELF: loading segment phdr: {:?} range {:#x?} vec range {:#x?}",
|
|
phdr, file_range, phys_range,
|
|
);
|
|
|
|
let src = &elf_bytes[file_range];
|
|
let dst = &mut rom[phys_range_adjusted];
|
|
dst.copy_from_slice(src);
|
|
}
|
|
}
|
|
|
|
let mut symbols = HashMap::new();
|
|
|
|
let strtab = elf.strtab;
|
|
for sym in elf.syms.iter() {
|
|
if let Some(Ok(name)) = strtab.get(sym.st_name) {
|
|
// TODO do I also want to save the symbol size ?
|
|
symbols.insert(name.to_owned(), sym.st_value as u32);
|
|
} else {
|
|
warn!("failed to parse symbol name sym {:?}", sym);
|
|
}
|
|
}
|
|
|
|
Ok(LoadRom::Elf {
|
|
data: rom,
|
|
symbols: symbols,
|
|
})
|
|
}
|
|
|
|
pub(super) fn load_from_file(path: &Path) -> LoadRomResult {
|
|
let bytes = read_bin_file(path)?;
|
|
|
|
match path.extension() {
|
|
Some(extension) => match extension.to_str() {
|
|
Some("zip") => try_load_zip(&bytes),
|
|
#[cfg(feature = "elf_support")]
|
|
Some("elf") => try_load_elf(&bytes),
|
|
_ => {
|
|
warn!("unknown file extension, loading as raw binary file");
|
|
Ok(LoadRom::Raw(bytes))
|
|
}
|
|
},
|
|
None => Ok(LoadRom::Raw(bytes)),
|
|
}
|
|
}
|
|
|
|
pub(super) fn load_from_bytes(bytes: Vec<u8>) -> LoadRomResult {
|
|
// first try as zip
|
|
if let Ok(result) = try_load_zip(&bytes) {
|
|
return Ok(result);
|
|
}
|
|
|
|
// else, try as elf
|
|
#[cfg(feature = "elf_support")]
|
|
{
|
|
if let Ok(result) = try_load_elf(&bytes) {
|
|
return Ok(result);
|
|
}
|
|
}
|
|
|
|
// if everything else failed, load the rom as raw binary
|
|
Ok(LoadRom::Raw(bytes))
|
|
}
|