Internally refactor loaded ROM imageset out of loaded ROM code

This commit is contained in:
Ilari Liusvaara 2015-04-14 11:25:10 +03:00
parent 1bf49d532d
commit b7aedc6cd9
6 changed files with 683 additions and 560 deletions

View file

@ -7,27 +7,10 @@
#include <stdexcept>
#include "core/misc.hpp"
#include "core/rom-small.hpp"
#include "core/romimage.hpp"
#include "interface/romtype.hpp"
#include "library/fileimage.hpp"
//ROM request.
struct rom_request
{
//List of core types.
std::vector<core_type*> cores;
//Selected core (default core on call).
bool core_guessed;
size_t selected;
//Filename selected (on entry, filename hint).
bool has_slot[ROM_SLOT_COUNT];
bool guessed[ROM_SLOT_COUNT];
std::string filename[ROM_SLOT_COUNT];
std::string hash[ROM_SLOT_COUNT];
std::string hashxml[ROM_SLOT_COUNT];
//Canceled flag.
bool canceled;
};
/**
* ROM loaded into memory.
*/
@ -91,19 +74,10 @@ struct loaded_rom
*/
void load_core_state(const std::vector<char>& buf, bool nochecksum = false) throw(std::runtime_error);
/**
* Is file a gamepak?
*
* parameter filename: The file to probe.
* retruns: True if gamepak, false if not.
* throws std::runtime_error: No such file.
*/
static bool is_gamepak(const std::string& filename) throw(std::bad_alloc, std::runtime_error);
/**
* Get internal type representation.
*/
core_type& get_internal_rom_type() { return *rtype; }
core_type& get_internal_rom_type() { return rtype(); }
/**
* Get internal region representation.
*/
@ -111,11 +85,11 @@ struct loaded_rom
/**
* Is same ROM type?
*/
bool is_of_type(core_type& type) { return (rtype == &type); }
bool is_of_type(core_type& type) { return (&rtype() == &type); }
/**
* Get gametype of this ROM.
*/
core_sysregion& get_sysregion() { return rtype->combine_region(*region); }
core_sysregion& get_sysregion() { return rtype().combine_region(*region); }
/**
* Set internal region representation.
*/
@ -126,119 +100,94 @@ struct loaded_rom
* parameter index: The index of ROM slot to access.
* returns: The ROM image (NULL image if index is out of range).
*/
fileimage::image& get_rom(size_t index) { return get_image(index, false); }
fileimage::image& get_rom(size_t index) { return image.get_image(index, false); }
/**
* Access ROM markup image.
*
* parameter index: The index of ROM slot to access.
* returns: The ROM markup image (NULL image if index is out of range).
*/
fileimage::image& get_markup(size_t index) { return get_image(index, true); }
fileimage::image& get_markup(size_t index) { return image.get_image(index, true); }
/**
* Get filename of ROM pack, if any.
*/
const std::string& get_pack_filename() { return load_filename; }
const std::string& get_pack_filename() { return image.get_pack_filename(); }
/**
* Get MSU-1 base fileaname.
*/
const std::string& get_msu1_base() { return msu1_base; }
const std::string& get_msu1_base() { return image.get_msu1_base(); }
//ROM methods.
std::string get_core_identifier() { return rtype->get_core_identifier(); }
std::string get_core_identifier() { return rtype().get_core_identifier(); }
std::pair<uint32_t, uint32_t> get_scale_factors(uint32_t width, uint32_t height)
{
return rtype->get_scale_factors(width, height);
return rtype().get_scale_factors(width, height);
}
const std::string& get_hname() { return rtype->get_hname(); }
core_sysregion& combine_region(core_region& reg) { return rtype->combine_region(reg); }
bool isnull() { return !rtype || rtype->isnull(); }
std::vector<std::string> get_trace_cpus() { return rtype->get_trace_cpus(); }
const std::string& get_hname() { return rtype().get_hname(); }
core_sysregion& combine_region(core_region& reg) { return rtype().combine_region(reg); }
bool isnull() { return rtype().isnull(); }
std::vector<std::string> get_trace_cpus() { return rtype().get_trace_cpus(); }
controller_set controllerconfig(std::map<std::string, std::string>& settings)
{
return rtype->controllerconfig(settings);
return rtype().controllerconfig(settings);
}
core_setting_group& get_settings() { return rtype->get_settings(); }
std::set<std::string> srams() { return rtype->srams(); }
double get_PAR() { return rtype->get_PAR(); }
std::string get_systemmenu_name() { return rtype->get_systemmenu_name(); }
unsigned action_flags(unsigned id) { return rtype->action_flags(id); }
std::set<const interface_action*> get_actions() { return rtype->get_actions(); }
core_setting_group& get_settings() { return rtype().get_settings(); }
std::set<std::string> srams() { return rtype().srams(); }
double get_PAR() { return rtype().get_PAR(); }
std::string get_systemmenu_name() { return rtype().get_systemmenu_name(); }
unsigned action_flags(unsigned id) { return rtype().action_flags(id); }
std::set<const interface_action*> get_actions() { return rtype().get_actions(); }
void execute_action(unsigned id, const std::vector<interface_action_paramval>& p)
{
return rtype->execute_action(id, p);
return rtype().execute_action(id, p);
}
std::pair<unsigned, unsigned> lightgun_scale() { return rtype->lightgun_scale(); }
const interface_device_reg* get_registers() { return rtype->get_registers(); }
bool get_pflag() { return rtype->get_pflag(); }
void set_pflag(bool pflag) { rtype->set_pflag(pflag); }
std::pair<uint64_t, uint64_t> get_bus_map() { return rtype->get_bus_map(); }
std::list<core_region*> get_regions() { return rtype->get_regions(); }
const std::string& get_iname() { return rtype->get_iname(); }
std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc) { return rtype->save_sram(); }
std::pair<unsigned, unsigned> lightgun_scale() { return rtype().lightgun_scale(); }
const interface_device_reg* get_registers() { return rtype().get_registers(); }
bool get_pflag() { return rtype().get_pflag(); }
void set_pflag(bool pflag) { rtype().set_pflag(pflag); }
std::pair<uint64_t, uint64_t> get_bus_map() { return rtype().get_bus_map(); }
std::list<core_region*> get_regions() { return rtype().get_regions(); }
const std::string& get_iname() { return rtype().get_iname(); }
std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc) { return rtype().save_sram(); }
void load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc)
{
rtype->load_sram(sram);
rtype().load_sram(sram);
}
std::list<core_vma_info> vma_list() { return rtype->vma_list(); }
framebuffer::raw& draw_cover() { return rtype->draw_cover(); }
int reset_action(bool hard) { return rtype->reset_action(hard); }
void pre_emulate_frame(portctrl::frame& cf) { return rtype->pre_emulate_frame(cf); }
void emulate() { rtype->emulate(); }
void runtosave() { rtype->runtosave(); }
std::pair<uint32_t, uint32_t> get_audio_rate() { return rtype->get_audio_rate(); }
std::list<core_vma_info> vma_list() { return rtype().vma_list(); }
framebuffer::raw& draw_cover() { return rtype().draw_cover(); }
int reset_action(bool hard) { return rtype().reset_action(hard); }
void pre_emulate_frame(portctrl::frame& cf) { return rtype().pre_emulate_frame(cf); }
void emulate() { rtype().emulate(); }
void runtosave() { rtype().runtosave(); }
std::pair<uint32_t, uint32_t> get_audio_rate() { return rtype().get_audio_rate(); }
void set_debug_flags(uint64_t addr, unsigned flags_set, unsigned flags_clear)
{
return rtype->set_debug_flags(addr, flags_set, flags_clear);
return rtype().set_debug_flags(addr, flags_set, flags_clear);
}
void set_cheat(uint64_t addr, uint64_t value, bool set)
{
return rtype->set_cheat(addr, value, set);
return rtype().set_cheat(addr, value, set);
}
void debug_reset()
{
rtype->debug_reset();
rtype().debug_reset();
}
//Region methods.
const std::string& orig_region_get_iname() { return orig_region->get_iname(); }
const std::string& orig_region_get_hname() { return orig_region->get_hname(); }
const std::string& orig_region_get_iname() { return image.get_region().get_iname(); }
const std::string& orig_region_get_hname() { return image.get_region().get_hname(); }
const std::string& region_get_iname() { return region->get_iname(); }
const std::string& region_get_hname() { return region->get_hname(); }
double region_approx_framerate() { return region->approx_framerate(); }
void region_fill_framerate_magic(uint64_t* magic) { region->fill_framerate_magic(magic); }
bool region_compatible_with(core_region& run)
{
return orig_region && orig_region->compatible_with(run);
return image.get_region().compatible_with(run);
}
private:
//Static NULL image.
static fileimage::image null_img;
//Loaded ROM images.
fileimage::image romimg[ROM_SLOT_COUNT];
//Loaded ROM XML (markup) images.
fileimage::image romxml[ROM_SLOT_COUNT];
//MSU-1 base filename.
std::string msu1_base;
//Load filename.
std::string load_filename;
//ROM type.
core_type* rtype;
core_type& rtype() { return image.get_type(); }
//The internal ROM image.
rom_image image;
//ROM region.
core_region* region;
//Region ROM was loaded as.
core_region* orig_region;
//Get image.
fileimage::image& get_image(size_t index, bool xml)
{
if(index < ROM_SLOT_COUNT) {
if(xml)
return romxml[index];
else
return romimg[index];
} else
return null_img;
}
//Handle bundle load case.
void load_bundle(const std::string& file, std::istream& spec, const std::string& tmpprefer)
throw(std::bad_alloc, std::runtime_error);
};
/**
@ -259,11 +208,6 @@ std::pair<core_type*, core_region*> get_current_rom_info() throw();
std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
throw(std::bad_alloc, std::runtime_error);
/**
* Set the hasher callback.
*/
void set_hasher_callback(std::function<void(uint64_t, uint64_t)> cb);
struct romload_request
{
//Pack file to load. Overrides everything else.
@ -285,15 +229,9 @@ regex_results get_argument(const std::vector<std::string>& cmdline, const std::s
std::string get_requested_core(const std::vector<std::string>& cmdline);
loaded_rom construct_rom(const std::string& movie_filename, const std::vector<std::string>& cmdline);
void try_guess_roms(rom_request& req);
void record_filehash(const std::string& file, uint64_t prefix, const std::string& hash);
std::string try_to_guess_rom(const std::string& hint, const std::string& hash, const std::string& xhash,
core_type& type, unsigned i);
//Map of preferred cores for each extension and type.
extern std::map<std::string, core_type*> preferred_core;
//Main hasher
extern fileimage::hash lsnes_image_hasher;
#endif

136
include/core/romimage.hpp Normal file
View file

@ -0,0 +1,136 @@
#ifndef _romimage__hpp__included__
#define _romimage__hpp__included__
#include "core/rom-small.hpp"
#include "interface/romtype.hpp"
#include "library/fileimage.hpp"
//ROM request.
struct rom_request
{
//List of core types.
std::vector<core_type*> cores;
//Selected core (default core on call).
bool core_guessed;
size_t selected;
//Filename selected (on entry, filename hint).
bool has_slot[ROM_SLOT_COUNT];
bool guessed[ROM_SLOT_COUNT];
std::string filename[ROM_SLOT_COUNT];
std::string hash[ROM_SLOT_COUNT];
std::string hashxml[ROM_SLOT_COUNT];
//Canceled flag.
bool canceled;
};
/**
* A collection of files making up a ROM image.
*/
class rom_image
{
public:
/**
* Create blank ROM
*/
rom_image() throw();
/**
* Take in ROM filename (or a bundle) and load it to memory.
*
* parameter file: The file to load
* parameter tmpprefer: The core name to prefer.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Loading ROM file failed.
*/
rom_image(const std::string& file, const std::string& tmpprefer = "") throw(std::bad_alloc,
std::runtime_error);
/**
* Take a ROM and load it.
*/
rom_image(const std::string& file, const std::string& core, const std::string& type,
const std::string& region);
/**
* Load a multi-file ROM.
*/
rom_image(const std::string file[ROM_SLOT_COUNT], const std::string& core, const std::string& type,
const std::string& region);
/**
* Take in ROM filename and load it to memory with specified type.
*
* parameter file: The file to load
* parameter ctype: The core type to use.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Loading ROM file failed.
*/
rom_image(const std::string& file, core_type& ctype) throw(std::bad_alloc, std::runtime_error);
/**
* Get ROM type.
*/
core_type& get_type() { return *rtype; }
/**
* Get ROM region.
*/
core_region& get_region() { return *orig_region; }
/**
* Do region setup. Changes orig_region to specified if NULL.
*/
void setup_region(core_region& reg);
/**
* Get image.
*/
fileimage::image& get_image(size_t index, bool xml)
{
if(index < ROM_SLOT_COUNT) {
if(xml)
return romxml[index];
else
return romimg[index];
} else
return null_img;
}
/**
* Get filename of ROM pack, if any.
*/
const std::string& get_pack_filename() { return load_filename; }
/**
* Get MSU-1 base fileaname.
*/
const std::string& get_msu1_base() { return msu1_base; }
/**
* Is file a gamepak?
*
* parameter filename: The file to probe.
* retruns: True if gamepak, false if not.
* throws std::runtime_error: No such file.
*/
static bool is_gamepak(const std::string& filename) throw(std::bad_alloc, std::runtime_error);
private:
//Static NULL image.
static fileimage::image null_img;
//Loaded ROM images.
fileimage::image romimg[ROM_SLOT_COUNT];
//Loaded ROM XML (markup) images.
fileimage::image romxml[ROM_SLOT_COUNT];
//MSU-1 base filename.
std::string msu1_base;
//Load filename.
std::string load_filename;
//ROM type.
core_type* rtype;
//ROM region.
core_region* region;
//Region ROM was loaded as.
core_region* orig_region;
//Handle bundle load case.
void load_bundle(const std::string& file, std::istream& spec, const std::string& tmpprefer)
throw(std::bad_alloc, std::runtime_error);
};
void record_filehash(const std::string& file, uint64_t prefix, const std::string& hash);
void set_hasher_callback(std::function<void(uint64_t, uint64_t)> cb);
//Map of preferred cores for each extension and type.
extern std::map<std::string, core_type*> preferred_core;
//Main hasher
extern fileimage::hash lsnes_image_hasher;
#endif

View file

@ -44,442 +44,44 @@ namespace
core_type* current_rom_type = &get_null_type();
core_region* current_region = &get_null_region();
core_type* prompt_core_fallback(const std::vector<core_type*>& choices)
{
if(choices.size() == 0)
return NULL;
if(choices.size() == 1)
return choices[0];
rom_request req;
req.cores = choices;
req.core_guessed = false;
req.selected = 0;
//Tell that all ROMs have been correctly guessed, leaving only core select.
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
req.has_slot[i] = false;
req.guessed[i] = false;
}
req.canceled = false;
graphics_driver_request_rom(req);
if(req.canceled)
throw std::runtime_error("Canceled ROM loading");
if(req.selected < choices.size())
return choices[req.selected];
return choices[0];
}
core_type* find_core_by_extension(const std::string& ext, const std::string& tmpprefer)
{
std::string key = "ext:" + ext;
std::list<core_type*> possible = core_type::get_core_types();
core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
std::vector<core_type*> fallbacks;
//Tmpprefer overrides normal preferred core.
if(tmpprefer != "")
for(auto i : possible)
if(i->get_core_identifier() == tmpprefer)
return i;
for(auto i : possible)
if(i->is_known_extension(ext)) {
fallbacks.push_back(i);
if(i == preferred)
return i;
}
core_type* fallback = prompt_core_fallback(fallbacks);
if(!fallback) throw std::runtime_error("No core available to load the ROM");
return fallback;
}
core_type* find_core_by_name(const std::string& name, const std::string& tmpprefer)
{
std::string key = "type:" + name;
std::list<core_type*> possible = core_type::get_core_types();
std::vector<core_type*> fallbacks;
core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
//Tmpprefer overrides normal preferred core.
if(tmpprefer != "")
for(auto i : possible)
if(i->get_iname() == tmpprefer)
return i;
for(auto i : possible)
if(i->get_iname() == name) {
fallbacks.push_back(i);
if(i == preferred)
return i;
}
core_type* fallback = prompt_core_fallback(fallbacks);
if(!fallback) throw std::runtime_error("No core available to load the ROM");
return fallback;
}
struct fileimage::image::info get_xml_info()
{
fileimage::image::info i;
i.type = fileimage::image::info::IT_MARKUP;
i.headersize = 0;
return i;
}
struct fileimage::image::info xlate_info(core_romimage_info ri)
{
fileimage::image::info i;
if(ri.pass_mode == 0) i.type = fileimage::image::info::IT_MEMORY;
if(ri.pass_mode == 1) i.type = fileimage::image::info::IT_FILE;
i.headersize = ri.headersize;
return i;
}
void record_files(loaded_rom& rom)
{
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
try {
auto& j = rom.get_rom(i);
record_filehash(j.filename, j.stripped, j.sha_256.read());
} catch(...) {}
try {
auto& j = rom.get_markup(i);
record_filehash(j.filename, j.stripped, j.sha_256.read());
} catch(...) {}
}
}
}
fileimage::hash lsnes_image_hasher;
fileimage::image loaded_rom::null_img;
std::pair<core_type*, core_region*> get_current_rom_info() throw()
{
return std::make_pair(current_rom_type, current_region);
}
loaded_rom::loaded_rom() throw()
: image()
{
rtype = &get_null_type();
region = orig_region = &get_null_region();
region = &image.get_region();
}
loaded_rom::loaded_rom(const std::string& file, core_type& ctype) throw(std::bad_alloc, std::runtime_error)
: image(file, ctype)
{
rtype = &ctype;
region = orig_region = &rtype->get_preferred_region();
unsigned romidx = 0;
std::string bios;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < ctype.get_image_count(); i++)
tmand |= ctype.get_image_info(i).mandatory;
if((bios = ctype.get_biosname()) != "") {
//This thing has a BIOS.
romidx = 1;
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(ctype.get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
pmand |= ctype.get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(ctype.get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= ctype.get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
return;
region = &image.get_region();
}
loaded_rom::loaded_rom(const std::string& file, const std::string& tmpprefer) throw(std::bad_alloc,
std::runtime_error)
: image(file, tmpprefer)
{
std::istream& spec = zip::openrel(file, "");
std::string s;
std::getline(spec, s);
istrip_CR(s);
if(!spec || s != "[GAMEPACK FILE]") {
//This is a Raw ROM image.
regex_results tmp;
std::string ext = regex(".*\\.([^.]*)?", file, "Can't read file extension")[1];
core_type* coretype = find_core_by_extension(ext, tmpprefer);
if(!coretype)
(stringfmt() << "Extension '" << ext << "' unknown").throwex();
rtype = coretype;
region = orig_region = &rtype->get_preferred_region();
unsigned romidx = 0;
std::string bios;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < rtype->get_image_count(); i++)
tmand |= rtype->get_image_info(i).mandatory;
if((bios = coretype->get_biosname()) != "") {
//This thing has a BIOS.
romidx = 1;
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "",
xlate_info(coretype->get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "",
get_xml_info());
pmand |= rtype->get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "",
xlate_info(coretype->get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= rtype->get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
return;
}
load_bundle(file, spec, tmpprefer);
}
void loaded_rom::load_bundle(const std::string& file, std::istream& spec, const std::string& tmpprefer) throw(std::bad_alloc,
std::runtime_error)
{
std::string s;
load_filename = file;
std::vector<std::string> lines;
while(std::getline(spec, s))
lines.push_back(strip_CR(s));
std::string platname = "";
std::string platreg = "";
for(auto i : lines) {
regex_results tmp;
if(tmp = regex("type[ \t]+(.+)", i))
platname = tmp[1];
if(tmp = regex("region[ \t]+(.+)", i))
platreg = tmp[1];
}
//Detect type.
rtype = find_core_by_name(platname, tmpprefer);
if(!rtype)
(stringfmt() << "Not a valid system type '" << platname << "'").throwex();
//Detect region.
bool goodreg = false;
orig_region = &rtype->get_preferred_region();
for(auto i: rtype->get_regions())
if(i->get_iname() == platreg) {
orig_region = i;
goodreg = true;
}
if(!goodreg && platreg != "")
(stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
region = orig_region;
//ROM files.
std::string cromimg[ROM_SLOT_COUNT];
std::string cromxml[ROM_SLOT_COUNT];
for(auto i : lines) {
regex_results tmp;
if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
continue;
size_t idxs = rtype->get_image_count();
size_t idx = idxs;
for(size_t i = 0; i < idxs; i++)
if(rtype->get_image_info(i).iname == tmp[2])
idx = i;
if(idx == idxs)
(stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
if(tmp[1] == "rom")
cromimg[idx] = tmp[3];
else
cromxml[idx] = tmp[3];
}
//Check ROMs.
unsigned mask1 = 0, mask2 = 0;
for(size_t i = 0; i < rtype->get_image_count(); i++) {
auto ii = rtype->get_image_info(i);
mask1 |= ii.mandatory;
if(cromimg[i] != "")
mask2 |= ii.mandatory;
if(cromimg[i] == "" && cromxml[i] != "") {
messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
cromxml[i] = "";
}
}
if(mask1 != mask2)
throw std::runtime_error("Required ROM missing");
//Load ROMs.
for(size_t i = 0; i < rtype->get_image_count(); i++) {
romimg[i] = fileimage::image(lsnes_image_hasher, cromimg[i], file,
xlate_info(rtype->get_image_info(i)));
romxml[i] = fileimage::image(lsnes_image_hasher, cromxml[i], file, get_xml_info());
}
record_files(*this); //Have to do this before patching.
//Patch ROMs.
for(auto i : lines) {
regex_results tmp;
if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
continue;
size_t idxs = rtype->get_image_count();
size_t idx = idxs;
for(size_t i = 0; i < idxs; i++)
if(rtype->get_image_info(i).iname == tmp[2])
idx = i;
if(idx == idxs)
(stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
int32_t offset = 0;
if(tmp[1] != "")
offset = parse_value<int32_t>(tmp[1]);
romimg[idx].patch(zip::readrel(tmp[3], file), offset);
}
//MSU-1 base.
if(cromimg[1] != "")
msu1_base = zip::resolverel(cromimg[1], file);
else
msu1_base = zip::resolverel(cromimg[0], file);
}
namespace
{
bool filter_by_core(core_type& ctype, const std::string& core)
{
return (core == "" || ctype.get_core_identifier() == core);
}
bool filter_by_type(core_type& ctype, const std::string& type)
{
return (type == "" || ctype.get_iname() == type);
}
bool filter_by_region(core_type& ctype, const std::string& region)
{
if(region == "")
return true;
for(auto i : ctype.get_regions())
if(i->get_iname() == region)
return true;
return false;
}
bool filter_by_extension(core_type& ctype, const std::string& file)
{
regex_results tmp = regex(".*\\.([^.]*)", file);
if(!tmp)
return false;
std::string ext = tmp[1];
return ctype.is_known_extension(ext);
}
bool filter_by_fileset(core_type& ctype, const std::string file[ROM_SLOT_COUNT])
{
uint32_t m = 0, t = 0;
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
if(i >= ctype.get_image_count() && file[i] != "")
return false;
auto s = ctype.get_image_info(i);
if(file[i] != "")
m |= s.mandatory;
t |= s.mandatory;
}
return (m == t);
}
core_region* detect_region(core_type* t, const std::string& region)
{
core_region* r = NULL;
for(auto i: t->get_regions())
if(i->get_iname() == region)
r = i;
if(!r && region != "")
(stringfmt() << "Not a valid system region '" << region << "'").throwex();
if(!r) r = &t->get_preferred_region(); //Default region.
return r;
}
region = &image.get_region();
}
loaded_rom::loaded_rom(const std::string& file, const std::string& core, const std::string& type,
const std::string& _region)
: image(file, core, type, _region)
{
core_type* t = NULL;
core_region* r = NULL;
bool fullspec = (core != "" && type != "");
for(auto i : core_type::get_core_types()) {
if(!filter_by_core(*i, core))
continue;
if(!filter_by_type(*i, type))
continue;
if(!fullspec && !filter_by_region(*i, _region))
continue;
if(!fullspec && !filter_by_extension(*i, file))
continue;
t = i;
}
if(!t) throw std::runtime_error("No matching core found");
r = detect_region(t, _region);
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < t->get_image_count(); i++)
tmand |= t->get_image_info(i).mandatory;
std::string bios = t->get_biosname();
unsigned romidx = (bios != "") ? 1 : 0;
if(bios != "") {
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(t->get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
pmand |= t->get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(t->get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= t->get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
rtype = t;
orig_region = region = r;
region = &image.get_region();
}
loaded_rom::loaded_rom(const std::string file[ROM_SLOT_COUNT], const std::string& core, const std::string& type,
const std::string& _region)
: image(file, core, type, _region)
{
core_type* t = NULL;
core_region* r = NULL;
bool fullspec = (core != "" && type != "");
for(auto i : core_type::get_core_types()) {
if(!filter_by_core(*i, core)) {
continue;
}
if(!filter_by_type(*i, type)) {
continue;
}
if(!fullspec && !filter_by_region(*i, _region)) {
continue;
}
if(!fullspec && !filter_by_fileset(*i, file)) {
continue;
}
t = i;
}
if(!t) throw std::runtime_error("No matching core found");
r = detect_region(t, _region);
std::string bios = t->get_biosname();
unsigned romidx = (bios != "") ? 1 : 0;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < 27; i++) {
if(i >= t->get_image_count())
continue;
if(file[i] != "")
pmand |= t->get_image_info(i).mandatory;
tmand |= t->get_image_info(i).mandatory;
romimg[i] = fileimage::image(lsnes_image_hasher, file[i], "", xlate_info(t->get_image_info(i)));
if(zip::file_exists(file[i] + ".xml"))
romxml[i] = fileimage::image(lsnes_image_hasher, file[i] + ".xml", "", get_xml_info());
}
msu1_base = zip::resolverel(file[romidx], "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
rtype = t;
orig_region = region = r;
region = &image.get_region();
}
void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
@ -489,32 +91,34 @@ void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc
core_type* old_type = current_rom_type;
core_core* old_core = current_rom_type->get_core();
current_rom_type = &get_null_type();
if(!orig_region && rtype != &get_null_type())
orig_region = &rtype->get_preferred_region();
if(&rtype() != &get_null_type())
image.setup_region(rtype().get_preferred_region());
if(!region)
region = orig_region;
if(rtype && !orig_region->compatible_with(*region))
region = &image.get_region();
if(!image.get_region().compatible_with(*region))
throw std::runtime_error("Trying to force incompatible region");
if(rtype && !rtype->set_region(*region))
if(!rtype().set_region(*region))
throw std::runtime_error("Trying to force unknown region");
core_romimage images[ROM_SLOT_COUNT];
for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
images[i].markup = (const char*)romxml[i];
images[i].data = (const unsigned char*)romimg[i];
images[i].size = (size_t)romimg[i];
auto& img = image.get_image(i, false);
auto& xml = image.get_image(i, true);
images[i].markup = (const char*)xml;
images[i].data = (const unsigned char*)img;
images[i].size = (size_t)img;
}
if(!rtype->load(images, settings, rtc_sec, rtc_subsec))
if(!rtype().load(images, settings, rtc_sec, rtc_subsec))
throw std::runtime_error("Can't load cartridge ROM");
region = &rtype->get_region();
rtype->power();
auto nominal_fps = rtype->get_video_rate();
auto nominal_hz = rtype->get_audio_rate();
region = &rtype().get_region();
rtype().power();
auto nominal_fps = rtype().get_video_rate();
auto nominal_hz = rtype().get_audio_rate();
core.framerate->set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);
core.mdumper->on_rate_change(nominal_hz.first, nominal_hz.second);
current_rom_type = rtype;
current_rom_type = &rtype();
current_region = region;
//If core changes, unload the cartridge.
if(old_core != current_rom_type->get_core())
@ -567,7 +171,7 @@ std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector
std::vector<char> loaded_rom::save_core_state(bool nochecksum) throw(std::bad_alloc, std::runtime_error)
{
std::vector<char> ret;
rtype->serialize(ret);
rtype().serialize(ret);
if(nochecksum)
return ret;
size_t offset = ret.size();
@ -585,7 +189,7 @@ std::vector<char> loaded_rom::save_core_state(bool nochecksum) throw(std::bad_al
void loaded_rom::load_core_state(const std::vector<char>& buf, bool nochecksum) throw(std::runtime_error)
{
if(nochecksum) {
rtype->unserialize(&buf[0], buf.size());
rtype().unserialize(&buf[0], buf.size());
return;
}
@ -601,29 +205,5 @@ void loaded_rom::load_core_state(const std::vector<char>& buf, bool nochecksum)
if(memcmp(tmp, &buf[buf.size() - 32], 32))
throw std::runtime_error("Savestate corrupt");
}
rtype->unserialize(&buf[0], buf.size() - 32);
rtype().unserialize(&buf[0], buf.size() - 32);
}
bool loaded_rom::is_gamepak(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
{
std::istream* spec = NULL;
try {
spec = &zip::openrel(filename, "");
std::string line;
std::getline(*spec, line);
istrip_CR(line);
bool ret = (line == "[GAMEPACK FILE]");
delete spec;
return ret;
} catch(...) {
delete spec;
return false;
}
}
void set_hasher_callback(std::function<void(uint64_t, uint64_t)> cb)
{
lsnes_image_hasher.set_callback(cb);
}
std::map<std::string, core_type*> preferred_core;

469
src/core/romimage.cpp Normal file
View file

@ -0,0 +1,469 @@
#include "core/romimage.hpp"
#include "core/instance.hpp"
#include "core/messages.hpp"
#include "core/nullcore.hpp"
#include "core/settings.hpp"
#include "core/window.hpp"
#include "library/zip.hpp"
fileimage::image rom_image::null_img;
fileimage::hash lsnes_image_hasher;
namespace
{
core_type* prompt_core_fallback(const std::vector<core_type*>& choices)
{
if(choices.size() == 0)
return NULL;
if(choices.size() == 1)
return choices[0];
rom_request req;
req.cores = choices;
req.core_guessed = false;
req.selected = 0;
//Tell that all ROMs have been correctly guessed, leaving only core select.
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
req.has_slot[i] = false;
req.guessed[i] = false;
}
req.canceled = false;
graphics_driver_request_rom(req);
if(req.canceled)
throw std::runtime_error("Canceled ROM loading");
if(req.selected < choices.size())
return choices[req.selected];
return choices[0];
}
core_type* find_core_by_extension(const std::string& ext, const std::string& tmpprefer)
{
std::string key = "ext:" + ext;
std::list<core_type*> possible = core_type::get_core_types();
core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
std::vector<core_type*> fallbacks;
//Tmpprefer overrides normal preferred core.
if(tmpprefer != "")
for(auto i : possible)
if(i->get_core_identifier() == tmpprefer)
return i;
for(auto i : possible)
if(i->is_known_extension(ext)) {
fallbacks.push_back(i);
if(i == preferred)
return i;
}
core_type* fallback = prompt_core_fallback(fallbacks);
if(!fallback) throw std::runtime_error("No core available to load the ROM");
return fallback;
}
core_type* find_core_by_name(const std::string& name, const std::string& tmpprefer)
{
std::string key = "type:" + name;
std::list<core_type*> possible = core_type::get_core_types();
std::vector<core_type*> fallbacks;
core_type* preferred = preferred_core.count(key) ? preferred_core[key] : NULL;
//Tmpprefer overrides normal preferred core.
if(tmpprefer != "")
for(auto i : possible)
if(i->get_iname() == tmpprefer)
return i;
for(auto i : possible)
if(i->get_iname() == name) {
fallbacks.push_back(i);
if(i == preferred)
return i;
}
core_type* fallback = prompt_core_fallback(fallbacks);
if(!fallback) throw std::runtime_error("No core available to load the ROM");
return fallback;
}
bool filter_by_core(core_type& ctype, const std::string& core)
{
return (core == "" || ctype.get_core_identifier() == core);
}
bool filter_by_type(core_type& ctype, const std::string& type)
{
return (type == "" || ctype.get_iname() == type);
}
bool filter_by_region(core_type& ctype, const std::string& region)
{
if(region == "")
return true;
for(auto i : ctype.get_regions())
if(i->get_iname() == region)
return true;
return false;
}
bool filter_by_extension(core_type& ctype, const std::string& file)
{
regex_results tmp = regex(".*\\.([^.]*)", file);
if(!tmp)
return false;
std::string ext = tmp[1];
return ctype.is_known_extension(ext);
}
bool filter_by_fileset(core_type& ctype, const std::string file[ROM_SLOT_COUNT])
{
uint32_t m = 0, t = 0;
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
if(i >= ctype.get_image_count() && file[i] != "")
return false;
auto s = ctype.get_image_info(i);
if(file[i] != "")
m |= s.mandatory;
t |= s.mandatory;
}
return (m == t);
}
core_region* detect_region(core_type* t, const std::string& region)
{
core_region* r = NULL;
for(auto i: t->get_regions())
if(i->get_iname() == region)
r = i;
if(!r && region != "")
(stringfmt() << "Not a valid system region '" << region << "'").throwex();
if(!r) r = &t->get_preferred_region(); //Default region.
return r;
}
struct fileimage::image::info get_xml_info()
{
fileimage::image::info i;
i.type = fileimage::image::info::IT_MARKUP;
i.headersize = 0;
return i;
}
struct fileimage::image::info xlate_info(core_romimage_info ri)
{
fileimage::image::info i;
if(ri.pass_mode == 0) i.type = fileimage::image::info::IT_MEMORY;
if(ri.pass_mode == 1) i.type = fileimage::image::info::IT_FILE;
i.headersize = ri.headersize;
return i;
}
void record_files(rom_image& rom)
{
for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
try {
auto& j = rom.get_image(i, false);
record_filehash(j.filename, j.stripped, j.sha_256.read());
} catch(...) {}
try {
auto& j = rom.get_image(i, true);
record_filehash(j.filename, j.stripped, j.sha_256.read());
} catch(...) {}
}
}
}
void rom_image::setup_region(core_region& reg)
{
if(!orig_region)
orig_region = &reg;
}
rom_image::rom_image() throw()
{
rtype = &get_null_type();
region = orig_region = &get_null_region();
}
rom_image::rom_image(const std::string& file, core_type& ctype) throw(std::bad_alloc, std::runtime_error)
{
rtype = &ctype;
orig_region = &rtype->get_preferred_region();
unsigned romidx = 0;
std::string bios;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < ctype.get_image_count(); i++)
tmand |= ctype.get_image_info(i).mandatory;
if((bios = ctype.get_biosname()) != "") {
//This thing has a BIOS.
romidx = 1;
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(ctype.get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
pmand |= ctype.get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(ctype.get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= ctype.get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
return;
}
rom_image::rom_image(const std::string& file, const std::string& tmpprefer) throw(std::bad_alloc,
std::runtime_error)
{
std::istream& spec = zip::openrel(file, "");
std::string s;
std::getline(spec, s);
istrip_CR(s);
if(!spec || s != "[GAMEPACK FILE]") {
//This is a Raw ROM image.
regex_results tmp;
std::string ext = regex(".*\\.([^.]*)?", file, "Can't read file extension")[1];
core_type* coretype = find_core_by_extension(ext, tmpprefer);
if(!coretype)
(stringfmt() << "Extension '" << ext << "' unknown").throwex();
rtype = coretype;
region = orig_region = &rtype->get_preferred_region();
unsigned romidx = 0;
std::string bios;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < rtype->get_image_count(); i++)
tmand |= rtype->get_image_info(i).mandatory;
if((bios = coretype->get_biosname()) != "") {
//This thing has a BIOS.
romidx = 1;
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "",
xlate_info(coretype->get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "",
get_xml_info());
pmand |= rtype->get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "",
xlate_info(coretype->get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= rtype->get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
return;
}
load_bundle(file, spec, tmpprefer);
}
void rom_image::load_bundle(const std::string& file, std::istream& spec, const std::string& tmpprefer)
throw(std::bad_alloc, std::runtime_error)
{
std::string s;
load_filename = file;
std::vector<std::string> lines;
while(std::getline(spec, s))
lines.push_back(strip_CR(s));
std::string platname = "";
std::string platreg = "";
for(auto i : lines) {
regex_results tmp;
if(tmp = regex("type[ \t]+(.+)", i))
platname = tmp[1];
if(tmp = regex("region[ \t]+(.+)", i))
platreg = tmp[1];
}
//Detect type.
rtype = find_core_by_name(platname, tmpprefer);
if(!rtype)
(stringfmt() << "Not a valid system type '" << platname << "'").throwex();
//Detect region.
bool goodreg = false;
orig_region = &rtype->get_preferred_region();
for(auto i: rtype->get_regions())
if(i->get_iname() == platreg) {
orig_region = i;
goodreg = true;
}
if(!goodreg && platreg != "")
(stringfmt() << "Not a valid system region '" << platreg << "'").throwex();
region = orig_region;
//ROM files.
std::string cromimg[ROM_SLOT_COUNT];
std::string cromxml[ROM_SLOT_COUNT];
for(auto i : lines) {
regex_results tmp;
if(!(tmp = regex("(rom|xml)[ \t]+([^ \t]+)[ \t]+(.*)", i)))
continue;
size_t idxs = rtype->get_image_count();
size_t idx = idxs;
for(size_t i = 0; i < idxs; i++)
if(rtype->get_image_info(i).iname == tmp[2])
idx = i;
if(idx == idxs)
(stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
if(tmp[1] == "rom")
cromimg[idx] = tmp[3];
else
cromxml[idx] = tmp[3];
}
//Check ROMs.
unsigned mask1 = 0, mask2 = 0;
for(size_t i = 0; i < rtype->get_image_count(); i++) {
auto ii = rtype->get_image_info(i);
mask1 |= ii.mandatory;
if(cromimg[i] != "")
mask2 |= ii.mandatory;
if(cromimg[i] == "" && cromxml[i] != "") {
messages << "WARNING: Slot " << ii.iname << ": XML without ROM." << std::endl;
cromxml[i] = "";
}
}
if(mask1 != mask2)
throw std::runtime_error("Required ROM missing");
//Load ROMs.
for(size_t i = 0; i < rtype->get_image_count(); i++) {
romimg[i] = fileimage::image(lsnes_image_hasher, cromimg[i], file,
xlate_info(rtype->get_image_info(i)));
romxml[i] = fileimage::image(lsnes_image_hasher, cromxml[i], file, get_xml_info());
}
record_files(*this); //Have to do this before patching.
//Patch ROMs.
for(auto i : lines) {
regex_results tmp;
if(!(tmp = regex("patch([+-][0-9]+)?[ \t]+([^ \t]+)[ \t]+(.*)", i)))
continue;
size_t idxs = rtype->get_image_count();
size_t idx = idxs;
for(size_t i = 0; i < idxs; i++)
if(rtype->get_image_info(i).iname == tmp[2])
idx = i;
if(idx == idxs)
(stringfmt() << "Not a valid ROM name '" << tmp[2] << "'").throwex();
int32_t offset = 0;
if(tmp[1] != "")
offset = parse_value<int32_t>(tmp[1]);
romimg[idx].patch(zip::readrel(tmp[3], file), offset);
}
//MSU-1 base.
if(cromimg[1] != "")
msu1_base = zip::resolverel(cromimg[1], file);
else
msu1_base = zip::resolverel(cromimg[0], file);
}
rom_image::rom_image(const std::string& file, const std::string& core, const std::string& type,
const std::string& _region)
{
core_type* t = NULL;
core_region* r = NULL;
bool fullspec = (core != "" && type != "");
for(auto i : core_type::get_core_types()) {
if(!filter_by_core(*i, core))
continue;
if(!filter_by_type(*i, type))
continue;
if(!fullspec && !filter_by_region(*i, _region))
continue;
if(!fullspec && !filter_by_extension(*i, file))
continue;
t = i;
}
if(!t) throw std::runtime_error("No matching core found");
r = detect_region(t, _region);
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < t->get_image_count(); i++)
tmand |= t->get_image_info(i).mandatory;
std::string bios = t->get_biosname();
unsigned romidx = (bios != "") ? 1 : 0;
if(bios != "") {
std::string basename = CORE().setcache->get("firmwarepath") + "/" + bios;
romimg[0] = fileimage::image(lsnes_image_hasher, basename, "", xlate_info(t->get_image_info(0)));
if(zip::file_exists(basename + ".xml"))
romxml[0] = fileimage::image(lsnes_image_hasher, basename + ".xml", "", get_xml_info());
pmand |= t->get_image_info(0).mandatory;
}
romimg[romidx] = fileimage::image(lsnes_image_hasher, file, "", xlate_info(t->get_image_info(romidx)));
if(zip::file_exists(file + ".xml"))
romxml[romidx] = fileimage::image(lsnes_image_hasher, file + ".xml", "", get_xml_info());
pmand |= t->get_image_info(romidx).mandatory;
msu1_base = zip::resolverel(file, "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
rtype = t;
orig_region = region = r;
}
rom_image::rom_image(const std::string file[ROM_SLOT_COUNT], const std::string& core, const std::string& type,
const std::string& _region)
{
core_type* t = NULL;
core_region* r = NULL;
bool fullspec = (core != "" && type != "");
for(auto i : core_type::get_core_types()) {
if(!filter_by_core(*i, core)) {
continue;
}
if(!filter_by_type(*i, type)) {
continue;
}
if(!fullspec && !filter_by_region(*i, _region)) {
continue;
}
if(!fullspec && !filter_by_fileset(*i, file)) {
continue;
}
t = i;
}
if(!t) throw std::runtime_error("No matching core found");
r = detect_region(t, _region);
std::string bios = t->get_biosname();
unsigned romidx = (bios != "") ? 1 : 0;
unsigned pmand = 0, tmand = 0;
for(unsigned i = 0; i < 27; i++) {
if(i >= t->get_image_count())
continue;
if(file[i] != "")
pmand |= t->get_image_info(i).mandatory;
tmand |= t->get_image_info(i).mandatory;
romimg[i] = fileimage::image(lsnes_image_hasher, file[i], "", xlate_info(t->get_image_info(i)));
if(zip::file_exists(file[i] + ".xml"))
romxml[i] = fileimage::image(lsnes_image_hasher, file[i] + ".xml", "", get_xml_info());
}
msu1_base = zip::resolverel(file[romidx], "");
record_files(*this);
if(pmand != tmand)
throw std::runtime_error("Required ROM images missing");
rtype = t;
orig_region = region = r;
}
bool rom_image::is_gamepak(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
{
std::istream* spec = NULL;
try {
spec = &zip::openrel(filename, "");
std::string line;
std::getline(*spec, line);
istrip_CR(line);
bool ret = (line == "[GAMEPACK FILE]");
delete spec;
return ret;
} catch(...) {
delete spec;
return false;
}
}
void set_hasher_callback(std::function<void(uint64_t, uint64_t)> cb)
{
lsnes_image_hasher.set_callback(cb);
}
std::map<std::string, core_type*> preferred_core;

View file

@ -6,7 +6,7 @@
#include "interface/romtype.hpp"
#include "library/string.hpp"
#include "core/rom.hpp"
#include "core/romimage.hpp"
#include "platform/wxwidgets/platform.hpp"

View file

@ -39,7 +39,7 @@ namespace
CHECK_UI_THREAD;
if(coreid.count(findex))
return findex; //Already resolved.
if(loaded_rom::is_gamepak(filename))
if(rom_image::is_gamepak(filename))
return 0; //Gamepaks don't resolve.
//Get the extension.