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"
21 #include "memorymanip.hpp"
27 typedef uint8_t uint8
;
28 typedef uint16_t uint16
;
29 typedef uint32_t uint32
;
31 typedef int16_t int16
;
32 typedef int32_t int32
;
33 #include <nall/platform.hpp>
34 #include <nall/endian.hpp>
35 #include <nall/varint.hpp>
36 #include <nall/bit.hpp>
37 #include <nall/serializer.hpp>
38 #include <nall/property.hpp>
40 #include <ui-libsnes/libsnes.hpp>
42 //Some anti-typo defs.
43 #define SNES_TYPE "snes"
44 #define SNES_PAL "snes_pal"
45 #define SNES_NTSC "snes_ntsc"
47 #define BSXSLOTTED "bsxslotted"
48 #define SUFAMITURBO "sufamiturbo"
49 #define SGB_TYPE "SGB"
50 #define SGB_PAL "sgb_pal"
51 #define SGB_NTSC "sgb_ntsc"
53 void strip_CR(std::string
& x
);
55 std::string
gtype::tostring(rom_type rtype
, rom_region region
) throw(std::bad_alloc
, std::runtime_error
)
60 case REGION_AUTO
: return "snes";
61 case REGION_NTSC
: return "snes_ntsc";
62 case REGION_PAL
: return "snes_pal";
66 case REGION_AUTO
: return "sgb";
67 case REGION_NTSC
: return "sgb_ntsc";
68 case REGION_PAL
: return "sgb_pal";
70 case ROMTYPE_BSX
: return "bsx";
71 case ROMTYPE_BSXSLOTTED
: return "bsxslotted";
72 case ROMTYPE_SUFAMITURBO
: return "sufamiturbo";
73 default: throw std::runtime_error("tostring: ROMTYPE_NONE");
77 std::string
gtype::tostring(gametype_t gametype
) throw(std::bad_alloc
, std::runtime_error
)
80 case GT_SNES_NTSC
: return "snes_ntsc";
81 case GT_SNES_PAL
: return "snes_pal";
82 case GT_SGB_NTSC
: return "sgb_ntsc";
83 case GT_SGB_PAL
: return "sgb_pal";
84 case GT_BSX
: return "bsx";
85 case GT_BSX_SLOTTED
: return "bsxslotted";
86 case GT_SUFAMITURBO
: return "sufamiturbo";
87 default: throw std::runtime_error("tostring: GT_INVALID");
91 gametype_t
gtype::togametype(rom_type rtype
, rom_region region
) throw(std::bad_alloc
, std::runtime_error
)
96 case REGION_AUTO
: return GT_SGB_NTSC
;
97 case REGION_NTSC
: return GT_SNES_NTSC
;
98 case REGION_PAL
: return GT_SNES_PAL
;
102 case REGION_AUTO
: return GT_SGB_NTSC
;
103 case REGION_NTSC
: return GT_SGB_NTSC
;
104 case REGION_PAL
: return GT_SGB_PAL
;
106 case ROMTYPE_BSX
: return GT_BSX
;
107 case ROMTYPE_BSXSLOTTED
: return GT_BSX_SLOTTED
;
108 case ROMTYPE_SUFAMITURBO
: return GT_SUFAMITURBO
;
109 default: throw std::runtime_error("togametype: ROMTYPE_NONE");
113 gametype_t
gtype::togametype(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
115 if(gametype
== "snes_ntsc")
117 if(gametype
== "snes_pal")
119 if(gametype
== "sgb_ntsc")
121 if(gametype
== "sgb_pal")
123 if(gametype
== "bsx")
125 if(gametype
== "bsxslotted")
126 return GT_BSX_SLOTTED
;
127 if(gametype
== "sufamiturbo")
128 return GT_SUFAMITURBO
;
129 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
132 rom_type
gtype::toromtype(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
134 if(gametype
== "snes_ntsc")
136 if(gametype
== "snes_pal")
138 if(gametype
== "snes")
140 if(gametype
== "sgb_ntsc")
142 if(gametype
== "sgb_pal")
144 if(gametype
== "sgb")
146 if(gametype
== "bsx")
148 if(gametype
== "bsxslotted")
149 return ROMTYPE_BSXSLOTTED
;
150 if(gametype
== "sufamiturbo")
151 return ROMTYPE_SUFAMITURBO
;
152 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
155 rom_type
gtype::toromtype(gametype_t gametype
) throw()
158 case GT_SNES_NTSC
: return ROMTYPE_SNES
;
159 case GT_SNES_PAL
: return ROMTYPE_SNES
;
160 case GT_SGB_NTSC
: return ROMTYPE_SGB
;
161 case GT_SGB_PAL
: return ROMTYPE_SGB
;
162 case GT_BSX
: return ROMTYPE_BSX
;
163 case GT_BSX_SLOTTED
: return ROMTYPE_BSXSLOTTED
;
164 case GT_SUFAMITURBO
: return ROMTYPE_SUFAMITURBO
;
165 case GT_INVALID
: throw std::runtime_error("toromtype: GT_INVALID");
169 rom_region
gtype::toromregion(const std::string
& gametype
) throw(std::bad_alloc
, std::runtime_error
)
171 if(gametype
== "snes_ntsc")
173 if(gametype
== "snes_pal")
175 if(gametype
== "snes")
177 if(gametype
== "sgb_ntsc")
179 if(gametype
== "sgb_pal")
181 if(gametype
== "sgb")
183 if(gametype
== "bsx")
185 if(gametype
== "bsxslotted")
187 if(gametype
== "sufamiturbo")
189 throw std::runtime_error("Unknown game type '" + gametype
+ "'");
192 rom_region
gtype::toromregion(gametype_t gametype
) throw()
195 case GT_SNES_NTSC
: return REGION_NTSC
;
196 case GT_SNES_PAL
: return REGION_PAL
;
197 case GT_SGB_NTSC
: return REGION_NTSC
;
198 case GT_SGB_PAL
: return REGION_PAL
;
199 case GT_BSX
: return REGION_NTSC
;
200 case GT_BSX_SLOTTED
: return REGION_NTSC
;
201 case GT_SUFAMITURBO
: return REGION_NTSC
;
202 case GT_INVALID
: throw std::runtime_error("toromregion: GT_INVALID");
209 bool option_set(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
211 for(auto i
: cmdline
)
217 const char* romtypes_to_recognize
[] = {
218 "rom", "bsx", "bsxslotted", "dmg", "slot-a", "slot-b",
219 "rom-xml", "bsx-xml", "bsxslotted-xml", "dmg-xml", "slot-a-xml", "slot-b-xml"
222 enum rom_type current_rom_type
= ROMTYPE_NONE
;
223 enum rom_region current_region
= REGION_NTSC
;
225 uint64_t readval(const std::vector
<char>& patch
, size_t offset
, size_t vsize
) throw(std::runtime_error
)
227 if(offset
>= patch
.size() || offset
+ vsize
> patch
.size())
228 throw std::runtime_error("IPS file corrupt");
230 for(size_t i
= 0; i
< vsize
; i
++)
231 val
= (val
<< 8) | static_cast<uint8_t>(patch
[offset
+ i
]);
235 std::string
findoption(const std::vector
<std::string
>& cmdline
, const std::string
& option
)
238 for(auto i
: cmdline
) {
240 if(arg
.length() < 3 + option
.length())
242 if(arg
[0] != '-' || arg
[1] != '-' || arg
.substr(2, option
.length()) != option
||
243 arg
[2 + option
.length()] != '=')
246 value
= arg
.substr(3 + option
.length());
248 std::cerr
<< "WARNING: Ignored duplicate option for '" << option
<< "'." << std::endl
;
250 throw std::runtime_error("Empty value for '" + option
+ "' is not allowed");
257 loaded_slot::loaded_slot() throw(std::bad_alloc
)
262 loaded_slot::loaded_slot(const std::string
& filename
, const std::string
& base
, bool xml_flag
) throw(std::bad_alloc
,
271 data
= read_file_relative(filename
, base
);
272 sha256
= sha256::hash(data
);
274 size_t osize
= data
.size();
275 data
.resize(osize
+ 1);
280 void loaded_slot::patch(const std::vector
<char>& patch
, int32_t offset
) throw(std::bad_alloc
, std::runtime_error
)
282 bool warned_extend
= false;
283 bool warned_negative
= false;
286 data
.resize(data
.size() - 1);
287 if(readval(patch
, poffset
, 5) != 0x5041544348)
288 throw std::runtime_error("Bad IPS file magic");
291 uint64_t addr
= readval(patch
, poffset
, 3);
294 uint64_t len
= readval(patch
, poffset
+ 3, 2);
301 roffset
= poffset
+ 5;
304 //RLE block. Read real size first.
305 len
= readval(patch
, poffset
+ 5, 2);
307 roffset
= poffset
+ 7;
310 for(uint64_t i
= 0; i
< len
; i
++) {
311 int64_t baddr
= addr
+ i
+ offset
;
314 std::cerr
<< "WARNING: IPS patch tries to modify negative offset. "
315 << "Bad patch or offset?" << std::endl
;
316 warned_negative
= true;
318 } else if(baddr
>= static_cast<int64_t>(data
.size())) {
320 std::cerr
<< "WARNING: IPS patch tries to extend the ROM. "
321 << "Bad patch or offset? " << std::endl
;
322 warned_extend
= true;
323 size_t oldsize
= data
.size();
324 data
.resize(baddr
+ 1);
325 for(size_t j
= oldsize
; j
<= static_cast<uint64_t>(baddr
); j
++)
328 size_t srcoff
= roffset
+ readstride
* i
;
329 if(srcoff
>= patch
.size())
330 throw std::runtime_error("Corrupt IPS patch");
331 data
[baddr
] = static_cast<uint8_t>(patch
[srcoff
]);
335 //Mark the slot as valid and update hash.
337 sha256
= sha256::hash(data
);
339 size_t osize
= data
.size();
340 data
.resize(osize
+ 1);
345 rom_files::rom_files() throw()
347 rtype
= ROMTYPE_NONE
;
348 region
= REGION_AUTO
;
352 rom_files::rom_files(const std::vector
<std::string
>& cmdline
) throw(std::bad_alloc
, std::runtime_error
)
354 rom
= rom_xml
= slota
= slota_xml
= slotb
= slotb_xml
= "";
355 std::string arr
[sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0])];
356 unsigned long flags
= 0;
357 for(size_t i
= 0; i
< sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0]); i
++) {
358 arr
[i
] = findoption(cmdline
, romtypes_to_recognize
[i
]);
362 rtype
= recognize_platform(flags
);
363 for(size_t i
= 0; i
< sizeof(romtypes_to_recognize
) / sizeof(romtypes_to_recognize
[0]); i
++) {
365 switch(recognize_commandline_rom(rtype
, romtypes_to_recognize
[i
])) {
366 case 0: rom
= arr
[i
]; break;
367 case 1: rom_xml
= arr
[i
]; break;
368 case 2: slota
= arr
[i
]; break;
369 case 3: slota_xml
= arr
[i
]; break;
370 case 4: slotb
= arr
[i
]; break;
371 case 5: slotb_xml
= arr
[i
]; break;
374 region
= (rtype
== ROMTYPE_SGB
|| rtype
== ROMTYPE_SNES
) ? REGION_AUTO
: REGION_NTSC
;
375 if(option_set(cmdline
, "--ntsc"))
376 region
= REGION_NTSC
;
377 else if(option_set(cmdline
, "--pal"))
383 void rom_files::resolve_relative() throw(std::bad_alloc
, std::runtime_error
)
385 rom
= resolve_file_relative(rom
, base_file
);
386 rom_xml
= resolve_file_relative(rom_xml
, base_file
);
387 slota
= resolve_file_relative(slota
, base_file
);
388 slota_xml
= resolve_file_relative(slota_xml
, base_file
);
389 slotb
= resolve_file_relative(slotb
, base_file
);
390 slotb_xml
= resolve_file_relative(slotb_xml
, base_file
);
395 std::pair
<enum rom_type
, enum rom_region
> get_current_rom_info() throw()
397 return std::make_pair(current_rom_type
, current_region
);
400 loaded_rom::loaded_rom() throw()
402 rtype
= ROMTYPE_NONE
;
403 region
= orig_region
= REGION_AUTO
;
406 loaded_rom::loaded_rom(const rom_files
& files
) throw(std::bad_alloc
, std::runtime_error
)
408 std::string _slota
= files
.slota
;
409 std::string _slota_xml
= files
.slota_xml
;
410 std::string _slotb
= files
.slotb
;
411 std::string _slotb_xml
= files
.slotb_xml
;
412 if(files
.rtype
== ROMTYPE_NONE
) {
413 rtype
= ROMTYPE_NONE
;
414 region
= orig_region
= files
.region
;
417 if((_slota
!= "" || _slota_xml
!= "") && files
.rtype
== ROMTYPE_SNES
) {
418 messages
<< "WARNING: SNES takes only 1 ROM image" << std::endl
;
422 if((_slotb
!= "" || _slotb_xml
!= "") && files
.rtype
!= ROMTYPE_SUFAMITURBO
) {
423 messages
<< "WARNING: Only Sufami Turbo takes 3 ROM images" << std::endl
;
427 if(files
.rom_xml
!= "" && files
.rom
== "")
428 messages
<< "WARNING: " << name_subrom(files
.rtype
, 0) << " specified without corresponding "
429 << name_subrom(files
.rtype
, 1) << std::endl
;
430 if(_slota_xml
!= "" && _slota
== "")
431 messages
<< "WARNING: " << name_subrom(files
.rtype
, 2) << " specified without corresponding "
432 << name_subrom(files
.rtype
, 3) << std::endl
;
433 if(_slotb_xml
!= "" && _slotb
== "")
434 messages
<< "WARNING: " << name_subrom(files
.rtype
, 4) << " specified without corresponding "
435 << name_subrom(files
.rtype
, 5) << std::endl
;
438 rom
= loaded_slot(files
.rom
, files
.base_file
);
439 rom_xml
= loaded_slot(files
.rom_xml
, files
.base_file
, true);
440 slota
= loaded_slot(_slota
, files
.base_file
);
441 slota_xml
= loaded_slot(_slota_xml
, files
.base_file
, true);
442 slotb
= loaded_slot(_slotb
, files
.base_file
);
443 slotb_xml
= loaded_slot(_slotb_xml
, files
.base_file
, true);
444 orig_region
= region
= files
.region
;
447 void loaded_rom::load() throw(std::bad_alloc
, std::runtime_error
)
449 current_rom_type
= ROMTYPE_NONE
;
450 if(region
== REGION_AUTO
&& orig_region
!= REGION_AUTO
)
451 region
= orig_region
;
452 if(region
!= orig_region
&& orig_region
!= REGION_AUTO
)
453 throw std::runtime_error("Trying to force incompatible region");
454 if(rtype
== ROMTYPE_NONE
)
455 throw std::runtime_error("Can't insert cartridge of type NONE!");
458 config
.region
= System::Region::Autodetect
;
461 config
.region
= System::Region::NTSC
;
464 config
.region
= System::Region::PAL
;
467 throw std::runtime_error("Trying to force unknown region");
471 if(!snes_load_cartridge_normal(rom_xml
, rom
, rom
))
472 throw std::runtime_error("Can't load cartridge ROM");
475 if(region
== REGION_PAL
)
476 throw std::runtime_error("BSX can't be PAL");
477 if(!snes_load_cartridge_bsx(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
478 throw std::runtime_error("Can't load cartridge ROM");
480 case ROMTYPE_BSXSLOTTED
:
481 if(region
== REGION_PAL
)
482 throw std::runtime_error("Slotted BSX can't be PAL");
483 if(!snes_load_cartridge_bsx_slotted(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
484 throw std::runtime_error("Can't load cartridge ROM");
487 if(!snes_load_cartridge_super_game_boy(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
))
488 throw std::runtime_error("Can't load cartridge ROM");
490 case ROMTYPE_SUFAMITURBO
:
491 if(region
== REGION_PAL
)
492 throw std::runtime_error("Sufami Turbo can't be PAL");
493 if(!snes_load_cartridge_sufami_turbo(rom_xml
, rom
, rom
, slota_xml
, slota
, slota
, slotb_xml
, slotb
,
495 throw std::runtime_error("Can't load cartridge ROM");
498 throw std::runtime_error("Unknown cartridge type");
500 if(region
== REGION_AUTO
)
501 region
= snes_get_region() ? REGION_PAL
: REGION_NTSC
;
503 current_rom_type
= rtype
;
504 current_region
= region
;
505 refresh_cart_mappings();
508 void loaded_rom::do_patch(const std::vector
<std::string
>& cmdline
) throw(std::bad_alloc
,
512 for(auto i
: cmdline
) {
514 if(opt
.length() >= 13 && opt
.substr(0, 13) == "--ips-offset=") {
516 offset
= parse_value
<int32_t>(opt
.substr(13));
517 } catch(std::exception
& e
) {
518 throw std::runtime_error("Invalid IPS offset option '" + opt
+ "': " + e
.what());
522 if(opt
.length() < 6 || opt
.substr(0, 6) != "--ips-")
524 size_t split
= opt
.find_first_of("=");
525 if(split
> opt
.length())
526 throw std::runtime_error("Invalid IPS patch argument '" + opt
+ "'");
527 std::string kind
= opt
.substr(6, split
- 6);
528 std::string filename
= opt
.substr(split
+ 1);
529 messages
<< "Patching " << kind
<< " using '" << filename
<< "'" << std::endl
;
530 std::vector
<char> ips
;
532 ips
= read_file_relative(filename
, "");
533 } catch(std::bad_alloc
& e
) {
535 } catch(std::exception
& e
) {
536 throw std::runtime_error("Can't read IPS '" + filename
+ "': " + e
.what());
539 switch(recognize_commandline_rom(rtype
, kind
)) {
540 case 0: rom
.patch(ips
, offset
); break;
541 case 1: rom_xml
.patch(ips
, offset
); break;
542 case 2: slota
.patch(ips
, offset
); break;
543 case 3: slota_xml
.patch(ips
, offset
); break;
544 case 4: slotb
.patch(ips
, offset
); break;
545 case 5: slotb_xml
.patch(ips
, offset
); break;
547 throw std::runtime_error("Invalid subROM '" + kind
+ "' to patch");
549 } catch(std::bad_alloc
& e
) {
551 } catch(std::exception
& e
) {
552 throw std::runtime_error("Can't Patch with IPS '" + filename
+ "': " + e
.what());
559 std::string
sram_name(const nall::string
& _id
, Cartridge::Slot slotname
)
561 std::string
id(_id
, _id
.length());
562 if(slotname
== Cartridge::Slot::SufamiTurboA
)
563 return "slota." + id
.substr(1);
564 if(slotname
== Cartridge::Slot::SufamiTurboB
)
565 return "slotb." + id
.substr(1);
570 std::map
<std::string
, std::vector
<char>> save_sram() throw(std::bad_alloc
)
572 std::map
<std::string
, std::vector
<char>> out
;
573 for(unsigned i
= 0; i
< cartridge
.nvram
.size(); i
++) {
574 Cartridge::NonVolatileRAM
& r
= cartridge
.nvram
[i
];
575 std::string savename
= sram_name(r
.id
, r
.slot
);
578 memcpy(&x
[0], r
.data
, r
.size
);
584 void load_sram(std::map
<std::string
, std::vector
<char>>& sram
) throw(std::bad_alloc
)
586 std::set
<std::string
> used
;
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 if(sram
.count(savename
)) {
593 std::vector
<char>& x
= sram
[savename
];
594 if(r
.size
!= x
.size())
595 messages
<< "WARNING: SRAM '" << savename
<< "': Loaded " << x
.size()
596 << " bytes, but the SRAM is " << r
.size
<< "." << std::endl
;
597 memcpy(r
.data
, &x
[0], (r
.size
< x
.size()) ? r
.size
: x
.size());
598 used
.insert(savename
);
600 messages
<< "WARNING: SRAM '" << savename
<< ": No data." << std::endl
;
603 if(!used
.count(i
.first
))
604 messages
<< "WARNING: SRAM '" << i
.first
<< ": Not found on cartridge." << std::endl
;
607 std::map
<std::string
, std::vector
<char>> load_sram_commandline(const std::vector
<std::string
>& cmdline
)
608 throw(std::bad_alloc
, std::runtime_error
)
610 std::map
<std::string
, std::vector
<char>> ret
;
611 for(auto i
: cmdline
) {
613 if(opt
.length() >= 11 && opt
.substr(0, 11) == "--continue=") {
614 size_t split
= opt
.find_first_of("=");
615 if(split
> opt
.length() - 1)
616 throw std::runtime_error("Bad SRAM option '" + opt
+ "'");
617 std::string file
= opt
.substr(split
+ 1);
620 std::string fname
= j
;
621 if(fname
.length() < 6 || fname
.substr(0, 5) != "sram.")
623 std::istream
& x
= r
[fname
];
625 std::vector
<char> out
;
626 boost::iostreams::back_insert_device
<std::vector
<char>> rd(out
);
627 boost::iostreams::copy(x
, rd
);
629 ret
[fname
.substr(5, split
- 5)] = out
;
637 if(opt
.length() < 8 || opt
.substr(0, 7) != "--sram-")
639 size_t split
= opt
.find_first_of("=");
640 if(split
> opt
.length() - 1)
641 throw std::runtime_error("Bad SRAM option '" + opt
+ "'");
642 std::string kind
= opt
.substr(7, split
- 7);
643 std::string file
= opt
.substr(split
+ 1);
645 throw std::runtime_error("Bad SRAM option '" + opt
+ "'");
647 ret
[kind
] = read_file_relative(file
, "");
648 } catch(std::bad_alloc
& e
) {
650 } catch(std::runtime_error
& e
) {
651 throw std::runtime_error("Can't load SRAM '" + kind
+ "': " + e
.what());
657 std::vector
<char> save_core_state() throw(std::bad_alloc
)
659 SNES::system
.runtosave();
660 std::vector
<char> ret
;
661 serializer s
= SNES::system
.serialize();
662 ret
.resize(s
.size());
663 memcpy(&ret
[0], s
.data(), s
.size());
664 size_t offset
= ret
.size();
665 unsigned char tmp
[32];
666 sha256::hash(tmp
, ret
);
667 ret
.resize(offset
+ 32);
668 memcpy(&ret
[offset
], tmp
, 32);
672 void load_core_state(const std::vector
<char>& buf
) throw(std::runtime_error
)
675 throw std::runtime_error("Savestate corrupt");
676 unsigned char tmp
[32];
677 sha256::hash(tmp
, reinterpret_cast<const uint8_t*>(&buf
[0]), buf
.size() - 32);
678 if(memcmp(tmp
, &buf
[buf
.size() - 32], 32))
679 throw std::runtime_error("Savestate corrupt");
680 serializer
s(reinterpret_cast<const uint8_t*>(&buf
[0]), buf
.size() - 32);
681 if(!SNES::system
.unserialize(s
))
682 throw std::runtime_error("SNES core rejected savestate");
693 std::list
<index_entry
> rom_index
;
695 void replace_index(std::list
<index_entry
> new_index
, const std::string
& source
)
697 std::list
<index_entry
> tmp_index
;
698 for(auto i
: rom_index
) {
700 tmp_index
.push_back(i
);
702 for(auto i
: new_index
) {
703 tmp_index
.push_back(i
);
705 rom_index
= new_index
;
709 void load_index_file(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
711 std::istream
& s
= open_file_relative(filename
, "");
714 std::list
<index_entry
> partial_index
;
716 while(std::getline(s
, line
)) {
720 tokensplitter
t(line
);
721 e
.hash
= static_cast<std::string
>(t
);
722 e
.relpath
= t
.tail();
724 if(e
.hash
.length() != 64 || e
.relpath
== "")
725 throw std::runtime_error("Bad index file");
726 partial_index
.push_back(e
);
728 replace_index(partial_index
, filename
);
736 std::string
lookup_file_by_sha256(const std::string
& hash
) throw(std::bad_alloc
, std::runtime_error
)
740 for(auto i
: rom_index
) {
744 std::istream
& o
= open_file_relative(i
.relpath
, i
.from
);
746 return resolve_file_relative(i
.relpath
, i
.from
);
751 throw std::runtime_error("No file with hash '" + hash
+ "' found in known indices");
754 std::string
name_subrom(enum rom_type major
, unsigned romnumber
) throw(std::bad_alloc
)
758 else if(romnumber
== 1)
760 else if(major
== ROMTYPE_BSX
&& romnumber
== 2)
762 else if(major
== ROMTYPE_BSX
&& romnumber
== 3)
764 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 2)
766 else if(major
== ROMTYPE_BSXSLOTTED
&& romnumber
== 3)
768 else if(major
== ROMTYPE_SGB
&& romnumber
== 2)
770 else if(major
== ROMTYPE_SGB
&& romnumber
== 3)
772 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 2)
774 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 3)
776 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 4)
778 else if(major
== ROMTYPE_SUFAMITURBO
&& romnumber
== 5)
780 else if(romnumber
% 2)
781 return "UNKNOWN XML";
783 return "UNKNOWN ROM";
787 int recognize_commandline_rom(enum rom_type major
, const std::string
& romname
) throw(std::bad_alloc
)
789 if(romname
== romtypes_to_recognize
[0])
791 else if(romname
== romtypes_to_recognize
[6])
793 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[1])
795 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[7])
797 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[2])
799 else if(major
== ROMTYPE_BSX
&& romname
== romtypes_to_recognize
[8])
801 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[3])
803 else if(major
== ROMTYPE_SGB
&& romname
== romtypes_to_recognize
[9])
805 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[4])
807 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[10])
809 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[5])
811 else if(major
== ROMTYPE_SUFAMITURBO
&& romname
== romtypes_to_recognize
[11])
817 rom_type
recognize_platform(unsigned long flags
) throw(std::bad_alloc
, std::runtime_error
)
819 if((flags
& 07700) >> 6 & ~(flags
& 077))
820 throw std::runtime_error("SubROM XML specified without corresponding subROM");
822 throw std::runtime_error("No SNES main cartridge ROM specified");
823 if((flags
& 077) == 1)
825 if((flags
& 077) == 3)
827 if((flags
& 077) == 5)
828 return ROMTYPE_BSXSLOTTED
;
829 if((flags
& 077) == 9)
831 if((flags
& 060) != 0 && (flags
& 017) == 1)
832 return ROMTYPE_SUFAMITURBO
;
833 throw std::runtime_error("Not valid combination of rom/bsx/bsxslotted/dmg/slot-a/slot-b");