screen::copy_from fix bug if vscale > 1 and originx > 0
[lsnes.git] / rom.cpp
blobe4ad7fe903863e4b6eae2c2ac67aefa72673d633
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 "fieldsplit.hpp"
20 #include "zip.hpp"
21 #include "misc.hpp"
22 #include "memorymanip.hpp"
23 #include <stdexcept>
24 #include <sstream>
25 #include <iomanip>
26 #include <cstdint>
27 #include <set>
28 typedef uint8_t uint8;
29 typedef uint16_t uint16;
30 typedef uint32_t uint32;
31 typedef int8_t int8;
32 typedef int16_t int16;
33 typedef int32_t int32;
34 #include <nall/platform.hpp>
35 #include <nall/endian.hpp>
36 #include <nall/varint.hpp>
37 #include <nall/bit.hpp>
38 #include <nall/serializer.hpp>
39 #include <nall/property.hpp>
40 using namespace nall;
41 #include <ui-libsnes/libsnes.hpp>
43 //Some anti-typo defs.
44 #define SNES_TYPE "snes"
45 #define SNES_PAL "snes_pal"
46 #define SNES_NTSC "snes_ntsc"
47 #define BSX "bsx"
48 #define BSXSLOTTED "bsxslotted"
49 #define SUFAMITURBO "sufamiturbo"
50 #define SGB_TYPE "SGB"
51 #define SGB_PAL "sgb_pal"
52 #define SGB_NTSC "sgb_ntsc"
54 void strip_CR(std::string& x);
56 std::string gtype::tostring(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
58 switch(rtype) {
59 case ROMTYPE_SNES:
60 switch(region) {
61 case REGION_AUTO: return "snes";
62 case REGION_NTSC: return "snes_ntsc";
63 case REGION_PAL: return "snes_pal";
65 case ROMTYPE_SGB:
66 switch(region) {
67 case REGION_AUTO: return "sgb";
68 case REGION_NTSC: return "sgb_ntsc";
69 case REGION_PAL: return "sgb_pal";
71 case ROMTYPE_BSX: return "bsx";
72 case ROMTYPE_BSXSLOTTED: return "bsxslotted";
73 case ROMTYPE_SUFAMITURBO: return "sufamiturbo";
74 default: throw std::runtime_error("tostring: ROMTYPE_NONE");
78 std::string gtype::tostring(gametype_t gametype) throw(std::bad_alloc, std::runtime_error)
80 switch(gametype) {
81 case GT_SNES_NTSC: return "snes_ntsc";
82 case GT_SNES_PAL: return "snes_pal";
83 case GT_SGB_NTSC: return "sgb_ntsc";
84 case GT_SGB_PAL: return "sgb_pal";
85 case GT_BSX: return "bsx";
86 case GT_BSX_SLOTTED: return "bsxslotted";
87 case GT_SUFAMITURBO: return "sufamiturbo";
88 default: throw std::runtime_error("tostring: GT_INVALID");
92 gametype_t gtype::togametype(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error)
94 switch(rtype) {
95 case ROMTYPE_SNES:
96 switch(region) {
97 case REGION_AUTO: return GT_SGB_NTSC;
98 case REGION_NTSC: return GT_SNES_NTSC;
99 case REGION_PAL: return GT_SNES_PAL;
101 case ROMTYPE_SGB:
102 switch(region) {
103 case REGION_AUTO: return GT_SGB_NTSC;
104 case REGION_NTSC: return GT_SGB_NTSC;
105 case REGION_PAL: return GT_SGB_PAL;
107 case ROMTYPE_BSX: return GT_BSX;
108 case ROMTYPE_BSXSLOTTED: return GT_BSX_SLOTTED;
109 case ROMTYPE_SUFAMITURBO: return GT_SUFAMITURBO;
110 default: throw std::runtime_error("togametype: ROMTYPE_NONE");
114 gametype_t gtype::togametype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
116 if(gametype == "snes_ntsc")
117 return GT_SNES_NTSC;
118 if(gametype == "snes_pal")
119 return GT_SNES_PAL;
120 if(gametype == "sgb_ntsc")
121 return GT_SGB_NTSC;
122 if(gametype == "sgb_pal")
123 return GT_SGB_PAL;
124 if(gametype == "bsx")
125 return GT_BSX;
126 if(gametype == "bsxslotted")
127 return GT_BSX_SLOTTED;
128 if(gametype == "sufamiturbo")
129 return GT_SUFAMITURBO;
130 throw std::runtime_error("Unknown game type '" + gametype + "'");
133 rom_type gtype::toromtype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
135 if(gametype == "snes_ntsc")
136 return ROMTYPE_SNES;
137 if(gametype == "snes_pal")
138 return ROMTYPE_SNES;
139 if(gametype == "snes")
140 return ROMTYPE_SNES;
141 if(gametype == "sgb_ntsc")
142 return ROMTYPE_SGB;
143 if(gametype == "sgb_pal")
144 return ROMTYPE_SGB;
145 if(gametype == "sgb")
146 return ROMTYPE_SGB;
147 if(gametype == "bsx")
148 return ROMTYPE_BSX;
149 if(gametype == "bsxslotted")
150 return ROMTYPE_BSXSLOTTED;
151 if(gametype == "sufamiturbo")
152 return ROMTYPE_SUFAMITURBO;
153 throw std::runtime_error("Unknown game type '" + gametype + "'");
156 rom_type gtype::toromtype(gametype_t gametype) throw()
158 switch(gametype) {
159 case GT_SNES_NTSC: return ROMTYPE_SNES;
160 case GT_SNES_PAL: return ROMTYPE_SNES;
161 case GT_SGB_NTSC: return ROMTYPE_SGB;
162 case GT_SGB_PAL: return ROMTYPE_SGB;
163 case GT_BSX: return ROMTYPE_BSX;
164 case GT_BSX_SLOTTED: return ROMTYPE_BSXSLOTTED;
165 case GT_SUFAMITURBO: return ROMTYPE_SUFAMITURBO;
166 case GT_INVALID: throw std::runtime_error("toromtype: GT_INVALID");
170 rom_region gtype::toromregion(const std::string& gametype) throw(std::bad_alloc, std::runtime_error)
172 if(gametype == "snes_ntsc")
173 return REGION_NTSC;
174 if(gametype == "snes_pal")
175 return REGION_PAL;
176 if(gametype == "snes")
177 return REGION_AUTO;
178 if(gametype == "sgb_ntsc")
179 return REGION_NTSC;
180 if(gametype == "sgb_pal")
181 return REGION_PAL;
182 if(gametype == "sgb")
183 return REGION_AUTO;
184 if(gametype == "bsx")
185 return REGION_NTSC;
186 if(gametype == "bsxslotted")
187 return REGION_NTSC;
188 if(gametype == "sufamiturbo")
189 return REGION_NTSC;
190 throw std::runtime_error("Unknown game type '" + gametype + "'");
193 rom_region gtype::toromregion(gametype_t gametype) throw()
195 switch(gametype) {
196 case GT_SNES_NTSC: return REGION_NTSC;
197 case GT_SNES_PAL: return REGION_PAL;
198 case GT_SGB_NTSC: return REGION_NTSC;
199 case GT_SGB_PAL: return REGION_PAL;
200 case GT_BSX: return REGION_NTSC;
201 case GT_BSX_SLOTTED: return REGION_NTSC;
202 case GT_SUFAMITURBO: return REGION_NTSC;
203 case GT_INVALID: throw std::runtime_error("toromregion: GT_INVALID");
208 namespace
210 bool option_set(const std::vector<std::string>& cmdline, const std::string& option)
212 for(auto i = cmdline.begin(); i != cmdline.end(); i++)
213 if(*i == option)
214 return true;
215 return false;
218 const char* romtypes_to_recognize[] = {
219 "rom", "bsx", "bsxslotted", "dmg", "slot-a", "slot-b",
220 "rom-xml", "bsx-xml", "bsxslotted-xml", "dmg-xml", "slot-a-xml", "slot-b-xml"
223 enum rom_type current_rom_type = ROMTYPE_NONE;
224 enum rom_region current_region = REGION_NTSC;
226 uint64_t readval(const std::vector<char>& patch, size_t offset, size_t vsize) throw(std::runtime_error)
228 if(offset >= patch.size() || offset + vsize > patch.size())
229 throw std::runtime_error("IPS file corrupt");
230 uint64_t val = 0;
231 for(size_t i = 0; i < vsize; i++)
232 val = (val << 8) | static_cast<uint8_t>(patch[offset + i]);
233 return val;
236 std::string findoption(const std::vector<std::string>& cmdline, const std::string& option)
238 std::string value;
239 for(auto i = cmdline.begin(); i != cmdline.end(); ++i) {
240 std::string arg = *i;
241 if(arg.length() < 3 + option.length())
242 continue;
243 if(arg[0] != '-' || arg[1] != '-' || arg.substr(2, option.length()) != option ||
244 arg[2 + option.length()] != '=')
245 continue;
246 if(value == "")
247 value = arg.substr(3 + option.length());
248 else
249 std::cerr << "WARNING: Ignored duplicate option for '" << option << "'." << std::endl;
250 if(value == "")
251 throw std::runtime_error("Empty value for '" + option + "' is not allowed");
253 return value;
258 loaded_slot::loaded_slot() throw(std::bad_alloc)
260 valid = false;
263 loaded_slot::loaded_slot(const std::string& filename, const std::string& base, bool xml_flag) throw(std::bad_alloc,
264 std::runtime_error)
266 xml = xml_flag;
267 if(filename == "") {
268 valid = false;
269 return;
271 valid = true;
272 data = read_file_relative(filename, base);
273 sha256 = sha256::hash(data);
274 if(xml) {
275 size_t osize = data.size();
276 data.resize(osize + 1);
277 data[osize] = 0;
281 void loaded_slot::patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error)
283 bool warned_extend = false;
284 bool warned_negative = false;
285 size_t poffset = 0;
286 if(xml && valid)
287 data.resize(data.size() - 1);
288 if(readval(patch, poffset, 5) != 0x5041544348)
289 throw std::runtime_error("Bad IPS file magic");
290 poffset += 5;
291 while(1) {
292 uint64_t addr = readval(patch, poffset, 3);
293 if(addr == 0x454F46)
294 break;
295 uint64_t len = readval(patch, poffset + 3, 2);
296 size_t readstride;
297 size_t roffset;
298 size_t opsize;
299 if(len) {
300 //Verbatim block.
301 readstride = 1;
302 roffset = poffset + 5;
303 opsize = 5 + len;
304 } else {
305 //RLE block. Read real size first.
306 len = readval(patch, poffset + 5, 2);
307 readstride = 0;
308 roffset = poffset + 7;
309 opsize = 8;
311 for(uint64_t i = 0; i < len; i++) {
312 int64_t baddr = addr + i + offset;
313 if(baddr < 0) {
314 if(!warned_negative)
315 std::cerr << "WARNING: IPS patch tries to modify negative offset. "
316 << "Bad patch or offset?" << std::endl;
317 warned_negative = true;
318 continue;
319 } else if(baddr >= static_cast<int64_t>(data.size())) {
320 if(!warned_extend)
321 std::cerr << "WARNING: IPS patch tries to extend the ROM. "
322 << "Bad patch or offset? " << std::endl;
323 warned_extend = true;
324 size_t oldsize = data.size();
325 data.resize(baddr + 1);
326 for(size_t j = oldsize; j <= static_cast<uint64_t>(baddr); j++)
327 data[j] = 0;
329 size_t srcoff = roffset + readstride * i;
330 if(srcoff >= patch.size())
331 throw std::runtime_error("Corrupt IPS patch");
332 data[baddr] = static_cast<uint8_t>(patch[srcoff]);
334 poffset += opsize;
336 //Mark the slot as valid and update hash.
337 valid = true;
338 sha256 = sha256::hash(data);
339 if(xml) {
340 size_t osize = data.size();
341 data.resize(osize + 1);
342 data[osize] = 0;
346 rom_files::rom_files() throw()
348 rtype = ROMTYPE_NONE;
349 region = REGION_AUTO;
353 rom_files::rom_files(const std::vector<std::string>& cmdline, window* win) throw(std::bad_alloc, std::runtime_error)
355 rom = rom_xml = slota = slota_xml = slotb = slotb_xml = "";
356 std::string arr[sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0])];
357 unsigned long flags = 0;
358 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
359 arr[i] = findoption(cmdline, romtypes_to_recognize[i]);
360 if(arr[i] != "")
361 flags |= (1L << i);
363 rtype = recognize_platform(flags);
364 for(size_t i = 0; i < sizeof(romtypes_to_recognize) / sizeof(romtypes_to_recognize[0]); i++) {
365 if(arr[i] != "")
366 switch(recognize_commandline_rom(rtype, romtypes_to_recognize[i])) {
367 case 0: rom = arr[i]; break;
368 case 1: rom_xml = arr[i]; break;
369 case 2: slota = arr[i]; break;
370 case 3: slota_xml = arr[i]; break;
371 case 4: slotb = arr[i]; break;
372 case 5: slotb_xml = arr[i]; break;
375 region = (rtype == ROMTYPE_SGB || rtype == ROMTYPE_SNES) ? REGION_AUTO : REGION_NTSC;
376 if(option_set(cmdline, "--ntsc"))
377 region = REGION_NTSC;
378 else if(option_set(cmdline, "--pal"))
379 region = REGION_PAL;
381 base_file = "";
384 void rom_files::resolve_relative() throw(std::bad_alloc, std::runtime_error)
386 rom = resolve_file_relative(rom, base_file);
387 rom_xml = resolve_file_relative(rom_xml, base_file);
388 slota = resolve_file_relative(slota, base_file);
389 slota_xml = resolve_file_relative(slota_xml, base_file);
390 slotb = resolve_file_relative(slotb, base_file);
391 slotb_xml = resolve_file_relative(slotb_xml, base_file);
392 base_file = "";
396 std::pair<enum rom_type, enum rom_region> get_current_rom_info() throw()
398 return std::make_pair(current_rom_type, current_region);
401 loaded_rom::loaded_rom() throw()
403 rtype = ROMTYPE_NONE;
404 region = orig_region = REGION_AUTO;
407 loaded_rom::loaded_rom(const rom_files& files, window* win) throw(std::bad_alloc, std::runtime_error)
409 std::string _slota = files.slota;
410 std::string _slota_xml = files.slota_xml;
411 std::string _slotb = files.slotb;
412 std::string _slotb_xml = files.slotb_xml;
413 if(files.rtype == ROMTYPE_NONE) {
414 rtype = ROMTYPE_NONE;
415 region = orig_region = files.region;
416 return;
418 if((_slota != "" || _slota_xml != "") && files.rtype == ROMTYPE_SNES) {
419 out(win) << "WARNING: SNES takes only 1 ROM image" << std::endl;
420 _slota = "";
421 _slota_xml = "";
423 if((_slotb != "" || _slotb_xml != "") && files.rtype != ROMTYPE_SUFAMITURBO) {
424 out(win) << "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl;
425 _slotb = "";
426 _slotb_xml = "";
428 if(files.rom_xml != "" && files.rom == "")
429 out(win) << "WARNING: " << name_subrom(files.rtype, 0) << " specified without corresponding "
430 << name_subrom(files.rtype, 1) << std::endl;
431 if(_slota_xml != "" && _slota == "")
432 out(win) << "WARNING: " << name_subrom(files.rtype, 2) << " specified without corresponding "
433 << name_subrom(files.rtype, 3) << std::endl;
434 if(_slotb_xml != "" && _slotb == "")
435 out(win) << "WARNING: " << name_subrom(files.rtype, 4) << " specified without corresponding "
436 << name_subrom(files.rtype, 5) << std::endl;
438 rtype = files.rtype;
439 rom = loaded_slot(files.rom, files.base_file);
440 rom_xml = loaded_slot(files.rom_xml, files.base_file, true);
441 slota = loaded_slot(_slota, files.base_file);
442 slota_xml = loaded_slot(_slota_xml, files.base_file, true);
443 slotb = loaded_slot(_slotb, files.base_file);
444 slotb_xml = loaded_slot(_slotb_xml, files.base_file, true);
445 orig_region = region = files.region;
448 void loaded_rom::load() throw(std::bad_alloc, std::runtime_error)
450 current_rom_type = ROMTYPE_NONE;
451 if(region == REGION_AUTO && orig_region != REGION_AUTO)
452 region = orig_region;
453 if(region != orig_region && orig_region != REGION_AUTO)
454 throw std::runtime_error("Trying to force incompatible region");
455 if(rtype == ROMTYPE_NONE)
456 throw std::runtime_error("Can't insert cartridge of type NONE!");
457 switch(region) {
458 case REGION_AUTO:
459 config.region = System::Region::Autodetect;
460 break;
461 case REGION_NTSC:
462 config.region = System::Region::NTSC;
463 break;
464 case REGION_PAL:
465 config.region = System::Region::PAL;
466 break;
467 default:
468 throw std::runtime_error("Trying to force unknown region");
470 switch(rtype) {
471 case ROMTYPE_SNES:
472 if(!snes_load_cartridge_normal(rom_xml, rom, rom))
473 throw std::runtime_error("Can't load cartridge ROM");
474 break;
475 case ROMTYPE_BSX:
476 if(region == REGION_PAL)
477 throw std::runtime_error("BSX can't be PAL");
478 if(!snes_load_cartridge_bsx(rom_xml, rom, rom, slota_xml, slota, slota))
479 throw std::runtime_error("Can't load cartridge ROM");
480 break;
481 case ROMTYPE_BSXSLOTTED:
482 if(region == REGION_PAL)
483 throw std::runtime_error("Slotted BSX can't be PAL");
484 if(!snes_load_cartridge_bsx_slotted(rom_xml, rom, rom, slota_xml, slota, slota))
485 throw std::runtime_error("Can't load cartridge ROM");
486 break;
487 case ROMTYPE_SGB:
488 if(!snes_load_cartridge_super_game_boy(rom_xml, rom, rom, slota_xml, slota, slota))
489 throw std::runtime_error("Can't load cartridge ROM");
490 break;
491 case ROMTYPE_SUFAMITURBO:
492 if(region == REGION_PAL)
493 throw std::runtime_error("Sufami Turbo can't be PAL");
494 if(!snes_load_cartridge_sufami_turbo(rom_xml, rom, rom, slota_xml, slota, slota, slotb_xml, slotb,
495 slotb))
496 throw std::runtime_error("Can't load cartridge ROM");
497 break;
498 default:
499 throw std::runtime_error("Unknown cartridge type");
501 if(region == REGION_AUTO)
502 region = snes_get_region() ? REGION_PAL : REGION_NTSC;
503 snes_power();
504 current_rom_type = rtype;
505 current_region = region;
506 refresh_cart_mappings();
509 void loaded_rom::do_patch(const std::vector<std::string>& cmdline, window* win) throw(std::bad_alloc,
510 std::runtime_error)
512 int32_t offset = 0;
513 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
514 std::string opt = *i;
515 if(opt.length() >= 13 && opt.substr(0, 13) == "--ips-offset=") {
516 try {
517 offset = parse_value<int32_t>(opt.substr(13));
518 } catch(std::exception& e) {
519 throw std::runtime_error("Invalid IPS offset option '" + opt + "': " + e.what());
521 continue;
523 if(opt.length() < 6 || opt.substr(0, 6) != "--ips-")
524 continue;
525 size_t split = opt.find_first_of("=");
526 if(split > opt.length())
527 throw std::runtime_error("Invalid IPS patch argument '" + opt + "'");
528 std::string kind = opt.substr(6, split - 6);
529 std::string filename = opt.substr(split + 1);
530 out(win) << "Patching " << kind << " using '" << filename << "'" << std::endl;
531 std::vector<char> ips;
532 try {
533 ips = read_file_relative(filename, "");
534 } catch(std::bad_alloc& e) {
535 OOM_panic(win);
536 } catch(std::exception& e) {
537 throw std::runtime_error("Can't read IPS '" + filename + "': " + e.what());
539 try {
540 switch(recognize_commandline_rom(rtype, kind)) {
541 case 0: rom.patch(ips, offset); break;
542 case 1: rom_xml.patch(ips, offset); break;
543 case 2: slota.patch(ips, offset); break;
544 case 3: slota_xml.patch(ips, offset); break;
545 case 4: slotb.patch(ips, offset); break;
546 case 5: slotb_xml.patch(ips, offset); break;
547 default:
548 throw std::runtime_error("Invalid subROM '" + kind + "' to patch");
550 } catch(std::bad_alloc& e) {
551 OOM_panic(win);
552 } catch(std::exception& e) {
553 throw std::runtime_error("Can't Patch with IPS '" + filename + "': " + e.what());
558 namespace
560 std::string sram_name(const nall::string& _id, Cartridge::Slot slotname)
562 std::string id(_id, _id.length());
563 if(slotname == Cartridge::Slot::SufamiTurboA)
564 return "slota." + id.substr(1);
565 if(slotname == Cartridge::Slot::SufamiTurboB)
566 return "slotb." + id.substr(1);
567 return id.substr(1);
571 std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc)
573 std::map<std::string, std::vector<char>> out;
574 for(unsigned i = 0; i < cartridge.nvram.size(); i++) {
575 Cartridge::NonVolatileRAM& r = cartridge.nvram[i];
576 std::string savename = sram_name(r.id, r.slot);
577 std::vector<char> x;
578 x.resize(r.size);
579 memcpy(&x[0], r.data, r.size);
580 out[savename] = x;
582 return out;
585 void load_sram(std::map<std::string, std::vector<char>>& sram, window* win) throw(std::bad_alloc)
587 std::set<std::string> used;
588 if(sram.empty())
589 return;
590 for(unsigned i = 0; i < cartridge.nvram.size(); i++) {
591 Cartridge::NonVolatileRAM& r = cartridge.nvram[i];
592 std::string savename = sram_name(r.id, r.slot);
593 if(sram.count(savename)) {
594 std::vector<char>& x = sram[savename];
595 if(r.size != x.size())
596 out(win) << "WARNING: SRAM '" << savename << "': Loaded " << x.size() << " bytes, "
597 << " but the SRAM is " << r.size << "." << std::endl;
598 memcpy(r.data, &x[0], (r.size < x.size()) ? r.size : x.size());
599 used.insert(savename);
600 } else
601 out(win) << "WARNING: SRAM '" << savename << ": No data." << std::endl;
603 for(auto i = sram.begin(); i != sram.end(); ++i)
604 if(!used.count(i->first))
605 out(win) << "WARNING: SRAM '" << i->first << ": Not found on cartridge." << std::endl;
608 std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
609 throw(std::bad_alloc, std::runtime_error)
611 std::map<std::string, std::vector<char>> ret;
612 for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
613 std::string opt = *i;
614 if(opt.length() >= 11 && opt.substr(0, 11) == "--continue=") {
615 size_t split = opt.find_first_of("=");
616 if(split > opt.length() - 1)
617 throw std::runtime_error("Bad SRAM option '" + opt + "'");
618 std::string file = opt.substr(split + 1);
619 zip_reader r(file);
620 for(auto j = r.begin(); j != r.end(); j++) {
621 std::string fname = *j;
622 if(fname.length() < 6 || fname.substr(0, 5) != "sram.")
623 continue;
624 std::istream& x = r[fname];
625 try {
626 std::vector<char> out;
627 boost::iostreams::back_insert_device<std::vector<char>> rd(out);
628 boost::iostreams::copy(x, rd);
629 delete &x;
630 ret[fname.substr(5, split - 5)] = out;
631 } catch(...) {
632 delete &x;
633 throw;
636 continue;
638 if(opt.length() < 8 || opt.substr(0, 7) != "--sram-")
639 continue;
640 size_t split = opt.find_first_of("=");
641 if(split > opt.length() - 1)
642 throw std::runtime_error("Bad SRAM option '" + opt + "'");
643 std::string kind = opt.substr(7, split - 7);
644 std::string file = opt.substr(split + 1);
645 if(kind == "")
646 throw std::runtime_error("Bad SRAM option '" + opt + "'");
647 try {
648 ret[kind] = read_file_relative(file, "");
649 } catch(std::bad_alloc& e) {
650 throw;
651 } catch(std::runtime_error& e) {
652 throw std::runtime_error("Can't load SRAM '" + kind + "': " + e.what());
655 return ret;
658 void emulate_frame() throw()
660 SNES::system.run();
663 void reset_snes() throw()
665 SNES::system.reset();
668 std::vector<char> save_core_state() throw(std::bad_alloc)
670 SNES::system.runtosave();
671 std::vector<char> ret;
672 serializer s = SNES::system.serialize();
673 ret.resize(s.size());
674 memcpy(&ret[0], s.data(), s.size());
675 size_t offset = ret.size();
676 unsigned char tmp[32];
677 sha256::hash(tmp, ret);
678 ret.resize(offset + 32);
679 memcpy(&ret[offset], tmp, 32);
680 return ret;
683 void load_core_state(const std::vector<char>& buf) throw(std::runtime_error)
685 if(buf.size() < 32)
686 throw std::runtime_error("Savestate corrupt");
687 unsigned char tmp[32];
688 sha256::hash(tmp, reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
689 if(memcmp(tmp, &buf[buf.size() - 32], 32))
690 throw std::runtime_error("Savestate corrupt");
691 serializer s(reinterpret_cast<const uint8_t*>(&buf[0]), buf.size() - 32);
692 if(!SNES::system.unserialize(s))
693 throw std::runtime_error("SNES core rejected savestate");
696 namespace
698 struct index_entry
700 std::string hash;
701 std::string relpath;
702 std::string from;
704 std::list<index_entry> rom_index;
706 void replace_index(std::list<index_entry> new_index, const std::string& source)
708 std::list<index_entry> tmp_index;
709 for(auto i = rom_index.begin(); i != rom_index.end(); i++) {
710 if(i->from != source)
711 tmp_index.push_back(*i);
713 for(auto i = new_index.begin(); i != new_index.end(); i++) {
714 tmp_index.push_back(*i);
716 rom_index = new_index;
720 void load_index_file(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
722 std::istream& s = open_file_relative(filename, "");
724 try {
725 std::list<index_entry> partial_index;
726 std::string line;
727 while(std::getline(s, line)) {
728 index_entry e;
729 if(line == "")
730 continue;
731 tokensplitter t(line);
732 e.hash = static_cast<std::string>(t);
733 e.relpath = t.tail();
734 e.from = filename;
735 if(e.hash.length() != 64 || e.relpath == "")
736 throw std::runtime_error("Bad index file");
737 partial_index.push_back(e);
739 replace_index(partial_index, filename);
740 } catch(...) {
741 delete &s;
742 throw;
744 delete &s;
747 std::string lookup_file_by_sha256(const std::string& hash) throw(std::bad_alloc, std::runtime_error)
749 if(hash == "")
750 return "";
751 for(auto i = rom_index.begin(); i != rom_index.end(); i++) {
752 if(i->hash != hash)
753 continue;
754 try {
755 std::istream& o = open_file_relative(i->relpath, i->from);
756 delete &o;
757 return resolve_file_relative(i->relpath, i->from);
758 } catch(...) {
759 continue;
762 throw std::runtime_error("No file with hash '" + hash + "' found in known indices");
765 std::string name_subrom(enum rom_type major, unsigned romnumber) throw(std::bad_alloc)
767 if(romnumber == 0)
768 return "ROM";
769 else if(romnumber == 1)
770 return "ROM XML";
771 else if(major == ROMTYPE_BSX && romnumber == 2)
772 return "BSX ROM";
773 else if(major == ROMTYPE_BSX && romnumber == 3)
774 return "BSX XML";
775 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 2)
776 return "BSX ROM";
777 else if(major == ROMTYPE_BSXSLOTTED && romnumber == 3)
778 return "BSX XML";
779 else if(major == ROMTYPE_SGB && romnumber == 2)
780 return "DMG ROM";
781 else if(major == ROMTYPE_SGB && romnumber == 3)
782 return "DMG XML";
783 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 2)
784 return "SLOT A ROM";
785 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 3)
786 return "SLOT A XML";
787 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 4)
788 return "SLOT B ROM";
789 else if(major == ROMTYPE_SUFAMITURBO && romnumber == 5)
790 return "SLOT B XML";
791 else if(romnumber % 2)
792 return "UNKNOWN XML";
793 else
794 return "UNKNOWN ROM";
798 int recognize_commandline_rom(enum rom_type major, const std::string& romname) throw(std::bad_alloc)
800 if(romname == romtypes_to_recognize[0])
801 return 0;
802 else if(romname == romtypes_to_recognize[6])
803 return 1;
804 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[1])
805 return 2;
806 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[7])
807 return 3;
808 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[2])
809 return 2;
810 else if(major == ROMTYPE_BSX && romname == romtypes_to_recognize[8])
811 return 3;
812 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[3])
813 return 2;
814 else if(major == ROMTYPE_SGB && romname == romtypes_to_recognize[9])
815 return 3;
816 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[4])
817 return 2;
818 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[10])
819 return 3;
820 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[5])
821 return 4;
822 else if(major == ROMTYPE_SUFAMITURBO && romname == romtypes_to_recognize[11])
823 return 5;
824 else
825 return -1;
828 rom_type recognize_platform(unsigned long flags) throw(std::bad_alloc, std::runtime_error)
830 if((flags & 07700) >> 6 & ~(flags & 077))
831 throw std::runtime_error("SubROM XML specified without corresponding subROM");
832 if((flags & 1) == 0)
833 throw std::runtime_error("No SNES main cartridge ROM specified");
834 if((flags & 077) == 1)
835 return ROMTYPE_SNES;
836 if((flags & 077) == 3)
837 return ROMTYPE_BSX;
838 if((flags & 077) == 5)
839 return ROMTYPE_BSXSLOTTED;
840 if((flags & 077) == 9)
841 return ROMTYPE_SGB;
842 if((flags & 060) != 0 && (flags & 017) == 1)
843 return ROMTYPE_SUFAMITURBO;
844 throw std::runtime_error("Not valid combination of rom/bsx/bsxslotted/dmg/slot-a/slot-b");