Settings-based controllers

This commit is contained in:
Ilari Liusvaara 2013-01-05 09:19:09 +02:00
parent 8d1b67338c
commit 981df4010c
25 changed files with 1571 additions and 1538 deletions

View file

@ -60,10 +60,9 @@ public:
* Set types of ports.
*
* Parameter ptype: The new types for ports.
* Parameter set_core: If true, set the core port types too, otherwise don't do that.
* Throws std::runtime_error: Illegal port type.
*/
void set_ports(const port_type_set& ptype, bool set_core) throw(std::runtime_error);
void set_ports(const port_type_set& ptype) throw(std::runtime_error);
/**
* Get status of current controls (with autohold/autofire factored in).
*

View file

@ -9,7 +9,7 @@
#include <vector>
#include "library/framebuffer.hpp"
#include "library/controller-data.hpp"
#include "core/romtype.hpp"
#include "interface/romtype.hpp"
//Get the CPU rate.
@ -62,10 +62,8 @@ unsigned core_get_poll_flag();
void core_set_poll_flag(unsigned pflag);
//Request reset on next frame.
void core_request_reset(long delay);
//The port type group.
extern port_type_group core_portgroup;
//Number of user ports.
extern unsigned core_userports;
//Valid port types.
extern port_type* core_port_types[];
//Core supports resets.
extern const bool core_supports_reset;
//Core supports delayed resets.

View file

@ -50,9 +50,9 @@ struct moviefile
*/
core_sysregion* gametype;
/**
* The set of port types.
* Settings.
*/
const port_type_set* ports;
std::map<std::string, std::string> settings;
/**
* Emulator Core version string.
*/

View file

@ -6,7 +6,7 @@
#include <vector>
#include <stdexcept>
#include "core/misc.hpp"
#include "core/romtype.hpp"
#include "interface/romtype.hpp"
/**
* Some loaded data or indication of no data.
@ -144,7 +144,8 @@ struct loaded_rom
* throws std::bad_alloc: Not enough memory
* throws std::runtime_error: Switching cartridges failed.
*/
void load(uint64_t rtc_sec, uint64_t rtc_subsec) throw(std::bad_alloc, std::runtime_error);
void load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
throw(std::bad_alloc, std::runtime_error);
};
/**

View file

@ -1,128 +0,0 @@
#ifndef _romtype__hpp__included__
#define _romtype__hpp__included__
#include <list>
#include <string>
#include <cstdint>
#include <vector>
struct core_region;
struct core_type;
struct core_sysregion;
struct core_region
{
public:
core_region(const std::string& iname, const std::string& hname, unsigned priority, unsigned handle,
bool multi, uint64_t* fmagic, int (*compatible)(unsigned rom, unsigned run));
static core_region& lookup(const std::string& name);
const std::string& get_iname();
const std::string& get_hname();
unsigned get_priority();
unsigned get_handle();
bool is_multi();
void fill_framerate_magic(uint64_t* magic); //4 elements filled.
double approx_framerate();
bool compatible_with(core_region& run);
private:
core_region(const core_region&);
core_region& operator=(const core_region&);
std::string iname;
std::string hname;
bool multi;
unsigned handle;
unsigned priority;
uint64_t magic[4];
int (*compatible)(unsigned rom, unsigned run);
};
struct core_romimage_info
{
//If headersize is NULL, always use 0.
core_romimage_info(const std::string& iname, const std::string& hname, unsigned mandatory,
unsigned (*headersize)(size_t imagesize));
//This sets pass_by_filename!
core_romimage_info(const std::string& iname, const std::string& hname, unsigned mandatory);
std::string iname;
std::string hname;
unsigned mandatory;
bool pass_by_filename;
unsigned (*headersize)(size_t imagesize);
};
struct core_romimage
{
const char* markup;
const unsigned char* data;
size_t size;
};
struct core_type
{
public:
core_type(const std::string& iname, const std::string& hname, unsigned id, int (*_load)(core_romimage* images,
uint64_t rtc_sec, uint64_t rtc_subsec), const std::string& extensions);
static std::list<core_type*> get_core_types();
void add_region(core_region& reg);
void add_romimage(core_romimage_info& info, unsigned index);
core_region& get_preferred_region();
std::list<core_region*> get_regions();
core_sysregion& combine_region(core_region& reg);
const std::string& get_iname();
const std::string& get_hname();
const std::list<std::string>& get_extensions();
bool is_known_extension(const std::string& ext);
std::string get_biosname();
unsigned get_id();
unsigned get_image_count();
core_romimage_info get_image_info(unsigned index);
bool load(core_romimage* images, uint64_t rtc_sec, uint64_t rtc_subsec);
private:
int (*loadimg)(core_romimage* images, uint64_t rtc_sec, uint64_t rtc_subsec);
core_type(const core_type&);
core_type& operator=(const core_type&);
unsigned id;
std::string iname;
std::string hname;
std::string biosname;
std::list<std::string> extensions;
std::list<core_region*> regions;
std::vector<core_romimage_info*> imageinfo;
};
struct core_type_region_bind
{
public:
core_type_region_bind(core_type& type, core_region& region);
private:
core_type_region_bind(const core_type_region_bind&);
core_type_region_bind& operator=(const core_type_region_bind&);
};
struct core_type_image_bind
{
public:
core_type_image_bind(core_type& type, core_romimage_info& imageinfo, unsigned index);
private:
core_type_image_bind(const core_type_image_bind&);
core_type_image_bind& operator=(const core_type_image_bind&);
};
struct core_sysregion
{
public:
core_sysregion(const std::string& name, core_type& type, core_region& region);
static core_sysregion& lookup(const std::string& name);
const std::string& get_name();
core_region& get_region();
core_type& get_type();
void fill_framerate_magic(uint64_t* magic); //4 elements filled.
private:
core_sysregion(const core_sysregion&);
core_sysregion& operator=(const core_sysregion&);
std::string name;
core_type& type;
core_region& region;
};
#endif

View file

@ -0,0 +1,17 @@
#ifndef _interface__controller__hpp__included__
#define _interface__controller__hpp__included__
#include "library/controller-data.hpp"
struct controller_set
{
struct port_index_map portindex;
std::vector<port_type*> ports;
};
struct core_port_types
{
port_type** types;
};
#endif

View file

@ -0,0 +1,154 @@
#ifndef _interface__romtype__hpp__included__
#define _interface__romtype__hpp__included__
#include <list>
#include <string>
#include <cstdint>
#include <vector>
#include "interface/controller.hpp"
#include "interface/setting.hpp"
struct core_region;
struct core_type;
struct core_sysregion;
struct core_romimage;
struct core_romimage_info;
struct core_region_params
{
const char* iname;
const char* hname;
unsigned priority;
unsigned handle;
bool multi;
uint64_t framemagic[4];
//Ended by UINT_MAX.
unsigned* compatible_runs;
};
struct core_romimage_info_params
{
const char* iname;
const char* hname;
unsigned mandatory;
int pass_mode; //0 => Content, 1 => File, 2 => Directory.
unsigned headersize; //Header size to remove (0 if there is never header to remove).
};
struct core_type_params
{
const char* iname;
const char* hname;
unsigned id;
unsigned reset_support;
int (*load_rom)(core_romimage* images, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec);
controller_set (*controllerconfig)(std::map<std::string, std::string>& settings);
const char* extensions; //Separate by ;
const char* bios; //Name of BIOS. NULL if none.
core_region** regions; //Terminate with NULL.
core_romimage_info** images; //Terminate with NULL.
core_setting_group* settings;
bool (*set_region)(core_region& region);
};
struct core_region
{
public:
core_region(const core_region_params& params);
const std::string& get_iname();
const std::string& get_hname();
unsigned get_priority();
unsigned get_handle();
bool is_multi();
void fill_framerate_magic(uint64_t* magic); //4 elements filled.
double approx_framerate();
bool compatible_with(core_region& run);
private:
core_region(const core_region&);
core_region& operator=(const core_region&);
std::string iname;
std::string hname;
bool multi;
unsigned handle;
unsigned priority;
uint64_t magic[4];
std::vector<unsigned> compatible;
};
struct core_romimage_info
{
core_romimage_info(const core_romimage_info_params& params);
std::string iname;
std::string hname;
unsigned mandatory;
int pass_mode;
unsigned headersize;
size_t get_headnersize(size_t imagesize);
};
struct core_romimage
{
const char* markup;
const unsigned char* data;
size_t size;
};
struct core_type
{
public:
core_type(core_type_params& params);
static std::list<core_type*> get_core_types();
core_region& get_preferred_region();
std::list<core_region*> get_regions();
core_sysregion& combine_region(core_region& reg);
const std::string& get_iname();
const std::string& get_hname();
const std::list<std::string>& get_extensions();
bool is_known_extension(const std::string& ext);
std::string get_biosname();
unsigned get_id();
unsigned get_image_count();
core_romimage_info get_image_info(unsigned index);
bool load(core_romimage* images, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec);
controller_set controllerconfig(std::map<std::string, std::string>& settings);
unsigned get_reset_support();
core_setting_group& get_settings();
bool set_region(core_region& region);
private:
core_type(const core_type&);
core_type& operator=(const core_type&);
int (*loadimg)(core_romimage* images, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec);
controller_set (*_controllerconfig)(std::map<std::string, std::string>& settings);
bool (*_set_region)(core_region& region);
unsigned id;
unsigned reset_support;
std::string iname;
std::string hname;
std::string biosname;
std::list<std::string> extensions;
std::list<core_region*> regions;
std::vector<core_romimage_info*> imageinfo;
core_setting_group* settings;
};
struct core_sysregion
{
public:
core_sysregion(const std::string& name, core_type& type, core_region& region);
static core_sysregion& lookup(const std::string& name);
const std::string& get_name();
core_region& get_region();
core_type& get_type();
void fill_framerate_magic(uint64_t* magic); //4 elements filled.
private:
core_sysregion(const core_sysregion&);
core_sysregion& operator=(const core_sysregion&);
std::string name;
core_type& type;
core_region& region;
};
#endif

View file

@ -0,0 +1,184 @@
#ifndef _interface__setting__hpp__included__
#define _interface__setting__hpp__included__
#include <string>
#include <stdexcept>
#include <vector>
#include <map>
#include <set>
struct core_setting;
struct core_setting_group;
/**
* A value for setting.
*/
struct core_setting_value
{
/**
* Create a new setting value.
*
* Parameter _setting: The setting this value is for.
* Parameter _iname: The internal value for setting.
* Parameter _hname: The human-readable value for setting.
*/
core_setting_value(struct core_setting& _setting, const std::string& _iname, const std::string& _hname,
signed index)
throw(std::bad_alloc);
/**
* Destructor.
*/
~core_setting_value();
/**
* Setting this is for.
*/
core_setting& setting;
/**
* Internal value.
*/
const std::string iname;
/**
* Human-readable value.
*/
const std::string hname;
/**
* Index.
*/
signed index;
private:
core_setting_value(core_setting_value&);
core_setting_value& operator=(core_setting_value&);
};
/**
* A setting.
*/
struct core_setting
{
/**
* Create a new setting.
*
* Parameter _group: The group setting is in.
* Parameter _iname: The internal name of setting.
* Parameter _hname: The human-readable name of setting.
* Parameter _dflt: The default value.
*/
core_setting(core_setting_group& _group, const std::string& _iname, const std::string& _hname,
const std::string& _dflt) throw(std::bad_alloc);
/**
* Create a new setting with regex.
*
* Parameter _group: The group setting is in.
* Parameter _iname: The internal name of setting.
* Parameter _hname: The human-readable name of setting.
* Parameter _dflt: The default value.
* Parameter _regex: The regular expression used for validation.
*/
core_setting(core_setting_group& _group, const std::string& _iname, const std::string& _hname,
const std::string& _dflt, const std::string& _regex) throw(std::bad_alloc);
/**
* Destructor.
*/
~core_setting();
/**
* Internal name.
*/
const std::string iname;
/**
* Human-readable name.
*/
const std::string hname;
/**
* Regular expression for validation of fretext setting.
*/
const std::string regex;
/**
* The default value.
*/
const std::string dflt;
/**
* The values.
*/
std::vector<core_setting_value*> values;
/**
* Is this setting a boolean?
*/
bool is_boolean() const throw();
/**
* Is this setting a freetext setting?
*/
bool is_freetext() const throw();
/**
* Get set of human-readable strings.
*/
std::vector<std::string> hvalues() const throw(std::runtime_error);
/**
* Translate hvalue to ivalue.
*/
std::string hvalue_to_ivalue(const std::string& hvalue) const throw(std::runtime_error);
/**
* Translate ivalue to index.
*/
signed ivalue_to_index(const std::string& ivalue) const throw(std::runtime_error);
/**
* Validate a value.
*
* Parameter value: The value to validate.
*/
bool validate(const std::string& value) const;
/**
* Register a value.
*/
void do_register(const std::string& name, core_setting_value& value);
/**
* Unregister a value.
*/
void do_unregister(const std::string& name);
/**
* The group setting belongs to.
*/
struct core_setting_group& group;
private:
core_setting(core_setting&);
core_setting& operator=(core_setting&);
};
/**
* A group of settings.
*/
struct core_setting_group
{
/**
* Create a new group of settings.
*/
core_setting_group() throw();
/**
* Destructor.
*/
~core_setting_group() throw();
/**
* Register a setting.
*/
void do_register(const std::string& name, core_setting& setting);
/**
* Unregister a setting.
*/
void do_unregister(const std::string& name);
/**
* The settings.
*/
std::map<std::string, core_setting*> settings;
/**
* Fill a map of settings with defaults.
*/
void fill_defaults(std::map<std::string, std::string>& values) throw(std::bad_alloc);
/**
* Get set of settings.
*/
std::set<std::string> get_setting_set();
private:
core_setting_group(core_setting_group&);
core_setting_group& operator=(core_setting_group&);
};
#endif

View file

@ -168,71 +168,6 @@ struct port_index_map
std::vector<std::pair<unsigned, unsigned>> pcid_map;
};
/**
* Group of port types.
*/
class port_type_group
{
public:
/**
* Construct a group.
*
* Throws std::bad_alloc: Not enough memory.
*/
port_type_group() throw(std::bad_alloc);
/**
* Destroy a group.
*/
~port_type_group() throw();
/**
* Get port type with specified name.
*
* Parameter name: The name of port type.
* Returns: The port type structure.
* Throws std::runtime_error: Specified port type unknown.
*/
port_type& get_type(const std::string& name) const throw(std::runtime_error);
/**
* Get set of all port types.
*
* Returns: The set of all port types.
* Throws std::bad_alloc: Not enough memory.
*/
std::set<port_type*> get_types() const throw(std::bad_alloc);
/**
* Get default port type for specified port.
*
* Parameter port: The port.
* Returns: The default port type.
* Throws std::runtime_error: Bad port.
*/
port_type& get_default_type(unsigned port) const throw(std::runtime_error);
/**
* Add a port type.
*
* Parameter type: Name of the type.
* Parameter port: The port type structure.
* Throws std::bad_alloc: Not enough memory.
*/
void register_type(const std::string& type, port_type& port);
/**
* Remove a port type.
*
* Parameter type: Name of the type.
* Throws std::bad_alloc: Not enough memory.
*/
void unregister_type(const std::string& type);
/**
* Set default type for port.
*
* Parameter port: The port.
* Parameter ptype: The port type to make default.
*/
void set_default(unsigned port, port_type& ptype);
private:
std::map<std::string, port_type*> types;
std::map<unsigned, port_type*> defaults;
};
class port_type_set;
@ -307,14 +242,13 @@ public:
/**
* Create a new port type.
*
* Parameter group: The group the type will belong to.
* Parameter iname: Internal name of the port type.
* Parameter hname: Human-readable name of the port type.
* Parameter id: Identifier.
* Parameter ssize: The storage size in bytes.
* Throws std::bad_alloc: Not enough memory.
*/
port_type(port_type_group& group, const std::string& iname, const std::string& hname, unsigned id,
port_type(const std::string& iname, const std::string& hname, unsigned id,
size_t ssize) throw(std::bad_alloc);
/**
* Unregister a port type.
@ -378,12 +312,6 @@ public:
* Controller info.
*/
port_controller_set* controller_info;
/**
* Set this controller as core controller.
*
* Parameter port: Port to set to.
*/
void (*set_core_controller)(unsigned port);
/**
* Get number of used control indices on controller.
*
@ -391,13 +319,6 @@ public:
* Returns: Number of used control indices.
*/
unsigned (*used_indices)(unsigned controller);
/**
* Construct index map. Only called for port0.
*
* Parameter types: The port types to construct the map for.
* Returns: The index map.
*/
struct port_index_map (*construct_map)(std::vector<port_type*> types);
/**
* Human-readable name.
*/
@ -418,10 +339,6 @@ public:
* Is given controller present?
*/
bool is_present(unsigned controller) const throw();
/**
* Group this port is in.
*/
port_type_group& ingroup;
private:
port_type(const port_type&);
port_type& operator=(const port_type&);
@ -441,10 +358,12 @@ public:
* Make a port type set with specified types. If called again with the same parameters, returns the same object.
*
* Parameter types: The types.
* Parameter control_map: The control map
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Illegal port types.
*/
static port_type_set& make(std::vector<port_type*> types) throw(std::bad_alloc, std::runtime_error);
static port_type_set& make(std::vector<port_type*> types, struct port_index_map control_map)
throw(std::bad_alloc, std::runtime_error);
/**
* Compare sets for equality.
*/
@ -611,7 +530,7 @@ public:
}
}
private:
port_type_set(std::vector<class port_type*> types);
port_type_set(std::vector<class port_type*> types, struct port_index_map control_map);
size_t* port_offsets;
class port_type** port_types;
unsigned port_count;
@ -1268,14 +1187,6 @@ private:
}
};
/**
* Get a dummy port type.
*
* Return value: Dummy port type.
*/
port_type& get_dummy_port_type() throw(std::bad_alloc);
/**
* Generic port control index function.
*/

View file

@ -20,8 +20,8 @@ CFLAGS += -DWITH_OPUS_CODEC
LDFLAGS += -lopus
endif
DUMMY_LIBRARY=core lua fonts library video dummy
PLATFORM_LIBRARY=core lua fonts library video platform
DUMMY_LIBRARY=core lua fonts library interface video dummy
PLATFORM_LIBRARY=core lua fonts library interface video platform
ALLOBJECT=__all__.$(OBJECT_SUFFIX)
ALLFLAGS=__all__.ldflags
DUMMY_LIBRARY_OBJS=$(patsubst %,%/$(ALLOBJECT),$(DUMMY_LIBRARY))
@ -42,6 +42,9 @@ fonts/$(ALLOBJECT): forcelook
library/$(ALLOBJECT): forcelook
$(MAKE) -C library
interface/$(ALLOBJECT): forcelook
$(MAKE) -C interface
lua/$(ALLOBJECT): forcelook
$(MAKE) -C lua
@ -72,6 +75,7 @@ precheck:
$(MAKE) -C lua precheck
$(MAKE) -C dummy precheck
$(MAKE) -C library precheck
$(MAKE) -C interface precheck
$(MAKE) -C platform precheck
$(MAKE) -C util precheck
$(MAKE) -C video precheck
@ -85,6 +89,7 @@ clean:
$(MAKE) -C lua clean
$(MAKE) -C dummy clean
$(MAKE) -C library clean
$(MAKE) -C interface clean
$(MAKE) -C platform clean
$(MAKE) -C util clean
$(MAKE) -C video clean

File diff suppressed because it is too large Load diff

View file

@ -148,13 +148,16 @@ namespace
static int done = 0;
if(done)
return;
process_port(0, core_portgroup.get_default_type(0));
for(unsigned i = 0; i < core_userports; i++) {
for(auto j : core_portgroup.get_types()) {
if(!j->legal(i))
for(unsigned i = 0;; i++) {
bool any = false;
for(unsigned j = 0; core_port_types[j]; j++) {
if(!core_port_types[j]->legal(i))
continue;
process_port(i + 1, *j);
any = true;
process_port(i, *core_port_types[j]);
}
if(!any)
break;
}
done = 1;
}

View file

@ -90,14 +90,10 @@ void controller_state::autofire(std::vector<controller_frame> pattern) throw(std
void reread_active_buttons();
void controller_state::set_ports(const port_type_set& ptype, bool set_core) throw(std::runtime_error)
void controller_state::set_ports(const port_type_set& ptype) throw(std::runtime_error)
{
const port_type_set* oldtype = types;
types = &ptype;
if(set_core) {
for(unsigned i = 1; i < types->ports(); i++)
types->port_type(i).set_core_controller(i);
}
if(oldtype != types) {
_input.set_types(ptype);
_autohold.set_types(ptype);

View file

@ -13,6 +13,7 @@
#include "core/dispatch.hpp"
#include "core/framebuffer.hpp"
#include "core/window.hpp"
#include "interface/romtype.hpp"
#include "library/pixfmt-rgb32.hpp"
#include "library/string.hpp"
#include "library/portfn.hpp"
@ -33,7 +34,6 @@
#define LOGICAL_BUTTON_SELECT 6
#define LOGICAL_BUTTON_START 7
port_type_group core_portgroup;
unsigned core_userports = 0;
extern const bool core_supports_reset = true;
extern const bool core_supports_dreset = false;
@ -61,6 +61,8 @@ namespace
uint32_t accumulator_r = 0;
unsigned accumulator_s = 0;
core_setting_group gambatte_settings;
void init_norom_framebuffer()
{
static bool done = false;
@ -95,93 +97,8 @@ namespace
};
} getinput;
int load_rom_common(core_romimage* img, unsigned flags, uint64_t rtc_sec, uint64_t rtc_subsec,
core_type* inttype)
{
const char* markup = img[0].markup;
int flags2 = 0;
if(markup) {
flags2 = atoi(markup);
flags2 &= 4;
}
flags |= flags2;
const unsigned char* data = img[0].data;
size_t size = img[0].size;
//Reset it really.
instance->~GB();
memset(instance, 0, sizeof(gambatte::GB));
new(instance) gambatte::GB;
instance->setInputGetter(&getinput);
instance->set_walltime_fn(walltime_fn);
memset(primary_framebuffer, 0, sizeof(primary_framebuffer));
frame_overflow = 0;
rtc_fixed = true;
rtc_fixed_val = rtc_sec;
instance->load(data, size, flags);
rtc_fixed = false;
romdata.resize(size);
memcpy(&romdata[0], data, size);
internal_rom = inttype;
do_reset_flag = false;
return 1;
}
int load_rom_dmg(core_romimage* img, uint64_t rtc_sec, uint64_t rtc_subsec)
{
return load_rom_common(img, gambatte::GB::FORCE_DMG, rtc_sec, rtc_subsec, &type_dmg);
}
int load_rom_gbc(core_romimage* img, uint64_t rtc_sec, uint64_t rtc_subsec)
{
return load_rom_common(img, 0, rtc_sec, rtc_subsec, &type_gbc);
}
int load_rom_gbc_gba(core_romimage* img, uint64_t rtc_sec, uint64_t rtc_subsec)
{
return load_rom_common(img, gambatte::GB::GBA_CGB, rtc_sec, rtc_subsec, &type_gbc_gba);
}
uint64_t magic[4] = {35112, 2097152, 16742706, 626688};
core_region region_world("world", "World", 0, 0, false, magic, regions_compatible);
core_romimage_info image_rom_dmg("rom", "Cartridge ROM", 1, NULL);
core_romimage_info image_rom_gbc("rom", "Cartridge ROM", 1, NULL);
core_romimage_info image_rom_gbca("rom", "Cartridge ROM", 1, NULL);
core_type type_dmg("dmg", "Game Boy", 1, load_rom_dmg, "gb;dmg");
core_type type_gbc("gbc", "Game Boy Color", 0, load_rom_gbc, "gbc;cgb");
core_type type_gbc_gba("gbc_gba", "Game Boy Color (GBA)", 2, load_rom_gbc_gba, "");
core_type_region_bind bind_A(type_dmg, region_world);
core_type_region_bind bind_B(type_gbc, region_world);
core_type_region_bind bind_C(type_gbc_gba, region_world);
core_type_image_bind bind_D(type_dmg, image_rom_dmg, 0);
core_type_image_bind bind_E(type_gbc, image_rom_gbc, 0);
core_type_image_bind bind_F(type_gbc_gba, image_rom_gbca, 0);
core_sysregion sr1("gdmg", type_dmg, region_world);
core_sysregion sr2("ggbc", type_gbc, region_world);
core_sysregion sr3("ggbca", type_gbc_gba, region_world);
const char* buttonnames[] = {"left", "right", "up", "down", "A", "B", "select", "start"};
void _set_core_controller(unsigned port) throw() {}
int get_button_id(unsigned controller, unsigned lbid) throw()
{
if(controller != 1)
return -1;
if(lbid == LOGICAL_BUTTON_A) return 0;
if(lbid == LOGICAL_BUTTON_B) return 1;
if(lbid == LOGICAL_BUTTON_SELECT) return 2;
if(lbid == LOGICAL_BUTTON_START) return 3;
if(lbid == LOGICAL_BUTTON_RIGHT) return 4;
if(lbid == LOGICAL_BUTTON_LEFT) return 5;
if(lbid == LOGICAL_BUTTON_UP) return 6;
if(lbid == LOGICAL_BUTTON_DOWN) return 7;
return -1;
}
void system_write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) throw()
{
if(idx < 2 && ctrl < 8)
@ -241,21 +158,11 @@ namespace
return len;
}
void set_core_controller_system(unsigned port) throw()
{
}
unsigned _used_indices(unsigned c)
{
return c ? ((c == 1) ? 8 : 0) : 2;
}
const char* _controller_name(unsigned c)
{
return c ? ((c == 1) ? "gamepad" : NULL) : "(system)";
}
struct port_index_map build_indices(std::vector<port_type*> types);
port_controller_button gamepad_A = {port_controller_button::TYPE_BUTTON, "A"};
port_controller_button gamepad_B = {port_controller_button::TYPE_BUTTON, "B"};
port_controller_button gamepad_s = {port_controller_button::TYPE_BUTTON, "select"};
@ -276,41 +183,136 @@ namespace
struct porttype_system : public port_type
{
porttype_system() : port_type(core_portgroup, "<SYSTEM>", "<SYSTEM>", 9999, 2)
porttype_system() : port_type("<SYSTEM>", "<SYSTEM>", 9999, 2)
{
write = system_write;
read = system_read;
display = system_display;
serialize = system_serialize;
deserialize = system_deserialize;
legal = generic_port_legal<0>;
construct_map = build_indices;
legal = generic_port_legal<1>;
used_indices = _used_indices;
controller_info = &_controller_info;
set_core_controller = set_core_controller_system;
core_portgroup.set_default(0, *this);
}
} psystem;
struct port_index_map build_indices(std::vector<port_type*> types)
int load_rom_common(core_romimage* img, unsigned flags, uint64_t rtc_sec, uint64_t rtc_subsec,
core_type* inttype)
{
struct port_index_map i;
i.indices.resize(100);
for(unsigned j = 0; j < 100; j++) {
i.indices[j].valid = (j < 12) && j != 2 && j != 3;
i.indices[j].port = 0;
i.indices[j].controller = (j < 4) ? 0 : 1;
i.indices[j].control = (j < 4) ? j : (j - 4);;
i.indices[j].marks_nonlag = (j >= 4);
const char* markup = img[0].markup;
int flags2 = 0;
if(markup) {
flags2 = atoi(markup);
flags2 &= 4;
}
i.logical_map.resize(1);
i.logical_map[0] = std::make_pair(0, 1);
i.pcid_map.resize(1);
i.pcid_map[0] = std::make_pair(0, 1);
return i;
flags |= flags2;
const unsigned char* data = img[0].data;
size_t size = img[0].size;
//Reset it really.
instance->~GB();
memset(instance, 0, sizeof(gambatte::GB));
new(instance) gambatte::GB;
instance->setInputGetter(&getinput);
instance->set_walltime_fn(walltime_fn);
memset(primary_framebuffer, 0, sizeof(primary_framebuffer));
frame_overflow = 0;
rtc_fixed = true;
rtc_fixed_val = rtc_sec;
instance->load(data, size, flags);
rtc_fixed = false;
romdata.resize(size);
memcpy(&romdata[0], data, size);
internal_rom = inttype;
do_reset_flag = false;
return 1;
}
int load_rom_dmg(core_romimage* img, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec)
{
return load_rom_common(img, gambatte::GB::FORCE_DMG, rtc_sec, rtc_subsec, &type_dmg);
}
int load_rom_gbc(core_romimage* img, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec)
{
return load_rom_common(img, 0, rtc_sec, rtc_subsec, &type_gbc);
}
int load_rom_gbc_gba(core_romimage* img, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec)
{
return load_rom_common(img, gambatte::GB::GBA_CGB, rtc_sec, rtc_subsec, &type_gbc_gba);
}
port_index_triple t(unsigned p, unsigned c, unsigned i, bool nl)
{
port_index_triple x;
x.port = p;
x.controller = c;
x.control = i;
x.marks_nonlag = nl;
return x;
}
controller_set _controllerconfig(std::map<std::string, std::string>& settings)
{
std::map<std::string, std::string> _settings = settings;
controller_set r;
r.ports.push_back(&psystem);
for(unsigned i = 0; i < 4; i++)
r.portindex.indices.push_back(t(0, 0, i, false));
for(unsigned i = 0; i < 8; i++)
r.portindex.indices.push_back(t(0, 1, i, true));
r.portindex.logical_map.push_back(std::make_pair(0, 1));
r.portindex.pcid_map.push_back(std::make_pair(0, 1));
return r;
}
unsigned world_compatible[] = {0, UINT_MAX};
core_region_params _region_world = {
"world", "World", 1, 0, false, {35112, 2097152, 16742706, 626688}, world_compatible
};
core_romimage_info_params _image_rom = {
"rom", "Cartridge ROM", 1, 0, 0
};
core_region region_world(_region_world);
core_romimage_info image_rom_dmg(_image_rom);
core_romimage_info image_rom_gbc(_image_rom);
core_romimage_info image_rom_gbca(_image_rom);
core_region* regions_gambatte[] = {&region_world, NULL};
core_romimage_info* dmg_images[] = {&image_rom_dmg, NULL};
core_romimage_info* gbc_images[] = {&image_rom_gbc, NULL};
core_romimage_info* gbca_images[] = {&image_rom_gbca, NULL};
core_type_params _type_dmg = {
"dmg", "Game Boy", 1, 1, load_rom_dmg, _controllerconfig, "gb;dmg", NULL,
regions_gambatte, dmg_images, &gambatte_settings, core_set_region
};
core_type_params _type_gbc = {
"gbc", "Game Boy Color", 0, 1, load_rom_gbc, _controllerconfig, "gbc;cgb", NULL,
regions_gambatte, gbc_images, &gambatte_settings, core_set_region
};
core_type_params _type_gbc_gba = {
"gbc_gba", "Game Boy Color (GBA)", 2, 1, load_rom_gbc_gba, _controllerconfig, "", NULL,
regions_gambatte, gbca_images, &gambatte_settings, core_set_region
};
core_type type_dmg(_type_dmg);
core_type type_gbc(_type_gbc);
core_type type_gbc_gba(_type_gbc_gba);
core_sysregion sr1("gdmg", type_dmg, region_world);
core_sysregion sr2("ggbc", type_gbc, region_world);
core_sysregion sr3("ggbca", type_gbc_gba, region_world);
}
port_type* core_port_types[] = {
&psystem, NULL
};
std::string get_logical_button_name(unsigned lbid) throw(std::bad_alloc)
{
if(lbid >= sizeof(buttonnames) / sizeof(buttonnames[0]))

View file

@ -232,6 +232,7 @@ extern time_t random_seed_value;
void do_load_beginning(bool reload) throw(std::bad_alloc, std::runtime_error)
{
bool force_rw = false;
if(!our_movie.gametype && !reload) {
messages << "Can't load movie without a ROM" << std::endl;
return;
@ -243,16 +244,23 @@ void do_load_beginning(bool reload) throw(std::bad_alloc, std::runtime_error)
//Force unlazy rrdata.
rrdata::read_base(our_movie.projectid, false);
rrdata::add_internal();
} else {
auto ctrldata = our_rom->rtype->controllerconfig(our_movie.settings);
port_type_set& portset = port_type_set::make(ctrldata.ports, ctrldata.portindex);
controls.set_ports(portset);
if(our_movie.input.get_types() != portset) {
//The input type changes, so set the types.
force_rw = true;
our_movie.input.clear(portset);
movb.get_movie().load(our_movie.rerecords, our_movie.projectid, our_movie.input);
}
}
try {
bool ro = movb.get_movie().readonly_mode();
bool ro = movb.get_movie().readonly_mode() && !force_rw;
movb.get_movie().reset_state();
random_seed_value = our_movie.movie_rtc_second;
our_rom->load(our_movie.movie_rtc_second, our_movie.movie_rtc_subsecond);
if(our_rom->rtype)
our_rom->load(our_movie.settings, our_movie.movie_rtc_second, our_movie.movie_rtc_subsecond);
our_movie.gametype = &our_rom->rtype->combine_region(*our_rom->region);
else
our_movie.gametype = NULL;
if(reload)
movb.get_movie().readonly_mode(ro);
@ -329,6 +337,9 @@ void do_load_state(struct moviefile& _movie, int lmode)
newmovie.restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
(lmode == LOAD_STATE_PRESERVE) ? &_movie.input : NULL, _movie.projectid);
auto ctrldata = our_rom->rtype->controllerconfig(_movie.settings);
port_type_set& portset = port_type_set::make(ctrldata.ports, ctrldata.portindex);
//Negative return.
rrdata::read_base(_movie.projectid, _movie.lazy_project_create);
rrdata::read(_movie.c_rrdata);
@ -336,17 +347,17 @@ void do_load_state(struct moviefile& _movie, int lmode)
try {
our_rom->region = _movie.gametype ? &(_movie.gametype->get_region()) : NULL;
random_seed_value = _movie.movie_rtc_second;
our_rom->load(_movie.movie_rtc_second, _movie.movie_rtc_subsecond);
our_rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
if(will_load_state) {
//Load the savestate and movie state.
//Set the core ports in order to avoid port state being reinitialized when loading.
controls.set_ports(*_movie.ports, true);
controls.set_ports(portset);
load_core_state(_movie.savestate);
core_set_poll_flag(_movie.poll_flag);
} else {
load_sram(_movie.movie_sram);
controls.set_ports(*_movie.ports, true);
controls.set_ports(portset);
_movie.rtc_second = _movie.movie_rtc_second;
_movie.rtc_subsecond = _movie.movie_rtc_subsecond;
if(!_movie.anchor_savestate.empty())

View file

@ -13,7 +13,6 @@
#define DEFAULT_RTC_SECOND 1000000000ULL
#define DEFAULT_RTC_SUBSECOND 0ULL
void read_linefile(zip_reader& r, const std::string& member, std::string& out, bool conditional = false)
throw(std::bad_alloc, std::runtime_error)
{
@ -30,17 +29,6 @@ void read_linefile(zip_reader& r, const std::string& member, std::string& out, b
}
}
template<typename T>
void read_numeric_file(zip_reader& r, const std::string& member, T& out, bool conditional = false)
throw(std::bad_alloc, std::runtime_error)
{
std::string _out;
read_linefile(r, member, _out, conditional);
if(conditional && _out == "")
return;
out = parse_value<int64_t>(_out);
}
void write_linefile(zip_writer& w, const std::string& member, const std::string& value, bool conditional = false)
throw(std::bad_alloc, std::runtime_error)
{
@ -56,6 +44,53 @@ void write_linefile(zip_writer& w, const std::string& member, const std::string&
}
}
namespace
{
std::map<std::string, std::string> read_settings(zip_reader& r)
{
std::map<std::string, std::string> x;
for(auto i : r) {
if(!regex_match("port[0-9]+|setting\\..+", i))
continue;
std::string s;
if(i.substr(0, 4) == "port")
s = i;
else
s = i.substr(8);
read_linefile(r, i, x[s], true);
}
return x;
}
void write_settings(zip_writer& w, const std::map<std::string, std::string>& settings,
core_setting_group& sgroup)
{
for(auto i : settings) {
if(!sgroup.settings.count(i.first))
continue;
if(sgroup.settings[i.first]->dflt == i.second)
continue;
if(regex_match("port[0-9]+", i.first))
write_linefile(w, i.first, i.second);
else
write_linefile(w, "setting." + i.first, i.second);
}
}
}
template<typename T>
void read_numeric_file(zip_reader& r, const std::string& member, T& out, bool conditional = false)
throw(std::bad_alloc, std::runtime_error)
{
std::string _out;
read_linefile(r, member, _out, conditional);
if(conditional && _out == "")
return;
out = parse_value<int64_t>(_out);
}
template<typename T>
void write_numeric_file(zip_writer& w, const std::string& member, T value) throw(std::bad_alloc,
std::runtime_error)
@ -322,24 +357,11 @@ void write_pollcounters(zip_writer& w, const std::string& file, const std::vecto
}
}
port_type& parse_controller_type(const std::string& type, unsigned port) throw(std::bad_alloc, std::runtime_error)
{
try {
port_type& i = core_portgroup.get_type(type);
if(!i.legal || !(i.legal(port - 1)))
throw 42;
return i;
} catch(...) {
(stringfmt() << "Illegal port " << port << " device '" << type << "'").throwex();
}
}
moviefile::moviefile() throw(std::bad_alloc)
{
static port_type_set dummy_types;
force_corrupt = false;
gametype = NULL;
ports = &dummy_types;
coreversion = "";
projectid = "";
rerecords = "0";
@ -374,15 +396,11 @@ moviefile::moviefile(const std::string& movie) throw(std::bad_alloc, std::runtim
} catch(std::exception& e) {
throw std::runtime_error("Illegal game type '" + tmp + "'");
}
std::vector<port_type*> pt;
pt.push_back(&core_portgroup.get_default_type(0));
for(unsigned i = 0; i < core_userports; i++) {
tmp = core_portgroup.get_default_type(i + 1).name;
read_linefile(r, (stringfmt() << "port" << (i + 1)).str(), tmp, true);
pt.push_back(&core_portgroup.get_type(tmp));
}
ports = &port_type_set::make(pt);
input.clear(*ports);
settings = read_settings(r);
auto ctrldata = gametype->get_type().controllerconfig(settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex);
input.clear(ports);
read_linefile(r, "gamename", gamename, true);
read_linefile(r, "projectid", projectid);
rerecords = read_rrdata(r, c_rrdata);
@ -447,9 +465,7 @@ void moviefile::save(const std::string& movie, unsigned compression) throw(std::
{
zip_writer w(movie, compression);
write_linefile(w, "gametype", gametype->get_name());
for(unsigned i = 0; i < core_userports; i++)
if(ports->port_type(i + 1).name != core_portgroup.get_default_type(i + 1).name)
write_linefile(w, (stringfmt() << "port" << (i + 1)).str(), ports->port_type(i + 1).name);
write_settings(w, settings, gametype->get_type().get_settings());
write_linefile(w, "gamename", gamename, true);
write_linefile(w, "systemid", "lsnes-rr1");
write_linefile(w, "controlsversion", "0");

View file

@ -10,6 +10,7 @@
#include "core/rom.hpp"
#include "core/settings.hpp"
#include "core/window.hpp"
#include "library/portfn.hpp"
#include "library/patch.hpp"
#include "library/sha256.hpp"
#include "library/string.hpp"
@ -39,8 +40,67 @@ namespace boost_fs = boost::filesystem;
namespace
{
core_type* current_rom_type = NULL;
core_region* current_region = NULL;
const char* null_chars = "F";
port_controller_button* null_buttons[] = {};
port_controller simple_controller = {"(system)", "system", 0, null_buttons};
port_controller* simple_controllers[] = {&simple_controller};
port_controller_set simple_port = {1, simple_controllers};
struct porttype_null : public port_type
{
porttype_null() : port_type("null", "null", 999997, 1)
{
write = generic_port_write<1, 0, 1>;
read = generic_port_read<1, 0, 1>;
display = generic_port_display<1, 0, 1, &null_chars>;
serialize = generic_port_serialize<1, 0, 1, &null_chars>;
deserialize = generic_port_deserialize<1, 0, 1>;
legal = generic_port_legal<1>;
controller_info = &simple_port;
used_indices = generic_used_indices<1, 1>;
}
} pnull;
port_index_triple sync_triple = {true, 0, 0, 0, false };
controller_set null_controllerconfig(std::map<std::string, std::string>& settings)
{
controller_set x;
x.ports.push_back(&pnull);
x.portindex.indices.push_back(sync_triple);
return x;
}
bool set_region_null(core_region& reg)
{
return true;
}
int load_rom_null(core_romimage* img, std::map<std::string, std::string>& settings,
uint64_t secs, uint64_t subsecs)
{
return 0;
}
core_setting_group null_settings;
unsigned null_compatible[] = {0, UINT_MAX};
struct core_region_params _null_region = {
"null", "(null)", 0, 0, false, {1, 60, 16666666, 40}, null_compatible
};
core_region null_region(_null_region);
core_region* null_regions[] = {&null_region, NULL};
core_romimage_info* null_images[] = {NULL};
core_type_params _type_null = {
"null", "(null)", 9999, 0, load_rom_null, null_controllerconfig,
"", NULL, null_regions, null_images, &null_settings, set_region_null
};
core_type type_null(_type_null);
core_sysregion sysregion_null("null", type_null, null_region);
core_type* current_rom_type = &type_null;
core_region* current_region = &null_region;
}
loaded_slot::loaded_slot() throw(std::bad_alloc)
@ -59,11 +119,11 @@ loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
if(filename == "") {
valid = false;
sha_256 = "";
filename_flag = (!xml && imginfo.pass_by_filename);
filename_flag = (!xml && imginfo.pass_mode);
return;
}
//XMLs are always loaded, no matter what.
if(!xml && imginfo.pass_by_filename) {
if(!xml && imginfo.pass_mode) {
std::string _filename = filename;
//Translate the passed filename to absolute one.
_filename = resolve_file_relative(_filename, base);
@ -86,8 +146,8 @@ loaded_slot::loaded_slot(const std::string& filename, const std::string& base,
filename_flag = false;
valid = true;
data = read_file_relative(filename, base);
if(!xml)
headered = imginfo.headersize(data.size());
if(!xml && imginfo.headersize)
headered = ((data.size() % (2 * imginfo.headersize)) == imginfo.headersize) ? imginfo.headersize : 0;
if(headered && !xml) {
if(data.size() >= headered) {
memmove(&data[0], &data[headered], data.size() - headered);
@ -136,8 +196,8 @@ std::pair<core_type*, core_region*> get_current_rom_info() throw()
loaded_rom::loaded_rom() throw()
{
rtype = NULL;
region = orig_region = NULL;
rtype = &type_null;
region = orig_region = &null_region;
}
loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runtime_error)
@ -187,11 +247,11 @@ loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runti
}
//Detect type.
rtype = NULL;
rtype = &type_null;
for(auto i : possible)
if(i->get_iname() == platname)
rtype = i;
if(!rtype)
if(rtype == &type_null)
(stringfmt() << "Not a valid system type '" << platname << "'").throwex();
//Detect region.
@ -272,16 +332,17 @@ loaded_rom::loaded_rom(const std::string& file) throw(std::bad_alloc, std::runti
msu1_base = resolve_file_relative(cromimg[0], file);
}
void loaded_rom::load(uint64_t rtc_sec, uint64_t rtc_subsec) throw(std::bad_alloc, std::runtime_error)
void loaded_rom::load(std::map<std::string, std::string>& settings, uint64_t rtc_sec, uint64_t rtc_subsec)
throw(std::bad_alloc, std::runtime_error)
{
current_rom_type = NULL;
if(!orig_region && rtype)
current_rom_type = &type_null;
if(!orig_region && rtype != &type_null)
orig_region = &rtype->get_preferred_region();
if(!region)
region = orig_region;
if(rtype && !orig_region->compatible_with(*region))
throw std::runtime_error("Trying to force incompatible region");
if(rtype && !core_set_region(*region))
if(rtype && !rtype->set_region(*region))
throw std::runtime_error("Trying to force unknown region");
core_romimage images[sizeof(romimg)/sizeof(romimg[0])];
@ -291,13 +352,17 @@ void loaded_rom::load(uint64_t rtc_sec, uint64_t rtc_subsec) throw(std::bad_allo
images[i].size = (size_t)romimg[i];
}
if(rtype) {
if(!rtype->load(images, rtc_sec, rtc_subsec))
if(!rtype->load(images, settings, rtc_sec, rtc_subsec))
throw std::runtime_error("Can't load cartridge ROM");
} else
core_unload_cartridge();
if(rtype == &type_null) {
region = &null_region;
} else {
region = &core_get_region();
core_power();
}
auto nominal_fps = get_video_rate();
auto nominal_hz = get_audio_rate();
set_nominal_framerate(1.0 * nominal_fps.first / nominal_fps.second);

16
src/interface/Makefile Normal file
View file

@ -0,0 +1,16 @@
OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard *.cpp))
.PRECIOUS: %.$(OBJECT_SUFFIX)
__all__.$(OBJECT_SUFFIX): $(OBJECTS)
$(REALLD) -r -o $@ $^
touch __all__.ldflags
%.$(OBJECT_SUFFIX): %.cpp
$(REALCC) $(CFLAGS) -c -o $@ $< -I../../include
precheck:
@true
clean:
rm -f *.$(OBJECT_SUFFIX) *.ldflags

View file

@ -1,4 +1,4 @@
#include "core/romtype.hpp"
#include "interface/romtype.hpp"
#include "library/minmax.hpp"
#include "library/string.hpp"
#include <map>
@ -7,6 +7,7 @@
#include <cctype>
#include <stdexcept>
#include <list>
#include <limits>
namespace
{
@ -22,58 +23,6 @@ namespace
return x;
}
std::map<std::string, core_region*>& regionss()
{
static std::map<std::string, core_region*> x;
return x;
}
std::list<std::pair<core_type*, core_region*>>& reglist1()
{
static std::list<std::pair<core_type*, core_region*>> x;
return x;
}
std::list<std::pair<core_type*, std::pair<core_romimage_info*, unsigned>>>& reglist2()
{
static std::list<std::pair<core_type*, std::pair<core_romimage_info*, unsigned>>> x;
return x;
}
void process_registrations()
{
auto& l = reglist1();
for(auto i = l.begin(); i != l.end();) {
auto& m = types();
bool done = false;
for(auto j : m)
if(j.second == i->first) {
auto iold = i;
i->first->add_region(*i->second);
i++;
l.erase(iold);
done = true;
}
if(!done)
i++;
}
auto& l2 = reglist2();
for(auto i = l2.begin(); i != l2.end();) {
auto& m = types();
bool done = false;
for(auto j : m)
if(j.second == i->first) {
auto iold = i;
i->first->add_romimage(*i->second.first, i->second.second);
i++;
l2.erase(iold);
done = true;
}
if(!done)
i++;
}
}
bool compare_coretype(core_type* a, core_type* b)
{
return (a->get_id() < b->get_id());
@ -90,27 +39,29 @@ namespace
}
}
core_region::core_region(const std::string& _iname, const std::string& _hname, unsigned _priority, unsigned _handle,
bool _multi, uint64_t* _fmagic, int (*_compatible)(unsigned rom, unsigned run))
: iname(_iname), hname(_hname), priority(_priority), handle(_handle), compatible(_compatible),
multi(_multi)
core_region::core_region(const core_region_params& params)
{
iname = params.iname;
hname = params.hname;
multi = params.multi;
handle = params.handle;
priority = params.priority;
for(size_t i = 0; i < 4; i++)
magic[i] = _fmagic[i];
regionss()[_iname] = this;
magic[i] = params.framemagic[4];
for(size_t i = 0;; i++)
if(params.compatible_runs[i] == std::numeric_limits<unsigned>::max())
break;
else
compatible.push_back(params.compatible_runs[i]);
}
bool core_region::compatible_with(core_region& run)
{
return (compatible(handle, run.handle) != 0);
}
core_region& core_region::lookup(const std::string& name)
{
if(regionss().count(name))
return *(regionss()[name]);
else
throw std::runtime_error("Bad region");
for(auto i : compatible)
if(i == run.handle)
return true;
return false;
}
bool core_region::is_multi()
@ -149,6 +100,160 @@ double core_region::approx_framerate()
return (double)magic[1] / magic[0];
}
core_romimage_info::core_romimage_info(const core_romimage_info_params& params)
{
iname = params.iname;
hname = params.hname;
mandatory = params.mandatory;
pass_mode = params.pass_mode;
headersize = params.headersize;
}
size_t core_romimage_info::get_headnersize(size_t imagesize)
{
if(headersize && imagesize % (2 * headersize) == headersize)
return headersize;
return 0;
}
core_type::core_type(core_type_params& params)
{
iname = params.iname;
hname = params.hname;
id = params.id;
reset_support = params.reset_support;
loadimg = params.load_rom;
_controllerconfig = params.controllerconfig;
_set_region = params.set_region;
settings = params.settings;
if(params.bios)
biosname = params.bios;
for(size_t i = 0; params.regions[i]; i++)
regions.push_back(params.regions[i]);
for(size_t i = 0; params.images[i]; i++)
imageinfo.push_back(params.images[i]);
if(params.extensions) {
std::string tmp = params.extensions;
while(tmp != "") {
std::string ext;
extract_token(tmp, ext, ";");
extensions.push_back(ext);
}
}
types()[iname] = this;
}
unsigned core_type::get_id()
{
return id;
}
const std::string& core_type::get_iname()
{
return iname;
}
const std::string& core_type::get_hname()
{
return hname;
}
unsigned core_type::get_image_count()
{
return imageinfo.size();
}
core_setting_group& core_type::get_settings()
{
return *settings;
}
core_romimage_info core_type::get_image_info(unsigned index)
{
if(index >= imageinfo.size())
throw std::runtime_error("Requested invalid image index");
return *imageinfo[index];
}
std::list<core_type*> core_type::get_core_types()
{
std::list<core_type*> ret;
for(auto i : types())
ret.push_back(i.second);
ret.sort(compare_coretype);
return ret;
}
unsigned core_type::get_reset_support()
{
return reset_support;
}
std::list<core_region*> core_type::get_regions()
{
std::list<core_region*> ret = regions;
ret.sort(compare_regions);
return ret;
}
core_region& core_type::get_preferred_region()
{
core_region* p = NULL;
unsigned cutoff = 0;
for(auto i : regions) {
unsigned pri = i->get_priority();
if(pri >= cutoff) {
cutoff = max(pri + 1, pri);
p = i;
}
}
return *p;
}
bool core_type::load(core_romimage* images, std::map<std::string, std::string>& settings, uint64_t rtc_sec,
uint64_t rtc_subsec)
{
return (loadimg(images, settings, rtc_sec, rtc_subsec) >= 0);
}
core_sysregion& core_type::combine_region(core_region& reg)
{
for(auto i : sysregions())
if(&(i.second->get_type()) == this && &(i.second->get_region()) == &reg)
return *(i.second);
throw std::runtime_error("Invalid region for system type");
}
const std::list<std::string>& core_type::get_extensions()
{
return extensions;
}
std::string core_type::get_biosname()
{
return biosname;
}
bool core_type::is_known_extension(const std::string& ext)
{
std::string _ext = ext;
std::transform(_ext.begin(), _ext.end(), _ext.begin(), ::tolower);
for(auto i : extensions)
if(i == _ext)
return true;
return false;
}
controller_set core_type::controllerconfig(std::map<std::string, std::string>& settings)
{
return _controllerconfig(settings);
}
bool core_type::set_region(core_region& region)
{
return _set_region(region);
}
core_sysregion::core_sysregion(const std::string& _name, core_type& _type, core_region& _region)
: name(_name), type(_type), region(_region)
{
@ -182,145 +287,3 @@ void core_sysregion::fill_framerate_magic(uint64_t* magic)
{
region.fill_framerate_magic(magic);
}
unsigned core_type::get_id()
{
return id;
}
void core_type::add_region(core_region& reg)
{
regions.push_back(&reg);
}
void core_type::add_romimage(core_romimage_info& info, unsigned index)
{
if(imageinfo.size() <= index)
imageinfo.resize(index + 1);
imageinfo[index] = &info;
}
const std::string& core_type::get_iname()
{
return iname;
}
const std::string& core_type::get_hname()
{
return hname;
}
unsigned core_type::get_image_count()
{
return imageinfo.size();
}
core_romimage_info::core_romimage_info(const std::string& _iname, const std::string& _hname, unsigned _mandatory,
unsigned (*_headersize)(size_t imagesize))
: iname(_iname), hname(_hname), headersize(_headersize ? _headersize : default_headersize),
mandatory(_mandatory), pass_by_filename(false)
{
}
core_romimage_info::core_romimage_info(const std::string& _iname, const std::string& _hname, unsigned _mandatory)
: iname(_iname), hname(_hname), headersize(NULL), mandatory(_mandatory), pass_by_filename(true)
{
}
core_romimage_info core_type::get_image_info(unsigned index)
{
if(index >= imageinfo.size())
throw std::runtime_error("Requested invalid image index");
return *imageinfo[index];
}
std::list<core_type*> core_type::get_core_types()
{
std::list<core_type*> ret;
for(auto i : types())
ret.push_back(i.second);
ret.sort(compare_coretype);
return ret;
}
std::list<core_region*> core_type::get_regions()
{
std::list<core_region*> ret = regions;
ret.sort(compare_regions);
return ret;
}
core_region& core_type::get_preferred_region()
{
core_region* p = NULL;
unsigned cutoff = 0;
for(auto i : regions) {
unsigned pri = i->get_priority();
if(pri >= cutoff) {
cutoff = max(pri + 1, pri);
p = i;
}
}
return *p;
}
bool core_type::load(core_romimage* images, uint64_t rtc_sec, uint64_t rtc_subsec)
{
return (loadimg(images, rtc_sec, rtc_subsec) >= 0);
}
core_sysregion& core_type::combine_region(core_region& reg)
{
for(auto i : sysregions())
if(&(i.second->get_type()) == this && &(i.second->get_region()) == &reg)
return *(i.second);
throw std::runtime_error("Invalid region for system type");
}
core_type::core_type(const std::string& _iname, const std::string& _hname, unsigned _id,
int (*_load)(core_romimage* images, uint64_t rtc_sec, uint64_t rtc_subsec), const std::string& _extensions)
: iname(_iname), hname(_hname), loadimg(_load), id(_id)
{
types()[iname] = this;
std::string ext;
std::string ext2 = _extensions;
if(ext2.find_first_of("!") < ext2.length())
extract_token(ext2, biosname, "!");
while(ext2 != "") {
extract_token(ext2, ext, ";");
extensions.push_back(ext);
}
process_registrations();
}
const std::list<std::string>& core_type::get_extensions()
{
return extensions;
}
std::string core_type::get_biosname()
{
return biosname;
}
bool core_type::is_known_extension(const std::string& ext)
{
std::string _ext = ext;
std::transform(_ext.begin(), _ext.end(), _ext.begin(), ::tolower);
for(auto i : extensions)
if(i == _ext)
return true;
return false;
}
core_type_region_bind::core_type_region_bind(core_type& type, core_region& region)
{
reglist1().push_back(std::make_pair(&type, &region));
process_registrations();
}
core_type_image_bind::core_type_image_bind(core_type& type, core_romimage_info& imageinfo, unsigned index)
{
reglist2().push_back(std::make_pair(&type, std::make_pair(&imageinfo, index)));
process_registrations();
}

138
src/interface/setting.cpp Normal file
View file

@ -0,0 +1,138 @@
#include "interface/setting.hpp"
#include "library/register-queue.hpp"
#include "library/string.hpp"
core_setting_value::core_setting_value(struct core_setting& _setting, const std::string& _iname,
const std::string& _hname, signed _index) throw(std::bad_alloc)
: setting(_setting), iname(_iname), hname(_hname), index(_index)
{
register_queue<core_setting, core_setting_value>::do_register(setting, iname, *this);
}
core_setting_value::~core_setting_value()
{
register_queue<core_setting, core_setting_value>::do_unregister(setting, iname);
}
core_setting::core_setting(core_setting_group& _group, const std::string& _iname, const std::string& _hname,
const std::string& _dflt) throw(std::bad_alloc)
: group(_group), iname(_iname), hname(_hname), dflt(_dflt), regex(".*")
{
register_queue<core_setting, core_setting_value>::do_ready(*this, true);
register_queue<core_setting_group, core_setting>::do_register(group, iname, *this);
}
core_setting::core_setting(core_setting_group& _group, const std::string& _iname, const std::string& _hname,
const std::string& _dflt, const std::string& _regex) throw(std::bad_alloc)
: group(_group), iname(_iname), hname(_hname), dflt(_dflt), regex(_regex)
{
register_queue<core_setting, core_setting_value>::do_ready(*this, true);
register_queue<core_setting_group, core_setting>::do_register(group, iname, *this);
}
core_setting::~core_setting()
{
register_queue<core_setting, core_setting_value>::do_ready(*this, false);
register_queue<core_setting_group, core_setting>::do_unregister(group, iname);
}
void core_setting::do_register(const std::string& name, core_setting_value& value)
{
values.push_back(&value);
}
void core_setting::do_unregister(const std::string& name)
{
for(auto i = values.begin(); i != values.end(); i++)
if((*i)->iname == name) {
values.erase(i);
return;
}
}
bool core_setting::is_boolean() const throw()
{
if(values.size() != 2)
return false;
std::string a = values[0]->iname;
std::string b = values[1]->iname;
if(a < b)
std::swap(a, b);
return (a == "0" && b == "1");
}
bool core_setting::is_freetext() const throw()
{
return (values.size() == 0);
}
bool core_setting::validate(const std::string& value) const
{
if(values.size() != 0) {
for(auto i : values)
if(i->iname == value)
return true;
return false;
} else
return regex_match(regex, value);
}
core_setting_group::core_setting_group() throw()
{
register_queue<core_setting_group, core_setting>::do_ready(*this, true);
}
core_setting_group::~core_setting_group() throw()
{
register_queue<core_setting_group, core_setting>::do_ready(*this, false);
}
void core_setting_group::do_register(const std::string& name, core_setting& setting)
{
settings[name] = &setting;
}
void core_setting_group::do_unregister(const std::string& name)
{
settings.erase(name);
}
void core_setting_group::fill_defaults(std::map<std::string, std::string>& values) throw(std::bad_alloc)
{
for(auto i : settings)
if(!values.count(i.first))
values[i.first] = i.second->dflt;
}
std::set<std::string> core_setting_group::get_setting_set()
{
std::set<std::string> r;
for(auto i : settings)
r.insert(i.first);
return r;
}
std::vector<std::string> core_setting::hvalues() const throw(std::runtime_error)
{
std::vector<std::string> x;
if(values.size() == 0)
throw std::runtime_error("hvalues() not valid for freetext settings");
for(auto i : values)
x.push_back(i->hname);
return x;
}
std::string core_setting::hvalue_to_ivalue(const std::string& hvalue) const throw(std::runtime_error)
{
for(auto i : values)
if(i->hname == hvalue)
return i->iname;
throw std::runtime_error("Invalid hvalue for setting");
}
signed core_setting::ivalue_to_index(const std::string& ivalue) const throw(std::runtime_error)
{
for(auto i : values)
if(i->iname == ivalue)
return i->index;
throw std::runtime_error("Invalid ivalue for setting");
}

View file

@ -8,34 +8,6 @@
namespace
{
void set_core_controller_illegal(unsigned port) throw()
{
std::cerr << "Attempt to set core port type to INVALID port type" << std::endl;
exit(1);
}
int button_id_illegal(unsigned controller, unsigned lbid)
{
return -1;
}
struct port_index_map invalid_construct_map(std::vector<port_type*> types)
{
struct port_index_map i;
i.indices.resize(1);
i.indices[0].valid = true;
i.indices[0].port = 0;
i.indices[0].controller = 0;
i.indices[0].control = 0;
return i;
}
inline const char* invalid_controller_name(unsigned c)
{
return c ? NULL : "(system)";
}
const char* invalid_chars = "";
const char* basecontrol_chars = "F";
port_controller_button* null_buttons[] = {};
@ -43,38 +15,18 @@ namespace
port_controller* simple_controllers[] = {&simple_controller};
port_controller_set simple_port = {1, simple_controllers};
struct port_type_group invalid_group;
struct porttype_invalid : public port_type
{
porttype_invalid() : port_type(invalid_group, "invalid-port-type", "invalid-port-type", 999999, 0)
{
write = generic_port_write<0, 0, 0>;
read = generic_port_read<0, 0, 0>;
display = generic_port_display<0, 0, 0, &invalid_chars>;
serialize = generic_port_serialize<0, 0, 0, &invalid_chars>;
deserialize = generic_port_deserialize<0, 0, 0>;
legal = generic_port_legal<0xFFFFFFFFU>;
controller_info = &simple_port;
used_indices = generic_used_indices<0, 0>;
construct_map = invalid_construct_map;
set_core_controller = set_core_controller_illegal;
}
};
struct porttype_basecontrol : public port_type
{
porttype_basecontrol() : port_type(invalid_group, "basecontrol", "basecontrol", 999998, 0)
porttype_basecontrol() : port_type("basecontrol", "basecontrol", 999998, 0)
{
write = generic_port_write<1, 0, 1>;
read = generic_port_read<1, 0, 1>;
display = generic_port_display<1, 0, 1, &basecontrol_chars>;
serialize = generic_port_serialize<1, 0, 1, &basecontrol_chars>;
deserialize = generic_port_deserialize<1, 0, 1>;
legal = generic_port_legal<0>;
legal = generic_port_legal<1>;
controller_info = &simple_port;
construct_map = invalid_construct_map;
used_indices = generic_used_indices<1, 1>;
set_core_controller = set_core_controller_illegal;
}
static port_type& get()
{
@ -82,128 +34,16 @@ namespace
return x;
}
};
struct pending_registration
{
port_type_group* group;
std::string name;
port_type* toreg;
};
globalwrap<mutex_class> reg_mutex;
globalwrap<std::set<port_type_group*>> ready_groups;
globalwrap<std::list<pending_registration>> pending_registrations;
void run_pending_registrations()
{
umutex_class m(reg_mutex());
auto i = pending_registrations().begin();
while(i != pending_registrations().end()) {
auto entry = i++;
if(ready_groups().count(entry->group)) {
entry->group->register_type(entry->name, *entry->toreg);
pending_registrations().erase(entry);
}
}
}
void add_registration(port_type_group& group, const std::string& name, port_type& type)
{
{
umutex_class m(reg_mutex());
pending_registration p;
p.group = &group;
p.name = name;
p.toreg = &type;
pending_registrations().push_back(p);
}
run_pending_registrations();
}
void delete_registration(port_type_group& group, const std::string& name)
{
{
umutex_class m(reg_mutex());
if(ready_groups().count(&group))
group.unregister_type(name);
else {
auto i = pending_registrations().begin();
while(i != pending_registrations().end()) {
auto entry = i++;
if(entry->group == &group && entry->name == name)
pending_registrations().erase(entry);
}
}
}
}
}
port_type_group::port_type_group() throw(std::bad_alloc)
{
{
umutex_class m(reg_mutex());
ready_groups().insert(this);
}
run_pending_registrations();
}
port_type_group::~port_type_group() throw()
{
{
umutex_class m(reg_mutex());
ready_groups().erase(this);
}
}
port_type& port_type_group::get_type(const std::string& name) const throw(std::runtime_error)
{
if(types.count(name))
return *(types.find(name)->second);
else
throw std::runtime_error("Unknown port type");
}
port_type& port_type_group::get_default_type(unsigned port) const throw(std::runtime_error)
{
if(defaults.count(port))
return *(defaults.find(port)->second);
else
throw std::runtime_error("No default for this port");
}
std::set<port_type*> port_type_group::get_types() const throw(std::bad_alloc)
{
std::set<port_type*> p;
for(auto i : types)
p.insert(i.second);
return p;
}
void port_type_group::set_default(unsigned port, port_type& ptype)
{
defaults[port] = &ptype;
}
void port_type_group::register_type(const std::string& type, port_type& port)
{
types[type] = &port;
}
void port_type_group::unregister_type(const std::string& type)
{
types.erase(type);
}
port_type::port_type(port_type_group& group, const std::string& iname, const std::string& _hname, unsigned id,
port_type::port_type(const std::string& iname, const std::string& _hname, unsigned id,
size_t ssize) throw(std::bad_alloc)
: ingroup(group), name(iname), hname(_hname), pt_id(id), storage_size(ssize)
: name(iname), hname(_hname), pt_id(id), storage_size(ssize)
{
add_registration(ingroup, name, *this);
}
port_type::~port_type() throw()
{
delete_registration(ingroup, name);
}
bool port_type::is_present(unsigned controller) const throw()
@ -257,13 +97,14 @@ port_type_set::port_type_set() throw()
indices_tab = &dummy_index;
}
port_type_set& port_type_set::make(std::vector<class port_type*> types) throw(std::bad_alloc, std::runtime_error)
port_type_set& port_type_set::make(std::vector<class port_type*> types, struct port_index_map control_map)
throw(std::bad_alloc, std::runtime_error)
{
for(auto i : bindings())
if(i.matches(types))
return *(i.stype);
//Not found, create new.
port_type_set& ret = *new port_type_set(types);
port_type_set& ret = *new port_type_set(types, control_map);
binding b;
b.types = types;
b.stype = &ret;
@ -271,12 +112,12 @@ port_type_set& port_type_set::make(std::vector<class port_type*> types) throw(st
return ret;
}
port_type_set::port_type_set(std::vector<class port_type*> types)
port_type_set::port_type_set(std::vector<class port_type*> types, struct port_index_map control_map)
{
port_count = types.size();
//Verify legality of port types.
for(size_t i = 0; i < port_count; i++)
if(!types[i] || (i > 0 && !types[i]->legal(i - 1)))
if(!types[i] || !types[i]->legal(i))
throw std::runtime_error("Illegal port types");
//Count maximum number of controller indices to determine the controller multiplier.
controller_multiplier = 1;
@ -288,8 +129,6 @@ port_type_set::port_type_set(std::vector<class port_type*> types)
for(size_t i = 0; i < port_count; i++)
port_multiplier = max(port_multiplier, controller_multiplier *
(size_t)types[i]->controller_info->controller_count);
//Query core about maps.
struct port_index_map control_map = types[0]->construct_map(types);
//Allocate the per-port tables.
port_offsets = new size_t[types.size()];
port_types = new class port_type*[types.size()];
@ -351,12 +190,6 @@ short read_axis_value(const char* buf, size_t& idx) throw()
return static_cast<short>(numval);
}
port_type& get_dummy_port_type() throw(std::bad_alloc)
{
static porttype_invalid inv;
return inv;
}
namespace
{
port_type_set& dummytypes()

View file

@ -388,6 +388,7 @@ private:
bool settings_mode;
std::string c_rom;
std::string c_file;
std::map<std::string, std::string> c_settings;
};
IMPLEMENT_APP(lsnes_app)
@ -416,6 +417,8 @@ bool lsnes_app::OnCmdLineParsed(wxCmdLineParser& parser)
c_rom = r[1];
if(r = regex("--load=(.+)", i))
c_file = r[1];
if(r = regex("--set=([^=]+)=(.+)", i))
c_settings[r[1]] = r[2];
}
}
@ -439,12 +442,12 @@ bool lsnes_app::OnInit()
messages << "BSNES version: " << bsnes_core_version << std::endl;
messages << "lsnes version: lsnes rr" << lsnes_version << std::endl;
std::vector<class port_type*> pt;
for(unsigned i = 0; i <= core_userports; i++)
pt.push_back(&core_portgroup.get_default_type(i));
port_type_set* pset = &port_type_set::make(pt);
loaded_rom dummy_rom;
std::map<std::string, std::string> settings;
auto ctrldata = dummy_rom.rtype->controllerconfig(settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex);
controls.set_ports(*pset, false);
controls.set_ports(ports);
std::string cfgpath = get_config_path();
messages << "Saving per-user data to: " << get_config_path() << std::endl;
@ -480,7 +483,7 @@ bool lsnes_app::OnInit()
try {
moviefile mov;
rom = new loaded_rom(c_rom);
rom->load(mov.movie_rtc_second, mov.movie_rtc_subsecond);
rom->load(c_settings, mov.movie_rtc_second, mov.movie_rtc_subsecond);
} catch(std::exception& e) {
std::cerr << "Can't load ROM: " << e.what() << std::endl;
return false;
@ -492,15 +495,17 @@ bool lsnes_app::OnInit()
try {
mov = new moviefile(c_file);
if(c_rom != "")
rom->load(mov->movie_rtc_second, mov->movie_rtc_subsecond);
rom->load(mov->settings, mov->movie_rtc_second, mov->movie_rtc_subsecond);
} catch(std::exception& e) {
std::cerr << "Can't load state: " << e.what() << std::endl;
return false;
}
else {
mov = new moviefile;
mov->ports = pset;
mov->input.clear(*mov->ports);
mov->settings = c_settings;
auto ctrldata = rom->rtype->controllerconfig(mov->settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex);
mov->input.clear(ports);
if(c_rom != "") {
//Initialize the remainder.
mov->coreversion = bsnes_core_version;
@ -510,8 +515,8 @@ bool lsnes_app::OnInit()
mov->romimg_sha256[i] = rom->romimg[i].sha_256;
mov->romxml_sha256[i] = rom->romxml[i].sha_256;
}
mov->gametype = &rom->rtype->combine_region(*rom->region);
}
mov->gametype = &rom->rtype->combine_region(*rom->region);
}
mov->start_paused = true;
boot_emulator(*rom, *mov);

View file

@ -11,6 +11,7 @@
#include "core/framerate.hpp"
#include "core/settings.hpp"
#include "core/window.hpp"
#include "interface/setting.hpp"
#include "library/string.hpp"
#include "library/zip.hpp"
#include <boost/lexical_cast.hpp>
@ -32,118 +33,6 @@ void patching_done(struct loaded_rom& rom, wxWindow* modwin);
namespace
{
port_type& get_controller_type(const std::string& s)
{
auto types = core_portgroup.get_types();
for(auto i : types)
if(s == i->hname)
return *i;
return get_dummy_port_type();
}
void load_cchoices(std::vector<wxString>& cc, unsigned port, unsigned& dfltidx)
{
cc.clear();
port_type& dflt = core_portgroup.get_default_type(port);
dfltidx = 0;
auto types = core_portgroup.get_types();
for(auto i : types)
if(i->legal && i->legal(port - 1)) {
cc.push_back(towxstring(i->hname));
if(i == &dflt)
dfltidx = cc.size() - 1;
}
}
struct loaded_slot& get_rom_slot(struct loaded_rom& rom, unsigned index)
{
if(index >= 2 * sizeof(rom.romimg) / sizeof(rom.romimg[0]))
return rom.romimg[0];
if(index & 1)
return rom.romxml[index / 2];
else
return rom.romimg[index / 2];
}
core_region& region_from_string(core_type& rtype, const std::string& str)
{
core_region* x = NULL;
for(auto i : rtype.get_regions())
if(i->get_hname() == str)
return *i;
return *x;
}
unsigned populate_region_choices(std::vector<wxString>& array)
{
array.push_back(wxT("(Default)"));
return 1;
}
unsigned populate_system_choices(std::vector<wxString>& array)
{
for(auto i : core_type::get_core_types())
array.push_back(towxstring(i->get_hname()));
return array.size();
}
bool check_present_roms(core_type& rtype, unsigned flags)
{
unsigned a = 0;
unsigned b = 0;
for(size_t i = 0; i < ROMSELECT_ROM_COUNT && i < rtype.get_image_count(); i++) {
if((flags >> i) & 1)
a |= rtype.get_image_info(i).mandatory;
b |= rtype.get_image_info(i).mandatory;
}
return (a == b);
}
std::string romname(core_type& rtype, unsigned index)
{
if(index >= rtype.get_image_count())
return "";
return rtype.get_image_info(index).hname;
}
unsigned romname_to_index(core_type& rtype, const wxString& _name)
{
std::string name = tostdstring(_name);
for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++) {
if(romname(rtype, i) == name)
return 2 * i;
if(romname(rtype, i) + MARKUP_POSTFIX == name)
return 2 * i + 1;
}
return 2 * ROMSELECT_ROM_COUNT;
}
unsigned fill_rom_names(core_type& rtype, std::string* array)
{
unsigned r = 0;
for(unsigned i = 0; i < ROMSELECT_ROM_COUNT; i++) {
std::string s = romname(rtype, i);
if(s.length())
array[r++] = s;
}
return r;
}
core_type& romtype_from_string(const std::string& str)
{
core_type* x = NULL;
for(auto i : core_type::get_core_types())
if(i->get_hname() == str)
return *i;
return *x;
}
bool has_forced_region(const std::string& str)
{
core_type& rtype = romtype_from_string(str);
return (rtype.get_regions().size() == 1);
}
class textboxloadfilename : public wxFileDropTarget
{
public:
@ -162,6 +51,64 @@ namespace
private:
wxTextCtrl* ctrl;
};
struct setting_select
{
setting_select() {}
setting_select(wxWindow* parent, const core_setting& s);
wxWindow* get_label() { return label; }
wxWindow* get_control();
const core_setting* get_setting() { return setting; }
std::string read();
private:
wxStaticText* label;
wxTextCtrl* text;
wxCheckBox* check;
wxComboBox* combo;
const core_setting* setting;
};
setting_select::setting_select(wxWindow* parent, const core_setting& s)
: setting(&s)
{
label = new wxStaticText(parent, wxID_ANY, towxstring(setting->hname));
text = NULL;
combo = NULL;
check = NULL;
if(setting->is_boolean()) {
check = new wxCheckBox(parent, wxID_ANY, towxstring(""));
check->SetValue(s.dflt != "0");
} else if(setting->is_freetext()) {
text = new wxTextCtrl(parent, wxID_ANY, towxstring(setting->dflt), wxDefaultPosition,
wxSize(400, -1));
} else {
std::vector<wxString> _hvalues;
std::string dflt = "";
for(auto i : setting->values) {
_hvalues.push_back(towxstring(i->hname));
if(i->iname == setting->dflt)
dflt = i->hname;
}
combo = new wxComboBox(parent, wxID_ANY, towxstring(dflt), wxDefaultPosition,
wxDefaultSize, _hvalues.size(), &_hvalues[0], wxCB_READONLY);
}
}
wxWindow* setting_select::get_control()
{
if(text) return text;
if(check) return check;
if(combo) return combo;
return NULL;
}
std::string setting_select::read()
{
if(text) return tostdstring(text->GetValue());
if(check) return check->GetValue() ? "1" : "0";
if(combo) return setting->hvalue_to_ivalue(tostdstring(combo->GetValue()));
return "";
}
}
class wxwin_project : public wxDialog
@ -179,7 +126,7 @@ private:
struct moviefile make_movie();
std::map<std::string, wxTextCtrl*> sram_files;
std::map<std::string, wxButton*> sram_choosers;
wxComboBox** controllertypes;
std::map<std::string, setting_select> settings;
wxTextCtrl* projectname;
wxTextCtrl* prefix;
wxTextCtrl* rtc_sec;
@ -230,14 +177,12 @@ wxwin_project::wxwin_project()
wxFlexGridSizer* new_sizer = new wxFlexGridSizer(3, 1, 0, 0);
new_panel->SetSizer(new_sizer);
//Controllertypes/Gamename/initRTC/SRAMs.
wxFlexGridSizer* mainblock = new wxFlexGridSizer(4 + core_userports + sram_set.size(), 2, 0, 0);
controllertypes = new wxComboBox*[core_userports];
for(unsigned i = 1; i <= core_userports; i++) {
mainblock->Add(new wxStaticText(new_panel, wxID_ANY, towxstring((stringfmt() << "Controller " << i
<< " Type:").str())), 0, wxGROW);
load_cchoices(cchoices, i, dfltidx);
mainblock->Add(controllertypes[i - 1] = new wxComboBox(new_panel, wxID_ANY, cchoices[dfltidx],
wxDefaultPosition, wxDefaultSize, cchoices.size(), &cchoices[0], wxCB_READONLY), 0, wxGROW);
wxFlexGridSizer* mainblock = new wxFlexGridSizer(4 + our_rom->rtype->get_settings().settings.size() +
sram_set.size(), 2, 0, 0);
for(auto i : our_rom->rtype->get_settings().settings) {
settings[i.second->iname] = setting_select(new_panel, *i.second);
mainblock->Add(settings[i.second->iname].get_label());
mainblock->Add(settings[i.second->iname].get_control());
}
mainblock->Add(new wxStaticText(new_panel, wxID_ANY, wxT("Initial RTC value:")), 0, wxGROW);
wxFlexGridSizer* initrtc = new wxFlexGridSizer(1, 3, 0, 0);
@ -367,11 +312,11 @@ struct moviefile wxwin_project::make_movie()
moviefile f;
f.force_corrupt = false;
f.gametype = &our_rom->rtype->combine_region(*our_rom->region);
std::vector<class port_type*> pt;
pt.push_back(&core_portgroup.get_default_type(0));
for(unsigned i = 1; i <= core_userports; i++)
pt.push_back(&get_controller_type(tostdstring(controllertypes[i - 1]->GetValue())));
f.ports = &port_type_set::make(pt);
for(auto i : settings) {
f.settings[i.first] = i.second.read();
if(!i.second.get_setting()->validate(f.settings[i.first]))
throw std::runtime_error((stringfmt() << "Bad value for setting " << i.first).str());
}
f.coreversion = bsnes_core_version;
f.gamename = tostdstring(projectname->GetValue());
f.prefix = sanitize_prefix(tostdstring(prefix->GetValue()));
@ -402,7 +347,9 @@ struct moviefile wxwin_project::make_movie()
f.movie_rtc_subsecond = f.rtc_subsecond = boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue()));
if(f.movie_rtc_subsecond < 0)
throw std::runtime_error("RTC subsecond must be positive");
f.input.clear(*f.ports);
auto ctrldata = our_rom->rtype->controllerconfig(f.settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex);
f.input.clear(ports);
return f;
}

View file

@ -269,8 +269,9 @@ int main(int argc, char** argv)
messages << "--- Loading ROM ---" << std::endl;
struct loaded_rom r;
try {
std::map<std::string, std::string> tmp;
r = load_rom_from_commandline(cmdline);
r.load(1000000000, 0);
r.load(tmp, 1000000000, 0);
} catch(std::bad_alloc& e) {
OOM_panic();
} catch(std::exception& e) {
@ -308,7 +309,7 @@ int main(int argc, char** argv)
//Load ROM before starting the dumper.
our_rom = &r;
our_rom->region = &movie.gametype->get_region();
our_rom->load(movie.movie_rtc_second, movie.movie_rtc_subsecond);
our_rom->load(movie.settings, movie.movie_rtc_second, movie.movie_rtc_subsecond);
startup_lua_scripts(cmdline);
dumper_startup(dumper, mode, prefix, length);
main_loop(r, movie, true);

View file

@ -1,135 +0,0 @@
#include "core/moviefile.hpp"
#include "core/rrdata.hpp"
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
std::string name_subrom(core_type& major, unsigned romnumber) throw(std::bad_alloc)
{
std::string name = "UNKNOWN";
if(romnumber < 2 * major.get_image_count())
name = major.get_image_info(romnumber / 2).hname;
if(romnumber % 2)
return name + " XML";
else if(name != "ROM")
return name + " ROM";
else
return "ROM";
}
std::string escape_string(std::string x)
{
std::ostringstream out;
for(size_t i = 0; i < x.length(); i++)
if((x[i] < 0 || x[i] > 31) && x[i] != '\\')
out << x[i];
else {
unsigned char y = static_cast<unsigned char>(x[i]);
out << "\\" << ('0' + static_cast<char>((y >> 6) & 7))
<< ('0' + static_cast<char>((y >> 3) & 7))
<< ('0' + static_cast<char>(y & 7));
}
return out.str();
}
int main(int argc, char** argv)
{
reached_main();
if(argc != 2) {
std::cerr << "Syntax: " << argv[0] << " <moviefile>" << std::endl;
return 1;
}
try {
uint64_t starting_point = 0;
struct moviefile m(argv[1]);
core_type* rtype = &m.gametype->get_type();
core_region* reg = &m.gametype->get_region();
std::cout << "Console: " << rtype->get_hname() << std::endl;
std::cout << "Region: " << reg->get_hname() << std::endl;
for(unsigned i = 0; i < m.ports->ports(); i++)
std::cout << "Port #" << i << ": " << m.ports->port_type(i).hname << std::endl;
std::cout << "Used emulator core: " << escape_string(m.coreversion) << std::endl;
if(m.gamename != "")
std::cout << "Game name: " << escape_string(m.gamename) << std::endl;
else
std::cout << "No game name available" << std::endl;
std::cout << "Project ID: " << escape_string(m.projectid) << std::endl;
for(size_t i = 0; i < sizeof(m.romimg_sha256)/sizeof(m.romimg_sha256[0]); i++) {
if(m.romimg_sha256[i] != "") {
std::cout << name_subrom(*rtype, 2 * i + 0) << " checksum: "
<< escape_string(m.romimg_sha256[i]) << std::endl;
if(m.romxml_sha256[i] != "") {
std::cout << name_subrom(*rtype, 2 * i + 1) << " checksum: "
<< escape_string(m.romimg_sha256[i]) << std::endl;
}
} else
std::cout << "No " << name_subrom(*rtype, 2 * i + 0) << " present" << std::endl;
}
for(auto i = m.authors.begin(); i != m.authors.end(); i++) {
if(i->first != "" && i->second != "")
std::cout << "Author: " << escape_string(i->first) << " (" << escape_string(i->second)
<< ")" << std::endl;
else if(i->first != "" && i->second == "")
std::cout << "Author: " << escape_string(i->first) << std::endl;
else if(i->first == "" && i->second != "")
std::cout << "Author: (" << escape_string(i->second) << ")" << std::endl;
}
if(m.authors.size() == 0)
std::cout << "No author info available" << std::endl;
if(m.is_savestate) {
std::cout << "Movie starts from savestate" << std::endl;
for(auto i = m.movie_sram.begin(); i != m.movie_sram.end(); i++)
std::cout << "Start SRAM: " << escape_string(i->first) << ": [" << i->second.size()
<< " bytes of binary data]" << std::endl;
if(!m.movie_sram.size())
std::cout << "No starting SRAM" << std::endl;
for(auto i = m.sram.begin(); i != m.sram.end(); i++)
std::cout << "Saved SRAM: " << escape_string(i->first) << ": [" << i->second.size()
<< " bytes of binary data]" << std::endl;
if(!m.sram.size())
std::cout << "No saved SRAM" << std::endl;
std::cout << "Savestate: [" << m.savestate.size() << " bytes of binary data]" << std::endl;
std::cout << "Host memory: [" << m.host_memory.size() << " bytes of binary data]"
<< std::endl;
std::cout << "Screenshot: [" << m.screenshot.size() << " bytes of binary data]" << std::endl;
if(m.screenshot.size() >= 4) {
uint16_t a = static_cast<uint8_t>(m.screenshot[0]);
uint16_t b = static_cast<uint8_t>(m.screenshot[1]);
std::cout << "Screenshot size: " << (a * 256 + b) << "*" << (m.screenshot.size() - 2)
/ (a * 256 + b) / 3 << std::endl;
} else
std::cout << "Screenshot is corrupt" << std::endl;
starting_point = m.save_frame;
std::cout << "Starting frame: " << starting_point << std::endl;
std::cout << "Lag frames so far: " << m.lagged_frames << std::endl;
} else if(m.movie_sram.size() > 0) {
std::cout << "Movie starts from SRAM" << std::endl;
for(auto i = m.movie_sram.begin(); i != m.movie_sram.end(); i++)
std::cout << "Start SRAM: " << escape_string(i->first) << " (" << i->second.size()
<< " bytes)" << std::endl;
} else
std::cout << "Movie starts from clean state" << std::endl;
std::cout << "Movie frame count: " << m.get_frame_count() << std::endl;
uint64_t length = m.get_movie_length();
{
std::ostringstream x;
if(length >= 3600000000000ULL) {
x << (length / 3600000000000ULL) << ":";
length %= 3600000000000ULL;
}
x << std::setw(2) << std::setfill('0') << (length / 60000000000ULL) << ":";
length %= 60000000000ULL;
x << std::setw(2) << std::setfill('0') << (length / 1000000000ULL) << ".";
length %= 1000000000ULL;
x << std::setw(3) << std::setfill('0') << (length + 500000) / 1000000;
std::cout << "Movie length: " << x.str() << std::endl;
}
std::cout << "Rerecord count: " << rrdata::count(m.c_rrdata) << std::endl;
} catch(std::exception& e) {
std::cerr << "ERROR: " << e.what() << std::endl;
return 1;
}
return 0;
}