Port the generic control stuff from wxwidgets work
[lsnes.git] / generic / rom.cpp
blob3fa02d38b5bb48526576a09fe8e55ca1d4b85a0e
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
3 using SNES::config;
4 using SNES::System;
5 using SNES::Cartridge;
6 using SNES::Interface;
7 using SNES::cartridge;
9 #include <boost/iostreams/categories.hpp>
10 #include <boost/iostreams/copy.hpp>
11 #include <boost/iostreams/stream.hpp>
12 #include <boost/iostreams/stream_buffer.hpp>
13 #include <boost/iostreams/filter/symmetric.hpp>
14 #include <boost/iostreams/filter/zlib.hpp>
15 #include <boost/iostreams/filtering_stream.hpp>
16 #include <boost/iostreams/device/back_inserter.hpp>
17 #include "rom.hpp"
18 #include "command.hpp"
19 #include "framerate.hpp"
20 #include "window.hpp"
21 #include "avsnoop.hpp"
22 #include "zip.hpp"
23 #include "misc.hpp"
24 #include "memorymanip.hpp"
25 #include <stdexcept>
26 #include <sstream>
27 #include <iomanip>
28 #include <cstdint>
29 #include <set>
30 typedef uint8_t uint8;
31 typedef uint16_t uint16;
32 typedef uint32_t uint32;
33 typedef int8_t int8;
34 typedef int16_t int16;
35 typedef int32_t int32;
36 #include <nall/platform.hpp>
37 #include <nall/endian.hpp>
38 #include <nall/varint.hpp>
39 #include <nall/bit.hpp>
40 #include <nall/serializer.hpp>
41 #include <nall/property.hpp>
42 using namespace nall;
43 #include <ui-libsnes/libsnes.hpp>
45 //Some anti-typo defs.
46 #define SNES_TYPE "snes"
47 #define SNES_PAL "snes_pal"
48 #define SNES_NTSC "snes_ntsc"
49 #define BSX "bsx"
50 #define BSXSLOTTED "bsxslotted"
51 #define SUFAMITURBO "sufamiturbo"
52 #define SGB_TYPE "SGB"
53 #define SGB_PAL "sgb_pal"
54 #define SGB_NTSC "sgb_ntsc"
56 void strip_CR(std::string& x);
58 std::string gtype::tostring(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
60 switch(rtype) {
61 case ROMTYPE_SNES:
62 switch(region) {
63 case REGION_AUTO: return "snes";
64 case REGION_NTSC: return "snes_ntsc";
65 case REGION_PAL: return "snes_pal";
67 case ROMTYPE_SGB:
68 switch(region) {
69 case REGION_AUTO: return "sgb";
70 case REGION_NTSC: return "sgb_ntsc";
71 case REGION_PAL: return "sgb_pal";
73 case ROMTYPE_BSX: return "bsx";
74 case ROMTYPE_BSXSLOTTED: return "bsxslotted";
75 case ROMTYPE_SUFAMITURBO: return "sufamiturbo";
76 default: throw std::runtime_error("tostring: ROMTYPE_NONE");
80 std::string gtype::tostring(gametype_t gametype) throw(std::bad_alloc, std::runtime_error)
82 switch(gametype) {
83 case GT_SNES_NTSC: return "snes_ntsc";
84 case GT_SNES_PAL: return "snes_pal";
85 case GT_SGB_NTSC: return "sgb_ntsc";
86 case GT_SGB_PAL: return "sgb_pal";
87 case GT_BSX: return "bsx";
88 case GT_BSX_SLOTTED: return "bsxslotted";
89 case GT_SUFAMITURBO: return "sufamiturbo";
90 default: throw std::runtime_error("tostring: GT_INVALID");
94 gametype_t gtype::togametype(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
96 switch(rtype) {
97 case ROMTYPE_SNES:
98 switch(region) {
99 case REGION_AUTO: return GT_SGB_NTSC;
100 case REGION_NTSC: return GT_SNES_NTSC;
101 case REGION_PAL: return GT_SNES_PAL;
103 case ROMTYPE_SGB:
104 switch(region) {
105 case REGION_AUTO: return GT_SGB_NTSC;
106 case REGION_NTSC: return GT_SGB_NTSC;
107 case REGION_PAL: return GT_SGB_PAL;
109 case ROMTYPE_BSX: return GT_BSX;
110 case ROMTYPE_BSXSLOTTED: return GT_BSX_SLOTTED;
111 case ROMTYPE_SUFAMITURBO: return GT_SUFAMITURBO;
112 default: throw std::runtime_error("togametype: ROMTYPE_NONE");
116 gametype_t gtype::togametype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
118 if(gametype == "snes_ntsc")
119 return GT_SNES_NTSC;
120 if(gametype == "snes_pal")
121 return GT_SNES_PAL;
122 if(gametype == "sgb_ntsc")
123 return GT_SGB_NTSC;
124 if(gametype == "sgb_pal")
125 return GT_SGB_PAL;
126 if(gametype == "bsx")
127 return GT_BSX;
128 if(gametype == "bsxslotted")
129 return GT_BSX_SLOTTED;
130 if(gametype == "sufamiturbo")
131 return GT_SUFAMITURBO;
132 throw std::runtime_error("Unknown game type '" + gametype + "'");
135 rom_type gtype::toromtype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
137 if(gametype == "snes_ntsc")
138 return ROMTYPE_SNES;
139 if(gametype == "snes_pal")
140 return ROMTYPE_SNES;
141 if(gametype == "snes")
142 return ROMTYPE_SNES;
143 if(gametype == "sgb_ntsc")
144 return ROMTYPE_SGB;
145 if(gametype == "sgb_pal")
146 return ROMTYPE_SGB;
147 if(gametype == "sgb")
148 return ROMTYPE_SGB;
149 if(gametype == "bsx")
150 return ROMTYPE_BSX;
151 if(gametype == "bsxslotted")
152 return ROMTYPE_BSXSLOTTED;
153 if(gametype == "sufamiturbo")
154 return ROMTYPE_SUFAMITURBO;
155 throw std::runtime_error("Unknown game type '" + gametype + "'");
158 rom_type gtype::toromtype(gametype_t gametype) throw()
160 switch(gametype) {
161 case GT_SNES_NTSC: return ROMTYPE_SNES;
162 case GT_SNES_PAL: return ROMTYPE_SNES;
163 case GT_SGB_NTSC: return ROMTYPE_SGB;
164 case GT_SGB_PAL: return ROMTYPE_SGB;
165 case GT_BSX: return ROMTYPE_BSX;
166 case GT_BSX_SLOTTED: return ROMTYPE_BSXSLOTTED;
167 case GT_SUFAMITURBO: return ROMTYPE_SUFAMITURBO;
168 case GT_INVALID: throw std::runtime_error("toromtype: GT_INVALID");
172 rom_region gtype::toromregion(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
174 if(gametype == "snes_ntsc")
175 return REGION_NTSC;
176 if(gametype == "snes_pal")
177 return REGION_PAL;
178 if(gametype == "snes")
179 return REGION_AUTO;
180 if(gametype == "sgb_ntsc")
181 return REGION_NTSC;
182 if(gametype == "sgb_pal")
183 return REGION_PAL;
184 if(gametype == "sgb")
185 return REGION_AUTO;
186 if(gametype == "bsx")
187 return REGION_NTSC;
188 if(gametype == "bsxslotted")
189 return REGION_NTSC;
190 if(gametype == "sufamiturbo")
191 return REGION_NTSC;
192 throw std::runtime_error("Unknown game type '" + gametype + "'");
195 rom_region gtype::toromregion(gametype_t gametype) throw()
197 switch(gametype) {
198 case GT_SNES_NTSC: return REGION_NTSC;
199 case GT_SNES_PAL: return REGION_PAL;
200 case GT_SGB_NTSC: return REGION_NTSC;
201 case GT_SGB_PAL: return REGION_PAL;
202 case GT_BSX: return REGION_NTSC;
203 case GT_BSX_SLOTTED: return REGION_NTSC;
204 case GT_SUFAMITURBO: return REGION_NTSC;
205 case GT_INVALID: throw std::runtime_error("toromregion: GT_INVALID");
210 namespace
212 bool option_set(const std::vector<std::string>& cmdline, const std::string& option)
214 for(auto i : cmdline)
215 if(i == option)
216 return true;
217 return false;
220 const char* romtypes_to_recognize[] = {
221 "rom", "bsx", "bsxslotted", "dmg", "slot-a", "slot-b",
222 "rom-xml", "bsx-xml", "bsxslotted-xml", "dmg-xml", "slot-a-xml", "slot-b-xml"
225 enum rom_type current_rom_type = ROMTYPE_NONE;
226 enum rom_region current_region = REGION_NTSC;
228 uint64_t readval(const std::vector<char>& patch, size_t offset, size_t vsize) throw(std::runtime_error)
230 if(offset >= patch.size() || offset + vsize > patch.size())
231 throw std::runtime_error("IPS file corrupt");
232 uint64_t val = 0;
233 for(size_t i = 0; i < vsize; i++)
234 val = (val << 8) | static_cast<uint8_t>(patch[offset + i]);
235 return val;
238 std::string findoption(const std::vector<std::string>& cmdline, const std::string& option)
240 std::string value;
241 for(auto i : cmdline) {
242 std::string arg = i;
243 if(arg.length() < 3 + option.length())
244 continue;
245 if(arg[0] != '-' || arg[1] != '-' || arg.substr(2, option.length()) != option ||
246 arg[2 + option.length()] != '=')
247 continue;
248 if(value == "")
249 value = arg.substr(3 + option.length());
250 else
251 std::cerr << "WARNING: Ignored duplicate option for '" << option << "'." << std::endl;
252 if(value == "")
253 throw std::runtime_error("Empty value for '" + option + "' is not allowed");
255 return value;
260 loaded_slot::loaded_slot() throw(std::bad_alloc)
262 valid = false;
265 loaded_slot::loaded_slot(const std::string& filename, const std::string& base, bool xml_flag) throw(std::bad_alloc,
266 std::runtime_error)
268 xml = xml_flag;
269 if(filename == "") {
270 valid = false;
271 return;
273 valid = true;
274 data = read_file_relative(filename, base);
275 sha256 = sha256::hash(data);
276 if(xml) {
277 size_t osize = data.size();
278 data.resize(osize + 1);
279 data[osize] = 0;
283 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
285 try {
286 std::vector<char> data2 = data;
287 bool warned_extend = false;
288 bool warned_negative = false;
289 size_t poffset = 0;
290 if(xml && valid)
291 data2.resize(data2.size() - 1);
292 if(readval(patch, poffset, 5) != 0x5041544348)
293 throw std::runtime_error("Bad IPS file magic");
294 poffset += 5;
295 while(1) {
296 uint64_t addr = readval(patch, poffset, 3);
297 if(addr == 0x454F46)
298 break;
299 uint64_t len = readval(patch, poffset + 3, 2);
300 size_t readstride;
301 size_t roffset;
302 size_t opsize;
303 if(len) {
304 //Verbatim block.
305 readstride = 1;
306 roffset = poffset + 5;
307 opsize = 5 + len;
308 } else {
309 //RLE block. Read real size first.
310 len = readval(patch, poffset + 5, 2);
311 readstride = 0;
312 roffset = poffset + 7;
313 opsize = 8;
315 for(uint64_t i = 0; i < len; i++) {
316 int64_t baddr = addr + i + offset;
317 if(baddr < 0) {
318 if(!warned_negative)
319 std::cerr << "WARNING: IPS patch tries to modify negative offset. "
320 << "Bad patch or offset?" << std::endl;
321 warned_negative = true;
322 continue;
323 } else if(baddr >= static_cast<int64_t>(data2.size())) {
324 if(!warned_extend)
325 std::cerr << "WARNING: IPS patch tries to extend the ROM. "
326 << "Bad patch or offset? " << std::endl;
327 warned_extend = true;
328 size_t oldsize = data2.size();
329 data2.resize(baddr + 1);
330 for(size_t j = oldsize; j <= static_cast<uint64_t>(baddr); j++)
331 data2[j] = 0;
333 size_t srcoff = roffset + readstride * i;
334 if(srcoff >= patch.size())
335 throw std::runtime_error("Corrupt IPS patch");
336 data2[baddr] = static_cast<uint8_t>(patch[srcoff]);
338 poffset += opsize;
340 //Mark the slot as valid and update hash.
341 valid = true;
342 std::string new_sha256 = sha256::hash(data2);
343 if(xml) {
344 size_t osize = data2.size();
345 data2.resize(osize + 1);
346 data2[osize] = 0;
348 data = data2;
349 sha256 = new_sha256;
350 } catch(...) {
351 throw;
355 rom_files::rom_files() throw()
357 rtype = ROMTYPE_NONE;
358 region = REGION_AUTO;
362 rom_files::rom_files(const std::vector<std::string>& cmdline) throw(std::bad_alloc, std::runtime_error)
364 rom = rom_xml = slota = slota_xml = slotb = slotb_xml = "";
365 std::string arr[sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0])];
366 unsigned long flags = 0;
367 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
368 arr[i] = findoption(cmdline, romtypes_to_recognize[i]);
369 if(arr[i] != "")
370 flags |= (1L << i);
372 rtype = recognize_platform(flags);
373 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
374 if(arr[i] != "")
375 switch(recognize_commandline_rom(rtype, romtypes_to_recognize[i])) {
376 case 0: rom = arr[i]; break;
377 case 1: rom_xml = arr[i]; break;
378 case 2: slota = arr[i]; break;
379 case 3: slota_xml = arr[i]; break;
380 case 4: slotb = arr[i]; break;
381 case 5: slotb_xml = arr[i]; break;
384 region = (rtype == ROMTYPE_SGB || rtype == ROMTYPE_SNES) ? REGION_AUTO : REGION_NTSC;
385 if(option_set(cmdline, "--ntsc"))
386 region = REGION_NTSC;
387 else if(option_set(cmdline, "--pal"))
388 region = REGION_PAL;
390 base_file = "";
393 void rom_files::resolve_relative() throw(std::bad_alloc, std::runtime_error)
395 rom = resolve_file_relative(rom, base_file);
396 rom_xml = resolve_file_relative(rom_xml, base_file);
397 slota = resolve_file_relative(slota, base_file);
398 slota_xml = resolve_file_relative(slota_xml, base_file);
399 slotb = resolve_file_relative(slotb, base_file);
400 slotb_xml = resolve_file_relative(slotb_xml, base_file);
401 base_file = "";
405 std::pair<enum rom_type, enum rom_region> get_current_rom_info() throw()
407 return std::make_pair(current_rom_type, current_region);
410 loaded_rom::loaded_rom() throw()
412 rtype = ROMTYPE_NONE;
413 region = orig_region = REGION_AUTO;
416 loaded_rom::loaded_rom(const rom_files& files) throw(std::bad_alloc, std::runtime_error)
418 std::string _slota = files.slota;
419 std::string _slota_xml = files.slota_xml;
420 std::string _slotb = files.slotb;
421 std::string _slotb_xml = files.slotb_xml;
422 if(files.rtype == ROMTYPE_NONE) {
423 rtype = ROMTYPE_NONE;
424 region = orig_region = files.region;
425 return;
427 if((_slota != "" || _slota_xml != "") && files.rtype == ROMTYPE_SNES) {
428 messages << "WARNING: SNES takes only 1 ROM image" << std::endl;
429 _slota = "";
430 _slota_xml = "";
432 if((_slotb != "" || _slotb_xml != "") && files.rtype != ROMTYPE_SUFAMITURBO) {
433 messages << "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl;
434 _slotb = "";
435 _slotb_xml = "";
437 if(files.rom_xml != "" && files.rom == "")
438 messages << "WARNING: " << name_subrom(files.rtype, 0) << " specified without corresponding "
439 << name_subrom(files.rtype, 1) << std::endl;
440 if(_slota_xml != "" && _slota == "")
441 messages << "WARNING: " << name_subrom(files.rtype, 2) << " specified without corresponding "
442 << name_subrom(files.rtype, 3) << std::endl;
443 if(_slotb_xml != "" && _slotb == "")
444 messages << "WARNING: " << name_subrom(files.rtype, 4) << " specified without corresponding "
445 << name_subrom(files.rtype, 5) << std::endl;
447 rtype = files.rtype;
448 rom = loaded_slot(files.rom, files.base_file);
449 rom_xml = loaded_slot(files.rom_xml, files.base_file, true);
450 slota = loaded_slot(_slota, files.base_file);
451 slota_xml = loaded_slot(_slota_xml, files.base_file, true);
452 slotb = loaded_slot(_slotb, files.base_file);
453 slotb_xml = loaded_slot(_slotb_xml, files.base_file, true);
454 orig_region = region = files.region;
457 void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
459 current_rom_type = ROMTYPE_NONE;
460 if(region == REGION_AUTO && orig_region != REGION_AUTO)
461 region = orig_region;
462 if(region != orig_region && orig_region != REGION_AUTO)
463 throw std::runtime_error("Trying to force incompatible region");
464 if(rtype == ROMTYPE_NONE)
465 throw std::runtime_error("Can't insert cartridge of type NONE!");
466 switch(region) {
467 case REGION_AUTO:
468 config.region = System::Region::Autodetect;
469 break;
470 case REGION_NTSC:
471 config.region = System::Region::NTSC;
472 break;
473 case REGION_PAL:
474 config.region = System::Region::PAL;
475 break;
476 default:
477 throw std::runtime_error("Trying to force unknown region");
479 switch(rtype) {
480 case ROMTYPE_SNES:
481 if(!snes_load_cartridge_normal(rom_xml, rom, rom))
482 throw std::runtime_error("Can't load cartridge ROM");
483 break;
484 case ROMTYPE_BSX:
485 if(region == REGION_PAL)
486 throw std::runtime_error("BSX can't be PAL");
487 if(!snes_load_cartridge_bsx(rom_xml, rom, rom, slota_xml, slota, slota))
488 throw std::runtime_error("Can't load cartridge ROM");
489 break;
490 case ROMTYPE_BSXSLOTTED:
491 if(region == REGION_PAL)
492 throw std::runtime_error("Slotted BSX can't be PAL");
493 if(!snes_load_cartridge_bsx_slotted(rom_xml, rom, rom, slota_xml, slota, slota))
494 throw std::runtime_error("Can't load cartridge ROM");
495 break;
496 case ROMTYPE_SGB:
497 if(!snes_load_cartridge_super_game_boy(rom_xml, rom, rom, slota_xml, slota, slota))
498 throw std::runtime_error("Can't load cartridge ROM");
499 break;
500 case ROMTYPE_SUFAMITURBO:
501 if(region == REGION_PAL)
502 throw std::runtime_error("Sufami Turbo can't be PAL");
503 if(!snes_load_cartridge_sufami_turbo(rom_xml, rom, rom, slota_xml, slota, slota, slotb_xml, slotb,
504 slotb))
505 throw std::runtime_error("Can't load cartridge ROM");
506 break;
507 default:
508 throw std::runtime_error("Unknown cartridge type");
510 if(region == REGION_AUTO)
511 region = snes_get_region() ? REGION_PAL : REGION_NTSC;
512 snes_power();
513 if(region == REGION_PAL)
514 set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_PAL_FRAME);
515 else
516 set_nominal_framerate(SNES::system.cpu_frequency() / DURATION_NTSC_FRAME);
517 window::set_sound_rate(SNES::system.apu_frequency(), 768);
518 av_snooper::_sound_rate(SNES::system.apu_frequency(), 768);
519 current_rom_type = rtype;
520 current_region = region;
521 refresh_cart_mappings();
524 void loaded_rom::do_patch(const std::vector<std::string>& cmdline) throw(std::bad_alloc,
525 std::runtime_error)
527 int32_t offset = 0;
528 for(auto i : cmdline) {
529 std::string opt = i;
530 if(opt.length() >= 13 && opt.substr(0, 13) == "--ips-offset=") {
531 try {
532 offset = parse_value<int32_t>(opt.substr(13));
533 } catch(std::exception& e) {
534 throw std::runtime_error("Invalid IPS offset option '" + opt + "': " + e.what());
536 continue;
538 if(opt.length() < 6 || opt.substr(0, 6) != "--ips-")
539 continue;
540 size_t split = opt.find_first_of("=");
541 if(split > opt.length())
542 throw std::runtime_error("Invalid IPS patch argument '" + opt + "'");
543 std::string kind = opt.substr(6, split - 6);
544 std::string filename = opt.substr(split + 1);
545 messages << "Patching " << kind << " using '" << filename << "'" << std::endl;
546 std::vector<char> ips;
547 try {
548 ips = read_file_relative(filename, "");
549 } catch(std::bad_alloc& e) {
550 OOM_panic();
551 } catch(std::exception& e) {
552 throw std::runtime_error("Can't read IPS '" + filename + "': " + e.what());
554 try {
555 switch(recognize_commandline_rom(rtype, kind)) {
556 case 0: rom.patch(ips, offset); break;
557 case 1: rom_xml.patch(ips, offset); break;
558 case 2: slota.patch(ips, offset); break;
559 case 3: slota_xml.patch(ips, offset); break;
560 case 4: slotb.patch(ips, offset); break;
561 case 5: slotb_xml.patch(ips, offset); break;
562 default:
563 throw std::runtime_error("Invalid subROM '" + kind + "' to patch");
565 } catch(std::bad_alloc& e) {
566 OOM_panic();
567 } catch(std::exception& e) {
568 throw std::runtime_error("Can't Patch with IPS '" + filename + "': " + e.what());
573 namespace
575 std::string sram_name(const nall::string& _id, Cartridge::Slot slotname)
577 std::string id(_id, _id.length());
578 if(slotname == Cartridge::Slot::SufamiTurboA)
579 return "slota." + id.substr(1);
580 if(slotname == Cartridge::Slot::SufamiTurboB)
581 return "slotb." + id.substr(1);
582 return id.substr(1);
586 std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc)
588 std::map<std::string, std::vector<char>> out;
589 for(unsigned i = 0; i < cartridge.nvram.size(); i++) {
590 Cartridge::NonVolatileRAM& r = cartridge.nvram[i];
591 std::string savename = sram_name(r.id, r.slot);
592 std::vector<char> x;
593 x.resize(r.size);
594 memcpy(&x[0], r.data, r.size);
595 out[savename] = x;
597 return out;
600 void load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc)
602 std::set<std::string> used;
603 if(sram.empty())
604 return;
605 for(unsigned i = 0; i < cartridge.nvram.size(); i++) {
606 Cartridge::NonVolatileRAM& r = cartridge.nvram[i];
607 std::string savename = sram_name(r.id, r.slot);
608 if(sram.count(savename)) {
609 std::vector<char>& x = sram[savename];
610 if(r.size != x.size())
611 messages << "WARNING: SRAM '" << savename << "': Loaded " << x.size()
612 << " bytes, but the SRAM is " << r.size << "." << std::endl;
613 memcpy(r.data, &x[0], (r.size < x.size()) ? r.size : x.size());
614 used.insert(savename);
615 } else
616 messages << "WARNING: SRAM '" << savename << ": No data." << std::endl;
618 for(auto i : sram)
619 if(!used.count(i.first))
620 messages << "WARNING: SRAM '" << i.first << ": Not found on cartridge." << std::endl;
623 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
624 throw(std::bad_alloc, std::runtime_error)
626 std::map<std::string, std::vector<char>> ret;
627 for(auto i : cmdline) {
628 std::string opt = i;
629 if(opt.length() >= 11 && opt.substr(0, 11) == "--continue=") {
630 size_t split = opt.find_first_of("=");
631 if(split > opt.length() - 1)
632 throw std::runtime_error("Bad SRAM option '" + opt + "'");
633 std::string file = opt.substr(split + 1);
634 zip_reader r(file);
635 for(auto j : r) {
636 std::string fname = j;
637 if(fname.length() < 6 || fname.substr(0, 5) != "sram.")
638 continue;
639 std::istream& x = r[fname];
640 try {
641 std::vector<char> out;
642 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
643 boost::iostreams::copy(x, rd);
644 delete &x;
645 ret[fname.substr(5, split - 5)] = out;
646 } catch(...) {
647 delete &x;
648 throw;
651 continue;
653 if(opt.length() < 8 || opt.substr(0, 7) != "--sram-")
654 continue;
655 size_t split = opt.find_first_of("=");
656 if(split > opt.length() - 1)
657 throw std::runtime_error("Bad SRAM option '" + opt + "'");
658 std::string kind = opt.substr(7, split - 7);
659 std::string file = opt.substr(split + 1);
660 if(kind == "")
661 throw std::runtime_error("Bad SRAM option '" + opt + "'");
662 try {
663 ret[kind] = read_file_relative(file, "");
664 } catch(std::bad_alloc& e) {
665 throw;
666 } catch(std::runtime_error& e) {
667 throw std::runtime_error("Can't load SRAM '" + kind + "': " + e.what());
670 return ret;
673 std::vector<char> save_core_state() throw(std::bad_alloc)
675 SNES::system.runtosave();
676 std::vector<char> ret;
677 serializer s = SNES::system.serialize();
678 ret.resize(s.size());
679 memcpy(&ret[0], s.data(), s.size());
680 size_t offset = ret.size();
681 unsigned char tmp[32];
682 sha256::hash(tmp, ret);
683 ret.resize(offset + 32);
684 memcpy(&ret[offset], tmp, 32);
685 return ret;
688 void load_core_state(const std::vector<char>& buf) throw(std::runtime_error)
690 if(buf.size() < 32)
691 throw std::runtime_error("Savestate corrupt");
692 unsigned char tmp[32];
693 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
694 if(memcmp(tmp, &buf[buf.size() - 32], 32))
695 throw std::runtime_error("Savestate corrupt");
696 serializer s(reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
697 if(!SNES::system.unserialize(s))
698 throw std::runtime_error("SNES core rejected savestate");
701 namespace
703 struct index_entry
705 std::string hash;
706 std::string relpath;
707 std::string from;
709 std::list<index_entry> rom_index;
711 void replace_index(std::list<index_entry> new_index, const std::string& source)
713 std::list<index_entry> tmp_index;
714 for(auto i : rom_index) {
715 if(i.from != source)
716 tmp_index.push_back(i);
718 for(auto i : new_index) {
719 tmp_index.push_back(i);
721 rom_index = new_index;
725 void load_index_file(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
727 std::istream& s = open_file_relative(filename, "");
729 try {
730 std::list<index_entry> partial_index;
731 std::string line;
732 while(std::getline(s, line)) {
733 index_entry e;
734 if(line == "")
735 continue;
736 tokensplitter t(line);
737 e.hash = static_cast<std::string>(t);
738 e.relpath = t.tail();
739 e.from = filename;
740 if(e.hash.length() != 64 || e.relpath == "")
741 throw std::runtime_error("Bad index file");
742 partial_index.push_back(e);
744 replace_index(partial_index, filename);
745 } catch(...) {
746 delete &s;
747 throw;
749 delete &s;
752 std::string lookup_file_by_sha256(const std::string& hash) throw(std::bad_alloc, std::runtime_error)
754 if(hash == "")
755 return "";
756 for(auto i : rom_index) {
757 if(i.hash != hash)
758 continue;
759 try {
760 std::istream& o = open_file_relative(i.relpath, i.from);
761 delete &o;
762 return resolve_file_relative(i.relpath, i.from);
763 } catch(...) {
764 continue;
767 throw std::runtime_error("No file with hash '" + hash + "' found in known indices");
770 std::string name_subrom(enum rom_type major, unsigned romnumber) throw(std::bad_alloc)
772 if(romnumber == 0)
773 return "ROM";
774 else if(romnumber == 1)
775 return "ROM XML";
776 else if(major == ROMTYPE_BSX && romnumber == 2)
777 return "BSX ROM";
778 else if(major == ROMTYPE_BSX && romnumber == 3)
779 return "BSX XML";
780 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 2)
781 return "BSX ROM";
782 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 3)
783 return "BSX XML";
784 else if(major == ROMTYPE_SGB && romnumber == 2)
785 return "DMG ROM";
786 else if(major == ROMTYPE_SGB && romnumber == 3)
787 return "DMG XML";
788 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 2)
789 return "SLOT A ROM";
790 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 3)
791 return "SLOT A XML";
792 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 4)
793 return "SLOT B ROM";
794 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 5)
795 return "SLOT B XML";
796 else if(romnumber % 2)
797 return "UNKNOWN XML";
798 else
799 return "UNKNOWN ROM";
803 int recognize_commandline_rom(enum rom_type major, const std::string& romname) throw(std::bad_alloc)
805 if(romname == romtypes_to_recognize[0])
806 return 0;
807 else if(romname == romtypes_to_recognize[6])
808 return 1;
809 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[1])
810 return 2;
811 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[7])
812 return 3;
813 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[2])
814 return 2;
815 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[8])
816 return 3;
817 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[3])
818 return 2;
819 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[9])
820 return 3;
821 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[4])
822 return 2;
823 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[10])
824 return 3;
825 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[5])
826 return 4;
827 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[11])
828 return 5;
829 else
830 return -1;
833 rom_type recognize_platform(unsigned long flags) throw(std::bad_alloc, std::runtime_error)
835 if((flags & 07700) >> 6 & ~(flags & 077))
836 throw std::runtime_error("SubROM XML specified without corresponding subROM");
837 if((flags & 1) == 0)
838 throw std::runtime_error("No SNES main cartridge ROM specified");
839 if((flags & 077) == 1)
840 return ROMTYPE_SNES;
841 if((flags & 077) == 3)
842 return ROMTYPE_BSX;
843 if((flags & 077) == 5)
844 return ROMTYPE_BSXSLOTTED;
845 if((flags & 077) == 9)
846 return ROMTYPE_SGB;
847 if((flags & 060) != 0 && (flags & 017) == 1)
848 return ROMTYPE_SUFAMITURBO;
849 throw std::runtime_error("Not valid combination of rom/bsx/bsxslotted/dmg/slot-a/slot-b");