Fix SRAM handling with Bsnes v087
[lsnes.git] / src / core / rom.cpp
blob1935ef57a6ca9272baefbc39b58e9107d1ab227c
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"
14 #include <stdexcept>
15 #include <sstream>
16 #include <iomanip>
17 #include <cstdint>
18 #include <set>
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"
32 #define BSX "bsx"
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)
41 switch(rtype) {
42 case ROMTYPE_SNES:
43 switch(region) {
44 case REGION_AUTO: return "snes";
45 case REGION_NTSC: return "snes_ntsc";
46 case REGION_PAL: return "snes_pal";
48 case ROMTYPE_SGB:
49 switch(region) {
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)
63 switch(gametype) {
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)
77 switch(rtype) {
78 case ROMTYPE_SNES:
79 switch(region) {
80 case REGION_AUTO: return GT_SGB_NTSC;
81 case REGION_NTSC: return GT_SNES_NTSC;
82 case REGION_PAL: return GT_SNES_PAL;
84 case ROMTYPE_SGB:
85 switch(region) {
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")
100 return GT_SNES_NTSC;
101 if(gametype == "snes_pal")
102 return GT_SNES_PAL;
103 if(gametype == "sgb_ntsc")
104 return GT_SGB_NTSC;
105 if(gametype == "sgb_pal")
106 return GT_SGB_PAL;
107 if(gametype == "bsx")
108 return GT_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")
119 return ROMTYPE_SNES;
120 if(gametype == "snes_pal")
121 return ROMTYPE_SNES;
122 if(gametype == "snes")
123 return ROMTYPE_SNES;
124 if(gametype == "sgb_ntsc")
125 return ROMTYPE_SGB;
126 if(gametype == "sgb_pal")
127 return ROMTYPE_SGB;
128 if(gametype == "sgb")
129 return ROMTYPE_SGB;
130 if(gametype == "bsx")
131 return ROMTYPE_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()
141 switch(gametype) {
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")
156 return REGION_NTSC;
157 if(gametype == "snes_pal")
158 return REGION_PAL;
159 if(gametype == "snes")
160 return REGION_AUTO;
161 if(gametype == "sgb_ntsc")
162 return REGION_NTSC;
163 if(gametype == "sgb_pal")
164 return REGION_PAL;
165 if(gametype == "sgb")
166 return REGION_AUTO;
167 if(gametype == "bsx")
168 return REGION_NTSC;
169 if(gametype == "bsxslotted")
170 return REGION_NTSC;
171 if(gametype == "sufamiturbo")
172 return REGION_NTSC;
173 throw std::runtime_error("Unknown game type '" + gametype + "'");
176 rom_region gtype::toromregion(gametype_t gametype) throw()
178 switch(gametype) {
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");
191 namespace
193 bool option_set(const std::vector<std::string>& cmdline, const std::string& option)
195 for(auto i : cmdline)
196 if(i == option)
197 return true;
198 return false;
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)
211 std::string value;
212 regex_results optp;
213 for(auto i : cmdline) {
214 if(!(optp = regex("--([^=]+)=(.*)", i)) || optp[1] != option)
215 continue;
216 if(value == "")
217 value = optp[2];
218 else
219 std::cerr << "WARNING: Ignored duplicate option for '" << option << "'." << std::endl;
220 if(value == "")
221 throw std::runtime_error("Empty value for '" + option + "' is not allowed");
223 return value;
228 loaded_slot::loaded_slot() throw(std::bad_alloc)
230 valid = false;
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;
237 xml = xml_flag;
238 if(filename == "") {
239 valid = false;
240 return;
242 valid = true;
243 data = read_file_relative(filename, base);
244 if(!xml && data.size() % 1024 == 512)
245 //Assume headered.
246 headered = true;
247 if(headered && !xml) {
248 if(data.size() >= 512) {
249 memmove(&data[0], &data[512], data.size() - 512);
250 data.resize(data.size() - 512);
251 } else {
252 data.resize(0);
255 sha256 = sha256::hash(data);
256 if(xml) {
257 size_t osize = data.size();
258 data.resize(osize + 1);
259 data[osize] = 0;
263 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
265 try {
266 std::vector<char> data2 = data;
267 size_t poffset = 0;
268 if(xml && valid)
269 data2.resize(data2.size() - 1);
270 data2 = do_patch_file(data2, patch, offset);
271 //Mark the slot as valid and update hash.
272 valid = true;
273 std::string new_sha256 = sha256::hash(data2);
274 if(xml) {
275 size_t osize = data2.size();
276 data2.resize(osize + 1);
277 data2[osize] = 0;
279 data = data2;
280 sha256 = new_sha256;
281 } catch(...) {
282 throw;
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]);
300 if(arr[i] != "")
301 flags |= (1L << i);
303 rtype = recognize_platform(flags);
304 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
305 if(arr[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"))
319 region = REGION_PAL;
321 base_file = "";
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);
332 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;
356 return;
358 if((_slota != "" || _slota_xml != "") && files.rtype == ROMTYPE_SNES) {
359 messages << "WARNING: SNES takes only 1 ROM image" << std::endl;
360 _slota = "";
361 _slota_xml = "";
363 if((_slotb != "" || _slotb_xml != "") && files.rtype != ROMTYPE_SUFAMITURBO) {
364 messages << "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl;
365 _slotb = "";
366 _slotb_xml = "";
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;
378 rtype = files.rtype;
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!");
397 switch(region) {
398 case REGION_AUTO:
399 SNES::config.region = SNES::System::Region::Autodetect;
400 break;
401 case REGION_NTSC:
402 SNES::config.region = SNES::System::Region::NTSC;
403 break;
404 case REGION_PAL:
405 SNES::config.region = SNES::System::Region::PAL;
406 break;
407 default:
408 throw std::runtime_error("Trying to force unknown region");
410 switch(rtype) {
411 case ROMTYPE_SNES:
412 if(!snes_load_cartridge_normal(rom_xml, rom, rom))
413 throw std::runtime_error("Can't load cartridge ROM");
414 break;
415 case ROMTYPE_BSX:
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");
420 break;
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");
426 break;
427 case ROMTYPE_SGB:
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");
430 break;
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,
435 slotb))
436 throw std::runtime_error("Can't load cartridge ROM");
437 break;
438 default:
439 throw std::runtime_error("Unknown cartridge type");
441 if(region == REGION_AUTO)
442 region = snes_get_region() ? REGION_PAL : REGION_NTSC;
443 snes_power();
444 if(region == REGION_PAL)
445 set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_PAL_FRAME);
446 else
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,
455 std::runtime_error)
457 int32_t offset = 0;
458 regex_results opt;
459 for(auto i : cmdline) {
460 if(opt = regex("--ips-offset=(.*)", i)) {
461 try {
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());
466 continue;
467 } else if(opt = regex("--ips-([^=]*)=(.+)", i)) {
468 messages << "Patching " << opt[1] << " using '" << opt[2] << "'" << std::endl;
469 std::vector<char> ips;
470 try {
471 ips = read_file_relative(opt[2], "");
472 } catch(std::bad_alloc& e) {
473 OOM_panic();
474 } catch(std::exception& e) {
475 throw std::runtime_error("Can't read IPS '" + opt[2] + "': " + e.what());
477 try {
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;
485 default:
486 throw std::runtime_error("Invalid subROM '" + opt[1] + "' to patch");
488 } catch(std::bad_alloc& e) {
489 OOM_panic();
490 } catch(std::exception& e) {
491 throw std::runtime_error("Can't Patch with IPS '" + opt[2] + "': " + e.what());
497 namespace
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...
503 if(id == "bsx.ram")
504 id = ".bss";
505 if(id == "bsx.psram")
506 id = ".bsp";
507 if(id == "program.rtc")
508 id = ".rtc";
509 if(id == "upd96050.ram")
510 id = ".dsp";
511 if(id == "program.ram")
512 id = ".srm";
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);
517 return 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);
527 std::vector<char> x;
528 x.resize(r.size);
529 memcpy(&x[0], r.data, r.size);
530 out[savename] = x;
532 return out;
535 void load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc)
537 std::set<std::string> used;
538 if(sram.empty())
539 return;
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);
550 } else
551 messages << "WARNING: SRAM '" << savename << ": No data." << std::endl;
553 for(auto i : sram)
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;
562 regex_results opt;
563 for(auto i : cmdline) {
564 if(opt = regex("--continue=(.+)", i)) {
565 zip_reader r(opt[1]);
566 for(auto j : r) {
567 auto sramname = regex("sram\\.(.*)", j);
568 if(!sram_name)
569 continue;
570 std::istream& x = r[j];
571 try {
572 std::vector<char> out;
573 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
574 boost::iostreams::copy(x, rd);
575 delete &x;
576 ret[sramname[1]] = out;
577 } catch(...) {
578 delete &x;
579 throw;
582 continue;
583 } else if(opt = regex("--sram-([^=]+)=(.+)", i)) {
584 try {
585 ret[opt[1]] = read_file_relative(opt[2], "");
586 } catch(std::bad_alloc& e) {
587 throw;
588 } catch(std::runtime_error& e) {
589 throw std::runtime_error("Can't load SRAM '" + opt[1] + "': " + e.what());
593 return ret;
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);
607 return ret;
610 void load_core_state(const std::vector<char>& buf) throw(std::runtime_error)
612 if(buf.size() < 32)
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)
625 if(romnumber == 0)
626 return "ROM";
627 else if(romnumber == 1)
628 return "ROM XML";
629 else if(major == ROMTYPE_BSX && romnumber == 2)
630 return "BSX ROM";
631 else if(major == ROMTYPE_BSX && romnumber == 3)
632 return "BSX XML";
633 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 2)
634 return "BSX ROM";
635 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 3)
636 return "BSX XML";
637 else if(major == ROMTYPE_SGB && romnumber == 2)
638 return "DMG ROM";
639 else if(major == ROMTYPE_SGB && romnumber == 3)
640 return "DMG XML";
641 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 2)
642 return "SLOT A ROM";
643 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 3)
644 return "SLOT A XML";
645 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 4)
646 return "SLOT B ROM";
647 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 5)
648 return "SLOT B XML";
649 else if(romnumber % 2)
650 return "UNKNOWN XML";
651 else
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])
659 return 0;
660 else if(romname == romtypes_to_recognize[6])
661 return 1;
662 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[1])
663 return 2;
664 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[7])
665 return 3;
666 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[2])
667 return 2;
668 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[8])
669 return 3;
670 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[3])
671 return 2;
672 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[9])
673 return 3;
674 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[4])
675 return 2;
676 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[10])
677 return 3;
678 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[5])
679 return 4;
680 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[11])
681 return 5;
682 else
683 return -1;
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");
690 if((flags & 1) == 0)
691 throw std::runtime_error("No SNES main cartridge ROM specified");
692 if((flags & 077) == 1)
693 return ROMTYPE_SNES;
694 if((flags & 077) == 3)
695 return ROMTYPE_BSX;
696 if((flags & 077) == 5)
697 return ROMTYPE_BSXSLOTTED;
698 if((flags & 077) == 9)
699 return ROMTYPE_SGB;
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");