2 #include <snes/snes.hpp>
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>
18 #include "command.hpp"
19 #include "framerate.hpp"
21 #include "avsnoop.hpp"
24 #include "memorymanip.hpp"
30 typedef uint8_t uint8
;
31 typedef uint16_t uint16
;
32 typedef uint32_t uint32
;
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>
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"
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
)
63 case REGION_AUTO
: return "snes";
64 case REGION_NTSC
: return "snes_ntsc";
65 case REGION_PAL
: return "snes_pal";
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
)
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
)
99 case REGION_AUTO
: return GT_SGB_NTSC
;
100 case REGION_NTSC
: return GT_SNES_NTSC
;
101 case REGION_PAL
: return GT_SNES_PAL
;
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")
120 if(gametype
== "snes_pal")
122 if(gametype
== "sgb_ntsc")
124 if(gametype
== "sgb_pal")
126 if(gametype
== "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")
139 if(gametype
== "snes_pal")
141 if(gametype
== "snes")
143 if(gametype
== "sgb_ntsc")
145 if(gametype
== "sgb_pal")
147 if(gametype
== "sgb")
149 if(gametype
== "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()
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")
176 if(gametype
== "snes_pal")
178 if(gametype
== "snes")
180 if(gametype
== "sgb_ntsc")
182 if(gametype
== "sgb_pal")
184 if(gametype
== "sgb")
186 if(gametype
== "bsx")
188 if(gametype
== "bsxslotted")
190 if(gametype
== "sufamiturbo")
192 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
195 rom_region
gtype::toromregion(gametype_t gametype
) throw()
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");
212 bool option_set(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
214 for(auto i
: cmdline
)
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");
233 for(size_t i
= 0; i
< vsize
; i
++)
234 val
= (val
<< 8) | static_cast<uint8_t>(patch
[offset
+ i
]);
238 std::string
findoption(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
241 for(auto i
: cmdline
) {
243 if(arg
.length() < 3 + option
.length())
245 if(arg
[0] != '-' || arg
[1] != '-' || arg
.substr(2, option
.length()) != option
||
246 arg
[2 + option
.length()] != '=')
249 value
= arg
.substr(3 + option
.length());
251 std::cerr
<< "WARNING: Ignored duplicate option for '" << option
<< "'." << std::endl
;
253 throw std::runtime_error("Empty value for '" + option
+ "' is not allowed");
260 loaded_slot::loaded_slot() throw(std::bad_alloc
)
265 loaded_slot::loaded_slot(const std::string
& filename
, const std::string
& base
, bool xml_flag
) throw(std::bad_alloc
,
274 data
= read_file_relative(filename
, base
);
275 sha256
= sha256::hash(data
);
277 size_t osize
= data
.size();
278 data
.resize(osize
+ 1);
283 void loaded_slot::patch(const std::vector
<char>& patch
, int32_t offset
) throw(std::bad_alloc
, std::runtime_error
)
286 std::vector
<char> data2
= data
;
287 bool warned_extend
= false;
288 bool warned_negative
= false;
291 data2
.resize(data2
.size() - 1);
292 if(readval(patch
, poffset
, 5) != 0x5041544348)
293 throw std::runtime_error("Bad IPS file magic");
296 uint64_t addr
= readval(patch
, poffset
, 3);
299 uint64_t len
= readval(patch
, poffset
+ 3, 2);
306 roffset
= poffset
+ 5;
309 //RLE block. Read real size first.
310 len
= readval(patch
, poffset
+ 5, 2);
312 roffset
= poffset
+ 7;
315 for(uint64_t i
= 0; i
< len
; i
++) {
316 int64_t baddr
= addr
+ i
+ offset
;
319 std::cerr
<< "WARNING: IPS patch tries to modify negative offset. "
320 << "Bad patch or offset?" << std::endl
;
321 warned_negative
= true;
323 } else if(baddr
>= static_cast<int64_t>(data2
.size())) {
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
++)
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
]);
340 //Mark the slot as valid and update hash.
342 std::string new_sha256
= sha256::hash(data2
);
344 size_t osize
= data2
.size();
345 data2
.resize(osize
+ 1);
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
]);
372 rtype
= recognize_platform(flags
);
373 for(size_t i
= 0; i
< sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0]); 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"))
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
);
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
;
427 if((_slota
!= "" || _slota_xml
!= "") && files
.rtype
== ROMTYPE_SNES
) {
428 messages
<< "WARNING: SNES takes only 1 ROM image" << std::endl
;
432 if((_slotb
!= "" || _slotb_xml
!= "") && files
.rtype
!= ROMTYPE_SUFAMITURBO
) {
433 messages
<< "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl
;
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
;
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!");
468 config
.region
= System::Region::Autodetect
;
471 config
.region
= System::Region::NTSC
;
474 config
.region
= System::Region::PAL
;
477 throw std::runtime_error("Trying to force unknown region");
481 if(!snes_load_cartridge_normal(rom_xml
, rom
, rom
))
482 throw std::runtime_error("Can't load cartridge ROM");
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");
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");
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");
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
,
505 throw std::runtime_error("Can't load cartridge ROM");
508 throw std::runtime_error("Unknown cartridge type");
510 if(region
== REGION_AUTO
)
511 region
= snes_get_region() ? REGION_PAL
: REGION_NTSC
;
513 if(region
== REGION_PAL
)
514 set_nominal_framerate(SNES::system
.cpu_frequency() / DURATION_PAL_FRAME
);
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
,
528 for(auto i
: cmdline
) {
530 if(opt
.length() >= 13 && opt
.substr(0, 13) == "--ips-offset=") {
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());
538 if(opt
.length() < 6 || opt
.substr(0, 6) != "--ips-")
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
;
548 ips
= read_file_relative(filename
, "");
549 } catch(std::bad_alloc
& e
) {
551 } catch(std::exception
& e
) {
552 throw std::runtime_error("Can't read IPS '" + filename
+ "': " + e
.what());
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;
563 throw std::runtime_error("Invalid subROM '" + kind
+ "' to patch");
565 } catch(std::bad_alloc
& e
) {
567 } catch(std::exception
& e
) {
568 throw std::runtime_error("Can't Patch with IPS '" + filename
+ "': " + e
.what());
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);
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
);
594 memcpy(&x
[0], r
.data
, r
.size
);
600 void load_sram(std::map
<std::string
, std::vector
<char>>& sram
) throw(std::bad_alloc
)
602 std::set
<std::string
> used
;
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
);
616 messages
<< "WARNING: SRAM '" << savename
<< ": No data." << std::endl
;
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
) {
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);
636 std::string fname
= j
;
637 if(fname
.length() < 6 || fname
.substr(0, 5) != "sram.")
639 std::istream
& x
= r
[fname
];
641 std::vector
<char> out
;
642 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
643 boost::iostreams::copy(x
, rd
);
645 ret
[fname
.substr(5, split
- 5)] = out
;
653 if(opt
.length() < 8 || opt
.substr(0, 7) != "--sram-")
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);
661 throw std::runtime_error("Bad SRAM option '" + opt
+ "'");
663 ret
[kind
] = read_file_relative(file
, "");
664 } catch(std::bad_alloc
& e
) {
666 } catch(std::runtime_error
& e
) {
667 throw std::runtime_error("Can't load SRAM '" + kind
+ "': " + e
.what());
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);
688 void load_core_state(const std::vector
<char>& buf
) throw(std::runtime_error
)
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");
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
) {
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
, "");
730 std::list
<index_entry
> partial_index
;
732 while(std::getline(s
, line
)) {
736 tokensplitter
t(line
);
737 e
.hash
= static_cast<std::string
>(t
);
738 e
.relpath
= t
.tail();
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
);
752 std::string
lookup_file_by_sha256(const std::string
& hash
) throw(std::bad_alloc
, std::runtime_error
)
756 for(auto i
: rom_index
) {
760 std::istream
& o
= open_file_relative(i
.relpath
, i
.from
);
762 return resolve_file_relative(i
.relpath
, i
.from
);
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
)
774 else if(romnumber
== 1)
776 else if(major
== ROMTYPE_BSX
&& romnumber
== 2)
778 else if(major
== ROMTYPE_BSX
&& romnumber
== 3)
780 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 2)
782 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 3)
784 else if(major
== ROMTYPE_SGB
&& romnumber
== 2)
786 else if(major
== ROMTYPE_SGB
&& romnumber
== 3)
788 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 2)
790 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 3)
792 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 4)
794 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 5)
796 else if(romnumber
% 2)
797 return "UNKNOWN XML";
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])
807 else if(romname
== romtypes_to_recognize
[6])
809 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[1])
811 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[7])
813 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[2])
815 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[8])
817 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[3])
819 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[9])
821 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[4])
823 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[10])
825 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[5])
827 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[11])
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");
838 throw std::runtime_error("No SNES main cartridge ROM specified");
839 if((flags
& 077) == 1)
841 if((flags
& 077) == 3)
843 if((flags
& 077) == 5)
844 return ROMTYPE_BSXSLOTTED
;
845 if((flags
& 077) == 9)
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");