@@ -166,6 +166,174 @@ enum Mbc {
166166 Mbc5 ( Mbc5 ) ,
167167}
168168
169+ enum MbcKind {
170+ Mbc0 ,
171+ Mbc1 ,
172+ Mbc1M ,
173+ Mbc2 ,
174+ Mbc3 ,
175+ Mbc5 ,
176+ }
177+
178+ pub struct MbcSpecification {
179+ // The mbc type, as indicated in the cartridge header.
180+ kind : MbcKind ,
181+ // The rom size, in bytes. Should be a value in ROM_SIZES.
182+ rom_size : usize ,
183+ // The ram size, in bytes. Should be a value in RAM_SIZES.
184+ ram_size : usize ,
185+ }
186+
187+ impl MbcSpecification {
188+ fn from_str ( string : & str ) -> Result < Self , String > {
189+ let mut parts = string. split ( ',' ) ;
190+
191+ let error_message = "expected 3 comma separated values" ;
192+
193+ let kind = parts. next ( ) . ok_or ( error_message) ?;
194+ let rom_size = parts. next ( ) . ok_or ( error_message) ?;
195+ let ram_size = parts. next ( ) . ok_or ( error_message) ?;
196+
197+ if parts. next ( ) . is_some ( ) {
198+ return Err ( error_message. to_string ( ) ) ;
199+ }
200+
201+ let kind = match kind {
202+ "MBC1" => MbcKind :: Mbc1 ,
203+ "MBC1M" => MbcKind :: Mbc1M ,
204+ "MBC2" => MbcKind :: Mbc2 ,
205+ "MBC3" => MbcKind :: Mbc3 ,
206+ "MBC5" => MbcKind :: Mbc5 ,
207+ _ => return Err ( format ! ( "invalid mbc type '{}'" , kind) ) ,
208+ } ;
209+
210+ let rom_size = crate :: parser:: parse_size ( rom_size)
211+ . ok ( )
212+ . or_else ( || crate :: parser:: parse_number ( rom_size) . ok ( ) )
213+ . ok_or_else ( || format ! ( "invalid rom size '{}'" , rom_size) ) ?
214+ as usize ;
215+
216+ ROM_SIZES
217+ . iter ( )
218+ . find ( |& & x| x == rom_size)
219+ . ok_or_else ( || format ! ( "invalid rom size '{}'" , rom_size) ) ?;
220+
221+ let ram_size = crate :: parser:: parse_size ( ram_size)
222+ . ok ( )
223+ . or_else ( || crate :: parser:: parse_number ( ram_size) . ok ( ) )
224+ . ok_or_else ( || format ! ( "invalid ram size '{}'" , ram_size) ) ?
225+ as usize ;
226+
227+ RAM_SIZES
228+ . iter ( )
229+ . find ( |& & x| x == ram_size)
230+ . ok_or_else ( || format ! ( "invalid ram size '{}'" , ram_size) ) ?;
231+
232+ Ok ( Self {
233+ kind,
234+ rom_size,
235+ ram_size,
236+ } )
237+ }
238+
239+ fn from_header ( header : & CartridgeHeader , error : & mut String , rom : & [ u8 ] ) -> Option < Self > {
240+ let rom_size = header
241+ . rom_size_in_bytes ( )
242+ . ok_or_else ( || format ! ( "Rom size type '{:02x}' is not supported" , header. rom_size) ) ;
243+
244+ let rom_size = match rom_size {
245+ Ok ( rom_size) if rom_size != rom. len ( ) => {
246+ let size = * ROM_SIZES . iter ( ) . find ( |& & x| x >= rom. len ( ) ) . unwrap ( ) ;
247+ writeln ! (
248+ error,
249+ "The ROM header expected rom size as '{}' bytes, but the given rom has '{}' bytes. Deducing size from ROM size as {}." ,
250+ rom_size,
251+ rom. len( ) ,
252+ size,
253+ ) . unwrap ( ) ;
254+ size
255+ }
256+ Ok ( size) => size,
257+ Err ( err) => {
258+ let size = * ROM_SIZES . iter ( ) . find ( |& & x| x >= rom. len ( ) ) . unwrap ( ) ;
259+ writeln ! ( error, "{}, deducing size from ROM size as {}" , err, size, ) . unwrap ( ) ;
260+ size
261+ }
262+ } ;
263+
264+ // Cartridge Type
265+ let mbc_kind = header. cartridge_type ;
266+ let kind = match mbc_kind {
267+ 0 | 8 | 9 => MbcKind :: Mbc0 ,
268+ 1 ..=3 => ' mbc1: {
269+ // Detect if it is a MBC1M card
270+ if header. rom_size == 5 {
271+ let mut number_of_games = 0 ;
272+ for i in 0 ..4 {
273+ let header = match CartridgeHeader :: from_bytes ( & rom[ i * 0x40000 ..] ) {
274+ Ok ( x) | Err ( ( Some ( x) , _) ) => x,
275+ Err ( ( None , _) ) => continue ,
276+ } ;
277+ if header. check_logo ( ) {
278+ number_of_games += 1 ;
279+ }
280+ }
281+ // multicarts will have, at least, a game selecion screen, and two other games.
282+ if number_of_games >= 3 {
283+ break ' mbc1 MbcKind :: Mbc1M ;
284+ }
285+ }
286+ MbcKind :: Mbc1
287+ }
288+ 5 | 6 => MbcKind :: Mbc2 ,
289+ 0x0F ..=0x13 => MbcKind :: Mbc3 ,
290+ 0x19 ..=0x1E => MbcKind :: Mbc5 ,
291+ _ => {
292+ writeln ! (
293+ error,
294+ "MBC type '{}' ({:02x}) is not supported" ,
295+ mbc_type_name( mbc_kind) ,
296+ mbc_kind
297+ )
298+ . unwrap ( ) ;
299+ return None ;
300+ }
301+ } ;
302+
303+ let ram_size_type = header. ram_size ;
304+
305+ let ram_size = if let MbcKind :: Mbc2 = kind {
306+ if ram_size_type != 0 {
307+ writeln ! (
308+ error,
309+ "Cartridge use MBC2, with a integrated ram (type '00'), but report the ram type '{:02x}'" ,
310+ ram_size_type,
311+ ) . unwrap ( ) ;
312+ }
313+ 0x200
314+ } else {
315+ match RAM_SIZES . get ( ram_size_type as usize ) . copied ( ) {
316+ Some ( x) => x,
317+ None => {
318+ writeln ! (
319+ error,
320+ "Ram size type '{:02x}' is not supported, using RAM size as 0x2000" ,
321+ ram_size_type,
322+ )
323+ . unwrap ( ) ;
324+ 0x2000
325+ }
326+ }
327+ } ;
328+
329+ Some ( Self {
330+ kind,
331+ rom_size,
332+ ram_size,
333+ } )
334+ }
335+ }
336+
169337#[ derive( PartialEq , Eq , Clone ) ]
170338pub struct Cartridge {
171339 pub header : CartridgeHeader ,
@@ -229,11 +397,32 @@ impl SaveState for Cartridge {
229397 Ok ( ( ) )
230398 }
231399}
400+
401+ #[ allow( clippy:: result_large_err) ]
232402impl Cartridge {
403+ pub fn new ( rom : Vec < u8 > ) -> Result < Self , ( String , Option < Self > ) > {
404+ Self :: new_maybe_with_spec ( rom, None )
405+ }
406+
407+ pub fn new_with_spec_str (
408+ rom : Vec < u8 > ,
409+ spec : Option < & str > ,
410+ ) -> Result < Self , ( String , Option < Self > ) > {
411+ Self :: new_maybe_with_spec (
412+ rom,
413+ match spec {
414+ None => None ,
415+ Some ( spec) => Some ( MbcSpecification :: from_str ( spec) . map_err ( |x| ( x, None ) ) ?) ,
416+ } ,
417+ )
418+ }
419+
233420 /// Create a new cartridge from the given ROM. If the ROM is invalid, return the a error
234421 /// message and a deduced cartridge, if possible.
235- #[ allow( clippy:: result_large_err) ]
236- pub fn new ( mut rom : Vec < u8 > ) -> Result < Self , ( String , Option < Self > ) > {
422+ fn new_maybe_with_spec (
423+ mut rom : Vec < u8 > ,
424+ spec : Option < MbcSpecification > ,
425+ ) -> Result < Self , ( String , Option < Self > ) > {
237426 let mut error = String :: new ( ) ;
238427
239428 let header = match CartridgeHeader :: from_bytes ( & rom) {
@@ -245,106 +434,34 @@ impl Cartridge {
245434 Err ( ( None , err) ) => return Err ( ( err, None ) ) ,
246435 } ;
247436
248- let rom_size = header
249- . rom_size_in_bytes ( )
250- . ok_or_else ( || format ! ( "Rom size type '{:02x}' is not supported" , header. rom_size) ) ;
251-
252- match rom_size {
253- Ok ( rom_size) if rom_size != rom. len ( ) => {
254- let size = * ROM_SIZES . iter ( ) . find ( |& & x| x >= rom. len ( ) ) . unwrap ( ) ;
255- writeln ! (
256- & mut error,
257- "The ROM header expected rom size as '{}' bytes, but the given rom has '{}' bytes. Deducing size from ROM size as {}." ,
258- rom_size,
259- rom. len( ) ,
260- size,
261- ) . unwrap ( ) ;
262- rom. resize ( size, 0 ) ;
263- }
264- Ok ( _) => { }
265- Err ( err) => {
266- let size = * ROM_SIZES . iter ( ) . find ( |& & x| x >= rom. len ( ) ) . unwrap ( ) ;
267- writeln ! (
268- & mut error,
269- "{}, deducing size from ROM size as {}" ,
270- err, size,
271- )
272- . unwrap ( ) ;
273- rom. resize ( size, 0 ) ;
274- }
437+ let spec = match spec {
438+ Some ( spec) => spec,
439+ None => match MbcSpecification :: from_header ( & header, & mut error, & rom) {
440+ Some ( v) => v,
441+ None => return Err ( ( error, None ) ) ,
442+ } ,
275443 } ;
276444
277- // Cartridge Type
278- let mbc_kind = header. cartridge_type ;
279- let mbc = match mbc_kind {
280- 0 | 8 | 9 => Mbc :: None ( Mbc0 { } ) ,
281- 1 ..=3 => ' mbc1: {
282- // Detect if it is a MBC1M card
283- if header. rom_size == 5 {
284- let mut number_of_games = 0 ;
285- for i in 0 ..4 {
286- let header = match CartridgeHeader :: from_bytes ( & rom[ i * 0x40000 ..] ) {
287- Ok ( x) | Err ( ( Some ( x) , _) ) => x,
288- Err ( ( None , _) ) => continue ,
289- } ;
290- if header. check_logo ( ) {
291- number_of_games += 1 ;
292- }
293- }
294- // multicarts will have, at least, a game selecion screen, and two other games.
295- if number_of_games >= 3 {
296- break ' mbc1 Mbc :: Mbc1M ( Mbc1M :: new ( ) ) ;
297- }
298- }
299- Mbc :: Mbc1 ( Mbc1 :: new ( ) )
300- }
301- 5 | 6 => Mbc :: Mbc2 ( Mbc2 :: new ( ) ) ,
302- 0x0F ..=0x13 => Mbc :: Mbc3 ( Mbc3 :: new ( ) ) ,
303- 0x19 ..=0x1E => Mbc :: Mbc5 ( Mbc5 :: new ( ) ) ,
304- _ => {
305- writeln ! (
306- & mut error,
307- "MBC type '{}' ({:02x}) is not supported" ,
308- mbc_type_name( mbc_kind) ,
309- mbc_kind
310- )
311- . unwrap ( ) ;
312- return Err ( ( error, None ) ) ;
313- }
314- } ;
445+ let rom_size = spec. rom_size ;
315446
316- let ram_size_type = header. ram_size ;
447+ // resize the rom in case the header expected a different size
448+ rom. resize ( rom_size, 0 ) ;
317449
318- let ram_size = if let Mbc :: Mbc2 ( _) = mbc {
319- if ram_size_type != 0 {
320- writeln ! (
321- & mut error,
322- "Cartridge use MBC2, with a integrated ram (type '00'), but report the ram type '{:02x}'" ,
323- ram_size_type,
324- ) . unwrap ( ) ;
325- }
326- 0x200
327- } else {
328- match RAM_SIZES . get ( ram_size_type as usize ) . copied ( ) {
329- Some ( x) => x,
330- None => {
331- writeln ! (
332- & mut error,
333- "Ram size type '{:02x}' is not supported, using RAM size as 0x2000" ,
334- ram_size_type,
335- )
336- . unwrap ( ) ;
337- 0x2000
338- }
339- }
450+ let mbc = match spec. kind {
451+ MbcKind :: Mbc0 => Mbc :: None ( Mbc0 { } ) ,
452+ MbcKind :: Mbc1 => Mbc :: Mbc1 ( Mbc1 :: new ( ) ) ,
453+ MbcKind :: Mbc1M => Mbc :: Mbc1M ( Mbc1M :: new ( ) ) ,
454+ MbcKind :: Mbc2 => Mbc :: Mbc2 ( Mbc2 :: new ( ) ) ,
455+ MbcKind :: Mbc3 => Mbc :: Mbc3 ( Mbc3 :: new ( ) ) ,
456+ MbcKind :: Mbc5 => Mbc :: Mbc5 ( Mbc5 :: new ( ) ) ,
340457 } ;
341458
342459 let cartridge = Self {
343460 header,
344461 lower_bank : 0 ,
345462 upper_bank : 1 ,
346463 rom,
347- ram : vec ! [ 0 ; ram_size] ,
464+ ram : vec ! [ 0 ; spec . ram_size] ,
348465 mbc,
349466 } ;
350467
0 commit comments