Remove SHA-256 indices
[lsnes.git] / src / core / rom.cpp
blob5af36cfa9e62277a2381306b93011911014d0f71
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framerate.hpp"
8 #include "core/memorymanip.hpp"
9 #include "core/misc.hpp"
10 #include "core/patchrom.hpp"
11 #include "core/rom.hpp"
12 #include "core/window.hpp"
13 #include "library/zip.hpp"
15 #include <stdexcept>
16 #include <sstream>
17 #include <iomanip>
18 #include <cstdint>
19 #include <set>
20 #include <boost/iostreams/categories.hpp>
21 #include <boost/iostreams/copy.hpp>
22 #include <boost/iostreams/stream.hpp>
23 #include <boost/iostreams/stream_buffer.hpp>
24 #include <boost/iostreams/filter/symmetric.hpp>
25 #include <boost/iostreams/filter/zlib.hpp>
26 #include <boost/iostreams/filtering_stream.hpp>
27 #include <boost/iostreams/device/back_inserter.hpp>
29 //Some anti-typo defs.
30 #define SNES_TYPE "snes"
31 #define SNES_PAL "snes_pal"
32 #define SNES_NTSC "snes_ntsc"
33 #define BSX "bsx"
34 #define BSXSLOTTED "bsxslotted"
35 #define SUFAMITURBO "sufamiturbo"
36 #define SGB_TYPE "SGB"
37 #define SGB_PAL "sgb_pal"
38 #define SGB_NTSC "sgb_ntsc"
40 std::string gtype::tostring(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
42 switch(rtype) {
43 case ROMTYPE_SNES:
44 switch(region) {
45 case REGION_AUTO: return "snes";
46 case REGION_NTSC: return "snes_ntsc";
47 case REGION_PAL: return "snes_pal";
49 case ROMTYPE_SGB:
50 switch(region) {
51 case REGION_AUTO: return "sgb";
52 case REGION_NTSC: return "sgb_ntsc";
53 case REGION_PAL: return "sgb_pal";
55 case ROMTYPE_BSX: return "bsx";
56 case ROMTYPE_BSXSLOTTED: return "bsxslotted";
57 case ROMTYPE_SUFAMITURBO: return "sufamiturbo";
58 default: throw std::runtime_error("tostring: ROMTYPE_NONE");
62 std::string gtype::tostring(gametype_t gametype) throw(std::bad_alloc, std::runtime_error)
64 switch(gametype) {
65 case GT_SNES_NTSC: return "snes_ntsc";
66 case GT_SNES_PAL: return "snes_pal";
67 case GT_SGB_NTSC: return "sgb_ntsc";
68 case GT_SGB_PAL: return "sgb_pal";
69 case GT_BSX: return "bsx";
70 case GT_BSX_SLOTTED: return "bsxslotted";
71 case GT_SUFAMITURBO: return "sufamiturbo";
72 default: throw std::runtime_error("tostring: GT_INVALID");
76 gametype_t gtype::togametype(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
78 switch(rtype) {
79 case ROMTYPE_SNES:
80 switch(region) {
81 case REGION_AUTO: return GT_SGB_NTSC;
82 case REGION_NTSC: return GT_SNES_NTSC;
83 case REGION_PAL: return GT_SNES_PAL;
85 case ROMTYPE_SGB:
86 switch(region) {
87 case REGION_AUTO: return GT_SGB_NTSC;
88 case REGION_NTSC: return GT_SGB_NTSC;
89 case REGION_PAL: return GT_SGB_PAL;
91 case ROMTYPE_BSX: return GT_BSX;
92 case ROMTYPE_BSXSLOTTED: return GT_BSX_SLOTTED;
93 case ROMTYPE_SUFAMITURBO: return GT_SUFAMITURBO;
94 default: throw std::runtime_error("togametype: ROMTYPE_NONE");
98 gametype_t gtype::togametype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
100 if(gametype == "snes_ntsc")
101 return GT_SNES_NTSC;
102 if(gametype == "snes_pal")
103 return GT_SNES_PAL;
104 if(gametype == "sgb_ntsc")
105 return GT_SGB_NTSC;
106 if(gametype == "sgb_pal")
107 return GT_SGB_PAL;
108 if(gametype == "bsx")
109 return GT_BSX;
110 if(gametype == "bsxslotted")
111 return GT_BSX_SLOTTED;
112 if(gametype == "sufamiturbo")
113 return GT_SUFAMITURBO;
114 throw std::runtime_error("Unknown game type '" + gametype + "'");
117 rom_type gtype::toromtype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
119 if(gametype == "snes_ntsc")
120 return ROMTYPE_SNES;
121 if(gametype == "snes_pal")
122 return ROMTYPE_SNES;
123 if(gametype == "snes")
124 return ROMTYPE_SNES;
125 if(gametype == "sgb_ntsc")
126 return ROMTYPE_SGB;
127 if(gametype == "sgb_pal")
128 return ROMTYPE_SGB;
129 if(gametype == "sgb")
130 return ROMTYPE_SGB;
131 if(gametype == "bsx")
132 return ROMTYPE_BSX;
133 if(gametype == "bsxslotted")
134 return ROMTYPE_BSXSLOTTED;
135 if(gametype == "sufamiturbo")
136 return ROMTYPE_SUFAMITURBO;
137 throw std::runtime_error("Unknown game type '" + gametype + "'");
140 rom_type gtype::toromtype(gametype_t gametype) throw()
142 switch(gametype) {
143 case GT_SNES_NTSC: return ROMTYPE_SNES;
144 case GT_SNES_PAL: return ROMTYPE_SNES;
145 case GT_SGB_NTSC: return ROMTYPE_SGB;
146 case GT_SGB_PAL: return ROMTYPE_SGB;
147 case GT_BSX: return ROMTYPE_BSX;
148 case GT_BSX_SLOTTED: return ROMTYPE_BSXSLOTTED;
149 case GT_SUFAMITURBO: return ROMTYPE_SUFAMITURBO;
150 case GT_INVALID: throw std::runtime_error("toromtype: GT_INVALID");
154 rom_region gtype::toromregion(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
156 if(gametype == "snes_ntsc")
157 return REGION_NTSC;
158 if(gametype == "snes_pal")
159 return REGION_PAL;
160 if(gametype == "snes")
161 return REGION_AUTO;
162 if(gametype == "sgb_ntsc")
163 return REGION_NTSC;
164 if(gametype == "sgb_pal")
165 return REGION_PAL;
166 if(gametype == "sgb")
167 return REGION_AUTO;
168 if(gametype == "bsx")
169 return REGION_NTSC;
170 if(gametype == "bsxslotted")
171 return REGION_NTSC;
172 if(gametype == "sufamiturbo")
173 return REGION_NTSC;
174 throw std::runtime_error("Unknown game type '" + gametype + "'");
177 rom_region gtype::toromregion(gametype_t gametype) throw()
179 switch(gametype) {
180 case GT_SNES_NTSC: return REGION_NTSC;
181 case GT_SNES_PAL: return REGION_PAL;
182 case GT_SGB_NTSC: return REGION_NTSC;
183 case GT_SGB_PAL: return REGION_PAL;
184 case GT_BSX: return REGION_NTSC;
185 case GT_BSX_SLOTTED: return REGION_NTSC;
186 case GT_SUFAMITURBO: return REGION_NTSC;
187 case GT_INVALID: throw std::runtime_error("toromregion: GT_INVALID");
192 namespace
194 bool option_set(const std::vector<std::string>& cmdline, const std::string& option)
196 for(auto i : cmdline)
197 if(i == option)
198 return true;
199 return false;
202 const char* romtypes_to_recognize[] = {
203 "rom", "bsx", "bsxslotted", "dmg", "slot-a", "slot-b",
204 "rom-xml", "bsx-xml", "bsxslotted-xml", "dmg-xml", "slot-a-xml", "slot-b-xml"
207 enum rom_type current_rom_type = ROMTYPE_NONE;
208 enum rom_region current_region = REGION_NTSC;
210 std::string findoption(const std::vector<std::string>& cmdline, const std::string& option)
212 std::string value;
213 for(auto i : cmdline) {
214 std::string arg = i;
215 if(arg.length() < 3 + option.length())
216 continue;
217 if(arg[0] != '-' || arg[1] != '-' || arg.substr(2, option.length()) != option ||
218 arg[2 + option.length()] != '=')
219 continue;
220 if(value == "")
221 value = arg.substr(3 + option.length());
222 else
223 std::cerr << "WARNING: Ignored duplicate option for '" << option << "'." << std::endl;
224 if(value == "")
225 throw std::runtime_error("Empty value for '" + option + "' is not allowed");
227 return value;
232 loaded_slot::loaded_slot() throw(std::bad_alloc)
234 valid = false;
237 loaded_slot::loaded_slot(const std::string& filename, const std::string& base, bool xml_flag)
238 throw(std::bad_alloc, std::runtime_error)
240 bool headered = false;
241 xml = xml_flag;
242 if(filename == "") {
243 valid = false;
244 return;
246 valid = true;
247 data = read_file_relative(filename, base);
248 if(!xml && data.size() % 1024 == 512)
249 //Assume headered.
250 headered = true;
251 if(headered && !xml) {
252 if(data.size() >= 512) {
253 memmove(&data[0], &data[512], data.size() - 512);
254 data.resize(data.size() - 512);
255 } else {
256 data.resize(0);
259 sha256 = sha256::hash(data);
260 if(xml) {
261 size_t osize = data.size();
262 data.resize(osize + 1);
263 data[osize] = 0;
267 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
269 try {
270 std::vector<char> data2 = data;
271 size_t poffset = 0;
272 if(xml && valid)
273 data2.resize(data2.size() - 1);
274 data2 = do_patch_file(data2, patch, offset);
275 //Mark the slot as valid and update hash.
276 valid = true;
277 std::string new_sha256 = sha256::hash(data2);
278 if(xml) {
279 size_t osize = data2.size();
280 data2.resize(osize + 1);
281 data2[osize] = 0;
283 data = data2;
284 sha256 = new_sha256;
285 } catch(...) {
286 throw;
290 rom_files::rom_files() throw()
292 rtype = ROMTYPE_NONE;
293 region = REGION_AUTO;
297 rom_files::rom_files(const std::vector<std::string>& cmdline) throw(std::bad_alloc, std::runtime_error)
299 rom = rom_xml = slota = slota_xml = slotb = slotb_xml = "";
300 std::string arr[sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0])];
301 unsigned long flags = 0;
302 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
303 arr[i] = findoption(cmdline, romtypes_to_recognize[i]);
304 if(arr[i] != "")
305 flags |= (1L << i);
307 rtype = recognize_platform(flags);
308 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
309 if(arr[i] != "")
310 switch(recognize_commandline_rom(rtype, romtypes_to_recognize[i])) {
311 case 0: rom = arr[i]; break;
312 case 1: rom_xml = arr[i]; break;
313 case 2: slota = arr[i]; break;
314 case 3: slota_xml = arr[i]; break;
315 case 4: slotb = arr[i]; break;
316 case 5: slotb_xml = arr[i]; break;
319 region = (rtype == ROMTYPE_SGB || rtype == ROMTYPE_SNES) ? REGION_AUTO : REGION_NTSC;
320 if(option_set(cmdline, "--ntsc"))
321 region = REGION_NTSC;
322 else if(option_set(cmdline, "--pal"))
323 region = REGION_PAL;
325 base_file = "";
328 void rom_files::resolve_relative() throw(std::bad_alloc, std::runtime_error)
330 rom = resolve_file_relative(rom, base_file);
331 rom_xml = resolve_file_relative(rom_xml, base_file);
332 slota = resolve_file_relative(slota, base_file);
333 slota_xml = resolve_file_relative(slota_xml, base_file);
334 slotb = resolve_file_relative(slotb, base_file);
335 slotb_xml = resolve_file_relative(slotb_xml, base_file);
336 base_file = "";
340 std::pair<enum rom_type, enum rom_region> get_current_rom_info() throw()
342 return std::make_pair(current_rom_type, current_region);
345 loaded_rom::loaded_rom() throw()
347 rtype = ROMTYPE_NONE;
348 region = orig_region = REGION_AUTO;
351 loaded_rom::loaded_rom(const rom_files& files) throw(std::bad_alloc, std::runtime_error)
353 std::string _slota = files.slota;
354 std::string _slota_xml = files.slota_xml;
355 std::string _slotb = files.slotb;
356 std::string _slotb_xml = files.slotb_xml;
357 if(files.rtype == ROMTYPE_NONE) {
358 rtype = ROMTYPE_NONE;
359 region = orig_region = files.region;
360 return;
362 if((_slota != "" || _slota_xml != "") && files.rtype == ROMTYPE_SNES) {
363 messages << "WARNING: SNES takes only 1 ROM image" << std::endl;
364 _slota = "";
365 _slota_xml = "";
367 if((_slotb != "" || _slotb_xml != "") && files.rtype != ROMTYPE_SUFAMITURBO) {
368 messages << "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl;
369 _slotb = "";
370 _slotb_xml = "";
372 if(files.rom_xml != "" && files.rom == "")
373 messages << "WARNING: " << name_subrom(files.rtype, 0) << " specified without corresponding "
374 << name_subrom(files.rtype, 1) << std::endl;
375 if(_slota_xml != "" && _slota == "")
376 messages << "WARNING: " << name_subrom(files.rtype, 2) << " specified without corresponding "
377 << name_subrom(files.rtype, 3) << std::endl;
378 if(_slotb_xml != "" && _slotb == "")
379 messages << "WARNING: " << name_subrom(files.rtype, 4) << " specified without corresponding "
380 << name_subrom(files.rtype, 5) << std::endl;
382 rtype = files.rtype;
383 rom = loaded_slot(files.rom, files.base_file, false);
384 rom_xml = loaded_slot(files.rom_xml, files.base_file, true);
385 slota = loaded_slot(_slota, files.base_file, false);
386 slota_xml = loaded_slot(_slota_xml, files.base_file, true);
387 slotb = loaded_slot(_slotb, files.base_file, false);
388 slotb_xml = loaded_slot(_slotb_xml, files.base_file, true);
389 orig_region = region = files.region;
392 void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
394 current_rom_type = ROMTYPE_NONE;
395 if(region == REGION_AUTO && orig_region != REGION_AUTO)
396 region = orig_region;
397 if(region != orig_region && orig_region != REGION_AUTO)
398 throw std::runtime_error("Trying to force incompatible region");
399 if(rtype == ROMTYPE_NONE)
400 throw std::runtime_error("Can't insert cartridge of type NONE!");
401 switch(region) {
402 case REGION_AUTO:
403 SNES::config.region = SNES::System::Region::Autodetect;
404 break;
405 case REGION_NTSC:
406 SNES::config.region = SNES::System::Region::NTSC;
407 break;
408 case REGION_PAL:
409 SNES::config.region = SNES::System::Region::PAL;
410 break;
411 default:
412 throw std::runtime_error("Trying to force unknown region");
414 switch(rtype) {
415 case ROMTYPE_SNES:
416 if(!snes_load_cartridge_normal(rom_xml, rom, rom))
417 throw std::runtime_error("Can't load cartridge ROM");
418 break;
419 case ROMTYPE_BSX:
420 if(region == REGION_PAL)
421 throw std::runtime_error("BSX can't be PAL");
422 if(!snes_load_cartridge_bsx(rom_xml, rom, rom, slota_xml, slota, slota))
423 throw std::runtime_error("Can't load cartridge ROM");
424 break;
425 case ROMTYPE_BSXSLOTTED:
426 if(region == REGION_PAL)
427 throw std::runtime_error("Slotted BSX can't be PAL");
428 if(!snes_load_cartridge_bsx_slotted(rom_xml, rom, rom, slota_xml, slota, slota))
429 throw std::runtime_error("Can't load cartridge ROM");
430 break;
431 case ROMTYPE_SGB:
432 if(!snes_load_cartridge_super_game_boy(rom_xml, rom, rom, slota_xml, slota, slota))
433 throw std::runtime_error("Can't load cartridge ROM");
434 break;
435 case ROMTYPE_SUFAMITURBO:
436 if(region == REGION_PAL)
437 throw std::runtime_error("Sufami Turbo can't be PAL");
438 if(!snes_load_cartridge_sufami_turbo(rom_xml, rom, rom, slota_xml, slota, slota, slotb_xml, slotb,
439 slotb))
440 throw std::runtime_error("Can't load cartridge ROM");
441 break;
442 default:
443 throw std::runtime_error("Unknown cartridge type");
445 if(region == REGION_AUTO)
446 region = snes_get_region() ? REGION_PAL : REGION_NTSC;
447 snes_power();
448 if(region == REGION_PAL)
449 set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_PAL_FRAME);
450 else
451 set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_NTSC_FRAME);
452 information_dispatch::do_sound_rate(SNES::system.apu_frequency(), 768);
453 current_rom_type = rtype;
454 current_region = region;
455 refresh_cart_mappings();
458 void loaded_rom::do_patch(const std::vector<std::string>& cmdline) throw(std::bad_alloc,
459 std::runtime_error)
461 int32_t offset = 0;
462 for(auto i : cmdline) {
463 std::string opt = i;
464 if(opt.length() >= 13 && opt.substr(0, 13) == "--ips-offset=") {
465 try {
466 offset = parse_value<int32_t>(opt.substr(13));
467 } catch(std::exception& e) {
468 throw std::runtime_error("Invalid IPS offset option '" + opt + "': " + e.what());
470 continue;
472 if(opt.length() < 6 || opt.substr(0, 6) != "--ips-")
473 continue;
474 size_t split = opt.find_first_of("=");
475 if(split > opt.length())
476 throw std::runtime_error("Invalid IPS patch argument '" + opt + "'");
477 std::string kind = opt.substr(6, split - 6);
478 std::string filename = opt.substr(split + 1);
479 messages << "Patching " << kind << " using '" << filename << "'" << std::endl;
480 std::vector<char> ips;
481 try {
482 ips = read_file_relative(filename, "");
483 } catch(std::bad_alloc& e) {
484 OOM_panic();
485 } catch(std::exception& e) {
486 throw std::runtime_error("Can't read IPS '" + filename + "': " + e.what());
488 try {
489 switch(recognize_commandline_rom(rtype, kind)) {
490 case 0: rom.patch(ips, offset); break;
491 case 1: rom_xml.patch(ips, offset); break;
492 case 2: slota.patch(ips, offset); break;
493 case 3: slota_xml.patch(ips, offset); break;
494 case 4: slotb.patch(ips, offset); break;
495 case 5: slotb_xml.patch(ips, offset); break;
496 default:
497 throw std::runtime_error("Invalid subROM '" + kind + "' to patch");
499 } catch(std::bad_alloc& e) {
500 OOM_panic();
501 } catch(std::exception& e) {
502 throw std::runtime_error("Can't Patch with IPS '" + filename + "': " + e.what());
507 namespace
509 std::string sram_name(const nall::string& _id, SNES::Cartridge::Slot slotname)
511 std::string id(_id, _id.length());
512 if(slotname == SNES::Cartridge::Slot::SufamiTurboA)
513 return "slota." + id.substr(1);
514 if(slotname == SNES::Cartridge::Slot::SufamiTurboB)
515 return "slotb." + id.substr(1);
516 return id.substr(1);
520 std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc)
522 std::map<std::string, std::vector<char>> out;
523 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
524 SNES::Cartridge::NonVolatileRAM& r = SNES::cartridge.nvram[i];
525 std::string savename = sram_name(r.id, r.slot);
526 std::vector<char> x;
527 x.resize(r.size);
528 memcpy(&x[0], r.data, r.size);
529 out[savename] = x;
531 return out;
534 void load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc)
536 std::set<std::string> used;
537 if(sram.empty())
538 return;
539 for(unsigned i = 0; i < SNES::cartridge.nvram.size(); i++) {
540 SNES::Cartridge::NonVolatileRAM& r = SNES::cartridge.nvram[i];
541 std::string savename = sram_name(r.id, r.slot);
542 if(sram.count(savename)) {
543 std::vector<char>& x = sram[savename];
544 if(r.size != x.size())
545 messages << "WARNING: SRAM '" << savename << "': Loaded " << x.size()
546 << " bytes, but the SRAM is " << r.size << "." << std::endl;
547 memcpy(r.data, &x[0], (r.size < x.size()) ? r.size : x.size());
548 used.insert(savename);
549 } else
550 messages << "WARNING: SRAM '" << savename << ": No data." << std::endl;
552 for(auto i : sram)
553 if(!used.count(i.first))
554 messages << "WARNING: SRAM '" << i.first << ": Not found on cartridge." << std::endl;
557 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
558 throw(std::bad_alloc, std::runtime_error)
560 std::map<std::string, std::vector<char>> ret;
561 for(auto i : cmdline) {
562 std::string opt = i;
563 if(opt.length() >= 11 && opt.substr(0, 11) == "--continue=") {
564 size_t split = opt.find_first_of("=");
565 if(split > opt.length() - 1)
566 throw std::runtime_error("Bad SRAM option '" + opt + "'");
567 std::string file = opt.substr(split + 1);
568 zip_reader r(file);
569 for(auto j : r) {
570 std::string fname = j;
571 if(fname.length() < 6 || fname.substr(0, 5) != "sram.")
572 continue;
573 std::istream& x = r[fname];
574 try {
575 std::vector<char> out;
576 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
577 boost::iostreams::copy(x, rd);
578 delete &x;
579 ret[fname.substr(5, split - 5)] = out;
580 } catch(...) {
581 delete &x;
582 throw;
585 continue;
587 if(opt.length() < 8 || opt.substr(0, 7) != "--sram-")
588 continue;
589 size_t split = opt.find_first_of("=");
590 if(split > opt.length() - 1)
591 throw std::runtime_error("Bad SRAM option '" + opt + "'");
592 std::string kind = opt.substr(7, split - 7);
593 std::string file = opt.substr(split + 1);
594 if(kind == "")
595 throw std::runtime_error("Bad SRAM option '" + opt + "'");
596 try {
597 ret[kind] = read_file_relative(file, "");
598 } catch(std::bad_alloc& e) {
599 throw;
600 } catch(std::runtime_error& e) {
601 throw std::runtime_error("Can't load SRAM '" + kind + "': " + e.what());
604 return ret;
607 std::vector<char> save_core_state() throw(std::bad_alloc)
609 std::vector<char> ret;
610 serializer s = SNES::system.serialize();
611 ret.resize(s.size());
612 memcpy(&ret[0], s.data(), s.size());
613 size_t offset = ret.size();
614 unsigned char tmp[32];
615 sha256::hash(tmp, ret);
616 ret.resize(offset + 32);
617 memcpy(&ret[offset], tmp, 32);
618 return ret;
621 void load_core_state(const std::vector<char>& buf) throw(std::runtime_error)
623 if(buf.size() < 32)
624 throw std::runtime_error("Savestate corrupt");
625 unsigned char tmp[32];
626 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
627 if(memcmp(tmp, &buf[buf.size() - 32], 32))
628 throw std::runtime_error("Savestate corrupt");
629 serializer s(reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
630 if(!SNES::system.unserialize(s))
631 throw std::runtime_error("SNES core rejected savestate");
634 std::string name_subrom(enum rom_type major, unsigned romnumber) throw(std::bad_alloc)
636 if(romnumber == 0)
637 return "ROM";
638 else if(romnumber == 1)
639 return "ROM XML";
640 else if(major == ROMTYPE_BSX && romnumber == 2)
641 return "BSX ROM";
642 else if(major == ROMTYPE_BSX && romnumber == 3)
643 return "BSX XML";
644 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 2)
645 return "BSX ROM";
646 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 3)
647 return "BSX XML";
648 else if(major == ROMTYPE_SGB && romnumber == 2)
649 return "DMG ROM";
650 else if(major == ROMTYPE_SGB && romnumber == 3)
651 return "DMG XML";
652 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 2)
653 return "SLOT A ROM";
654 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 3)
655 return "SLOT A XML";
656 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 4)
657 return "SLOT B ROM";
658 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 5)
659 return "SLOT B XML";
660 else if(romnumber % 2)
661 return "UNKNOWN XML";
662 else
663 return "UNKNOWN ROM";
667 int recognize_commandline_rom(enum rom_type major, const std::string& romname) throw(std::bad_alloc)
669 if(romname == romtypes_to_recognize[0])
670 return 0;
671 else if(romname == romtypes_to_recognize[6])
672 return 1;
673 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[1])
674 return 2;
675 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[7])
676 return 3;
677 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[2])
678 return 2;
679 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[8])
680 return 3;
681 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[3])
682 return 2;
683 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[9])
684 return 3;
685 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[4])
686 return 2;
687 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[10])
688 return 3;
689 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[5])
690 return 4;
691 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[11])
692 return 5;
693 else
694 return -1;
697 rom_type recognize_platform(unsigned long flags) throw(std::bad_alloc, std::runtime_error)
699 if((flags & 07700) >> 6 & ~(flags & 077))
700 throw std::runtime_error("SubROM XML specified without corresponding subROM");
701 if((flags & 1) == 0)
702 throw std::runtime_error("No SNES main cartridge ROM specified");
703 if((flags & 077) == 1)
704 return ROMTYPE_SNES;
705 if((flags & 077) == 3)
706 return ROMTYPE_BSX;
707 if((flags & 077) == 5)
708 return ROMTYPE_BSXSLOTTED;
709 if((flags & 077) == 9)
710 return ROMTYPE_SGB;
711 if((flags & 060) != 0 && (flags & 017) == 1)
712 return ROMTYPE_SUFAMITURBO;
713 throw std::runtime_error("Not valid combination of rom/bsx/bsxslotted/dmg/slot-a/slot-b");