1 #include "core/bsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/framerate.hpp"
6 #include "core/memorymanip.hpp"
7 #include "core/misc.hpp"
8 #include "core/patchrom.hpp"
9 #include "core/rom.hpp"
10 #include "core/window.hpp"
11 #include "library/string.hpp"
12 #include "library/zip.hpp"
19 #include <boost/iostreams/categories.hpp>
20 #include <boost/iostreams/copy.hpp>
21 #include <boost/iostreams/stream.hpp>
22 #include <boost/iostreams/stream_buffer.hpp>
23 #include <boost/iostreams/filter/symmetric.hpp>
24 #include <boost/iostreams/filter/zlib.hpp>
25 #include <boost/iostreams/filtering_stream.hpp>
26 #include <boost/iostreams/device/back_inserter.hpp>
28 //Some anti-typo defs.
29 #define SNES_TYPE "snes"
30 #define SNES_PAL "snes_pal"
31 #define SNES_NTSC "snes_ntsc"
33 #define BSXSLOTTED "bsxslotted"
34 #define SUFAMITURBO "sufamiturbo"
35 #define SGB_TYPE "SGB"
36 #define SGB_PAL "sgb_pal"
37 #define SGB_NTSC "sgb_ntsc"
39 std::string
gtype::tostring(rom_type rtype
, rom_region region
) throw(std::bad_alloc
, std::runtime_error
)
44 case REGION_AUTO
: return "snes";
45 case REGION_NTSC
: return "snes_ntsc";
46 case REGION_PAL
: return "snes_pal";
50 case REGION_AUTO
: return "sgb";
51 case REGION_NTSC
: return "sgb_ntsc";
52 case REGION_PAL
: return "sgb_pal";
54 case ROMTYPE_BSX
: return "bsx";
55 case ROMTYPE_BSXSLOTTED
: return "bsxslotted";
56 case ROMTYPE_SUFAMITURBO
: return "sufamiturbo";
57 default: throw std::runtime_error("tostring: ROMTYPE_NONE");
61 std::string
gtype::tostring(gametype_t gametype
) throw(std::bad_alloc
, std::runtime_error
)
64 case GT_SNES_NTSC
: return "snes_ntsc";
65 case GT_SNES_PAL
: return "snes_pal";
66 case GT_SGB_NTSC
: return "sgb_ntsc";
67 case GT_SGB_PAL
: return "sgb_pal";
68 case GT_BSX
: return "bsx";
69 case GT_BSX_SLOTTED
: return "bsxslotted";
70 case GT_SUFAMITURBO
: return "sufamiturbo";
71 default: throw std::runtime_error("tostring: GT_INVALID");
75 gametype_t
gtype::togametype(rom_type rtype
, rom_region region
) throw(std::bad_alloc
, std::runtime_error
)
80 case REGION_AUTO
: return GT_SGB_NTSC
;
81 case REGION_NTSC
: return GT_SNES_NTSC
;
82 case REGION_PAL
: return GT_SNES_PAL
;
86 case REGION_AUTO
: return GT_SGB_NTSC
;
87 case REGION_NTSC
: return GT_SGB_NTSC
;
88 case REGION_PAL
: return GT_SGB_PAL
;
90 case ROMTYPE_BSX
: return GT_BSX
;
91 case ROMTYPE_BSXSLOTTED
: return GT_BSX_SLOTTED
;
92 case ROMTYPE_SUFAMITURBO
: return GT_SUFAMITURBO
;
93 default: throw std::runtime_error("togametype: ROMTYPE_NONE");
97 gametype_t
gtype::togametype(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
99 if(gametype
== "snes_ntsc")
101 if(gametype
== "snes_pal")
103 if(gametype
== "sgb_ntsc")
105 if(gametype
== "sgb_pal")
107 if(gametype
== "bsx")
109 if(gametype
== "bsxslotted")
110 return GT_BSX_SLOTTED
;
111 if(gametype
== "sufamiturbo")
112 return GT_SUFAMITURBO
;
113 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
116 rom_type
gtype::toromtype(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
118 if(gametype
== "snes_ntsc")
120 if(gametype
== "snes_pal")
122 if(gametype
== "snes")
124 if(gametype
== "sgb_ntsc")
126 if(gametype
== "sgb_pal")
128 if(gametype
== "sgb")
130 if(gametype
== "bsx")
132 if(gametype
== "bsxslotted")
133 return ROMTYPE_BSXSLOTTED
;
134 if(gametype
== "sufamiturbo")
135 return ROMTYPE_SUFAMITURBO
;
136 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
139 rom_type
gtype::toromtype(gametype_t gametype
) throw()
142 case GT_SNES_NTSC
: return ROMTYPE_SNES
;
143 case GT_SNES_PAL
: return ROMTYPE_SNES
;
144 case GT_SGB_NTSC
: return ROMTYPE_SGB
;
145 case GT_SGB_PAL
: return ROMTYPE_SGB
;
146 case GT_BSX
: return ROMTYPE_BSX
;
147 case GT_BSX_SLOTTED
: return ROMTYPE_BSXSLOTTED
;
148 case GT_SUFAMITURBO
: return ROMTYPE_SUFAMITURBO
;
149 case GT_INVALID
: throw std::runtime_error("toromtype: GT_INVALID");
153 rom_region
gtype::toromregion(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
155 if(gametype
== "snes_ntsc")
157 if(gametype
== "snes_pal")
159 if(gametype
== "snes")
161 if(gametype
== "sgb_ntsc")
163 if(gametype
== "sgb_pal")
165 if(gametype
== "sgb")
167 if(gametype
== "bsx")
169 if(gametype
== "bsxslotted")
171 if(gametype
== "sufamiturbo")
173 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
176 rom_region
gtype::toromregion(gametype_t gametype
) throw()
179 case GT_SNES_NTSC
: return REGION_NTSC
;
180 case GT_SNES_PAL
: return REGION_PAL
;
181 case GT_SGB_NTSC
: return REGION_NTSC
;
182 case GT_SGB_PAL
: return REGION_PAL
;
183 case GT_BSX
: return REGION_NTSC
;
184 case GT_BSX_SLOTTED
: return REGION_NTSC
;
185 case GT_SUFAMITURBO
: return REGION_NTSC
;
186 case GT_INVALID
: throw std::runtime_error("toromregion: GT_INVALID");
193 bool option_set(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
195 for(auto i
: cmdline
)
201 const char* romtypes_to_recognize
[] = {
202 "rom", "bsx", "bsxslotted", "dmg", "slot-a", "slot-b",
203 "rom-xml", "bsx-xml", "bsxslotted-xml", "dmg-xml", "slot-a-xml", "slot-b-xml"
206 enum rom_type current_rom_type
= ROMTYPE_NONE
;
207 enum rom_region current_region
= REGION_NTSC
;
209 std::string
findoption(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
213 for(auto i
: cmdline
) {
214 if(!(optp
= regex("--([^=]+)=(.*)", i
)) || optp
[1] != option
)
219 std::cerr
<< "WARNING: Ignored duplicate option for '" << option
<< "'." << std::endl
;
221 throw std::runtime_error("Empty value for '" + option
+ "' is not allowed");
228 loaded_slot::loaded_slot() throw(std::bad_alloc
)
233 loaded_slot::loaded_slot(const std::string
& filename
, const std::string
& base
, bool xml_flag
)
234 throw(std::bad_alloc
, std::runtime_error
)
236 bool headered
= false;
243 data
= read_file_relative(filename
, base
);
244 if(!xml
&& data
.size() % 1024 == 512)
247 if(headered
&& !xml
) {
248 if(data
.size() >= 512) {
249 memmove(&data
[0], &data
[512], data
.size() - 512);
250 data
.resize(data
.size() - 512);
255 sha256
= sha256::hash(data
);
257 size_t osize
= data
.size();
258 data
.resize(osize
+ 1);
263 void loaded_slot::patch(const std::vector
<char>& patch
, int32_t offset
) throw(std::bad_alloc
, std::runtime_error
)
266 std::vector
<char> data2
= data
;
269 data2
.resize(data2
.size() - 1);
270 data2
= do_patch_file(data2
, patch
, offset
);
271 //Mark the slot as valid and update hash.
273 std::string new_sha256
= sha256::hash(data2
);
275 size_t osize
= data2
.size();
276 data2
.resize(osize
+ 1);
286 rom_files::rom_files() throw()
288 rtype
= ROMTYPE_NONE
;
289 region
= REGION_AUTO
;
293 rom_files::rom_files(const std::vector
<std::string
>& cmdline
) throw(std::bad_alloc
, std::runtime_error
)
295 rom
= rom_xml
= slota
= slota_xml
= slotb
= slotb_xml
= "";
296 std::string arr
[sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0])];
297 unsigned long flags
= 0;
298 for(size_t i
= 0; i
< sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0]); i
++) {
299 arr
[i
] = findoption(cmdline
, romtypes_to_recognize
[i
]);
303 rtype
= recognize_platform(flags
);
304 for(size_t i
= 0; i
< sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0]); i
++) {
306 switch(recognize_commandline_rom(rtype
, romtypes_to_recognize
[i
])) {
307 case 0: rom
= arr
[i
]; break;
308 case 1: rom_xml
= arr
[i
]; break;
309 case 2: slota
= arr
[i
]; break;
310 case 3: slota_xml
= arr
[i
]; break;
311 case 4: slotb
= arr
[i
]; break;
312 case 5: slotb_xml
= arr
[i
]; break;
315 region
= (rtype
== ROMTYPE_SGB
|| rtype
== ROMTYPE_SNES
) ? REGION_AUTO
: REGION_NTSC
;
316 if(option_set(cmdline
, "--ntsc"))
317 region
= REGION_NTSC
;
318 else if(option_set(cmdline
, "--pal"))
324 void rom_files::resolve_relative() throw(std::bad_alloc
, std::runtime_error
)
326 rom
= resolve_file_relative(rom
, base_file
);
327 rom_xml
= resolve_file_relative(rom_xml
, base_file
);
328 slota
= resolve_file_relative(slota
, base_file
);
329 slota_xml
= resolve_file_relative(slota_xml
, base_file
);
330 slotb
= resolve_file_relative(slotb
, base_file
);
331 slotb_xml
= resolve_file_relative(slotb_xml
, base_file
);
336 std::pair
<enum rom_type
, enum rom_region
> get_current_rom_info() throw()
338 return std::make_pair(current_rom_type
, current_region
);
341 loaded_rom::loaded_rom() throw()
343 rtype
= ROMTYPE_NONE
;
344 region
= orig_region
= REGION_AUTO
;
347 loaded_rom::loaded_rom(const rom_files
& files
) throw(std::bad_alloc
, std::runtime_error
)
349 std::string _slota
= files
.slota
;
350 std::string _slota_xml
= files
.slota_xml
;
351 std::string _slotb
= files
.slotb
;
352 std::string _slotb_xml
= files
.slotb_xml
;
353 if(files
.rtype
== ROMTYPE_NONE
) {
354 rtype
= ROMTYPE_NONE
;
355 region
= orig_region
= files
.region
;
358 if((_slota
!= "" || _slota_xml
!= "") && files
.rtype
== ROMTYPE_SNES
) {
359 messages
<< "WARNING: SNES takes only 1 ROM image" << std::endl
;
363 if((_slotb
!= "" || _slotb_xml
!= "") && files
.rtype
!= ROMTYPE_SUFAMITURBO
) {
364 messages
<< "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl
;
368 if(files
.rom_xml
!= "" && files
.rom
== "")
369 messages
<< "WARNING: " << name_subrom(files
.rtype
, 0) << " specified without corresponding "
370 << name_subrom(files
.rtype
, 1) << std::endl
;
371 if(_slota_xml
!= "" && _slota
== "")
372 messages
<< "WARNING: " << name_subrom(files
.rtype
, 2) << " specified without corresponding "
373 << name_subrom(files
.rtype
, 3) << std::endl
;
374 if(_slotb_xml
!= "" && _slotb
== "")
375 messages
<< "WARNING: " << name_subrom(files
.rtype
, 4) << " specified without corresponding "
376 << name_subrom(files
.rtype
, 5) << std::endl
;
379 rom
= loaded_slot(files
.rom
, files
.base_file
, false);
380 rom_xml
= loaded_slot(files
.rom_xml
, files
.base_file
, true);
381 slota
= loaded_slot(_slota
, files
.base_file
, false);
382 slota_xml
= loaded_slot(_slota_xml
, files
.base_file
, true);
383 slotb
= loaded_slot(_slotb
, files
.base_file
, false);
384 slotb_xml
= loaded_slot(_slotb_xml
, files
.base_file
, true);
385 orig_region
= region
= files
.region
;
388 void loaded_rom::load() throw(std::bad_alloc
, std::runtime_error
)
390 current_rom_type
= ROMTYPE_NONE
;
391 if(region
== REGION_AUTO
&& orig_region
!= REGION_AUTO
)
392 region
= orig_region
;
393 if(region
!= orig_region
&& orig_region
!= REGION_AUTO
)
394 throw std::runtime_error("Trying to force incompatible region");
395 if(rtype
== ROMTYPE_NONE
)
396 throw std::runtime_error("Can't insert cartridge of type NONE!");
399 SNES::config
.region
= SNES::System::Region::Autodetect
;
402 SNES::config
.region
= SNES::System::Region::NTSC
;
405 SNES::config
.region
= SNES::System::Region::PAL
;
408 throw std::runtime_error("Trying to force unknown region");
412 if(!snes_load_cartridge_normal(rom_xml
, rom
, rom
))
413 throw std::runtime_error("Can't load cartridge ROM");
416 if(region
== REGION_PAL
)
417 throw std::runtime_error("BSX can't be PAL");
418 if(!snes_load_cartridge_bsx(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
419 throw std::runtime_error("Can't load cartridge ROM");
421 case ROMTYPE_BSXSLOTTED
:
422 if(region
== REGION_PAL
)
423 throw std::runtime_error("Slotted BSX can't be PAL");
424 if(!snes_load_cartridge_bsx_slotted(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
425 throw std::runtime_error("Can't load cartridge ROM");
428 if(!snes_load_cartridge_super_game_boy(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
429 throw std::runtime_error("Can't load cartridge ROM");
431 case ROMTYPE_SUFAMITURBO
:
432 if(region
== REGION_PAL
)
433 throw std::runtime_error("Sufami Turbo can't be PAL");
434 if(!snes_load_cartridge_sufami_turbo(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
, slotb_xml
, slotb
,
436 throw std::runtime_error("Can't load cartridge ROM");
439 throw std::runtime_error("Unknown cartridge type");
441 if(region
== REGION_AUTO
)
442 region
= snes_get_region() ? REGION_PAL
: REGION_NTSC
;
444 if(region
== REGION_PAL
)
445 set_nominal_framerate(SNES::system
.cpu_frequency() / DURATION_PAL_FRAME
);
447 set_nominal_framerate(SNES::system
.cpu_frequency() / DURATION_NTSC_FRAME
);
448 information_dispatch::do_sound_rate(SNES::system
.apu_frequency(), 768);
449 current_rom_type
= rtype
;
450 current_region
= region
;
451 refresh_cart_mappings();
454 void loaded_rom::do_patch(const std::vector
<std::string
>& cmdline
) throw(std::bad_alloc
,
459 for(auto i
: cmdline
) {
460 if(opt
= regex("--ips-offset=(.*)", i
)) {
462 offset
= parse_value
<int32_t>(opt
[1]);
463 } catch(std::exception
& e
) {
464 throw std::runtime_error("Invalid IPS offset option '" + i
+ "': " + e
.what());
467 } else if(opt
= regex("--ips-([^=]*)=(.+)", i
)) {
468 messages
<< "Patching " << opt
[1] << " using '" << opt
[2] << "'" << std::endl
;
469 std::vector
<char> ips
;
471 ips
= read_file_relative(opt
[2], "");
472 } catch(std::bad_alloc
& e
) {
474 } catch(std::exception
& e
) {
475 throw std::runtime_error("Can't read IPS '" + opt
[2] + "': " + e
.what());
478 switch(recognize_commandline_rom(rtype
, opt
[1])) {
479 case 0: rom
.patch(ips
, offset
); break;
480 case 1: rom_xml
.patch(ips
, offset
); break;
481 case 2: slota
.patch(ips
, offset
); break;
482 case 3: slota_xml
.patch(ips
, offset
); break;
483 case 4: slotb
.patch(ips
, offset
); break;
484 case 5: slotb_xml
.patch(ips
, offset
); break;
486 throw std::runtime_error("Invalid subROM '" + opt
[1] + "' to patch");
488 } catch(std::bad_alloc
& e
) {
490 } catch(std::exception
& e
) {
491 throw std::runtime_error("Can't Patch with IPS '" + opt
[2] + "': " + e
.what());
499 std::string
sram_name(const nall::string
& _id
, SNES::Cartridge::Slot slotname
)
501 std::string
id(_id
, _id
.length());
502 //Fixup name change by bsnes v087...
505 if(id
== "bsx.psram")
507 if(id
== "program.rtc")
509 if(id
== "upd96050.ram")
511 if(id
== "program.ram")
513 if(slotname
== SNES::Cartridge::Slot::SufamiTurboA
)
514 return "slota." + id
.substr(1);
515 if(slotname
== SNES::Cartridge::Slot::SufamiTurboB
)
516 return "slotb." + id
.substr(1);
521 std::map
<std::string
, std::vector
<char>> save_sram() throw(std::bad_alloc
)
523 std::map
<std::string
, std::vector
<char>> out
;
524 for(unsigned i
= 0; i
< SNES::cartridge
.nvram
.size(); i
++) {
525 SNES::Cartridge::NonVolatileRAM
& r
= SNES::cartridge
.nvram
[i
];
526 std::string savename
= sram_name(r
.id
, r
.slot
);
529 memcpy(&x
[0], r
.data
, r
.size
);
535 void load_sram(std::map
<std::string
, std::vector
<char>>& sram
) throw(std::bad_alloc
)
537 std::set
<std::string
> used
;
540 for(unsigned i
= 0; i
< SNES::cartridge
.nvram
.size(); i
++) {
541 SNES::Cartridge::NonVolatileRAM
& r
= SNES::cartridge
.nvram
[i
];
542 std::string savename
= sram_name(r
.id
, r
.slot
);
543 if(sram
.count(savename
)) {
544 std::vector
<char>& x
= sram
[savename
];
545 if(r
.size
!= x
.size())
546 messages
<< "WARNING: SRAM '" << savename
<< "': Loaded " << x
.size()
547 << " bytes, but the SRAM is " << r
.size
<< "." << std::endl
;
548 memcpy(r
.data
, &x
[0], (r
.size
< x
.size()) ? r
.size
: x
.size());
549 used
.insert(savename
);
551 messages
<< "WARNING: SRAM '" << savename
<< ": No data." << std::endl
;
554 if(!used
.count(i
.first
))
555 messages
<< "WARNING: SRAM '" << i
.first
<< ": Not found on cartridge." << std::endl
;
558 std::map
<std::string
, std::vector
<char>> load_sram_commandline(const std::vector
<std::string
>& cmdline
)
559 throw(std::bad_alloc
, std::runtime_error
)
561 std::map
<std::string
, std::vector
<char>> ret
;
563 for(auto i
: cmdline
) {
564 if(opt
= regex("--continue=(.+)", i
)) {
565 zip_reader
r(opt
[1]);
567 auto sramname
= regex("sram\\.(.*)", j
);
570 std::istream
& x
= r
[j
];
572 std::vector
<char> out
;
573 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
574 boost::iostreams::copy(x
, rd
);
576 ret
[sramname
[1]] = out
;
583 } else if(opt
= regex("--sram-([^=]+)=(.+)", i
)) {
585 ret
[opt
[1]] = read_file_relative(opt
[2], "");
586 } catch(std::bad_alloc
& e
) {
588 } catch(std::runtime_error
& e
) {
589 throw std::runtime_error("Can't load SRAM '" + opt
[1] + "': " + e
.what());
596 std::vector
<char> save_core_state() throw(std::bad_alloc
)
598 std::vector
<char> ret
;
599 serializer s
= SNES::system
.serialize();
600 ret
.resize(s
.size());
601 memcpy(&ret
[0], s
.data(), s
.size());
602 size_t offset
= ret
.size();
603 unsigned char tmp
[32];
604 sha256::hash(tmp
, ret
);
605 ret
.resize(offset
+ 32);
606 memcpy(&ret
[offset
], tmp
, 32);
610 void load_core_state(const std::vector
<char>& buf
) throw(std::runtime_error
)
613 throw std::runtime_error("Savestate corrupt");
614 unsigned char tmp
[32];
615 sha256::hash(tmp
, reinterpret_cast<const uint8_t*>(&buf
[0]), buf
.size() - 32);
616 if(memcmp(tmp
, &buf
[buf
.size() - 32], 32))
617 throw std::runtime_error("Savestate corrupt");
618 serializer
s(reinterpret_cast<const uint8_t*>(&buf
[0]), buf
.size() - 32);
619 if(!SNES::system
.unserialize(s
))
620 throw std::runtime_error("SNES core rejected savestate");
623 std::string
name_subrom(enum rom_type major
, unsigned romnumber
) throw(std::bad_alloc
)
627 else if(romnumber
== 1)
629 else if(major
== ROMTYPE_BSX
&& romnumber
== 2)
631 else if(major
== ROMTYPE_BSX
&& romnumber
== 3)
633 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 2)
635 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 3)
637 else if(major
== ROMTYPE_SGB
&& romnumber
== 2)
639 else if(major
== ROMTYPE_SGB
&& romnumber
== 3)
641 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 2)
643 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 3)
645 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 4)
647 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 5)
649 else if(romnumber
% 2)
650 return "UNKNOWN XML";
652 return "UNKNOWN ROM";
656 int recognize_commandline_rom(enum rom_type major
, const std::string
& romname
) throw(std::bad_alloc
)
658 if(romname
== romtypes_to_recognize
[0])
660 else if(romname
== romtypes_to_recognize
[6])
662 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[1])
664 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[7])
666 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[2])
668 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[8])
670 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[3])
672 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[9])
674 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[4])
676 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[10])
678 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[5])
680 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[11])
686 rom_type
recognize_platform(unsigned long flags
) throw(std::bad_alloc
, std::runtime_error
)
688 if((flags
& 07700) >> 6 & ~(flags
& 077))
689 throw std::runtime_error("SubROM XML specified without corresponding subROM");
691 throw std::runtime_error("No SNES main cartridge ROM specified");
692 if((flags
& 077) == 1)
694 if((flags
& 077) == 3)
696 if((flags
& 077) == 5)
697 return ROMTYPE_BSXSLOTTED
;
698 if((flags
& 077) == 9)
700 if((flags
& 060) != 0 && (flags
& 017) == 1)
701 return ROMTYPE_SUFAMITURBO
;
702 throw std::runtime_error("Not valid combination of rom/bsx/bsxslotted/dmg/slot-a/slot-b");