open Unix open Printf (** http://bgb.bircd.org/pandocs.htm#thecartridgeheader *) exception Read_error type memory_bank_controller = | ROM_ONLY | MBC1 type t = { nintendo_logo : bytes; title : string; (* mem_type : memory_bank_controller; *) rom_size : int; ram_size : int; (* header_checksum : bytes; global_checksum : bytes; *) } (** The Nintendo logo is a checksum in the header *) let check_nintendo_logo cartridge = let reference = Bytes.of_string "\xCE\xED\x66\x66\xCC\x0D\x00\x0B\x03\x73\x00\x83\x00\x0C\x00\x0D\ \x00\x08\x11\x1F\x88\x89\x00\x0E\xDC\xCC\x6E\xE6\xDD\xDD\xD9\x99\ \xBB\xBB\x67\x63\x6E\x0E\xEC\xCC\xDD\xDC\x99\x9F\xBB\xB9\x33\x3E" in (Bytes.compare cartridge.nintendo_logo reference) == 0 (** ROM size is expressed in KB *) let get_ROM_size b = match Bytes.get b 0 |> int_of_char with | 0x00 -> 32 (* no ROM banking *) | 0x01 -> 64 (* 4 banks *) | 0x02 -> 128 (* 8 banks *) | 0x03 -> 256 (* 16 banks *) | 0x04 -> 512 (* 32 banks *) | 0x05 -> 1000 (* 64 banks - only 63 banks used by MBC1 *) | 0x06 -> 2000 (* 128 banks - only 125 banks used by MBC1 *) | 0x07 -> 4000 (* 256 banks *) | 0x52 -> 1100 (* 72 banks *) | 0x53 -> 1200 (* 80 banks *) | 0x54 -> 1500 (* 96 banks *) | _ -> raise Read_error (** RAM size is expressed in KB *) let get_RAM_size b = match Bytes.get b 0 |> int_of_char with | 0x00 -> 0 | 0x01 -> 2 | 0x02 -> 8 | 0x03 -> 32 (* 4 x 8KB banks *) | _ -> raise Read_error let read_cartridge file = try let fd = openfile file [Unix.O_RDONLY] 0o644 in try let nin_logo_offset = 0x0104 in if (lseek fd nin_logo_offset SEEK_SET) != nin_logo_offset then raise Read_error; (* Nintendo logo *) let nin_logo_size = 48 in let nintendo_logo = Bytes.create nin_logo_size in if (read fd nintendo_logo 0 nin_logo_size) != nin_logo_size then raise Read_error; (* Title *) let title_size = 16 in let title_b = Bytes.create title_size in if (read fd title_b 0 title_size) != title_size then raise Read_error; let title = Bytes.to_string title_b in (* ROM size *) let byte = Bytes.create 1 in let _ = lseek fd 4 SEEK_CUR in if (read fd byte 0 1) != 1 then raise Read_error; let rom_size = get_ROM_size byte in (* RAM size *) if (read fd byte 0 1) != 1 then raise Read_error; let ram_size = get_RAM_size byte in Unix.close fd; Some { nintendo_logo; title; rom_size; ram_size } with Read_error -> Unix.close fd; None with Unix_error(err, cmd, msg) -> None