Compare commits

...
Sign in to create a new pull request.

2 commits
master ... D

Author SHA1 Message Date
Ilari Liusvaara
6736ecc285 WIP: Support keyboard input device 2014-09-14 12:08:44 +03:00
Ilari Liusvaara
425e1fc097 Support decoupling video/input framerate 2014-09-14 10:18:16 +03:00
30 changed files with 679 additions and 76 deletions

View file

@ -51,6 +51,8 @@ struct _lsnes_status
bool rtc_valid; //RTC time valid?
std::u32string rtc; //RTC time.
std::vector<std::u32string> inputs; //Input display.
bool vi_valid; //VI count valid?
uint64_t vi_counter; //VI counter.
std::map<std::string, std::u32string> mvars; //Memory watches.
std::map<std::string, std::u32string> lvars; //Lua variables.
};

View file

@ -129,6 +129,7 @@ struct emulator_instance
status_updater* supdater;
threads::id emu_thread;
time_t random_seed_value;
bool vi_prev_frame;
dtor_list D;
private:
emulator_instance(const emulator_instance&);

View file

@ -25,7 +25,8 @@ enum lsnes_movie_tags
TAG_RAMCONTENT = 0xd3ec3770,
TAG_ROMHINT = 0x6f715830,
TAG_BRANCH = 0xf2e60707,
TAG_BRANCH_NAME = 0x6dcb2155
TAG_BRANCH_NAME = 0x6dcb2155,
TAG_VICOUNTER = 0x55758e30,
};
#endif

View file

@ -236,6 +236,18 @@ struct moviefile
* Active macros at savestate.
*/
std::map<std::string, uint64_t> active_macros;
/**
* VI counters valid.
*/
bool vi_valid;
/**
* VI counter.
*/
uint64_t vi_counter;
/**
* VI counter for this frame.
*/
uint32_t vi_this_frame;
/**
* Get number of frames in movie.
*

View file

@ -109,8 +109,9 @@ extern "C" {
#define LSNES_CORE_CAP1_MEMWATCH 0x00010000U
//Core supports lightguns (By setting lightgun_height/lightgun_width in LSNES_CORE_GET_AV_STATE).
#define LSNES_CORE_CAP1_LIGHTGUN 0x00020000U
//Core signals type extensions #1 (VFR flag). Only supported if emu_flags1 >= 2
#define LSNES_CORE_CAP1_TYPEEXT1 0x00040000U
//Reserved capabilities.
#define LSNES_CORE_CAP1_RESERVED18 0x00040000U
#define LSNES_CORE_CAP1_RESERVED19 0x00080000U
#define LSNES_CORE_CAP1_RESERVED20 0x00100000U
#define LSNES_CORE_CAP1_RESERVED21 0x00200000U
@ -349,6 +350,9 @@ struct lsnes_core_get_type_info
struct lsnes_core_get_type_info_romimage* images;
//Output: List of settings. Terminated by setting with NULL iname.
struct lsnes_core_get_type_info_param* settings;
//Output: Nonzero if input and output framerates should be decoupled.
//Present only if LSNES_CORE_CAP1_TYPEEXT1 is set.
int is_vfr;
};
//Request 3: Request information about region.

View file

@ -611,6 +611,7 @@ public:
std::vector<std::string> get_trace_cpus() { return core->get_trace_cpus(); }
void debug_reset() { core->debug_reset(); }
bool isnull() { return core->isnull(); }
bool is_vfr() { return t_is_vfr(); }
protected:
/**
* Load a ROM slot set. Changes the ROM currently loaded for core.
@ -630,6 +631,10 @@ protected:
* Returns: The controller configuration.
*/
virtual controller_set t_controllerconfig(std::map<std::string, std::string>& settings) = 0;
/**
* Is VFR (defaults to false).
*/
virtual bool t_is_vfr();
private:
core_type(const core_type&);
core_type& operator=(const core_type&);

View file

@ -191,6 +191,7 @@ struct port_controller_button
TYPE_RAXIS, //Relative Axis (mouse).
TYPE_TAXIS, //Throttle Axis (does not pair).
TYPE_LIGHTGUN, //Lightgun axis.
TYPE_KEYBOARD, //Keyboard. The names are translated via namemap.
};
enum _type type;
char32_t symbol;
@ -216,6 +217,8 @@ struct port_controller
std::string cclass; //Controller class.
std::string type; //Controller type.
std::vector<port_controller_button> buttons; //Buttons.
std::map<signed, std::u32string> namemap; //Button translation map.
unsigned reserved_idx; //Number of extra reserved indices.
/**
* Count number of analog actions on this controller.
*/
@ -233,6 +236,15 @@ struct port_controller
return NULL;
return &buttons[i];
}
/**
* Access specified entry in namemap. Returns '???' if unknown.
*/
std::u32string get_namemap_entry(short entry) const
{
if(!namemap.count(entry))
return U"???";
return namemap.find(entry)->second;
}
};
/**
@ -336,12 +348,12 @@ public:
* Get number of used control indices on controller.
*
* Parameter controller: Number of controller.
* Returns: Number of used control indices.
* Returns: Number of used control indices. First is number of actual indices, second is number of reserved ones.
*/
unsigned used_indices(unsigned controller)
std::pair<unsigned, unsigned> used_indices(unsigned controller) const
{
auto c = controller_info->get(controller);
return c ? c->buttons.size() : 0;
return c ? std::make_pair((unsigned)c->buttons.size(), c->reserved_idx) : std::make_pair(0U, 0U);
}
/**
* Human-readable name.
@ -519,6 +531,12 @@ public:
throw std::runtime_error("Bad legacy PCID");
return legacy_pcids[pcid];
}
/**
* Set pollcounters mask.
*
* Parameter mask: The mask to write.
*/
void set_pollcounter_mask(uint32_t* mask) const;
private:
port_type_set(std::vector<class port_type*> types, struct port_index_map control_map);
size_t* port_offsets;
@ -566,10 +584,14 @@ public:
* Assign the pollcounter_vector.
*/
pollcounter_vector& operator=(const pollcounter_vector& v) throw(std::bad_alloc);
/**
* Zero all unmasked poll counters and clear all DRDY bits. System flag is cleared.
*/
void clear_unmasked() throw();
/**
* Zero all poll counters and clear all DRDY bits. System flag is cleared.
*/
void clear() throw();
void clear_all() throw();
/**
* Set all DRDY bits.
*/
@ -707,8 +729,47 @@ public:
* Get raw pollcounter data.
*/
const uint32_t* rawdata() const throw() { return ctrs; }
/**
* Write reserved index for controller.
*
* Parameter port: The port.
* Parameter controller: The controller.
* Parameter _index: The reserved index (0-based).
* Parameter val: The value to write.
* Throws std::runtime_error: Specified reserved slot is not valid.
*/
void reserved(unsigned port, unsigned controller, unsigned _index, uint32_t val)
throw(std::runtime_error)
{
auto counts = (port < types->ports()) ? types->port_type(port).used_indices(controller) :
std::make_pair(0U, 0U);
unsigned pindex = types->triple_to_index(port, controller, _index + counts.first);
if(pindex == 0xFFFFFFFFU)
throw std::runtime_error("write_reserved: Invalid reserved index");
ctrs[pindex] = val;
}
/**
* Read reserved index for controller.
*
* Parameter port: The port.
* Parameter controller: The controller.
* Parameter _index: The reserved index (0-based).
* Returns: The value read.
* Throws std::runtime_error: Specified reserved slot is not valid.
*/
uint32_t reserved(unsigned port, unsigned controller, unsigned _index)
throw(std::runtime_error)
{
auto counts = (port < types->ports()) ? types->port_type(port).used_indices(controller) :
std::make_pair(0U, 0U);
unsigned pindex = types->triple_to_index(port, controller, _index + counts.first);
if(pindex == 0xFFFFFFFFU)
throw std::runtime_error("write_reserved: Invalid reserved index");
return ctrs[pindex];
}
private:
uint32_t* ctrs;
uint32_t* ctrs_valid_mask;
const port_type_set* types;
bool framepflag;
};

View file

@ -14,6 +14,8 @@ struct lua_unsaferewind
uint64_t ptr;
uint64_t secs;
uint64_t ssecs;
uint64_t vi_counter;
uint32_t vi_this_frame;
std::vector<uint32_t> pollcounters;
std::vector<char> hostmemory;
std::string print()

12
lua.lyx
View file

@ -6255,6 +6255,18 @@ Syntax: number movie.currentframe()
Return number of current frame.
\end_layout
\begin_layout Subsection
movie.currentvi: Get current VI number
\end_layout
\begin_layout Itemize
Syntax: number movie.currentvi()
\end_layout
\begin_layout Standard
Return number of current VI.
\end_layout
\begin_layout Subsection
movie.framecount: Get move frame count
\end_layout

View file

@ -193,6 +193,9 @@ void status_updater::update()
}
_status.inputs.push_back(_buffer);
}
//VI counts. Only display if type is VFR.
_status.vi_valid = mlogic.get_mfile().gametype->get_type().is_vfr();
_status.vi_counter = mlogic.get_mfile().vi_counter;
//Lua variables.
_status.lvars = lua2.get_watch_vars();
//Memory watches.

View file

@ -121,6 +121,7 @@ emulator_instance::emulator_instance()
settings->add_set(lsnes_setgrp);
dispatch->set_error_streams(&messages.getstream());
random_seed_value = 0;
vi_prev_frame = false;
}
emulator_instance::~emulator_instance()

View file

@ -90,7 +90,8 @@ controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_allo
if(core.lua2->requests_subframe_paint)
core.fbuf->redraw_framebuffer();
if(subframe) {
//If VI did not occur last frame, do subframe anyway.
if(subframe || !core.vi_prev_frame) {
if(core.runmode->is_advance_subframe()) {
//Note that platform::wait() may change value of cancel flag.
if(!core.runmode->test_cancel()) {
@ -156,8 +157,11 @@ controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_allo
}
core.runmode->set_point(emulator_runmode::P_START);
core.supdater->update();
//Clear the previous VI flag, so next poll will be subframe.
core.vi_prev_frame = false;
}
platform::flush_command_queue();
//TODO: Collapse keyboard symbols.
controller_frame tmp = core.controls->get(core.mlogic->get_movie().get_current_frame());
core.rom->pre_emulate_frame(tmp); //Preset controls, the lua will override if needed.
core.lua2->callback_do_input(tmp, subframe);
@ -273,7 +277,14 @@ public:
void output_frame(framebuffer::raw& screen, uint32_t fps_n, uint32_t fps_d)
{
auto& core = CORE();
core.lua2->callback_do_frame_emulated();
//VI occured.
auto& mfile = core.mlogic->get_mfile();
mfile.vi_this_frame++;
mfile.vi_counter++;
//This is done elsewhere for VFR.
if(!mfile.gametype->get_type().is_vfr())
core.lua2->callback_do_frame_emulated();
core.runmode->set_point(emulator_runmode::P_VIDEO);
core.fbuf->redraw_framebuffer(screen, false, true);
auto rate = core.rom->get_audio_rate();
@ -847,7 +858,7 @@ namespace
//If there is a pending load, perform it. Return 1 on successful load, 0 if nothing to load, -1 on load
//failing.
int handle_load()
int _handle_load()
{
auto& core = CORE();
std::string old_project = *core.mlogic ? core.mlogic->get_mfile().projectid : "";
@ -930,6 +941,17 @@ nothing_to_do:
return 0;
}
int handle_load()
{
auto& core = CORE();
int r = _handle_load();
if(r > 0) {
//Set the vi prev frame flag, as not to mess up frame advance at first oppurtunity.
core.vi_prev_frame = true;
}
return r;
}
//If there are pending saves, perform them.
void handle_saves()
{
@ -1023,6 +1045,10 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
core.runmode->set_pause_cond(initial.start_paused);
platform::set_paused(core.runmode->is_paused());
stop_at_frame_active = false;
//Always set VI last frame flag when loading any savestate or movie, as not to distrupt frame advance at
//first oppurtunity. And since we loadstated above, VI count this frame is set apporiately.
core.vi_prev_frame = true;
core.lua2->run_startup_scripts();
@ -1032,7 +1058,9 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
just_did_loadstate = first_round;
continue;
}
core.framerate->ack_frame_tick(framerate_regulator::get_utime());
//Only consider frames with VI real frames.
if(core.vi_prev_frame)
core.framerate->ack_frame_tick(framerate_regulator::get_utime());
core.runmode->decay_skiplag();
if(!first_round) {
@ -1079,9 +1107,20 @@ void main_loop(struct loaded_rom& rom, struct moviefile& initial, bool load_has_
just_did_loadstate = false;
}
core.dbg->do_callback_frame(core.mlogic->get_movie().get_current_frame(), false);
//VI count this frame should be 0 here.
core.rom->emulate();
//This is done elsewhere for non-VFR.
if(core.mlogic->get_mfile().gametype->get_type().is_vfr())
core.lua2->callback_do_frame_emulated();
//Reset the vi this frame count to 0, as it is only meaningful inside emulate.
//Also, if VIs occured, set flag informing that so frame advance can stop only on output frames.
auto& VIs = core.mlogic->get_mfile().vi_this_frame;
if(VIs > 0)
core.vi_prev_frame = true;
VIs = 0;
random_mix_timing_entropy();
if(core.runmode->is_freerunning())
//Only wait if VI, to get about proper framerate.
if(core.runmode->is_freerunning() && core.vi_prev_frame)
platform::wait(core.framerate->to_wait_frame(framerate_regulator::get_utime()));
first_round = false;
core.lua2->callback_do_frame();

View file

@ -437,6 +437,19 @@ namespace
core.controls->set_macro_frames(std::map<std::string, uint64_t>());
}
}
//Clear the data that should be cleared when movie is at beginning.
void clear_runtime_movie_data(emulator_instance& core)
{
auto& mf = core.mlogic->get_mfile();
mf.is_savestate = false;
mf.host_memory.clear();
core.mlogic->get_movie().reset_state();
mf.vi_valid = true;
mf.vi_counter = 0;
mf.vi_this_frame = 0;
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
}
}
void do_load_rom() throw(std::bad_alloc, std::runtime_error)
@ -468,10 +481,7 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
core.mlogic->get_mfile().romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
core.mlogic->get_mfile().romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
}
core.mlogic->get_mfile().is_savestate = false;
core.mlogic->get_mfile().host_memory.clear();
core.mlogic->get_movie().reset_state();
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
clear_runtime_movie_data(core);
core.lua2->callback_do_rewind();
} catch(std::bad_alloc& e) {
OOM_panic();
@ -484,25 +494,16 @@ void do_load_rom() throw(std::bad_alloc, std::runtime_error)
//The more complicated Read-Write case.
//We need to create a new movie and movie file.
temporary_handle<moviefile> _movie;
_movie.get()->force_corrupt = false;
_movie.get()->gametype = NULL; //Not yet known.
_movie.get()->coreversion = core.rom->get_core_identifier();
_movie.get()->projectid = get_random_hexstring(40);
_movie.get()->rerecords = "0";
_movie.get()->rerecords_mem = 0;
for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
_movie.get()->namehint[i] = core.rom->romimg[i].namehint;
_movie.get()->romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
_movie.get()->romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
}
_movie.get()->is_savestate = false;
_movie.get()->save_frame = 0;
_movie.get()->lagged_frames = 0;
_movie.get()->poll_flag = false;
_movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
_movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
_movie.get()->start_paused = false;
_movie.get()->lazy_project_create = true;
port_type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
_movie.get()->input = NULL;
_movie.get()->create_default_branch(portset2);
@ -564,10 +565,7 @@ void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
core.dispatch->mode_change(true);
try {
handle_load_core(core.mlogic->get_mfile(), portset, false);
core.mlogic->get_mfile().is_savestate = false;
core.mlogic->get_mfile().host_memory.clear();
core.mlogic->get_movie().reset_state();
core.fbuf->redraw_framebuffer(core.rom->draw_cover());
clear_runtime_movie_data(core);
core.lua2->callback_do_rewind();
} catch(std::bad_alloc& e) {
OOM_panic();
@ -760,6 +758,15 @@ void do_load_state(struct moviefile& _movie, int lmode, bool& used)
core.mlogic->set_mfile(_movie, true);
used = true;
//Set up VI counter if none is available.
if(!_movie.vi_valid) {
//If no VI counter available, set it to frame-1 and there have been 0 this frame.
uint64_t f = core.mlogic->get_movie().get_current_frame();
_movie.vi_counter = (f > 0) ? (f - 1) : 0;
_movie.vi_this_frame = 0;
_movie.vi_valid = true;
}
set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
//Activate RW mode if needed.

View file

@ -20,6 +20,8 @@
void moviefile::brief_info::binary_io(int _stream)
{
bool vi_override = false;
binarystream::input in(_stream);
sysregion = in.string();
//Discard the settings.
@ -32,8 +34,14 @@ void moviefile::brief_info::binary_io(int _stream)
this->corename = s.string_implicit();
}},{TAG_PROJECT_ID, [this](binarystream::input& s) {
this->projectid = s.string_implicit();
}},{TAG_SAVESTATE, [this](binarystream::input& s) {
}},{TAG_SAVESTATE, [this, &vi_override](binarystream::input& s) {
//TAG_VICOUNTER overrides this.
if(!vi_override)
this->current_frame = s.number();
}},{TAG_VICOUNTER, [this, &vi_override](binarystream::input& s) {
//TAG_VICOUNTER overrides TAG_SAVESTATE.
this->current_frame = s.number();
vi_override = true;
}},{TAG_RRDATA, [this](binarystream::input& s) {
std::vector<char> c_rrdata;
s.blob_implicit(c_rrdata);
@ -177,6 +185,11 @@ void moviefile::binary_io(int _stream, rrdata_set& rrd) throw(std::bad_alloc, st
});
}
out.extension(TAG_VICOUNTER, [this](binarystream::output& s) {
s.number(vi_counter);
s.number(vi_this_frame);
});
int64_t next_bnum = 0;
std::map<std::string, uint64_t> branch_table;
for(auto& i : branches) {
@ -212,6 +225,7 @@ void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc,
auto ctrldata = gametype->get_type().controllerconfig(settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
input = NULL;
vi_valid = false;
in.extension({
{TAG_ANCHOR_SAVE, [this](binarystream::input& s) {
@ -288,6 +302,10 @@ void moviefile::binary_io(int _stream, core_type& romtype) throw(std::bad_alloc,
uint64_t l = s.number();
std::string x = s.string_implicit();
this->subtitles[moviefile_subtiming(f, l)] = x;
}},{TAG_VICOUNTER, [this](binarystream::input& s) {
vi_counter = s.number();
vi_this_frame = s.number();
vi_valid = true;
}}
}, binarystream::null_default);

View file

@ -200,9 +200,12 @@ void moviefile::brief_info::load(zip::reader& r)
r.read_linefile("gametype", sysregion);
r.read_linefile("coreversion", corename);
r.read_linefile("projectid", projectid);
if(r.has_member("savestate"))
r.read_numeric_file("saveframe", current_frame);
else
if(r.has_member("savestate")) {
if(r.has_member("vicount"))
r.read_numeric_file("vicount", current_frame);
else
r.read_numeric_file("saveframe", current_frame);
} else
current_frame = 0;
r.read_numeric_file("rerecords", rerecords);
r.read_linefile("rom.sha256", hash[0], true);
@ -242,6 +245,10 @@ void moviefile::load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, s
auto ctrldata = gametype->get_type().controllerconfig(settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
vi_valid = true;
vi_counter = 0;
vi_this_frame = 0;
branches.clear();
r.read_linefile("gamename", gamename, true);
r.read_linefile("projectid", projectid);
@ -271,6 +278,12 @@ void moviefile::load(zip::reader& r, core_type& romtype) throw(std::bad_alloc, s
if(r.has_member("savestate.anchor"))
r.read_raw_file("savestate.anchor", anchor_savestate);
if(r.has_member("savestate")) {
vi_valid = false;
if(r.has_member("vicounter")) {
r.read_numeric_file("vicounter", vi_counter);
r.read_numeric_file("vithisframe", vi_this_frame);
vi_valid = true;
}
is_savestate = true;
r.read_numeric_file("saveframe", save_frame, true);
r.read_numeric_file("lagcounter", lagged_frames, true);

View file

@ -173,8 +173,10 @@ void moviefile::save(zip::writer& w, rrdata_set& rrd) throw(std::bad_alloc, std:
w.write_numeric_file("starttime.second", movie_rtc_second);
w.write_numeric_file("starttime.subsecond", movie_rtc_subsecond);
if(!anchor_savestate.empty())
w.write_raw_file("savestate.anchor", anchor_savestate);
w.write_raw_file("savestate.anchor", anchor_savestate);
if(is_savestate) {
w.write_numeric_file("vicounter", vi_counter);
w.write_numeric_file("vithisframe", vi_this_frame);
w.write_numeric_file("saveframe", save_frame);
w.write_numeric_file("lagcounter", lagged_frames);
write_pollcounters(w, "pollcounters", pollcounters);

View file

@ -109,12 +109,18 @@ moviefile::moviefile() throw(std::bad_alloc)
coreversion = "";
projectid = "";
rerecords = "0";
rerecords_mem = 0;
is_savestate = false;
save_frame = 0;
lagged_frames = 0;
movie_rtc_second = rtc_second = DEFAULT_RTC_SECOND;
movie_rtc_subsecond = rtc_subsecond = DEFAULT_RTC_SUBSECOND;
start_paused = false;
lazy_project_create = true;
poll_flag = 0;
vi_valid = true;
vi_counter = 0;
vi_this_frame = 0;
}
moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_settings, uint64_t rtc_sec,
@ -125,7 +131,10 @@ moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_sett
coreversion = rom.get_core_identifier();
projectid = get_random_hexstring(40);
rerecords = "0";
rerecords_mem = 0;
is_savestate = false;
save_frame = 0;
lagged_frames = 0;
movie_rtc_second = rtc_second = rtc_sec;
movie_rtc_subsecond = rtc_subsecond = rtc_subsec;
start_paused = false;
@ -133,6 +142,9 @@ moviefile::moviefile(loaded_rom& rom, std::map<std::string, std::string>& c_sett
poll_flag = 0;
settings = c_settings;
input = NULL;
vi_valid = true;
vi_counter = 0;
vi_this_frame = 0;
auto ctrldata = rom.controllerconfig(settings);
port_type_set& ports = port_type_set::make(ctrldata.ports, ctrldata.portindex());
create_default_branch(ports);
@ -329,6 +341,9 @@ void moviefile::copy_fields(const moviefile& mv)
lazy_project_create = mv.lazy_project_create;
subtitles = mv.subtitles;
active_macros = mv.active_macros;
vi_valid = mv.vi_valid;
vi_counter = mv.vi_counter;
vi_this_frame = mv.vi_this_frame;
}
void moviefile::fork_branch(const std::string& oldname, const std::string& newname)

View file

@ -1,4 +1,4 @@
OBJECTS=test.$(OBJECT_SUFFIX)
OBJECTS=test.$(OBJECT_SUFFIX) test2.$(OBJECT_SUFFIX)
.PRECIOUS: %.$(OBJECT_SUFFIX) %.files

View file

@ -0,0 +1,212 @@
/***************************************************************************
* Copyright (C) 2012-2013 by Ilari Liusvaara *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "lsnes.hpp"
#include <sstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "core/audioapi.hpp"
#include "core/misc.hpp"
#include "core/command.hpp"
#include "core/controllerframe.hpp"
#include "core/dispatch.hpp"
#include "core/framebuffer.hpp"
#include "core/instance.hpp"
#include "core/messages.hpp"
#include "interface/callbacks.hpp"
#include "interface/cover.hpp"
#include "interface/romtype.hpp"
#include "library/framebuffer-pixfmt-rgb32.hpp"
#include "library/string.hpp"
#include "library/controller-data.hpp"
#include "library/serialization.hpp"
#include "library/minmax.hpp"
#include "library/framebuffer.hpp"
#include "library/hex.hpp"
namespace
{
uint32_t cover_fbmem[480 * 432];
bool pflag = false;
//Framebuffer.
struct framebuffer::info cover_fbinfo = {
&framebuffer::pixfmt_rgb32, //Format.
(char*)cover_fbmem, //Memory.
480, 432, 1920, //Physical size.
480, 432, 1920, //Logical size.
0, 0 //Offset.
};
struct interface_device_reg test_registers[] = {
{NULL, NULL, NULL}
};
#include "ports.inc"
controller_set test_controllerconfig(std::map<std::string, std::string>& settings)
{
std::map<std::string, std::string> _settings = settings;
controller_set r;
r.ports.push_back(&psystem);
r.ports.push_back(&ptype1);
r.ports.push_back(&ptype2);
r.logical_map.push_back(std::make_pair(1, 0));
r.logical_map.push_back(std::make_pair(2, 0));
return r;
}
void redraw_cover_fbinfo()
{
for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++)
cover_fbmem[i] = 0x00000000;
cover_render_string(cover_fbmem, 0, 0, "TEST MODE", 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
void redraw_screen()
{
for(size_t i = 0; i < sizeof(cover_fbmem) / sizeof(cover_fbmem[0]); i++)
cover_fbmem[i] = 0x00000000;
{
std::ostringstream str;
unsigned k = 0;
for(unsigned i = 0; i < 6; i++)
str << ecore_callbacks->get_input(1, 0, i) << " ";
for(unsigned i = 0; i < 15; i++)
if(ecore_callbacks->get_input(1, 0, i + 6)) k |= (1 << i);
str << hex::to16(k);
cover_render_string(cover_fbmem, 0, 0, str.str(), 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
{
std::ostringstream str;
unsigned k = 0;
for(unsigned i = 0; i < 6; i++)
str << ecore_callbacks->get_input(2, 0, i) << " ";
for(unsigned i = 0; i < 15; i++)
if(ecore_callbacks->get_input(2, 0, i + 6)) k |= (1 << i);
str << hex::to16(k);
cover_render_string(cover_fbmem, 0, 16, str.str(), 0xFFFFFF, 0x00000, 480, 432, 1920, 4);
}
}
struct _test_core2 : public core_core, public core_type, public core_region, public core_sysregion
{
_test_core2()
: core_core({&psystem, &ptype1, &ptype2}, {
{0, "xyzzy", "xyzzy", {
{"Magic", "enum:[\"foo\",\"bar\",\"baz\",[\"qux\",\"zot\"]]"}
}}
}),
core_type({{
.iname = "test2",
.hname = "test2",
.id = 0,
.sysname = "Test2",
.bios = NULL,
.regions = {this},
.images = {{"rom", "Cartridge ROM", 1, 0, 0, "test2"}},
.settings = {},
.core = this,
}}),
core_region({{"world", "World", 0, 0, false, {1, 1000}, {0}}}),
core_sysregion("test2", *this, *this) { hide(); }
std::string c_core_identifier() { return "TEST2"; }
bool c_set_region(core_region& region) { return (&region == this); }
std::pair<uint32_t, uint32_t> c_video_rate() { return std::make_pair(60, 1); }
double c_get_PAR() { return 1.0; }
std::pair<uint32_t, uint32_t> c_audio_rate() { return std::make_pair(48000, 1); }
std::map<std::string, std::vector<char>> c_save_sram() throw(std::bad_alloc) {
std::map<std::string, std::vector<char>> s;
return s;
}
void c_load_sram(std::map<std::string, std::vector<char>>& sram) throw(std::bad_alloc) {}
void c_serialize(std::vector<char>& out) { out.resize(1); out[0] = fmod; }
void c_unserialize(const char* in, size_t insize) { fmod = in[0]; }
core_region& c_get_region() { return *this; }
void c_power() {}
void c_unload_cartridge() {}
std::pair<uint32_t, uint32_t> c_get_scale_factors(uint32_t width, uint32_t height) {
return std::make_pair(max(512 / width, (uint32_t)1), max(448 / height, (uint32_t)1));
}
void c_install_handler() {}
void c_uninstall_handler() {}
void c_emulate() {
int16_t audio[800] = {0};
pflag = false;
redraw_screen();
framebuffer::info inf;
inf.type = &framebuffer::pixfmt_rgb32;
inf.mem = const_cast<char*>(reinterpret_cast<const char*>(cover_fbmem));
inf.physwidth = 480;
inf.physheight = 432;
inf.physstride = 1920;
inf.width = 480;
inf.height = 432;
inf.stride = 1920;
inf.offset_x = 0;
inf.offset_y = 0;
framebuffer::raw ls(inf);
fmod++;
if(fmod == 50) fmod = 0;
if(fmod == 0 || fmod == 16 || fmod == 33)
ecore_callbacks->output_frame(ls, 60,1);
CORE().audio->submit_buffer(audio, 48, false, 48000);
}
void c_runtosave() {}
bool c_get_pflag() { return pflag; }
void c_set_pflag(bool _pflag) { pflag = _pflag; }
framebuffer::raw& c_draw_cover() {
static framebuffer::raw x(cover_fbinfo);
redraw_cover_fbinfo();
return x;
}
std::string c_get_core_shortname() { return "test"; }
void c_pre_emulate_frame(controller_frame& cf) {}
void c_execute_action(unsigned id, const std::vector<interface_action_paramval>& p)
{
if(id == 0)
messages << "ID #0, choice: " << p[0].i << std::endl;
}
const interface_device_reg* c_get_registers() { return test_registers; }
int t_load_rom(core_romimage* images, std::map<std::string, std::string>& settings,
uint64_t rtc_sec, uint64_t rtc_subsec)
{
return 0;
}
controller_set t_controllerconfig(std::map<std::string, std::string>& settings)
{
return test_controllerconfig(settings);
}
bool t_is_vfr() { return true; }
std::pair<uint64_t, uint64_t> c_get_bus_map() { return std::make_pair(0, 0); }
std::list<core_vma_info> c_vma_list() { return std::list<core_vma_info>(); }
std::set<std::string> c_srams() { return std::set<std::string>(); }
unsigned c_action_flags(unsigned id) { return 1; }
int c_reset_action(bool hard) { return -1; }
void c_set_debug_flags(uint64_t addr, unsigned int sflags, unsigned int cflags) {}
void c_set_cheat(uint64_t addr, uint64_t value, bool set) {}
void c_debug_reset() {}
std::vector<std::string> c_get_trace_cpus()
{
return std::vector<std::string>();
}
unsigned char fmod;
} test2_core;
}

View file

@ -667,6 +667,10 @@ failed:
{
internal_pflag = true;
}
bool supports_vfr_sel()
{
return (caps1 & LSNES_CORE_CAP1_TYPEEXT1);
}
private:
std::string fullname;
std::string shortname;
@ -690,8 +694,9 @@ failed:
struct c_core_type : public core_type
{
c_core_type(c_lib_init& lib, core_type_params& p, std::map<unsigned, port_type*> _ports,
unsigned _rcount, unsigned _id)
: core_type(p), ports(_ports), entrypoint(lib.get_entrypoint()), rcount(_rcount), id(_id)
unsigned _rcount, unsigned _id, bool _vfr)
: core_type(p), ports(_ports), entrypoint(lib.get_entrypoint()), rcount(_rcount), id(_id),
vfr(_vfr)
{
}
~c_core_type() throw()
@ -748,6 +753,10 @@ failed:
}
return cset;
}
bool t_is_vfr()
{
return vfr;
}
private:
void copy_settings(std::vector<char>& tmpmem, std::vector<lsnes_core_system_setting>& tmpmem2,
std::map<std::string, std::string>& settings)
@ -778,6 +787,7 @@ failed:
entrypoint_fn entrypoint;
unsigned rcount;
unsigned id;
bool vfr;
};
std::vector<char> msgbuf;
@ -1056,12 +1066,13 @@ no_parameters:
if(!cores.count(_core))
throw std::runtime_error("create_type: Unknown core");
p.core = cores[_core];
bool is_vfr = dynamic_cast<c_core_core*>(p.core)->supports_vfr_sel() && r.is_vfr;
for(unsigned* reg = r.regions; *reg != 0xFFFFFFFFU; reg++) {
if(!regions.count(*reg))
throw std::runtime_error("create_type: Unknown region");
p.regions.push_back(regions[*reg]);
}
return new c_core_type(lib, p, cores[_core]->get_ports(), rcount, type);
return new c_core_type(lib, p, cores[_core]->get_ports(), rcount, type, is_vfr);
}
void initialize_core2(entrypoint_fn fn, std::map<unsigned, core_sysregion*>& sysregs,
@ -1085,7 +1096,7 @@ no_parameters:
//Enumerate what the thing supports.
entrypoint_fn entrypoint(fn);
lsnes_core_enumerate_cores r;
r.emu_flags1 = 1;
r.emu_flags1 = 2;
r.message = callback_message;
r.get_input = callback_get_input;
r.notify_action_update = callback_notify_action_update;

View file

@ -569,6 +569,11 @@ std::vector<std::string> core_core::get_trace_cpus()
return c_get_trace_cpus();
}
bool core_type::t_is_vfr()
{
return false;
}
emucore_callbacks::~emucore_callbacks() throw()
{
}

View file

@ -159,8 +159,10 @@ port_type_set::port_type_set(std::vector<class port_type*> types, struct port_in
//Count maximum number of controller indices to determine the controller multiplier.
controller_multiplier = 1;
for(size_t i = 0; i < port_count; i++)
for(unsigned j = 0; j < types[i]->controller_info->controllers.size(); j++)
controller_multiplier = max(controller_multiplier, (size_t)types[i]->used_indices(j));
for(unsigned j = 0; j < types[i]->controller_info->controllers.size(); j++) {
auto idxcnt = types[i]->used_indices(j);
controller_multiplier = max(controller_multiplier, (size_t)(idxcnt.first + idxcnt.second));
}
//Count maximum number of controllers to determine the port multiplier.
port_multiplier = 1;
for(size_t i = 0; i < port_count; i++)
@ -193,6 +195,28 @@ port_type_set::port_type_set(std::vector<class port_type*> types, struct port_in
}
}
void port_type_set::set_pollcounter_mask(uint32_t* mask) const
{
for(unsigned i = 0; i < indices_size; i++) {
unsigned v = indices_tab[i];
//i is the index into map (which decomposes into triple). v is the index in array (which may be
//invalid).
if(v == 0xFFFFFFFFUL)
continue;
unsigned _port = i / port_multiplier;
unsigned _controller = (i % port_multiplier) / controller_multiplier;
unsigned _button = i % controller_multiplier;
if(_port >= port_count)
continue;
//Determine if this is reserved or not (it is reserved if index is beyond limit) and set bit.
auto idxcnt = port_types[_port]->used_indices(_controller);
if(_button >= idxcnt.first)
mask[v >> 5] &= ~(1U << (v & 31));
else
mask[v >> 5] |= (1U << (v & 31));
}
}
short read_axis_value(const char* buf, size_t& idx) throw()
{
char ch;
@ -272,7 +296,6 @@ void controller_frame::display(unsigned port, unsigned controller, char32_t* buf
}
const port_controller& pc = ptype.controller_info->controllers[controller];
bool need_space = false;
short val;
for(unsigned i = 0; i < pc.buttons.size(); i++) {
const port_controller_button& pcb = pc.buttons[i];
if(need_space && pcb.type != port_controller_button::TYPE_NULL) {
@ -289,8 +312,14 @@ void controller_frame::display(unsigned port, unsigned controller, char32_t* buf
case port_controller_button::TYPE_RAXIS:
case port_controller_button::TYPE_TAXIS:
case port_controller_button::TYPE_LIGHTGUN:
val = ptype.read(&ptype, backingmem, controller, i);
buf += writeu32val(buf, val);
buf += writeu32val(buf, ptype.read(&ptype, backingmem, controller, i));
need_space = true;
break;
case port_controller_button::TYPE_KEYBOARD:
//Write name.
auto str = pc.get_namemap_entry(ptype.read(&ptype, backingmem, controller, i));
for(auto x : str)
*(buf++) = x;
need_space = true;
break;
}
@ -301,22 +330,34 @@ void controller_frame::display(unsigned port, unsigned controller, char32_t* buf
pollcounter_vector::pollcounter_vector() throw(std::bad_alloc)
{
types = &dummytypes();
ctrs = new uint32_t[types->indices()];
clear();
size_t elts_base = types->indices();
size_t elts_mask = (elts_base + 31) / 32;
size_t elts_all = elts_base + elts_mask;
ctrs = new uint32_t[elts_all];
memset(ctrs_valid_mask = ctrs + elts_base, 0xFF, sizeof(uint32_t) * elts_mask);
clear_all();
}
pollcounter_vector::pollcounter_vector(const port_type_set& p) throw(std::bad_alloc)
{
types = &p;
ctrs = new uint32_t[types->indices()];
clear();
size_t elts_base = types->indices();
size_t elts_mask = (elts_base + 31) / 32;
size_t elts_all = elts_base + elts_mask;
ctrs = new uint32_t[elts_all];
types->set_pollcounter_mask(ctrs_valid_mask = ctrs + elts_base);
clear_all();
}
pollcounter_vector::pollcounter_vector(const pollcounter_vector& p) throw(std::bad_alloc)
{
ctrs = new uint32_t[p.types->indices()];
size_t elts_base = p.types->indices();
size_t elts_mask = (elts_base + 31) / 32;
size_t elts_all = elts_base + elts_mask;
ctrs = new uint32_t[elts_all];
ctrs_valid_mask = ctrs + elts_base;
types = p.types;
memcpy(ctrs, p.ctrs, sizeof(uint32_t) * p.types->indices());
memcpy(ctrs, p.ctrs, sizeof(uint32_t) * elts_all);
framepflag = p.framepflag;
}
@ -324,21 +365,34 @@ pollcounter_vector& pollcounter_vector::operator=(const pollcounter_vector& p) t
{
if(this == &p)
return *this;
uint32_t* n = new uint32_t[p.types->indices()];
size_t elts_base = p.types->indices();
size_t elts_mask = (elts_base + 31) / 32;
size_t elts_all = elts_base + elts_mask;
uint32_t* n = new uint32_t[elts_all];
types = p.types;
memcpy(n, p.ctrs, sizeof(uint32_t) * p.types->indices());
memcpy(n, p.ctrs, sizeof(uint32_t) * elts_all);
delete[] ctrs;
ctrs = n;
ctrs_valid_mask = ctrs + elts_base;
framepflag = p.framepflag;
return *this;
}
pollcounter_vector::~pollcounter_vector() throw()
{
//ctrs_valid_mask is part of this.
delete[] ctrs;
}
void pollcounter_vector::clear() throw()
void pollcounter_vector::clear_unmasked() throw()
{
for(size_t i = 0; i < types->indices(); i++)
if((ctrs_valid_mask[i >> 5] >> (i & 31)) & 1)
ctrs[i] = 0;
framepflag = false;
}
void pollcounter_vector::clear_all() throw()
{
memset(ctrs, 0, sizeof(uint32_t) * types->indices());
framepflag = false;
@ -346,46 +400,65 @@ void pollcounter_vector::clear() throw()
void pollcounter_vector::set_all_DRDY() throw()
{
//Only manipulate valid pcounters.
for(size_t i = 0; i < types->indices(); i++)
ctrs[i] |= 0x80000000UL;
if((ctrs_valid_mask[i >> 5] >> (i & 31)) & 1)
ctrs[i] |= 0x80000000UL;
}
void pollcounter_vector::clear_DRDY(unsigned idx) throw()
{
ctrs[idx] &= 0x7FFFFFFFUL;
//Only manipulate valid pcounters.
if((ctrs_valid_mask[idx >> 5] >> (idx & 31)) & 1)
ctrs[idx] &= 0x7FFFFFFFUL;
}
bool pollcounter_vector::get_DRDY(unsigned idx) throw()
{
return ((ctrs[idx] & 0x80000000UL) != 0);
//For not valid pcounters, DRDY is permanently 1.
if((ctrs_valid_mask[idx >> 5] >> (idx & 31)) & 1)
return ((ctrs[idx] & 0x80000000UL) != 0);
else
return true;
}
bool pollcounter_vector::has_polled() throw()
{
uint32_t res = 0;
for(size_t i = 0; i < types->indices() ; i++)
res |= ctrs[i];
for(size_t i = 0; i < types->indices(); i++)
if((ctrs_valid_mask[i >> 5] >> (i & 31)) & 1)
res |= ctrs[i];
return ((res & 0x7FFFFFFFUL) != 0);
}
uint32_t pollcounter_vector::get_polls(unsigned idx) throw()
{
return ctrs[idx] & 0x7FFFFFFFUL;
//For not valid pcounters, polls is permanently 0.
if((ctrs_valid_mask[idx >> 5] >> (idx & 31)) & 1)
return ctrs[idx] & 0x7FFFFFFFUL;
else
return 0;
}
uint32_t pollcounter_vector::increment_polls(unsigned idx) throw()
{
uint32_t x = ctrs[idx] & 0x7FFFFFFFUL;
++ctrs[idx];
return x;
//Only manipulate valid pcounters, for others, permanently 0.
if((ctrs_valid_mask[idx >> 5] >> (idx & 31)) & 1) {
uint32_t x = ctrs[idx] & 0x7FFFFFFFUL;
++ctrs[idx];
return x;
} else
return 0;
}
uint32_t pollcounter_vector::max_polls() throw()
{
uint32_t max = 0;
for(unsigned i = 0; i < types->indices(); i++) {
uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL;
max = (max < tmp) ? tmp : max;
if((ctrs_valid_mask[i >> 5] >> (i & 31)) & 1) {
uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL;
max = (max < tmp) ? tmp : max;
}
}
return max;
}
@ -393,7 +466,6 @@ uint32_t pollcounter_vector::max_polls() throw()
void pollcounter_vector::save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc)
{
mem.resize(types->indices());
//Compatiblity fun.
for(size_t i = 0; i < types->indices(); i++)
mem[i] = ctrs[i];
}
@ -824,6 +896,7 @@ unsigned port_controller::analog_actions() const
break;
case port_controller_button::TYPE_NULL:
case port_controller_button::TYPE_BUTTON:
case port_controller_button::TYPE_KEYBOARD:
;
};
}
@ -868,6 +941,7 @@ std::pair<unsigned, unsigned> port_controller::analog_action(unsigned k) const
break;
case port_controller_button::TYPE_NULL:
case port_controller_button::TYPE_BUTTON:
case port_controller_button::TYPE_KEYBOARD:
;
};
}

View file

@ -23,6 +23,22 @@ namespace
return y.str();
}
std::string quote(const std::u32string& _s)
{
auto s = utf8::to8(_s);
std::ostringstream y;
y << "U\"";
for(auto i : s)
if(i == '\"')
y << "\"";
else if(i == '\\')
y << "\\\\";
else
y << i;
y << "\"";
return y.str();
}
std::string read_str(const JSON::node& root, const JSON::pointer& ptr)
{
if(root.type_of(ptr) != JSON::string)
@ -117,6 +133,22 @@ namespace
return ret;
}
port_controller_button pcb_key(const JSON::node& root, const JSON::pointer& ptr)
{
auto pshadow = ptr.field("shadow");
auto pname = ptr.field("name");
struct port_controller_button ret;
ret.type = port_controller_button::TYPE_KEYBOARD;
ret.symbol = U'\0';
ret.shadow = (root.type_of(pshadow) != JSON::none) ? read_bool(root, pshadow) : false;
ret.rmin = 0;
ret.rmax = 0;
ret.centers = false;
ret.macro = "";
ret.msymbol = '\0';
return ret;
}
struct port_controller_button pcs_parse_button(const JSON::node& root, JSON::pointer ptr)
{
if(root.type_of_indirect(ptr) != JSON::object)
@ -129,6 +161,8 @@ namespace
return pcb_button(root, ptr);
else if(type == "axis" || type == "raxis" || type == "taxis" || type == "lightgun")
return pcb_axis(root, ptr, type);
else if(type == "key")
return pcb_key(root, ptr);
else
(stringfmt() << "Unknown type '" << type << "' for '" << ptr << "'").throwex();
return pcb_null(root, ptr); //NOTREACHED.
@ -153,6 +187,22 @@ namespace
ret.cclass = cclass;
ret.type = type;
ret.buttons = buttons;
JSON::pointer _buttonmap = ptr.field("buttonmap");
//Namemap is OPTIONAL. If present, it must be an array, giving the entries as strings (or nil)
//for not present.
if(root.type_of_indirect(_buttonmap) != JSON::none) {
if(root.type_of_indirect(_buttonmap) != JSON::array)
(stringfmt() << "Expected array or not present for '" << _buttonmap << "'").throwex();
for(auto i = root[_buttonmap].begin(); i != root[_buttonmap].end(); ++i) {
if(i->type() == JSON::string) {
ret.namemap[i.index()] = i->as_string();
} else if(i->type() != JSON::none) {
(stringfmt() << "Expected string or not present for '"
<< _buttonmap.index(i.index()) << "'").throwex();
}
}
}
ret.reserved_idx = (ret.namemap.size() + 31) / 32; //One bit per namemap entry.
return ret;
}
@ -202,6 +252,7 @@ namespace
case port_controller_button::TYPE_RAXIS: s << "RAXIS"; break;
case port_controller_button::TYPE_TAXIS: s << "TAXIS"; break;
case port_controller_button::TYPE_LIGHTGUN: s << "LIGHTGUN"; break;
case port_controller_button::TYPE_KEYBOARD: s << "KEYBOARD"; break;
}
s << "," << (int)b.symbol << ", " << quote(b.name) << ", " << b.shadow << ", " << b.rmin << ", "
<< b.rmax << ", " << b.centers << ", " << quote(b.macro) << ", " << (int)b.msymbol << "}";
@ -216,7 +267,10 @@ namespace
write_button(s, i);
s << ",\n";
}
s << "}};\n";
s << "},{";
for(auto i : c.namemap)
s << "\t{" << i.first << ", " << quote(i.second) << "},\n";
s << "}," << c.reserved_idx << "};\n";
}
void write_portdata(std::ostream& s, const port_controller_set& cs, unsigned& idx)
@ -249,6 +303,7 @@ namespace
case port_controller_button::TYPE_NULL: break;
case port_controller_button::TYPE_RAXIS: x+=16; break;
case port_controller_button::TYPE_TAXIS: x+=16; break;
case port_controller_button::TYPE_KEYBOARD: x+=16; break;
};
}
return (x + 7) / 8;
@ -266,6 +321,7 @@ namespace
case port_controller_button::TYPE_NULL: break;
case port_controller_button::TYPE_RAXIS: break;
case port_controller_button::TYPE_TAXIS: break;
case port_controller_button::TYPE_KEYBOARD: break;
};
}
return (x + 7) / 8;
@ -297,6 +353,7 @@ namespace
case port_controller_button::TYPE_RAXIS:
case port_controller_button::TYPE_TAXIS:
case port_controller_button::TYPE_LIGHTGUN:
case port_controller_button::TYPE_KEYBOARD:
ii.type = 2;
ii.offset = aoffset + 2 * axisidx2;
axisidx2++;
@ -343,6 +400,7 @@ namespace
case port_controller_button::TYPE_RAXIS:
case port_controller_button::TYPE_TAXIS:
case port_controller_button::TYPE_LIGHTGUN:
case port_controller_button::TYPE_KEYBOARD:
ins.type = 1;
ins.offset = aoffset + 2 * axisidx;
ret.push_back(ins);

View file

@ -120,7 +120,7 @@ void movie::next_frame() throw(std::bad_alloc)
}
//Reset the poll counters and DRDY flags.
pollcounters.clear();
pollcounters.clear_unmasked();
//Increment the current frame counter and subframe counter. Note that first subframe is undefined for
//frame 0 and 0 for frame 1.
@ -346,7 +346,7 @@ void movie::reset_state() throw()
readonly = true;
current_frame = 0;
current_frame_first_subframe = 0;
pollcounters.clear();
pollcounters.clear_all();
lag_frames = 0;
clear_caches();
}

View file

@ -465,6 +465,7 @@ namespace
case port_controller_button::TYPE_RAXIS: L.pushstring("raxis"); break;
case port_controller_button::TYPE_TAXIS: L.pushstring("axis"); break;
case port_controller_button::TYPE_LIGHTGUN: L.pushstring("lightgun"); break;
case port_controller_button::TYPE_KEYBOARD: L.pushstring("keyboard"); break;
};
L.rawset(-3);
if(cs.buttons[i].symbol) {

View file

@ -452,7 +452,11 @@ void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_
run_callback(*on_movie_lost, "unsaferewind");
mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
try { core.mlogic->get_mfile().host_memory = u2->hostmemory; } catch(...) {}
auto& mf = core.mlogic->get_mfile();
mf.vi_counter = u2->vi_counter;
mf.vi_this_frame = u2->vi_this_frame;
mf.vi_valid = true;
try { mf.host_memory = u2->hostmemory; } catch(...) {}
run_callback(*on_post_rewind);
delete reinterpret_cast<lua::objpin<lua_unsaferewind>*>(u);
} catch(...) {
@ -460,13 +464,16 @@ void lua_state::callback_do_unsafe_rewind(const std::vector<char>& save, uint64_
}
} else {
//Save
run_callback(*on_set_rewind, lua::state::fn_tag([&core, save, secs, ssecs, &mov](lua::state& L) ->
int {
run_callback(*on_set_rewind, lua::state::fn_tag([&core, save, secs, ssecs, &mov](lua::state& L)
-> int {
lua_unsaferewind* u2 = lua::_class<lua_unsaferewind>::create(*core.lua);
u2->state = save;
u2->secs = secs,
u2->ssecs = ssecs;
u2->hostmemory = core.mlogic->get_mfile().host_memory;
auto& mf = core.mlogic->get_mfile();
u2->vi_this_frame = mf.vi_this_frame;
u2->vi_counter = mf.vi_counter;
u2->hostmemory = mf.host_memory;
mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
return 1;
}));

View file

@ -13,6 +13,13 @@ namespace
return 1;
}
int currentvi(lua::state& L, lua::parameters& P)
{
auto& m = CORE().mlogic->get_mfile();
L.pushnumber(m.vi_counter);
return 1;
}
int lagcounter(lua::state& L, lua::parameters& P)
{
auto& m = CORE().mlogic->get_movie();
@ -136,6 +143,7 @@ namespace
lua::functions LUA_movie_fns(lua_func_misc, "movie", {
{"currentframe", currentframe},
{"currentvi", currentvi},
{"lagcount", lagcounter},
{"framecount", framecount},
{"rerecords", rerecords},

View file

@ -74,6 +74,16 @@ namespace
uint64_t divsl[] = {1000000, 100000, 10000, 1000, 100, 10, 0};
const unsigned divcnt = sizeof(divs)/sizeof(divs[0]);
bool is_ctype(int type)
{
return (type == 0 || type == 1 || type == 2);
}
bool is_wtype(int type)
{
return (type == -1 || type == 1 || type == 2);
}
class exp_imp_type
{
public:
@ -110,7 +120,7 @@ struct control_info
unsigned position_left;
unsigned reserved; //Must be at least 6 for axes.
unsigned index; //Index in poll vector.
int type; //-2 => Port, -1 => Fixed, 0 => Button, 1 => axis.
int type; //-2 => Port, -1 => Fixed, 0 => Button, 1 => axis, 2 => Key
char32_t ch;
std::u32string title;
unsigned port;
@ -124,6 +134,7 @@ struct control_info
unsigned port, unsigned controller);
static control_info axisinfo(unsigned& p, const std::u32string& title, unsigned idx,
unsigned port, unsigned controller, port_controller_button::_type _axistype, int _rmin, int _rmax);
//TODO: keyinfo().
};
control_info control_info::portinfo(unsigned& p, unsigned port, unsigned controller)
@ -261,6 +272,8 @@ void frame_controls::add_port(unsigned& c, unsigned pid, const port_type& p, con
controlinfo.push_back(control_info::axisinfo(c, utf8::to32(pcb.name), idx, pid, i,
pcb.type, pcb.rmin, pcb.rmax));
last_multibyte = true;
} else if(pcb.type == port_controller_button::TYPE_KEYBOARD) {
//TODO
}
}
if(nextp > c)
@ -322,7 +335,7 @@ void frame_controls::format_lines()
//Line2
for(auto i : controlinfo) {
auto _title = i.title;
if(i.type == -1 || i.type == 1)
if(is_wtype(i.type))
std::copy(_title.begin(), _title.end(), &cp2[i.position_left + off]);
if(i.type == 0)
cp2[i.position_left + off] = i.ch;
@ -389,6 +402,9 @@ namespace
first = false;
last_axis = true;
break;
case 2: //key
//TODO.
break;
}
}
return x.str();
@ -450,6 +466,9 @@ namespace
first = false;
last_axis = true;
break;
case 2: //Key.
//TODO.
break;
}
}
}
@ -496,7 +515,7 @@ namespace
{
std::set<unsigned> r;
for(auto i : info.get_controlinfo()) {
if(i.port == port && i.controller == controller && (i.type == 0 || i.type == 1))
if(i.port == port && i.controller == controller && is_ctype(i.type))
r.insert(i.index);
}
return r;
@ -1016,6 +1035,9 @@ void wxeditor_movie::_moviepanel::render_linen(text_framebuffer& fb, controller_
char c[7];
sprintf(c, "%6d", fcontrols.read_index(f, i.index));
fb.write(c, 0, divcnt + 1 + i.position_left, y, 0x000000, bgc);
} else if(i.type == 2) {
//Key.
//TODO.
}
}
}
@ -1416,6 +1438,8 @@ void wxeditor_movie::_moviepanel::on_mouse0(unsigned x, unsigned y, bool polarit
}
} else
do_alter_axis(idx, press_line, line);
} else if(i.type == 2) {
//TODO.
}
}
}
@ -1831,7 +1855,7 @@ void wxeditor_movie::_moviepanel::on_mouse2(unsigned x, unsigned y, bool polarit
} else {
for(auto i : fcontrols.get_controlinfo())
if(press_x >= i.position_left + off && press_x < i.position_left + i.reserved + off) {
if(i.type == 0 || i.type == 1) {
if(is_ctype(i.type)) {
clicked_button = true;
clicked = i;
controller_name = (stringfmt() << "controller " << i.port << "-"
@ -1847,7 +1871,7 @@ void wxeditor_movie::_moviepanel::on_mouse2(unsigned x, unsigned y, bool polarit
uint64_t ebutton_low = clicked_button ? first_editable(clicked.index) : std::numeric_limits<uint64_t>::max();
uint64_t econtroller_low = ebutton_low;
for(auto i : fcontrols.get_controlinfo())
if(i.port == clicked.port && i.controller == clicked.controller && (i.type == 0 || i.type == 1))
if(i.port == clicked.port && i.controller == clicked.controller && is_ctype(i.type))
econtroller_low = max(econtroller_low, first_editable(i.index));
bool click_zero = (clicked_button && !clicked.port && !clicked.controller);
@ -1876,6 +1900,7 @@ void wxeditor_movie::_moviepanel::on_mouse2(unsigned x, unsigned y, bool polarit
press_line < linecount) || (rpress_line >= ebutton_low && rpress_line < linecount)));
//Sweep axis is enabled if change axis is enabled and lines don't match.
enable_sweep_axis = (enable_change_axis && press_line != rpress_line);
//TODO: Handle clicked.type == 2.
//Insert frame is enabled if this frame is completely editable and press and release lines match.
enable_insert_frame = (!not_editable && press_line + 1 >= eframe_low && press_line < linecount &&
press_line == rpress_line);

View file

@ -522,6 +522,8 @@ void wxeditor_tasinput::update_controls()
current->Add(t.check);
t.check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(wxeditor_tasinput::on_control), NULL, this);
} else if(i.type == port_controller_button::TYPE_KEYBOARD) {
//TODO.
} else {
t.panel = new xypanel(current_p, inst, current, i, this,
wxCommandEventHandler(wxeditor_tasinput::on_control), next_id);

View file

@ -1253,6 +1253,8 @@ void wxwin_mainwindow::update_statusbar()
s << "Frame: " << vars.curframe;
else
s << "Frame: " << vars.curframe << "/" << vars.length;
if(vars.vi_valid)
s << " VI: " << vars.vi_counter;
s << " Lag: " << vars.lag;
if(vars.subframe == _lsnes_status::subframe_savepoint)
s << " Subframe: S";