Skip to content

Commit

Permalink
Merge pull request #13 from Rodrigodd/override-mbc
Browse files Browse the repository at this point in the history
Add option for overriding MBC type
  • Loading branch information
Rodrigodd authored Nov 29, 2024
2 parents 1a17ea7 + 5bda6a1 commit 93431c4
Show file tree
Hide file tree
Showing 12 changed files with 802 additions and 206 deletions.
465 changes: 360 additions & 105 deletions Cargo.lock

Large diffs are not rendered by default.

358 changes: 269 additions & 89 deletions core/src/gameboy/cartridge.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions core/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::io::{Read, Seek, SeekFrom};

mod size;

pub use size::{parse_number, parse_size};

fn read_u32(file: &mut impl Read) -> Result<u32, std::io::Error> {
let mut value = [0; 4];
file.read_exact(&mut value)?;
Expand Down
114 changes: 114 additions & 0 deletions core/src/parser/size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::num::ParseIntError;

#[derive(Debug, PartialEq)]
pub enum ParseSizeError {
InvalidFormat,
InvalidSuffix,
NumberTooLarge,
ParseError(ParseIntError),
}

pub fn parse_number(input: &str) -> Result<u64, ParseIntError> {
if let Some(stripped) = input.strip_prefix("0x") {
u64::from_str_radix(stripped, 16)
} else {
input.parse()
}
}

pub fn parse_size(input: &str) -> Result<u64, ParseSizeError> {
if input.is_empty() {
return Err(ParseSizeError::InvalidFormat);
}

let (num_part, suffix) = input.trim().split_at(
input
.find(|c: char| !c.is_ascii_digit() && c != 'x')
.unwrap_or(input.len()),
);

let num = parse_number(num_part).map_err(ParseSizeError::ParseError)?;

let multiplier = match suffix.trim() {
"B" => 1,
"kB" => 1_024,
"MB" => 1_024 * 1_024,
"GB" => 1_024 * 1_024 * 1_024,
"" => return Err(ParseSizeError::InvalidFormat),
_ => return Err(ParseSizeError::InvalidSuffix),
};

let result = num
.checked_mul(multiplier as u64)
.ok_or(ParseSizeError::NumberTooLarge)?;

if result > u32::MAX as u64 {
return Err(ParseSizeError::NumberTooLarge);
}

Ok(result)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_number_decimal() {
assert_eq!(parse_number("123"), Ok(123));
assert_eq!(parse_number("0"), Ok(0));
}

#[test]
fn test_parse_number_hexadecimal() {
assert_eq!(parse_number("0x10"), Ok(16));
assert_eq!(parse_number("0x1F4"), Ok(500));
}

#[test]
fn test_parse_number_invalid() {
assert!(parse_number("not_a_number").is_err());
assert!(parse_number("0xZZZ").is_err());
}

#[test]
fn test_parse_size_with_hexadecimal() {
assert_eq!(parse_size("0x100B"), Ok(256));
assert_eq!(parse_size("0x1kB"), Ok(1_024));
assert_eq!(parse_size("0x2MB"), Ok(2 * 1_024 * 1_024));
}

#[test]
fn test_valid_sizes() {
assert_eq!(parse_size("0B"), Ok(0));
assert_eq!(parse_size("256B"), Ok(256));
assert_eq!(parse_size("1kB"), Ok(1_024));
assert_eq!(parse_size("2MB"), Ok(2 * 1_024 * 1_024));
assert_eq!(parse_size("1GB"), Ok(1_024 * 1_024 * 1_024));
}

#[test]
fn test_invalid_formats() {
assert_eq!(parse_size(""), Err(ParseSizeError::InvalidFormat));
assert_eq!(parse_size("256"), Err(ParseSizeError::InvalidFormat));
assert_eq!(parse_size("256MBExtra"), Err(ParseSizeError::InvalidSuffix));
}

#[test]
fn test_invalid_suffix() {
assert_eq!(parse_size("256mB"), Err(ParseSizeError::InvalidSuffix));
assert_eq!(parse_size("256TB"), Err(ParseSizeError::InvalidSuffix));
}

#[test]
fn test_parse_errors() {
assert!(matches!(
parse_size("not_a_numberMB"),
Err(ParseSizeError::ParseError(_))
));
assert!(matches!(
parse_size("0xnot_hexB"),
Err(ParseSizeError::ParseError(_))
));
}
}
2 changes: 1 addition & 1 deletion core/tests/check_interrupt_prediction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fn test_interrupt_prediction(rom: &str, timeout: u64) -> bool {
let rom = std::fs::read(rom_path).unwrap();
let cartridge = match Cartridge::new(rom) {
Ok(x) => x,
Err(x) => {
Err((x, _)) => {
eprintln!("Error reading rom: {}", x);
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion jit/tests/check_jit_compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fn test_interrupt_prediction(rom: &str, timeout: u64) -> bool {
};
let cartridge = match Cartridge::new(rom) {
Ok(x) => x,
Err(x) => {
Err((x, _)) => {
eprintln!("Error reading rom: {}", x);
return true;
}
Expand Down
15 changes: 11 additions & 4 deletions libretro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,16 @@ extern "C" fn retro_load_game(info: Option<&retro_game_info>) -> bool {
log::info!("retro load game");

// load rom data into core
let Some(info) = info else { return false } ;
let Some(info) = info else { return false };

let data = unsafe { std::slice::from_raw_parts(info.data as *const u8, info.size as usize) };
let cartridge = match Cartridge::new(data.to_vec()) {
Ok(x) => x,
Err(err) => {
Ok(rom) => rom,
Err((err, Some(rom))) => {
log::warn!("Warnings when loading rom: {}", err);
rom
}
Err((err, None)) => {
log::error!("Error loading rom: {}", err);
return false;
}
Expand Down Expand Up @@ -371,7 +375,10 @@ impl log::Log for RetroLogger {
let Ok(mut file) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path) else { return };
.open(path)
else {
return;
};

let _ = writeln!(
&mut file,
Expand Down
9 changes: 8 additions & 1 deletion license/about-android.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
ignore-dev-dependencies = true
ignore-build-dependencies = true
accepted = ["Apache-2.0", "MIT", "Unicode-DFS-2016", "ISC", "BSD-3-Clause"]
accepted = [
"Apache-2.0",
"MIT",
"Unicode-DFS-2016",
"Unicode-3.0",
"ISC",
"BSD-3-Clause",
]

targets = ["aarch64-linux-android"]
1 change: 1 addition & 0 deletions license/about-linux.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ accepted = [
"Apache-2.0",
"MIT",
"Unicode-DFS-2016",
"Unicode-3.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
Expand Down
1 change: 1 addition & 0 deletions license/about-windows.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ accepted = [
"Apache-2.0",
"MIT",
"Unicode-DFS-2016",
"Unicode-3.0",
"ISC",
"BSD-3-Clause",
"BSL-1.0",
Expand Down
19 changes: 15 additions & 4 deletions native/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::path::PathBuf;

use clap::{ArgAction, Args, Parser, Subcommand};
use gameroy_lib::config::parse_screen_size;
use gameroy_lib::{config, gameroy, rom_loading::load_gameboy, RomFile};
use gameroy_lib::{config, gameroy, rom_loading::load_gameboy_with_spec, RomFile};

mod bench;

Expand All @@ -35,7 +35,7 @@ pub struct Cli {
//
// The disassembly produced follows no particular synxtax, and don't show all instructions or
// data. It only shows instructions that are statically reachable from the entry point.
#[arg(long)]
#[arg(long, requires("rom_path"))]
disassembly: bool,

/// Play the given .vbm file
Expand Down Expand Up @@ -74,6 +74,17 @@ pub struct Cli {
#[arg(long, value_name = "WIDTHxHEIGHT")]
screen_size: Option<String>,

/// The MBC type of the rom
///
/// Overrides the MBC type of the rom, useful in case its is not correctly detected. Must be a
/// string in the format "<type>,<rom_sise>,<ram_size>", where <type> is the MBC type (either
/// "MBC1", "MBC1M", "MBC2", "MBC3" or "MBC5"), <rom_size> is the size of the rom in bytes, and
/// <ram_size> is the size of the ram in bytes. The sizes must be a power of 2 multiple of
/// 0x4000. The size can be in decimal or hexadecimal format, and can have a suffix of "B",
///
#[arg(long)]
mbc: Option<String>,

#[command(subcommand)]
command: Option<Commands>,
}
Expand Down Expand Up @@ -202,7 +213,7 @@ pub fn main() {
Err(e) => return eprintln!("failed to load '{}': {}", rom_path, e),
};

let gb = load_gameboy(rom, None);
let gb = load_gameboy_with_spec(rom, None, args.mbc.as_deref());
let mut gb = match gb {
Ok(x) => x,
Err(e) => return eprintln!("failed to load rom: {}", e),
Expand Down Expand Up @@ -230,7 +241,7 @@ pub fn main() {

let file = RomFile::from_path(PathBuf::from(rom_path));

let gb = load_gameboy(rom, None);
let gb = load_gameboy_with_spec(rom, None, args.mbc.as_deref());
match gb {
Ok(x) => Some((file, x)),
Err(e) => return eprintln!("failed to load rom: {}", e),
Expand Down
18 changes: 17 additions & 1 deletion src/rom_loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,25 @@ cfg_if::cfg_if! {
}

pub fn load_gameboy(rom: Vec<u8>, ram: Option<Vec<u8>>) -> Result<Box<GameBoy>, String> {
load_gameboy_with_spec(rom, ram, None)
}

pub fn load_gameboy_with_spec(
rom: Vec<u8>,
ram: Option<Vec<u8>>,
spec: Option<&str>,
) -> Result<Box<GameBoy>, String> {
let boot_rom = load_boot_rom();

let mut cartridge = Cartridge::new(rom)?;
let mut cartridge = match Cartridge::new_with_spec_str(rom, spec) {
Ok(rom) => Ok(rom),
Err((warn, Some(rom))) => {
println!("Warning: {}", warn.strip_suffix('\n').unwrap_or(&warn));
log::warn!("{}", warn);
Ok(rom)
}
Err((err, None)) => Err(err),
}?;
log::info!("Cartridge type: {}", cartridge.kind_name());

if let Some(ram) = ram {
Expand Down

0 comments on commit 93431c4

Please sign in to comment.