Internally refactor loaded ROM imageset out of loaded ROM code
This commit is contained in:
parent
1bf49d532d
commit
b7aedc6cd9
6 changed files with 683 additions and 560 deletions
|
@ -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
136
include/core/romimage.hpp
Normal 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
|
478
src/core/rom.cpp
478
src/core/rom.cpp
|
@ -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
469
src/core/romimage.cpp
Normal 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 = ®
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue