Pack movie data in memory
Instead of using full 2*100 bytes for each subframe of movie data, pack it in controller-dependent way, reducing the memory usage to 7-20 bytes per subframe (90-96% reduction).
This commit is contained in:
parent
95122ae109
commit
ec20694f7b
29 changed files with 2289 additions and 1100 deletions
|
@ -1,7 +1,7 @@
|
|||
#ifndef _controller__hpp__included__
|
||||
#define _controller__hpp__included__
|
||||
|
||||
#include "controllerdata.hpp"
|
||||
#include "controllerframe.hpp"
|
||||
|
||||
/**
|
||||
* Look up physcial controller ID by logical controller ID.
|
||||
|
@ -46,7 +46,7 @@ void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core = t
|
|||
* Parameter frame: Current frame number.
|
||||
* Returns: Current controls, taking autohold and autofire into account.
|
||||
*/
|
||||
controls_t get_current_controls(uint64_t frame);
|
||||
controller_frame get_current_controls(uint64_t frame);
|
||||
|
||||
/**
|
||||
* Send analog input.
|
||||
|
|
|
@ -1,427 +0,0 @@
|
|||
#ifndef _controllerdata__hpp__included__
|
||||
#define _controllerdata__hpp__included__
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#define ENCODE_SPECIAL_NO_OUTPUT 0xFFFFFFFFU
|
||||
|
||||
/**
|
||||
* What version to write as control version?
|
||||
*/
|
||||
#define WRITE_CONTROL_VERSION 0
|
||||
/**
|
||||
* System control: Frame sync flag
|
||||
*/
|
||||
#define CONTROL_FRAME_SYNC 0
|
||||
/**
|
||||
* System control: System reset button
|
||||
*/
|
||||
#define CONTROL_SYSTEM_RESET 1
|
||||
/**
|
||||
* High part of cycle count for system reset (multiplier 10000).
|
||||
*/
|
||||
#define CONTROL_SYSTEM_RESET_CYCLES_HI 2
|
||||
/**
|
||||
* Low part of cycle count for system reset (multiplier 1).
|
||||
*/
|
||||
#define CONTROL_SYSTEM_RESET_CYCLES_LO 3
|
||||
/**
|
||||
* Number of system controls.
|
||||
*/
|
||||
#define MAX_SYSTEM_CONTROLS 4
|
||||
/**
|
||||
* SNES has 2 controller ports.
|
||||
*/
|
||||
#define MAX_PORTS 2
|
||||
/**
|
||||
* Multitap can connect 4 controllers to a single port.
|
||||
*/
|
||||
#define MAX_CONTROLLERS_PER_PORT 4
|
||||
/**
|
||||
* Ordinary gamepad has 12 buttons/axis total (more than anything else supported).
|
||||
*/
|
||||
#define CONTROLLER_CONTROLS 12
|
||||
/**
|
||||
* The total number of controls (currently 100).
|
||||
*/
|
||||
#define TOTAL_CONTROLS (MAX_SYSTEM_CONTROLS + MAX_PORTS * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT)
|
||||
|
||||
struct controls_t;
|
||||
|
||||
/**
|
||||
* Decoders
|
||||
*/
|
||||
class cdecode
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This is type of functions that perform decoding of port fields.
|
||||
*
|
||||
* parameter port: The number of port to decode.
|
||||
* parameter line: The line to decode from.
|
||||
* parameter pos: Position on the line to start from.
|
||||
* parameter controls: Buffer to place the read controls to.
|
||||
* returns: End of fields (end of string or on '|') or start of next field (otherwise).
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Bad input.
|
||||
*/
|
||||
typedef size_t (*fn_t)(unsigned port, const std::string& line, size_t pos, short* controls);
|
||||
|
||||
/**
|
||||
* This is a decoder for the system field. Note that this is not compatible with fn_t as parameters are diffrent.
|
||||
*
|
||||
* parameter port: The number of port to decode.
|
||||
* parameter line: The line to decode from.
|
||||
* parameter pos: Position on the line to start from.
|
||||
* parameter controls: Buffer to place the read controls to.
|
||||
* parameter version: The version of control structure to read.
|
||||
* returns: End of fields or start of next field.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Bad input.
|
||||
*/
|
||||
static size_t system(const std::string& line, size_t pos, short* controls, unsigned version)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type none (see fn_t).
|
||||
*/
|
||||
static size_t none(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type gamepad (see fn_t).
|
||||
*/
|
||||
static size_t gamepad(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type multitap (see fn_t).
|
||||
*/
|
||||
static size_t multitap(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type mouse (see fn_t).
|
||||
*/
|
||||
static size_t mouse(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type superscope (see fn_t).
|
||||
*/
|
||||
static size_t superscope(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type justifier (see fn_t).
|
||||
*/
|
||||
static size_t justifier(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* This is a port decoder for port type justifiers (see fn_t).
|
||||
*/
|
||||
static size_t justifiers(unsigned port, const std::string& line, size_t pos, short* controls)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encoders
|
||||
*/
|
||||
class cencode
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This is the type of functions that perform encoding of port fields.
|
||||
*
|
||||
* parameter port: To number of port to encode.
|
||||
* parameter buffer: Buffer to store the encoded data to.
|
||||
* parameter pos: Position to start writing to.
|
||||
* parameter controls: Buffer to read the controls from.
|
||||
* returns: Position after written data, or ENCODE_SPECIAL_NO_OUTPUT if one wants no output, suppressing even the
|
||||
* field terminator.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
typedef size_t (*fn_t)(unsigned port, char* buffer, size_t pos, const short* controls);
|
||||
|
||||
/**
|
||||
* This is encoder for the system field. Note that the parameters are bit diffrent and this can't be put into fn_t.
|
||||
*
|
||||
* parameter buffer: Buffer to store the encoded data to.
|
||||
* parameter pos: Position to start writing to.
|
||||
* parameter controls: Buffer to read the controls from.
|
||||
* returns: Position after written data, or ENCODE_SPECIAL_NO_OUTPUT if one wants no output, suppressing even the
|
||||
* field terminator.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
static size_t system(char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type none. See fn_t.
|
||||
*/
|
||||
static size_t none(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type gamepad. See fn_t.
|
||||
*/
|
||||
static size_t gamepad(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type multitap. See fn_t.
|
||||
*/
|
||||
static size_t multitap(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type mouse. See fn_t.
|
||||
*/
|
||||
static size_t mouse(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type superscope. See fn_t.
|
||||
*/
|
||||
static size_t superscope(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type justifier. See fn_t.
|
||||
*/
|
||||
static size_t justifier(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This is a port encoder for port type justifiers. See fn_t.
|
||||
*/
|
||||
static size_t justifiers(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure holds controls for single (sub)frame or instant of time.
|
||||
*/
|
||||
struct controls_t
|
||||
{
|
||||
/**
|
||||
* Creates new controls structure. All buttons are released and all axes are 0 (neutral).
|
||||
*
|
||||
* parameter sync: If true, write 1 (pressed) to frame sync subfield, else write 0 (released). Default false.
|
||||
*/
|
||||
explicit controls_t(bool sync = false) throw();
|
||||
|
||||
/**
|
||||
* This constructor takes in a line of input, port decoders and system field version and decodes the controls.
|
||||
*
|
||||
* parameter line: The line to decode.
|
||||
* parameter decoders: The decoders for each port.
|
||||
* parameter version: Version for the system field.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Invalid input line.
|
||||
*/
|
||||
controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
/**
|
||||
* This method takes in port encoders and encodes the controls.
|
||||
*
|
||||
* parameter encoders: The encoders for each port.
|
||||
* returns: The encoded controls.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
std::string tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This method takes in controller (port, controller, control) tuple and returns reference to the value of that
|
||||
* control.
|
||||
*
|
||||
* parameter port: The port number
|
||||
* parameter controller: The controller number within that port.
|
||||
* parameter control: The control number within that controller.
|
||||
* returns: Reference to control value.
|
||||
* throws std::logic_error: port, controller or control is invalid.
|
||||
*/
|
||||
const short& operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* This method takes in control index and returns reference to the value of that control.
|
||||
*
|
||||
* parameter control: The control index number.
|
||||
* returns: Reference to control value.
|
||||
* throws std::logic_error: control index is invalid.
|
||||
*/
|
||||
const short& operator()(unsigned control) const throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* This method takes in controller (port, controller, control) tuple and returns reference to the value of that
|
||||
* control.
|
||||
*
|
||||
* parameter port: The port number
|
||||
* parameter controller: The controller number within that port.
|
||||
* parameter control: The control number within that controller.
|
||||
* returns: Reference to control value.
|
||||
* throws std::logic_error: port, controller or control is invalid.
|
||||
*/
|
||||
short& operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* This method takes in control index and returns reference to the value of that control.
|
||||
*
|
||||
* parameter control: The control index number.
|
||||
* returns: Reference to control value.
|
||||
* throws std::logic_error: control index is invalid.
|
||||
*/
|
||||
short& operator()(unsigned control) throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* Perform XOR per-control.
|
||||
*
|
||||
* parameter other: The othe field to XOR with.
|
||||
* returns: The XOR result.
|
||||
*/
|
||||
controls_t operator^(controls_t other) throw();
|
||||
|
||||
/**
|
||||
* This field contains the raw controller data. Avoid manipulating directly.
|
||||
*/
|
||||
short controls[TOTAL_CONTROLS];
|
||||
|
||||
/**
|
||||
* Equality
|
||||
*/
|
||||
bool operator==(const controls_t& c) const throw();
|
||||
};
|
||||
|
||||
/**
|
||||
* This enumeration gives the type of port.
|
||||
*/
|
||||
enum porttype_t
|
||||
{
|
||||
/**
|
||||
* No device
|
||||
*/
|
||||
PT_NONE = 0, //Nothing connected to port.
|
||||
/**
|
||||
* Gamepad
|
||||
*/
|
||||
PT_GAMEPAD = 1,
|
||||
/**
|
||||
* Multitap (with 4 gamepads connected)
|
||||
*/
|
||||
PT_MULTITAP = 2,
|
||||
/**
|
||||
* Mouse
|
||||
*/
|
||||
PT_MOUSE = 3,
|
||||
/**
|
||||
* Superscope (only allowed for port 2).
|
||||
*/
|
||||
PT_SUPERSCOPE = 4,
|
||||
/**
|
||||
* Justifier (only allowed for port 2).
|
||||
*/
|
||||
PT_JUSTIFIER = 5,
|
||||
/**
|
||||
* 2 Justifiers (only allowed for port 2).
|
||||
*/
|
||||
PT_JUSTIFIERS = 6,
|
||||
/**
|
||||
* Number of controller types.
|
||||
*/
|
||||
PT_LAST_CTYPE = 6,
|
||||
/**
|
||||
* Invalid controller type.
|
||||
*/
|
||||
PT_INVALID = PT_LAST_CTYPE + 1
|
||||
};
|
||||
|
||||
/**
|
||||
* This enumeration gives the type of device.
|
||||
*/
|
||||
enum devicetype_t
|
||||
{
|
||||
/**
|
||||
* No device
|
||||
*/
|
||||
DT_NONE = 0,
|
||||
/**
|
||||
* Gamepad (note that multitap controllers are gamepads)
|
||||
*/
|
||||
DT_GAMEPAD = 1,
|
||||
/**
|
||||
* Mouse
|
||||
*/
|
||||
DT_MOUSE = 3,
|
||||
/**
|
||||
* Superscope
|
||||
*/
|
||||
DT_SUPERSCOPE = 4,
|
||||
/**
|
||||
* Justifier (note that justifiers is two of these).
|
||||
*/
|
||||
DT_JUSTIFIER = 5
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about port type.
|
||||
*/
|
||||
struct port_type
|
||||
{
|
||||
/**
|
||||
* Name of type.
|
||||
*/
|
||||
const char* name;
|
||||
/**
|
||||
* Decoder function.
|
||||
*/
|
||||
cdecode::fn_t decoder;
|
||||
/**
|
||||
* Encoder function.
|
||||
*/
|
||||
cencode::fn_t encoder;
|
||||
/**
|
||||
* Port type value.
|
||||
*/
|
||||
porttype_t ptype;
|
||||
/**
|
||||
* Number of devices.
|
||||
*/
|
||||
unsigned devices;
|
||||
/**
|
||||
* Type of each connected device.
|
||||
*/
|
||||
devicetype_t dtype;
|
||||
/**
|
||||
* True if valid for port1&2, false if valid for only for port 2.
|
||||
*/
|
||||
bool valid_port1;
|
||||
/**
|
||||
* BSNES controller type ID.
|
||||
*/
|
||||
unsigned bsnes_type;
|
||||
/**
|
||||
* Lookup port type by name.
|
||||
*
|
||||
* parameter name: Name of the port type to look up.
|
||||
* parameter port2: True if controller is for port 2, false if for port 1.
|
||||
* returns: The port type structure
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Invalid port type.
|
||||
*/
|
||||
static const port_type& lookup(const std::string& name, bool port2 = true) throw(std::bad_alloc,
|
||||
std::runtime_error);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Information about port types, index by port type value (porttype_t).
|
||||
*/
|
||||
extern port_type port_types[];
|
||||
|
||||
/**
|
||||
* This method takes in controller (port, controller, control) tuple and returns the system index corresponding to
|
||||
* that control.
|
||||
*
|
||||
* parameter port: The port number
|
||||
* parameter controller: The controller number within that port.
|
||||
* parameter control: The control number within that controller.
|
||||
* returns: The control index.
|
||||
* throws std::logic_error: port, controller or control is invalid.
|
||||
*/
|
||||
unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error);
|
||||
|
||||
#endif
|
935
include/core/controllerframe.hpp
Normal file
935
include/core/controllerframe.hpp
Normal file
|
@ -0,0 +1,935 @@
|
|||
#ifndef _controllerframe__hpp__included__
|
||||
#define _controllerframe__hpp__included__
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
/**
|
||||
* For now, reserve 20 bytes, for:
|
||||
*
|
||||
* - 5 bytes for system.
|
||||
* - 6 bytes for port 1 (multitap).
|
||||
* - 9 bytes for port 2 (justifiers).
|
||||
*/
|
||||
#define MAXIMUM_CONTROLLER_FRAME_SIZE 20
|
||||
|
||||
/**
|
||||
* Maximum amount of data controller_frame::display() can write.
|
||||
*/
|
||||
#define MAX_DISPLAY_LENGTH 128
|
||||
/**
|
||||
* Maximum amount of data controller_frame::serialize() can write.
|
||||
*/
|
||||
#define MAX_SERIALIZED_SIZE 256
|
||||
/**
|
||||
* Maximum number of ports.
|
||||
*/
|
||||
#define MAX_PORTS 2
|
||||
/**
|
||||
* Maximum number of controllers per one port.
|
||||
*/
|
||||
#define MAX_CONTROLLERS_PER_PORT 4
|
||||
/**
|
||||
* Maximum numbers of controls per one controller.
|
||||
*/
|
||||
#define MAX_CONTROLS_PER_CONTROLLER 12
|
||||
/**
|
||||
* Number of button controls.
|
||||
*/
|
||||
#define MAX_BUTTONS MAX_PORTS * MAX_CONTROLLERS_PER_PORT * MAX_CONTROLS_PER_CONTROLLER
|
||||
/**
|
||||
* Size of controller page.
|
||||
*/
|
||||
#define CONTROLLER_PAGE_SIZE 65500
|
||||
/**
|
||||
* Special return value for deserialize() indicating no input was taken.
|
||||
*/
|
||||
#define DESERIALIZE_SPECIAL_BLANK 0xFFFFFFFFUL
|
||||
|
||||
/**
|
||||
* This enumeration gives the type of port.
|
||||
*/
|
||||
enum porttype_t
|
||||
{
|
||||
/**
|
||||
* No device
|
||||
*/
|
||||
PT_NONE = 0, //Nothing connected to port.
|
||||
/**
|
||||
* Gamepad
|
||||
*/
|
||||
PT_GAMEPAD = 1,
|
||||
/**
|
||||
* Multitap (with 4 gamepads connected)
|
||||
*/
|
||||
PT_MULTITAP = 2,
|
||||
/**
|
||||
* Mouse
|
||||
*/
|
||||
PT_MOUSE = 3,
|
||||
/**
|
||||
* Superscope (only allowed for port 2).
|
||||
*/
|
||||
PT_SUPERSCOPE = 4,
|
||||
/**
|
||||
* Justifier (only allowed for port 2).
|
||||
*/
|
||||
PT_JUSTIFIER = 5,
|
||||
/**
|
||||
* 2 Justifiers (only allowed for port 2).
|
||||
*/
|
||||
PT_JUSTIFIERS = 6,
|
||||
/**
|
||||
* Number of controller types.
|
||||
*/
|
||||
PT_LAST_CTYPE = 6,
|
||||
/**
|
||||
* Invalid controller type.
|
||||
*/
|
||||
PT_INVALID = PT_LAST_CTYPE + 1
|
||||
};
|
||||
|
||||
/**
|
||||
* This enumeration gives the type of device.
|
||||
*/
|
||||
enum devicetype_t
|
||||
{
|
||||
/**
|
||||
* No device
|
||||
*/
|
||||
DT_NONE = 0,
|
||||
/**
|
||||
* Gamepad (note that multitap controllers are gamepads)
|
||||
*/
|
||||
DT_GAMEPAD = 1,
|
||||
/**
|
||||
* Mouse
|
||||
*/
|
||||
DT_MOUSE = 3,
|
||||
/**
|
||||
* Superscope
|
||||
*/
|
||||
DT_SUPERSCOPE = 4,
|
||||
/**
|
||||
* Justifier (note that justifiers is two of these).
|
||||
*/
|
||||
DT_JUSTIFIER = 5
|
||||
};
|
||||
|
||||
/**
|
||||
* Is not field terminator.
|
||||
*
|
||||
* Parameter ch: The character.
|
||||
* Returns: True if character is not terminator, false if character is terminator.
|
||||
*/
|
||||
inline bool is_nonterminator(char ch) throw()
|
||||
{
|
||||
return (ch != '|' && ch != '\r' && ch != '\n' && ch != '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Read button value.
|
||||
*
|
||||
* Parameter buf: Buffer to read from.
|
||||
* Parameter idx: Index to buffer. Updated.
|
||||
* Returns: The read value.
|
||||
*/
|
||||
inline bool read_button_value(const char* buf, size_t& idx) throw()
|
||||
{
|
||||
char ch = buf[idx];
|
||||
if(is_nonterminator(ch))
|
||||
idx++;
|
||||
return (ch != '|' && ch != '\r' && ch != '\n' && ch != '\0' && ch != '.' && ch != ' ' && ch != '\t');
|
||||
}
|
||||
|
||||
/**
|
||||
* Read axis value.
|
||||
*
|
||||
* Parameter buf: Buffer to read from.
|
||||
* Parameter idx: Index to buffer. Updated.
|
||||
* Returns: The read value.
|
||||
*/
|
||||
short read_axis_value(const char* buf, size_t& idx) throw();
|
||||
|
||||
/**
|
||||
* Skip whitespace.
|
||||
*
|
||||
* Parameter buf: Buffer to read from.
|
||||
* Parameter idx: Index to buffer. Updated.
|
||||
*/
|
||||
inline void skip_field_whitespace(const char* buf, size_t& idx) throw()
|
||||
{
|
||||
while(buf[idx] == ' ' || buf[idx] == '\t')
|
||||
idx++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip rest of the field.
|
||||
*
|
||||
* Parameter buf: Buffer to read from.
|
||||
* Parameter idx: Index to buffer. Updated.
|
||||
* Parameter include_pipe: If true, also skip the '|'.
|
||||
*/
|
||||
inline void skip_rest_of_field(const char* buf, size_t& idx, bool include_pipe) throw()
|
||||
{
|
||||
while(is_nonterminator(buf[idx]))
|
||||
idx++;
|
||||
if(include_pipe && buf[idx] == '|')
|
||||
idx++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize short.
|
||||
*/
|
||||
inline void serialize_short(unsigned char* buf, short val)
|
||||
{
|
||||
buf[0] = static_cast<unsigned short>(val) >> 8;
|
||||
buf[1] = static_cast<unsigned short>(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize short.
|
||||
*/
|
||||
inline short unserialize_short(const unsigned char* buf)
|
||||
{
|
||||
return static_cast<short>((static_cast<unsigned short>(buf[0]) << 8) | static_cast<unsigned short>(buf[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about port type.
|
||||
*/
|
||||
struct porttype_info
|
||||
{
|
||||
/**
|
||||
* Look up information about port type.
|
||||
*
|
||||
* Parameter p: The port type.
|
||||
* Returns: Infor about port type.
|
||||
* Throws std::runtime_error: Invalid port type.
|
||||
*/
|
||||
static const porttype_info& lookup(porttype_t p) throw(std::runtime_error);
|
||||
/**
|
||||
* Look up information about port type.
|
||||
*
|
||||
* Parameter p: The port type string.
|
||||
* Returns: Infor about port type.
|
||||
* Throws std::runtime_error: Invalid port type.
|
||||
*/
|
||||
static const porttype_info& lookup(const std::string& p) throw(std::runtime_error);
|
||||
/**
|
||||
* Register port type.
|
||||
*
|
||||
* Parameter ptype: Type value for port type.
|
||||
* Parameter pname: The name of port type.
|
||||
* Parameter psize: The size of storage for this type.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
porttype_info(porttype_t ptype, const std::string& pname, size_t psize) throw(std::bad_alloc);
|
||||
/**
|
||||
* Unregister port type.
|
||||
*/
|
||||
~porttype_info() throw();
|
||||
/**
|
||||
* Writes controller data into compressed representation.
|
||||
*
|
||||
* Parameter buffer: The buffer storing compressed representation of controller state.
|
||||
* Parameter idx: Index of controller.
|
||||
* Parameter ctrl: The control to manipulate.
|
||||
* Parameter x: New value for control. Only zero/nonzero matters for buttons.
|
||||
*/
|
||||
virtual void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw() = 0;
|
||||
/**
|
||||
* Read controller data from compressed representation.
|
||||
*
|
||||
* Parameter buffer: The buffer storing compressed representation of controller state.
|
||||
* Parameter idx: Index of controller.
|
||||
* Parameter ctrl: The control to query.
|
||||
* Returns: The value of control. Buttons return 0 or 1.
|
||||
*/
|
||||
virtual short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw() = 0;
|
||||
/**
|
||||
* Format compressed controller data into input display.
|
||||
*
|
||||
* Parameter buffer: The buffer storing compressed representation of controller state.
|
||||
* Parameter idx: Index of controller.
|
||||
* Parameter buf: The buffer to write NUL-terminated display string to. Assumed to be MAX_DISPLAY_LENGTH bytes in size.
|
||||
*/
|
||||
virtual void display(const unsigned char* buffer, unsigned idx, char* buf) const throw() = 0;
|
||||
/**
|
||||
* Take compressed controller data and serialize it into textual representation.
|
||||
*
|
||||
* - The initial '|' is also written.
|
||||
*
|
||||
* Parameter buffer: The buffer storing compressed representation of controller state.
|
||||
* Parameter textbuf: The text buffer to write to.
|
||||
* Returns: Number of bytes written.
|
||||
*/
|
||||
virtual size_t serialize(const unsigned char* buffer, char* textbuf) const throw() = 0;
|
||||
/**
|
||||
* Unserialize textual representation into compressed controller state.
|
||||
*
|
||||
* - Only stops reading on '|', NUL, CR or LF in the final read field. That byte is not read.
|
||||
*
|
||||
* Parameter buffer: The buffer storing compressed representation of controller state.
|
||||
* Parameter textbuf: The text buffer to read.
|
||||
* Returns: Number of bytes read.
|
||||
* Throws std::runtime_error: Bad serialization.
|
||||
*/
|
||||
virtual size_t deserialize(unsigned char* buffer, const char* textbuf) const throw() = 0;
|
||||
/**
|
||||
* Return device type for given index.
|
||||
*
|
||||
* Parameter idx: The index of controller.
|
||||
* Returns: The type of device.
|
||||
*/
|
||||
virtual devicetype_t devicetype(unsigned idx) const throw() = 0;
|
||||
/**
|
||||
* Number of controllers connected to this port.
|
||||
*/
|
||||
virtual unsigned controllers() const throw() = 0;
|
||||
/**
|
||||
* Internal type value for port.
|
||||
*/
|
||||
virtual unsigned internal_type() const throw() = 0;
|
||||
/**
|
||||
* Return if type is legal for port.
|
||||
*
|
||||
* Parameter port: Number of port.
|
||||
* Returns: True if legal, false if not.
|
||||
*/
|
||||
virtual bool legal(unsigned port) const throw() = 0;
|
||||
/**
|
||||
* Port type value.
|
||||
*/
|
||||
porttype_t value;
|
||||
/**
|
||||
* Number of bytes it takes to store this.
|
||||
*/
|
||||
size_t storage_size;
|
||||
/**
|
||||
* Name of port type.
|
||||
*/
|
||||
std::string name;
|
||||
private:
|
||||
porttype_info(const porttype_info&);
|
||||
porttype_info& operator=(const porttype_info&);
|
||||
};
|
||||
|
||||
/**
|
||||
* Poll counter vector.
|
||||
*/
|
||||
class pollcounter_vector
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create new pollcounter vector filled with all zeroes and all DRDY bits clear.
|
||||
*/
|
||||
pollcounter_vector() throw();
|
||||
/**
|
||||
* Zero all poll counters and clear all DRDY bits. System flag is cleared.
|
||||
*/
|
||||
void clear() throw();
|
||||
/**
|
||||
* Set all DRDY bits.
|
||||
*/
|
||||
void set_all_DRDY() throw();
|
||||
/**
|
||||
* Clear specified DRDY bit.
|
||||
*
|
||||
* Parameter pid: The physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
*/
|
||||
void clear_DRDY(unsigned pid, unsigned ctrl) throw();
|
||||
/**
|
||||
* Get state of DRDY bit.
|
||||
*
|
||||
* Parameter pid: The physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
* Returns: The DRDY state.
|
||||
*/
|
||||
bool get_DRDY(unsigned pid, unsigned ctrl) throw();
|
||||
/**
|
||||
* Get state of DRDY bit.
|
||||
*
|
||||
* Parameter idx: The control index.
|
||||
* Returns: The DRDY state.
|
||||
*/
|
||||
bool get_DRDY(unsigned idx) throw()
|
||||
{
|
||||
return get_DRDY(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
|
||||
}
|
||||
/**
|
||||
* Is any poll count nonzero or is system flag set?
|
||||
*
|
||||
* Returns: True if at least one poll count is nonzero or if system flag is set. False otherwise.
|
||||
*/
|
||||
bool has_polled() throw();
|
||||
/**
|
||||
* Read the actual poll count on specified control.
|
||||
*
|
||||
* Parameter pid: The physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
* Return: The poll count.
|
||||
*/
|
||||
uint32_t get_polls(unsigned pid, unsigned ctrl) throw();
|
||||
/**
|
||||
* Read the actual poll count on specified control.
|
||||
*
|
||||
* Parameter idx: The control index.
|
||||
* Return: The poll count.
|
||||
*/
|
||||
uint32_t get_polls(unsigned idx) throw()
|
||||
{
|
||||
return get_polls(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
|
||||
}
|
||||
/**
|
||||
* Increment poll count on specified control.
|
||||
*
|
||||
* Parameter pid: The physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
* Return: The poll count pre-increment.
|
||||
*/
|
||||
uint32_t increment_polls(unsigned pid, unsigned ctrl) throw();
|
||||
/**
|
||||
* Set the system flag.
|
||||
*/
|
||||
void set_system() throw();
|
||||
/**
|
||||
* Get the system flag.
|
||||
*
|
||||
* Returns: The state of system flag.
|
||||
*/
|
||||
bool get_system() throw();
|
||||
/**
|
||||
* Get highest poll counter value.
|
||||
*
|
||||
* - System flag counts as 1 poll.
|
||||
*
|
||||
* Returns: The maximum poll count (at least 1 if system flag is set).
|
||||
*/
|
||||
uint32_t max_polls() throw();
|
||||
/**
|
||||
* Save state to memory block.
|
||||
*
|
||||
* Parameter mem: The memory block to save to.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
void save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc);
|
||||
/**
|
||||
* Load state from memory block.
|
||||
*
|
||||
* Parameter mem: The block from restore from.
|
||||
*/
|
||||
void load_state(const std::vector<uint32_t>& mem) throw();
|
||||
/**
|
||||
* Check if state can be loaded without errors.
|
||||
*
|
||||
* Returns: True if load is possible, false otherwise.
|
||||
*/
|
||||
bool check(const std::vector<uint32_t>& mem) throw();
|
||||
private:
|
||||
uint32_t ctrs[MAX_BUTTONS];
|
||||
bool system_flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Single (sub)frame of controls.
|
||||
*/
|
||||
class controller_frame
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor. Invalid port types, dedicated memory.
|
||||
*/
|
||||
controller_frame() throw();
|
||||
/**
|
||||
* Create subframe of controls with specified controller types and dedicated memory.
|
||||
*
|
||||
* Parameter p1: Type of port1.
|
||||
* Parameter p2: Type of port2.
|
||||
*
|
||||
* Throws std::runtime_error: Invalid port type.
|
||||
*/
|
||||
controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error);
|
||||
/**
|
||||
* Create subframe of controls with specified controller types and specified memory.
|
||||
*
|
||||
* Parameter memory: The backing memory.
|
||||
* Parameter p1: Type of port1.
|
||||
* Parameter p2: Type of port2.
|
||||
*
|
||||
* Throws std::runtime_error: Invalid port type or NULL memory.
|
||||
*/
|
||||
controller_frame(unsigned char* memory, porttype_t p1 = PT_GAMEPAD, porttype_t p2 = PT_NONE)
|
||||
throw(std::runtime_error);
|
||||
/**
|
||||
* Copy construct a frame. The memory will be dedicated.
|
||||
*
|
||||
* Parameter obj: The object to copy.
|
||||
*/
|
||||
controller_frame(const controller_frame& obj) throw();
|
||||
/**
|
||||
* Assign a frame. The types must either match or memory must be dedicated.
|
||||
*
|
||||
* Parameter obj: The object to copy.
|
||||
* Returns: Reference to this.
|
||||
* Throws std::runtime_error: The types don't match and memory is not dedicated.
|
||||
*/
|
||||
controller_frame& operator=(const controller_frame& obj) throw(std::runtime_error);
|
||||
/**
|
||||
* Get type of port.
|
||||
*
|
||||
* Parameter port: Number of port.
|
||||
* Returns: The type of port.
|
||||
*/
|
||||
porttype_t get_port_type(unsigned port) throw()
|
||||
{
|
||||
return (port < MAX_PORTS) ? types[port] : PT_NONE;
|
||||
}
|
||||
/**
|
||||
* Get blank dedicated frame of same port types.
|
||||
*
|
||||
* Return blank frame.
|
||||
*/
|
||||
controller_frame blank_frame() throw()
|
||||
{
|
||||
return controller_frame(types[0], types[1]);
|
||||
}
|
||||
/**
|
||||
* Set type of port. Input for that port is zeroized.
|
||||
*
|
||||
* Parameter port: Number of port.
|
||||
* Parameter type: The new type.
|
||||
* Throws std::runtime_error: Bad port type or non-dedicated memory.
|
||||
*/
|
||||
void set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error);
|
||||
/**
|
||||
* Check that types match.
|
||||
*
|
||||
* Parameter obj: Another object.
|
||||
* Returns: True if types match, false otherwise.
|
||||
*/
|
||||
bool types_match(const controller_frame& obj) const throw()
|
||||
{
|
||||
for(size_t i = 0; i < MAX_PORTS; i++)
|
||||
if(types[i] != obj.types[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Perform XOR between controller frames.
|
||||
*
|
||||
* Parameter another: The another object.
|
||||
* Returns: The XOR result (dedicated memory).
|
||||
* Throws std::runtime_error: Type mismatch.
|
||||
*/
|
||||
controller_frame operator^(const controller_frame& another) throw(std::runtime_error)
|
||||
{
|
||||
controller_frame x(*this);
|
||||
for(size_t i = 0; i < MAX_PORTS; i++)
|
||||
if(types[i] != another.types[i])
|
||||
throw std::runtime_error("controller_frame::operator^: Type mismatch");
|
||||
for(size_t i = 0; i < totalsize; i++)
|
||||
x.backing[i] ^= another.backing[i];
|
||||
return x;
|
||||
}
|
||||
/**
|
||||
* Set the sync flag.
|
||||
*
|
||||
* Parameter x: The value to set the sync flag to.
|
||||
*/
|
||||
void sync(bool x) throw()
|
||||
{
|
||||
if(x)
|
||||
backing[0] |= 1;
|
||||
else
|
||||
backing[0] &= ~1;
|
||||
}
|
||||
/**
|
||||
* Get the sync flag.
|
||||
*
|
||||
* Return value: Value of sync flag.
|
||||
*/
|
||||
bool sync() throw()
|
||||
{
|
||||
return ((backing[0] & 1) != 0);
|
||||
}
|
||||
/**
|
||||
* Quick get sync flag for buffer.
|
||||
*/
|
||||
static bool sync(const unsigned char* mem) throw()
|
||||
{
|
||||
return ((mem[0] & 1) != 0);
|
||||
}
|
||||
/**
|
||||
* Set the reset flag.
|
||||
*
|
||||
* Parameter x: The value to set the reset flag to.
|
||||
*/
|
||||
void reset(bool x) throw()
|
||||
{
|
||||
if(x)
|
||||
backing[0] |= 2;
|
||||
else
|
||||
backing[0] &= ~2;
|
||||
}
|
||||
/**
|
||||
* Get the reset flag.
|
||||
*
|
||||
* Return value: Value of resset flag.
|
||||
*/
|
||||
bool reset() throw()
|
||||
{
|
||||
return ((backing[0] & 2) != 0);
|
||||
}
|
||||
/**
|
||||
* Set the reset delay.
|
||||
*
|
||||
* Parameter x: The value to set reset delay to.
|
||||
*/
|
||||
void delay(std::pair<short, short> x) throw()
|
||||
{
|
||||
backing[1] = static_cast<unsigned short>(x.first) >> 8;
|
||||
backing[2] = static_cast<unsigned short>(x.first);
|
||||
backing[3] = static_cast<unsigned short>(x.second) >> 8;
|
||||
backing[4] = static_cast<unsigned short>(x.second);
|
||||
}
|
||||
/**
|
||||
* Get the reset delay.
|
||||
*
|
||||
* Return value: Value of reset delay.
|
||||
*/
|
||||
std::pair<short, short> delay() throw()
|
||||
{
|
||||
short x, y;
|
||||
x = static_cast<short>(static_cast<unsigned short>(backing[1]) << 8);
|
||||
x |= static_cast<short>(static_cast<unsigned short>(backing[2]));
|
||||
y = static_cast<short>(static_cast<unsigned short>(backing[3]) << 8);
|
||||
y |= static_cast<short>(static_cast<unsigned short>(backing[4]));
|
||||
return std::make_pair(x, y);
|
||||
}
|
||||
/**
|
||||
* Get size of frame.
|
||||
*
|
||||
* Returns: The number of bytes it takes to store frame of this type.
|
||||
*/
|
||||
size_t size()
|
||||
{
|
||||
return totalsize;
|
||||
}
|
||||
/**
|
||||
* Set axis/button value.
|
||||
*
|
||||
* Parameter pid: Physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
* Parameter x: The new value.
|
||||
*/
|
||||
void axis(unsigned pid, unsigned ctrl, short x) throw()
|
||||
{
|
||||
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
|
||||
pinfo[port]->write(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, ctrl, x);
|
||||
}
|
||||
/**
|
||||
* Set axis/button value.
|
||||
*
|
||||
* Parameter idx: Control index.
|
||||
* Parameter x: The new value.
|
||||
*/
|
||||
void axis2(unsigned idx, short x) throw()
|
||||
{
|
||||
axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER, x);
|
||||
}
|
||||
/**
|
||||
* Get axis/button value.
|
||||
*
|
||||
* Parameter pid: Physical controller id.
|
||||
* Parameter ctrl: The control id.
|
||||
* Return value: The axis value.
|
||||
*/
|
||||
short axis(unsigned pid, unsigned ctrl) throw()
|
||||
{
|
||||
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
|
||||
return pinfo[port]->read(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get axis/button value.
|
||||
*
|
||||
* Parameter idx: Index of control.
|
||||
* Return value: The axis value.
|
||||
*/
|
||||
short axis2(unsigned idx) throw()
|
||||
{
|
||||
return axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
|
||||
}
|
||||
/**
|
||||
* Get controller display.
|
||||
*
|
||||
* Parameter pid: Physical controller id.
|
||||
* Parameter buf: Buffer to write nul-terminated display to.
|
||||
*/
|
||||
void display(unsigned pid, char* buf) throw()
|
||||
{
|
||||
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
|
||||
return pinfo[port]->display(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, buf);
|
||||
}
|
||||
/**
|
||||
* Get device type.
|
||||
*
|
||||
* Parameter pid: Physical controller id.
|
||||
* Returns: Device type.
|
||||
*/
|
||||
devicetype_t devicetype(unsigned pid) throw()
|
||||
{
|
||||
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
|
||||
return pinfo[port]->devicetype(pid % MAX_CONTROLLERS_PER_PORT);
|
||||
}
|
||||
/**
|
||||
* Deserialize frame from text format.
|
||||
*
|
||||
* Parameter buf: The buffer containing text representation. Terminated by NUL, CR or LF.
|
||||
* Throws std::runtime_error: Bad serialized representation.
|
||||
*/
|
||||
void deserialize(const char* buf) throw(std::runtime_error)
|
||||
{
|
||||
size_t offset = 0;
|
||||
offset += system_deserialize(backing, buf);
|
||||
if(buf[offset] == '|')
|
||||
offset++;
|
||||
for(size_t i = 0; i < MAX_PORTS; i++) {
|
||||
size_t s = pinfo[i]->deserialize(backing + offsets[i], buf + offset);
|
||||
if(s != DESERIALIZE_SPECIAL_BLANK) {
|
||||
offset += s;
|
||||
if(buf[offset] == '|')
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Serialize frame to text format.
|
||||
*
|
||||
* Parameter buf: The buffer to write NUL-terminated text representation to.
|
||||
*/
|
||||
void serialize(char* buf) throw()
|
||||
{
|
||||
size_t offset = 0;
|
||||
offset += system_serialize(backing, buf);
|
||||
for(size_t i = 0; i < MAX_PORTS; i++) {
|
||||
offset += pinfo[i]->serialize(backing + offsets[i], buf + offset);
|
||||
buf[offset++] = (i < MAX_PORTS - 1) ? '|' : '\0';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return copy with dedicated memory.
|
||||
*
|
||||
* Parameter sync: If set, the frame will have sync flag set, otherwise it will have sync flag clear.
|
||||
* Returns: Copy of this frame.
|
||||
*/
|
||||
controller_frame copy(bool sync)
|
||||
{
|
||||
controller_frame c(*this);
|
||||
c.sync(sync);
|
||||
return c;
|
||||
}
|
||||
/**
|
||||
* Compare two frames.
|
||||
*
|
||||
* Parameter obj: Another frame.
|
||||
* Returns: True if equal, false if not.
|
||||
*/
|
||||
bool operator==(const controller_frame& obj) const throw()
|
||||
{
|
||||
if(!types_match(obj))
|
||||
return false;
|
||||
return !memcmp(backing, obj.backing, totalsize);
|
||||
}
|
||||
/**
|
||||
* Compare two frames.
|
||||
*
|
||||
* Parameter obj: Another frame.
|
||||
* Returns: True if not equal, false if equal.
|
||||
*/
|
||||
bool operator!=(const controller_frame& obj) const throw()
|
||||
{
|
||||
return !(*this == obj);
|
||||
}
|
||||
private:
|
||||
size_t totalsize;
|
||||
unsigned char memory[MAXIMUM_CONTROLLER_FRAME_SIZE];
|
||||
unsigned char* backing;
|
||||
porttype_t types[MAX_PORTS];
|
||||
size_t offsets[MAX_PORTS];
|
||||
const porttype_info* pinfo[MAX_PORTS];
|
||||
static size_t system_serialize(const unsigned char* buffer, char* textbuf);
|
||||
static size_t system_deserialize(unsigned char* buffer, const char* textbuf);
|
||||
void set_types(const porttype_t* tarr);
|
||||
};
|
||||
|
||||
/**
|
||||
* Vector of controller frames.
|
||||
*/
|
||||
class controller_frame_vector
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct new controller frame vector.
|
||||
*
|
||||
* Parameter p1: Type of port 1.
|
||||
* Parameter p2: Type of port 2.
|
||||
* Throws std::runtime_error: Illegal port types.
|
||||
*/
|
||||
controller_frame_vector(enum porttype_t p1 = PT_GAMEPAD, enum porttype_t p2 = PT_NONE)
|
||||
throw(std::runtime_error);
|
||||
/**
|
||||
* Destroy controller frame vector
|
||||
*/
|
||||
~controller_frame_vector() throw();
|
||||
/**
|
||||
* Copy controller frame vector.
|
||||
*
|
||||
* Parameter obj: The object to copy.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
controller_frame_vector(const controller_frame_vector& vector) throw(std::bad_alloc);
|
||||
/**
|
||||
* Assign controller frame vector.
|
||||
*
|
||||
* Parameter obj: The object to copy.
|
||||
* Returns: Reference to this.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
controller_frame_vector& operator=(const controller_frame_vector& vector) throw(std::bad_alloc);
|
||||
/**
|
||||
* Blank vector and change the type of ports.
|
||||
*
|
||||
* Parameter p1: Type of port 1.
|
||||
* Parameter p2: Type of port 2.
|
||||
* Throws std::runtime_error: Illegal port types.
|
||||
*/
|
||||
void clear(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error);
|
||||
/**
|
||||
* Blank vector.
|
||||
*/
|
||||
void clear() throw()
|
||||
{
|
||||
clear(types[0], types[1]);
|
||||
}
|
||||
/**
|
||||
* Get number of subframes.
|
||||
*/
|
||||
size_t size()
|
||||
{
|
||||
return frames;
|
||||
}
|
||||
/**
|
||||
* Access specified subframe.
|
||||
*
|
||||
* Parameter x: The frame number.
|
||||
* Returns: The controller frame.
|
||||
* Throws std::runtime_error: Invalid frame index.
|
||||
*/
|
||||
controller_frame operator[](size_t x)
|
||||
{
|
||||
size_t page = x / frames_per_page;
|
||||
size_t pageoffset = frame_size * (x % frames_per_page);
|
||||
if(x >= frames)
|
||||
throw std::runtime_error("controller_frame_vector::operator[]: Illegal index");
|
||||
if(page != cache_page_num) {
|
||||
cache_page = &pages[page];
|
||||
cache_page_num = page;
|
||||
}
|
||||
return controller_frame(cache_page->content + pageoffset, types[0], types[1]);
|
||||
}
|
||||
/**
|
||||
* Append a subframe.
|
||||
*
|
||||
* Parameter frame: The frame to append.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
* Throws std::runtime_error: Port type mismatch.
|
||||
*/
|
||||
void append(controller_frame frame) throw(std::bad_alloc, std::runtime_error);
|
||||
/**
|
||||
* Change length of vector.
|
||||
*
|
||||
* - Reducing length of vector will discard extra elements.
|
||||
* - Extending length of vector will add all-zero elements.
|
||||
*
|
||||
* Parameter newsize: New size of vector.
|
||||
* Throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
void resize(size_t newsize) throw(std::bad_alloc);
|
||||
/**
|
||||
* Walk the indexes of sync subframes.
|
||||
*
|
||||
* - If frame is in range and there is at least one more sync subframe after it, the index of first sync subframe
|
||||
* after given frame.
|
||||
* - If frame is in range, but there are no more sync subframes after it, the length of vector is returned.
|
||||
* - If frame is out of range, the given frame is returned.
|
||||
*
|
||||
* Parameter frame: The frame number to start search from.
|
||||
* Returns: Index of next sync frame.
|
||||
*/
|
||||
size_t walk_sync(size_t frame) throw()
|
||||
{
|
||||
return walk_helper(frame, true);
|
||||
}
|
||||
/**
|
||||
* Get number of subframes in frame. The given subframe is assumed to be sync subframe.
|
||||
*
|
||||
* - The return value is the same as (walk_sync(frame) - frame).
|
||||
*
|
||||
* Parameter frame: The frame number to start search from.
|
||||
* Returns: Number of subframes in this frame.
|
||||
*/
|
||||
size_t subframe_count(size_t frame) throw()
|
||||
{
|
||||
return walk_helper(frame, false);
|
||||
}
|
||||
/**
|
||||
* Count number of subframes in vector with sync flag set.
|
||||
*
|
||||
* Returns: The number of frames.
|
||||
*/
|
||||
size_t count_frames() throw();
|
||||
/**
|
||||
* Return blank controller frame with correct type and dedicated memory.
|
||||
*
|
||||
* Parameter sync: If set, the frame will have sync flag set, otherwise it will have sync flag clear.
|
||||
* Returns: Blank frame.
|
||||
*/
|
||||
controller_frame blank_frame(bool sync)
|
||||
{
|
||||
controller_frame c(types[0], types[1]);
|
||||
c.sync(sync);
|
||||
return c;
|
||||
}
|
||||
private:
|
||||
class page
|
||||
{
|
||||
public:
|
||||
page() { memset(content, 0, CONTROLLER_PAGE_SIZE); }
|
||||
unsigned char content[CONTROLLER_PAGE_SIZE];
|
||||
};
|
||||
size_t frames_per_page;
|
||||
size_t frame_size;
|
||||
size_t frames;
|
||||
porttype_t types[MAX_PORTS];
|
||||
size_t cache_page_num;
|
||||
page* cache_page;
|
||||
std::map<size_t, page> pages;
|
||||
size_t walk_helper(size_t frame, bool sflag) throw();
|
||||
void clear_cache()
|
||||
{
|
||||
cache_page_num = 0;
|
||||
cache_page_num--;
|
||||
cache_page = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -12,7 +12,7 @@ extern "C"
|
|||
std::string get_string_argument(lua_State* LS, unsigned argindex, const char* fname);
|
||||
bool get_boolean_argument(lua_State* LS, unsigned argindex, const char* fname);
|
||||
extern lua_render_context* lua_render_ctx;
|
||||
extern controls_t* lua_input_controllerdata;
|
||||
extern controller_frame* lua_input_controllerdata;
|
||||
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define _lua__hpp__included__
|
||||
|
||||
#include "render.hpp"
|
||||
#include "controllerdata.hpp"
|
||||
#include "controllerframe.hpp"
|
||||
|
||||
struct lua_State;
|
||||
|
||||
|
@ -69,7 +69,7 @@ void init_lua() throw();
|
|||
void quit_lua() throw();
|
||||
void lua_callback_do_paint(struct lua_render_context* ctx) throw();
|
||||
void lua_callback_do_video(struct lua_render_context* ctx) throw();
|
||||
void lua_callback_do_input(controls_t& data, bool subframe) throw();
|
||||
void lua_callback_do_input(controller_frame& data, bool subframe) throw();
|
||||
void lua_callback_do_reset() throw();
|
||||
void lua_callback_do_frame() throw();
|
||||
void lua_callback_do_readwrite() throw();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include "controllerdata.hpp"
|
||||
#include "controllerframe.hpp"
|
||||
|
||||
/**
|
||||
* Movie being played back or recorded
|
||||
|
@ -101,66 +101,42 @@ public:
|
|||
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
|
||||
* flag is unset.
|
||||
*
|
||||
* parameter controlindex: The index of control to read it for.
|
||||
* parameter pid: Physical controller id.
|
||||
* parameter index: Control ID.
|
||||
* returns: The read value.
|
||||
* throws std::logic_error: Invalid control index.
|
||||
*/
|
||||
bool get_DRDY(unsigned controlindex) throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
|
||||
* flag is unset.
|
||||
*
|
||||
* This differs from get_DRDY(unsigned) in that this takes (port, controller,index) tuple.
|
||||
*
|
||||
* parameter port: The port controller is connected to (0 or 1)
|
||||
* parameter controller: The controller number within port (0 to 3)
|
||||
* parameter index: The index of control in controller (0 to 11)
|
||||
* returns: The read value.
|
||||
* throws std::logic_error: Invalid control index.
|
||||
*/
|
||||
bool get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error);
|
||||
bool get_DRDY(unsigned pid, unsigned index) throw(std::logic_error);
|
||||
|
||||
/**
|
||||
* Set all data ready flags
|
||||
*/
|
||||
void set_all_DRDY() throw();
|
||||
|
||||
/**
|
||||
* Poll a control. Note that index 0 (sync flag) always reads as released.
|
||||
*
|
||||
* parameter controlindex: The index
|
||||
* returns: The read value
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::logic_error: Invalid control index or before movie start.
|
||||
*/
|
||||
short next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error);
|
||||
|
||||
/**
|
||||
* Poll a control by (port, controller, index) tuple.
|
||||
*
|
||||
* parameter port: The port controller is connected to (0 or 1)
|
||||
* parameter controller: The controller number within port (0 to 3)
|
||||
* parameter pid: Physical controller ID.
|
||||
* parameter index: The index of control in controller (0 to 11)
|
||||
* returns: The read value
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::logic_error: Invalid port, controller or index or before movie start.
|
||||
*/
|
||||
short next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error);
|
||||
short next_input(unsigned pid, unsigned index) throw(std::bad_alloc, std::logic_error);
|
||||
|
||||
/**
|
||||
* Set current control values. These are read in readwrite mode.
|
||||
*
|
||||
* parameter controls: The new controls.
|
||||
*/
|
||||
void set_controls(controls_t controls) throw();
|
||||
void set_controls(controller_frame controls) throw();
|
||||
|
||||
/**
|
||||
* Get current control values in effect.
|
||||
*
|
||||
* returns: Controls
|
||||
*/
|
||||
controls_t get_controls() throw();
|
||||
controller_frame get_controls() throw();
|
||||
|
||||
/**
|
||||
* Loads a movie plus some other parameters. The playback pointer is positioned to start of movie and readonly
|
||||
|
@ -172,7 +148,7 @@ public:
|
|||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Bad movie data.
|
||||
*/
|
||||
void load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
|
||||
void load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
|
@ -181,7 +157,7 @@ public:
|
|||
* returns: The movie data.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
std::vector<controls_t> save() throw(std::bad_alloc);
|
||||
controller_frame_vector save() throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* This method serializes the state of movie code.
|
||||
|
@ -209,7 +185,7 @@ public:
|
|||
* Throws std::runtime_error: Movie check failure.
|
||||
*/
|
||||
size_t restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
|
||||
std::vector<controls_t>* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
|
||||
controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
|
||||
std::runtime_error);
|
||||
|
||||
/**
|
||||
|
@ -248,7 +224,7 @@ public:
|
|||
* returns: The controls for subframe. If subframe is too great, reads last present subframe. If frame is outside
|
||||
* movie, reads all released.
|
||||
*/
|
||||
controls_t read_subframe(uint64_t frame, uint64_t subframe) throw();
|
||||
controller_frame read_subframe(uint64_t frame, uint64_t subframe) throw();
|
||||
private:
|
||||
//TRUE if readonly mode is active.
|
||||
bool readonly;
|
||||
|
@ -257,15 +233,15 @@ private:
|
|||
//Project ID.
|
||||
std::string _project_id;
|
||||
//The actual controller data.
|
||||
std::vector<controls_t> movie_data;
|
||||
controller_frame_vector movie_data;
|
||||
//Current frame + 1 (0 before next_frame() has been called.
|
||||
uint64_t current_frame;
|
||||
//First subframe in current frame (movie_data.size() if no subframes have been stored).
|
||||
uint64_t current_frame_first_subframe;
|
||||
//How many times has each control been polled (bit 31 is data ready bit)?
|
||||
uint32_t pollcounters[TOTAL_CONTROLS];
|
||||
pollcounter_vector pollcounters;
|
||||
//Current state of buttons.
|
||||
controls_t current_controls;
|
||||
controller_frame current_controls;
|
||||
//Number of known lag frames.
|
||||
uint64_t lag_frames;
|
||||
//Number of frames in movie.
|
||||
|
@ -320,7 +296,7 @@ public:
|
|||
*
|
||||
* parameter subframe: True if this is for subframe update, false if for frame update.
|
||||
*/
|
||||
controls_t update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error);
|
||||
controller_frame update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error);
|
||||
private:
|
||||
movie mov;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
#include "controllerdata.hpp"
|
||||
#include "controllerframe.hpp"
|
||||
#include "rom.hpp"
|
||||
|
||||
|
||||
|
@ -143,7 +143,7 @@ struct moviefile
|
|||
/**
|
||||
* Input for each (sub)frame.
|
||||
*/
|
||||
std::vector<controls_t> input; //Input for each frame.
|
||||
controller_frame_vector input; //Input for each frame.
|
||||
/**
|
||||
* Current RTC second.
|
||||
*/
|
||||
|
|
16
manual.lyx
16
manual.lyx
|
@ -4848,5 +4848,21 @@ Remove leftover dummy SRAM slot
|
|||
Fix controller numbers.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
rr1-beta2
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Fix lsnes-dumpavi after interface change.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Also give BSNES patches for v085.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Pack movie data in memory.
|
||||
\end_layout
|
||||
|
||||
\end_body
|
||||
\end_document
|
||||
|
|
|
@ -2355,3 +2355,11 @@ set-axis joystick0axis19 disabled
|
|||
|
||||
• Fix controller numbers.
|
||||
|
||||
13.31 rr1-beta2
|
||||
|
||||
• Fix lsnes-dumpavi after interface change.
|
||||
|
||||
• Also give BSNES patches for v085.
|
||||
|
||||
• Pack movie data in memory.
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@ namespace
|
|||
int analog_indices[3] = {-1, -1, -1};
|
||||
bool analog_is_mouse[3];
|
||||
//Current controls.
|
||||
controls_t curcontrols;
|
||||
controls_t autoheld_controls;
|
||||
std::vector<controls_t> autofire_pattern;
|
||||
controller_frame curcontrols;
|
||||
controller_frame autoheld_controls;
|
||||
std::vector<controller_frame> autofire_pattern;
|
||||
|
||||
void update_analog_indices() throw()
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ namespace
|
|||
}
|
||||
|
||||
//Do button action.
|
||||
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor, controls_t& c)
|
||||
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor, controller_frame& c)
|
||||
{
|
||||
enum devicetype_t p = controller_type_by_logical(ui_id);
|
||||
int x = controller_index_by_logical(ui_id);
|
||||
|
@ -145,9 +145,9 @@ namespace
|
|||
break;
|
||||
};
|
||||
if(do_xor)
|
||||
c((x & 4) ? 1 : 0, x & 3, bid) ^= newstate;
|
||||
c.axis(x, bid, c.axis(x, bid) ^ newstate);
|
||||
else
|
||||
c((x & 4) ? 1 : 0, x & 3, bid) = newstate;
|
||||
c.axis(x, bid, newstate);
|
||||
}
|
||||
|
||||
//Do button action.
|
||||
|
@ -169,14 +169,14 @@ namespace
|
|||
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
||||
if(!t)
|
||||
throw std::runtime_error("Need at least one frame for autofire");
|
||||
std::vector<controls_t> new_autofire_pattern;
|
||||
std::vector<controller_frame> new_autofire_pattern;
|
||||
init_buttonmap();
|
||||
while(t) {
|
||||
std::string fpattern = t;
|
||||
if(fpattern == "-")
|
||||
new_autofire_pattern.push_back(controls_t());
|
||||
new_autofire_pattern.push_back(curcontrols.blank_frame());
|
||||
else {
|
||||
controls_t c;
|
||||
controller_frame c(curcontrols.blank_frame());
|
||||
while(fpattern != "") {
|
||||
size_t split = fpattern.find_first_of(",");
|
||||
std::string button = fpattern;
|
||||
|
@ -273,8 +273,8 @@ namespace
|
|||
|
||||
int controller_index_by_logical(unsigned lid) throw()
|
||||
{
|
||||
unsigned p1devs = port_types[porttypes[0]].devices;
|
||||
unsigned p2devs = port_types[porttypes[1]].devices;
|
||||
unsigned p1devs = porttype_info::lookup(porttypes[0]).controllers();
|
||||
unsigned p2devs = porttype_info::lookup(porttypes[1]).controllers();
|
||||
if(lid >= p1devs + p2devs)
|
||||
return -1;
|
||||
//Exceptional: If p1 is none, map all to p2.
|
||||
|
@ -310,23 +310,27 @@ devicetype_t controller_type_by_logical(unsigned lid) throw()
|
|||
int x = controller_index_by_logical(lid);
|
||||
if(x < 0)
|
||||
return DT_NONE;
|
||||
enum porttype_t rawtype = porttypes[x >> 2];
|
||||
if((x & 3) < port_types[rawtype].devices)
|
||||
return port_types[rawtype].dtype;
|
||||
else
|
||||
return DT_NONE;
|
||||
enum porttype_t rawtype = porttypes[x / MAX_CONTROLLERS_PER_PORT];
|
||||
return porttype_info::lookup(rawtype).devicetype(x % MAX_CONTROLLERS_PER_PORT);
|
||||
}
|
||||
|
||||
void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core) throw()
|
||||
{
|
||||
if(set_core && ptype != PT_INVALID)
|
||||
snes_set_controller_port_device(port != 0, port_types[ptype].bsnes_type);
|
||||
snes_set_controller_port_device(port != 0, porttype_info::lookup(ptype).internal_type());
|
||||
porttype_t oldtype = curcontrols.get_port_type(port);
|
||||
if(oldtype != ptype) {
|
||||
curcontrols.set_port_type(port, ptype);
|
||||
autoheld_controls.set_port_type(port, ptype);
|
||||
//The old autofire pattern no longer applies.
|
||||
autofire_pattern.clear();
|
||||
}
|
||||
porttypes[port] = ptype;
|
||||
update_analog_indices();
|
||||
information_dispatch::do_autohold_reconfigure();
|
||||
}
|
||||
|
||||
controls_t get_current_controls(uint64_t frame)
|
||||
controller_frame get_current_controls(uint64_t frame)
|
||||
{
|
||||
if(autofire_pattern.size())
|
||||
return curcontrols ^ autoheld_controls ^ autofire_pattern[frame % autofire_pattern.size()];
|
||||
|
@ -350,29 +354,26 @@ void send_analog_input(int32_t x, int32_t y, unsigned index)
|
|||
messages << "No analog controller in slot #" << (index + 1) << std::endl;
|
||||
return;
|
||||
}
|
||||
curcontrols(aindex >> 2, aindex & 3, 0) = x;
|
||||
curcontrols(aindex >> 2, aindex & 3, 1) = y;
|
||||
curcontrols.axis(aindex, 0, x);
|
||||
curcontrols.axis(aindex, 1, y);
|
||||
}
|
||||
|
||||
void set_curcontrols_reset(int32_t delay)
|
||||
{
|
||||
if(delay >= 0) {
|
||||
curcontrols(CONTROL_SYSTEM_RESET) = 1;
|
||||
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
|
||||
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
|
||||
curcontrols.reset(true);
|
||||
curcontrols.delay(std::make_pair(delay / 10000, delay % 10000));
|
||||
} else {
|
||||
curcontrols(CONTROL_SYSTEM_RESET) = 0;
|
||||
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = 0;
|
||||
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = 0;
|
||||
curcontrols.reset(false);
|
||||
curcontrols.delay(std::make_pair(0, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void change_autohold(unsigned pid, unsigned idx, bool newstate)
|
||||
{
|
||||
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
|
||||
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= MAX_CONTROLS_PER_CONTROLLER)
|
||||
return;
|
||||
autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) = (newstate ? 1 : 0);
|
||||
autoheld_controls.axis(pid, idx, newstate ? 1 : 0);
|
||||
information_dispatch::do_autohold_update(pid, idx, newstate);
|
||||
update_movie_state();
|
||||
information_dispatch::do_status_update();
|
||||
|
@ -380,9 +381,9 @@ void change_autohold(unsigned pid, unsigned idx, bool newstate)
|
|||
|
||||
bool get_autohold(unsigned pid, unsigned idx)
|
||||
{
|
||||
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
|
||||
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= MAX_CONTROLS_PER_CONTROLLER)
|
||||
return false;
|
||||
return (autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) != 0);
|
||||
return (autoheld_controls.axis(pid, idx) != 0);
|
||||
}
|
||||
|
||||
std::string get_button_name(unsigned lidx)
|
||||
|
|
82
src/core/controller_gamepad.cpp
Normal file
82
src/core/controller_gamepad.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* buttons = "BYsSudlrAXLR";
|
||||
|
||||
struct porttype_gamepad : public porttype_info
|
||||
{
|
||||
porttype_gamepad() : porttype_info(PT_GAMEPAD, "gamepad", 2) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 12 || idx > 0)
|
||||
return;
|
||||
if(x)
|
||||
buffer[ctrl / 8] |= (1 << (ctrl % 8));
|
||||
else
|
||||
buffer[ctrl / 8] &= ~(1 << (ctrl % 8));
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 12 || idx > 0)
|
||||
return 0;
|
||||
return ((buffer[ctrl / 8] & (1 << (ctrl % 8))) != 0);
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx > 0) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
buf[i] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i] : ' ';
|
||||
buf[12] = '\0';
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
textbuf[0] = '|';
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
textbuf[i + 1] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i] : '.';
|
||||
return 13;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
buffer[1] = 0;
|
||||
size_t idx = 0;
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[i / 8] |= (1 << (i % 8));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx == 0) ? DT_GAMEPAD : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_JOYPAD;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} gamepad;
|
||||
}
|
99
src/core/controller_justifier.cpp
Normal file
99
src/core/controller_justifier.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct porttype_justifier : public porttype_info
|
||||
{
|
||||
porttype_justifier() : porttype_info(PT_JUSTIFIER, "justifier", 5) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 0)
|
||||
return;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
serialize_short(buffer + 2 * ctrl + 1, x);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if(x)
|
||||
buffer[0] |= (1 << (ctrl - 2));
|
||||
else
|
||||
buffer[0] &= ~(1 << (ctrl - 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 0)
|
||||
return 0;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
return unserialize_short(buffer + 2 * ctrl + 1);
|
||||
case 2:
|
||||
case 3:
|
||||
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx > 0) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
|
||||
((buffer[0] & 1) ? 'T' : '-'), ((buffer[0] & 2) ? 'S' : '-'));
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
char tmp[128];
|
||||
sprintf(tmp, "|%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'), ((buffer[0] & 2) ? 'S' : '.'),
|
||||
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
|
||||
size_t len = strlen(tmp);
|
||||
memcpy(textbuf, tmp, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
size_t idx = 0;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 1;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 2;
|
||||
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx == 0) ? DT_JUSTIFIER : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_JUSTIFIER;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return (port > 0);
|
||||
}
|
||||
} justifier;
|
||||
}
|
113
src/core/controller_justifiers.cpp
Normal file
113
src/core/controller_justifiers.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct porttype_justifiers : public porttype_info
|
||||
{
|
||||
porttype_justifiers() : porttype_info(PT_JUSTIFIERS, "justifiers", 9) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 1)
|
||||
return;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
serialize_short(buffer + 4 * idx + 2 * ctrl + 1, x);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if(x)
|
||||
buffer[0] |= (1 << (2 * idx + ctrl - 2));
|
||||
else
|
||||
buffer[0] &= ~(1 << (2 * idx + ctrl - 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 1)
|
||||
return 0;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
return unserialize_short(buffer + 4 * idx + 2 * ctrl + 1);
|
||||
case 2:
|
||||
case 3:
|
||||
return ((buffer[0] & (1 << (2 * idx + ctrl - 2))) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx == 0)
|
||||
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1),
|
||||
unserialize_short(buffer + 3), ((buffer[0] & 1) ? 'T' : '-'),
|
||||
((buffer[0] & 2) ? 'S' : '-'));
|
||||
else if(idx == 1)
|
||||
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 5),
|
||||
unserialize_short(buffer + 7), ((buffer[0] & 4) ? 'T' : '-'),
|
||||
((buffer[0] & 8) ? 'S' : '-'));
|
||||
else
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
char tmp[128];
|
||||
sprintf(tmp, "|%c%c %i %i|%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'),
|
||||
((buffer[0] & 2) ? 'S' : '.'), unserialize_short(buffer + 1),
|
||||
unserialize_short(buffer + 3), ((buffer[0] & 4) ? 'T' : '.'),
|
||||
((buffer[0] & 8) ? 'S' : '.'), unserialize_short(buffer + 5),
|
||||
unserialize_short(buffer + 7));
|
||||
size_t len = strlen(tmp);
|
||||
memcpy(textbuf, tmp, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
size_t idx = 0;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 1;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 2;
|
||||
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, true);
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 4;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 8;
|
||||
serialize_short(buffer + 5, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 7, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx < 2) ? DT_JUSTIFIER : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_JUSTIFIERS;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return (port > 0);
|
||||
}
|
||||
} justifiers;
|
||||
}
|
99
src/core/controller_mouse.cpp
Normal file
99
src/core/controller_mouse.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct porttype_mouse : public porttype_info
|
||||
{
|
||||
porttype_mouse() : porttype_info(PT_MOUSE, "mouse", 5) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 0)
|
||||
return;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
serialize_short(buffer + 2 * ctrl + 1, x);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if(x)
|
||||
buffer[0] |= (1 << (ctrl - 2));
|
||||
else
|
||||
buffer[0] &= ~(1 << (ctrl - 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 4 || idx > 0)
|
||||
return 0;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
return unserialize_short(buffer + 2 * ctrl + 1);
|
||||
case 2:
|
||||
case 3:
|
||||
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx > 0) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
|
||||
((buffer[0] & 1) ? 'L' : '-'), ((buffer[0] & 2) ? 'R' : '-'));
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
char tmp[128];
|
||||
sprintf(tmp, "|%c%c %i %i", ((buffer[0] & 1) ? 'L' : '.'), ((buffer[0] & 2) ? 'R' : '.'),
|
||||
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
|
||||
size_t len = strlen(tmp);
|
||||
memcpy(textbuf, tmp, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
size_t idx = 0;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 1;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 2;
|
||||
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx == 0) ? DT_MOUSE : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_MOUSE;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} mouse;
|
||||
}
|
96
src/core/controller_multitap.cpp
Normal file
96
src/core/controller_multitap.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* buttons = "BYsSudlrAXLR";
|
||||
|
||||
struct porttype_multitap : public porttype_info
|
||||
{
|
||||
porttype_multitap() : porttype_info(PT_MULTITAP, "multitap", 6) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 12 || idx > 4)
|
||||
return;
|
||||
unsigned i = idx * 12 + ctrl;
|
||||
if(x)
|
||||
buffer[i / 8] |= (1 << (i % 8));
|
||||
else
|
||||
buffer[i / 8] &= ~(1 << (i % 8));
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 12 || idx > 4)
|
||||
return 0;
|
||||
unsigned i = idx * 12 + ctrl;
|
||||
return ((buffer[i / 8] & (1 << (i % 8))) != 0);
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx > 3) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
unsigned j = idx * 12;
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
buf[i] = (buffer[(i + j) / 8] & (1 << ((i + j) % 8))) ? buttons[i % 12] : ' ';
|
||||
buf[12] = '\0';
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
unsigned j = 0;
|
||||
for(unsigned i = 0; i < 48; i++) {
|
||||
if(i % 12 == 0)
|
||||
textbuf[j++] = '|';
|
||||
textbuf[j++] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i % 12] : '.';
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
buffer[1] = 0;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = 0;
|
||||
buffer[4] = 0;
|
||||
buffer[5] = 0;
|
||||
const char* orig_texbuf = textbuf;
|
||||
size_t index = 0;
|
||||
for(unsigned i = 0; i < 48; i++) {
|
||||
if(read_button_value(textbuf, index))
|
||||
buffer[i / 8] |= (1 << (i % 8));
|
||||
if(i % 12 == 0 && i > 0)
|
||||
skip_rest_of_field(textbuf, index, true);
|
||||
}
|
||||
skip_rest_of_field(textbuf, index, false);
|
||||
return index;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx < 4) ? DT_GAMEPAD : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_MULTITAP;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} multitap;
|
||||
}
|
56
src/core/controller_none.cpp
Normal file
56
src/core/controller_none.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct porttype_none : public porttype_info
|
||||
{
|
||||
porttype_none() : porttype_info(PT_NONE, "none", 0) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
return DESERIALIZE_SPECIAL_BLANK;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_NONE;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} none;
|
||||
}
|
109
src/core/controller_superscope.cpp
Normal file
109
src/core/controller_superscope.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerframe.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct porttype_superscope : public porttype_info
|
||||
{
|
||||
porttype_superscope() : porttype_info(PT_SUPERSCOPE, "superscope", 5) {}
|
||||
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
|
||||
{
|
||||
if(ctrl >= 6 || idx > 0)
|
||||
return;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
serialize_short(buffer + 2 * ctrl + 1, x);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
if(x)
|
||||
buffer[0] |= (1 << (ctrl - 2));
|
||||
else
|
||||
buffer[0] &= ~(1 << (ctrl - 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
|
||||
{
|
||||
if(ctrl >= 6 || idx > 0)
|
||||
return 0;
|
||||
switch(ctrl) {
|
||||
case 0:
|
||||
case 1:
|
||||
return unserialize_short(buffer + 2 * ctrl + 1);
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
|
||||
{
|
||||
if(idx > 0) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
sprintf(buf, "%i %i %c%c%c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
|
||||
((buffer[0] & 1) ? 'T' : '-'), ((buffer[0] & 2) ? 'C' : '-'),
|
||||
((buffer[0] & 4) ? 'U' : '-'), ((buffer[0] & 8) ? 'P' : '-'));
|
||||
}
|
||||
|
||||
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
|
||||
{
|
||||
char tmp[128];
|
||||
sprintf(tmp, "|%c%c%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'), ((buffer[0] & 2) ? 'C' : '.'),
|
||||
((buffer[0] & 4) ? 'U' : '.'), ((buffer[0] & 8) ? 'P' : '.'),
|
||||
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
|
||||
size_t len = strlen(tmp);
|
||||
memcpy(textbuf, tmp, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
|
||||
{
|
||||
buffer[0] = 0;
|
||||
size_t idx = 0;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 1;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 2;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 4;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 8;
|
||||
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
devicetype_t devicetype(unsigned idx) const throw()
|
||||
{
|
||||
return (idx == 0) ? DT_SUPERSCOPE : DT_NONE;
|
||||
}
|
||||
|
||||
unsigned controllers() const throw()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned internal_type() const throw()
|
||||
{
|
||||
return SNES_DEVICE_SUPER_SCOPE;
|
||||
}
|
||||
|
||||
bool legal(unsigned port) const throw()
|
||||
{
|
||||
return (port > 0);
|
||||
}
|
||||
} superscope;
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include <ui-libsnes/libsnes.hpp>
|
||||
|
||||
#include "core/controllerdata.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
namespace
|
||||
{
|
||||
inline unsigned ccindex(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
|
||||
{
|
||||
if(port >= MAX_PORTS || controller >= MAX_CONTROLLERS_PER_PORT || control >= CONTROLLER_CONTROLS) {
|
||||
std::ostringstream x;
|
||||
x << "ccindex: Invalid (port, controller, control) tuple (" << port << "," << controller
|
||||
<< "," << control << ")";
|
||||
throw std::logic_error(x.str());
|
||||
}
|
||||
return MAX_SYSTEM_CONTROLS + port * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT +
|
||||
CONTROLLER_CONTROLS * controller + control;
|
||||
}
|
||||
|
||||
bool parse_button_ctrl(const std::string& str, size_t& pos) throw()
|
||||
{
|
||||
if(pos >= str.length())
|
||||
return false;
|
||||
switch(str[pos]) {
|
||||
case '.':
|
||||
case ' ':
|
||||
case '\t':
|
||||
pos++;
|
||||
case '|':
|
||||
return false;
|
||||
default:
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
short parse_number_ctrl(const std::string& str, size_t& pos) throw()
|
||||
{
|
||||
char ch;
|
||||
//Skip ws.
|
||||
while(pos < str.length()) {
|
||||
char ch = str[pos];
|
||||
if(ch != ' ' && ch != '\t')
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
//Read the sign if any.
|
||||
if(pos >= str.length() || (ch = str[pos]) == '|')
|
||||
return 0;
|
||||
bool negative = false;
|
||||
if(ch == '-') {
|
||||
negative = true;
|
||||
pos++;
|
||||
}
|
||||
if(ch == '+')
|
||||
pos++;
|
||||
|
||||
//Read numeric value.
|
||||
int numval = 0;
|
||||
while(pos < str.length() && isdigit(static_cast<unsigned char>(ch = str[pos]))) {
|
||||
numval = numval * 10 + (ch - '0');
|
||||
pos++;
|
||||
}
|
||||
if(negative)
|
||||
numval = -numval;
|
||||
|
||||
return static_cast<short>(numval);
|
||||
}
|
||||
|
||||
void parse_end_of_field(const std::string& str, size_t& pos) throw()
|
||||
{
|
||||
while(pos < str.length() && str[pos] != '|')
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t cdecode::system(const std::string& line, size_t pos, short* controls, unsigned version) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
controls[0] = parse_button_ctrl(line, pos); //Frame sync.
|
||||
controls[1] = parse_button_ctrl(line, pos); //Reset.
|
||||
controls[2] = parse_number_ctrl(line, pos); //Reset cycles hi.
|
||||
controls[3] = parse_number_ctrl(line, pos); //Reset cycles lo.
|
||||
parse_end_of_field(line, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::none(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::gamepad(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
controls[ccindex(port, 0, i)] = parse_button_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::multitap(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
for(unsigned j = 0; j < 4; j++) {
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
controls[ccindex(port, j, i)] = parse_button_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
pos++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::mouse(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::superscope(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 4)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 5)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::justifier(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
|
||||
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cdecode::justifiers(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++) {
|
||||
controls[ccindex(port, i, 2)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, i, 3)] = parse_button_ctrl(line, pos);
|
||||
controls[ccindex(port, i, 0)] = parse_number_ctrl(line, pos);
|
||||
controls[ccindex(port, i, 1)] = parse_number_ctrl(line, pos);
|
||||
parse_end_of_field(line, pos);
|
||||
pos++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t cencode::system(char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
buffer[bufferpos++] = controls[0] ? 'F' : '.';
|
||||
buffer[bufferpos++] = controls[1] ? 'R' : '.';
|
||||
if(controls[2] || controls[3]) {
|
||||
bufferpos += sprintf(buffer + bufferpos, " %i %i", static_cast<int>(controls[2]),
|
||||
static_cast<int>(controls[3]));
|
||||
}
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::none(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
return ENCODE_SPECIAL_NO_OUTPUT;
|
||||
}
|
||||
|
||||
size_t cencode::gamepad(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
static const char* characters = "BYsSudlrAXLR";
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
buffer[bufferpos++] = controls[ccindex(port, 0, i)] ? characters[i] : '.';
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::multitap(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
static const char* characters = "BYsSudlrAXLR";
|
||||
for(unsigned j = 0; j < 4; j++) {
|
||||
for(unsigned i = 0; i < 12; i++)
|
||||
buffer[bufferpos++] = controls[ccindex(port, j, i)] ? characters[i] : '.';
|
||||
buffer[bufferpos++] = '|';
|
||||
}
|
||||
bufferpos--; //Eat the last '|', it shouldn't be there.
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::mouse(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'L' : '.',
|
||||
controls[ccindex(port, 0, 3)] ? 'R' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
|
||||
static_cast<int>(controls[ccindex(port, 0, 1)]));
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::superscope(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
bufferpos += sprintf(buffer + bufferpos, "%c%c%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
|
||||
controls[ccindex(port, 0, 3)] ? 'C' : '.', controls[ccindex(port, 0, 4)] ? 'U' : '.',
|
||||
controls[ccindex(port, 0, 5)] ? 'P' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
|
||||
static_cast<int>(controls[ccindex(port, 0, 1)]));
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::justifier(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
|
||||
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
|
||||
static_cast<int>(controls[ccindex(port, 0, 1)]));
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
size_t cencode::justifiers(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
|
||||
{
|
||||
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
|
||||
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
|
||||
static_cast<int>(controls[ccindex(port, 0, 1)]));
|
||||
buffer[bufferpos++] = '|';
|
||||
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
|
||||
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
|
||||
static_cast<int>(controls[ccindex(port, 0, 1)]));
|
||||
return bufferpos;
|
||||
}
|
||||
|
||||
|
||||
unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
|
||||
{
|
||||
return ccindex(port, controller, control);
|
||||
}
|
||||
|
||||
|
||||
controls_t controls_t::operator^(controls_t other) throw()
|
||||
{
|
||||
controls_t x;
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
|
||||
x.controls[i] = controls[i] ^ ((i < MAX_SYSTEM_CONTROLS) ? 0 : other.controls[i]);
|
||||
return x;
|
||||
}
|
||||
|
||||
controls_t::controls_t(bool sync) throw()
|
||||
{
|
||||
memset(controls, 0, sizeof(controls));
|
||||
if(sync)
|
||||
controls[CONTROL_FRAME_SYNC] = 1;
|
||||
}
|
||||
|
||||
const short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error)
|
||||
{
|
||||
return controls[ccindex(port, controller, control)];
|
||||
}
|
||||
|
||||
const short& controls_t::operator()(unsigned control) const throw(std::logic_error)
|
||||
{
|
||||
if(control >= TOTAL_CONTROLS)
|
||||
throw std::logic_error("controls_t::operator(): Invalid control index");
|
||||
return controls[control];
|
||||
}
|
||||
|
||||
short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
|
||||
{
|
||||
return controls[ccindex(port, controller, control)];
|
||||
}
|
||||
|
||||
short& controls_t::operator()(unsigned control) throw(std::logic_error)
|
||||
{
|
||||
if(control >= TOTAL_CONTROLS)
|
||||
throw std::logic_error("controls_t::operator(): Invalid control index");
|
||||
return controls[control];
|
||||
}
|
||||
|
||||
controls_t::controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
memset(controls, 0, sizeof(controls));
|
||||
size_t position = 0;
|
||||
position = cdecode::system(line, position, controls, version);
|
||||
for(unsigned i = 0; i < decoders.size(); i++) {
|
||||
if(position < line.length() && line[position] == '|')
|
||||
position++;
|
||||
position = decoders[i](i, line, position, controls);
|
||||
}
|
||||
}
|
||||
|
||||
std::string controls_t::tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc)
|
||||
{
|
||||
char buffer[1024];
|
||||
size_t linelen = 0, tmp;
|
||||
tmp = cencode::system(buffer, linelen, controls);
|
||||
for(unsigned i = 0; i < encoders.size(); i++) {
|
||||
if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
|
||||
buffer[(linelen = tmp)++] = '|';
|
||||
tmp = encoders[i](i, buffer, linelen, controls);
|
||||
}
|
||||
if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
|
||||
linelen = tmp;
|
||||
return std::string(buffer, buffer + linelen);
|
||||
}
|
||||
|
||||
bool controls_t::operator==(const controls_t& c) const throw()
|
||||
{
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
|
||||
if(controls[i] != c.controls[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const port_type& port_type::lookup(const std::string& name, bool port2) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
for(unsigned i = 0; i <= PT_LAST_CTYPE; i++) {
|
||||
if(name != port_types[i].name)
|
||||
continue;
|
||||
if(!port2 && !port_types[i].valid_port1)
|
||||
throw std::runtime_error("Can't connect " + name + " to port #1");
|
||||
return port_types[i];
|
||||
}
|
||||
throw std::runtime_error("Unknown port type '" + name + "'");
|
||||
}
|
||||
|
||||
port_type port_types[] = {
|
||||
{ "none", cdecode::none, cencode::none, PT_NONE, 0, DT_NONE, true, SNES_DEVICE_NONE },
|
||||
{ "gamepad", cdecode::gamepad, cencode::gamepad, PT_GAMEPAD, 1, DT_GAMEPAD, true, SNES_DEVICE_JOYPAD },
|
||||
{ "multitap", cdecode::multitap, cencode::multitap, PT_MULTITAP, 4, DT_GAMEPAD, true, SNES_DEVICE_MULTITAP },
|
||||
{ "mouse", cdecode::mouse, cencode::mouse, PT_MOUSE, 1, DT_MOUSE, true, SNES_DEVICE_MOUSE },
|
||||
{ "superscope", cdecode::superscope, cencode::superscope, PT_SUPERSCOPE, 1, DT_SUPERSCOPE, false,
|
||||
SNES_DEVICE_SUPER_SCOPE },
|
||||
{ "justifier", cdecode::justifier, cencode::justifier, PT_JUSTIFIER, 1, DT_JUSTIFIER, false,
|
||||
SNES_DEVICE_JUSTIFIER },
|
||||
{ "justifiers", cdecode::justifiers, cencode::justifiers, PT_JUSTIFIERS, 2, DT_JUSTIFIER, false,
|
||||
SNES_DEVICE_JUSTIFIERS }
|
||||
};
|
449
src/core/controllerframe.cpp
Normal file
449
src/core/controllerframe.cpp
Normal file
|
@ -0,0 +1,449 @@
|
|||
#include "core/controllerframe.hpp"
|
||||
|
||||
#define SYSTEM_BYTES 5
|
||||
|
||||
namespace
|
||||
{
|
||||
std::set<porttype_info*>& porttypes()
|
||||
{
|
||||
static std::set<porttype_info*> p;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
const porttype_info& porttype_info::lookup(porttype_t p) throw(std::runtime_error)
|
||||
{
|
||||
for(auto i : porttypes())
|
||||
if(p == i->value)
|
||||
return *i;
|
||||
throw std::runtime_error("Bad port type");
|
||||
}
|
||||
|
||||
const porttype_info& porttype_info::lookup(const std::string& p) throw(std::runtime_error)
|
||||
{
|
||||
for(auto i : porttypes())
|
||||
if(p == i->name)
|
||||
return *i;
|
||||
throw std::runtime_error("Bad port type");
|
||||
}
|
||||
|
||||
porttype_info::~porttype_info() throw()
|
||||
{
|
||||
porttypes().erase(this);
|
||||
}
|
||||
|
||||
porttype_info::porttype_info(porttype_t ptype, const std::string& pname, size_t psize) throw(std::bad_alloc)
|
||||
{
|
||||
value = ptype;
|
||||
name = pname;
|
||||
storage_size = psize;
|
||||
porttypes().insert(this);
|
||||
}
|
||||
|
||||
pollcounter_vector::pollcounter_vector() throw()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void pollcounter_vector::clear() throw()
|
||||
{
|
||||
system_flag = false;
|
||||
memset(ctrs, 0, sizeof(ctrs));
|
||||
}
|
||||
|
||||
void pollcounter_vector::set_all_DRDY() throw()
|
||||
{
|
||||
for(size_t i = 0; i < MAX_BUTTONS ; i++)
|
||||
ctrs[i] |= 0x80000000UL;
|
||||
}
|
||||
|
||||
#define INDEXOF(pid, ctrl) ((pid) * MAX_CONTROLS_PER_CONTROLLER + (ctrl))
|
||||
|
||||
void pollcounter_vector::clear_DRDY(unsigned pid, unsigned ctrl) throw()
|
||||
{
|
||||
ctrs[INDEXOF(pid, ctrl)] &= 0x7FFFFFFFUL;
|
||||
}
|
||||
|
||||
bool pollcounter_vector::get_DRDY(unsigned pid, unsigned ctrl) throw()
|
||||
{
|
||||
return ((ctrs[INDEXOF(pid, ctrl)] & 0x80000000UL) != 0);
|
||||
}
|
||||
|
||||
bool pollcounter_vector::has_polled() throw()
|
||||
{
|
||||
uint32_t res = system_flag ? 1 : 0;
|
||||
for(size_t i = 0; i < MAX_BUTTONS ; i++)
|
||||
res |= ctrs[i];
|
||||
return ((res & 0x7FFFFFFFUL) != 0);
|
||||
}
|
||||
|
||||
uint32_t pollcounter_vector::get_polls(unsigned pid, unsigned ctrl) throw()
|
||||
{
|
||||
return ctrs[INDEXOF(pid, ctrl)] & 0x7FFFFFFFUL;
|
||||
}
|
||||
|
||||
uint32_t pollcounter_vector::increment_polls(unsigned pid, unsigned ctrl) throw()
|
||||
{
|
||||
size_t i = INDEXOF(pid, ctrl);
|
||||
uint32_t x = ctrs[i] & 0x7FFFFFFFUL;
|
||||
++ctrs[i];
|
||||
return x;
|
||||
}
|
||||
|
||||
void pollcounter_vector::set_system() throw()
|
||||
{
|
||||
system_flag = true;
|
||||
}
|
||||
|
||||
bool pollcounter_vector::get_system() throw()
|
||||
{
|
||||
return system_flag;
|
||||
}
|
||||
|
||||
uint32_t pollcounter_vector::max_polls() throw()
|
||||
{
|
||||
uint32_t max = system_flag ? 1 : 0;
|
||||
for(unsigned i = 0; i < MAX_BUTTONS; i++) {
|
||||
uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL;
|
||||
max = (max < tmp) ? tmp : max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void pollcounter_vector::save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc)
|
||||
{
|
||||
mem.resize(4 + MAX_BUTTONS );
|
||||
//Compatiblity fun.
|
||||
mem[0] = 0x80000000UL;
|
||||
mem[1] = system_flag ? 1 : 0x80000000UL;
|
||||
mem[2] = system_flag ? 1 : 0x80000000UL;
|
||||
mem[3] = system_flag ? 1 : 0x80000000UL;
|
||||
for(size_t i = 0; i < MAX_BUTTONS ; i++)
|
||||
mem[4 + i] = ctrs[i];
|
||||
}
|
||||
|
||||
void pollcounter_vector::load_state(const std::vector<uint32_t>& mem) throw()
|
||||
{
|
||||
system_flag = (mem[1] | mem[2] | mem[3]) & 0x7FFFFFFFUL;
|
||||
for(size_t i = 0; i < MAX_BUTTONS ; i++)
|
||||
ctrs[i] = mem[i + 4];
|
||||
}
|
||||
|
||||
bool pollcounter_vector::check(const std::vector<uint32_t>& mem) throw()
|
||||
{
|
||||
return (mem.size() == MAX_BUTTONS + 4);
|
||||
}
|
||||
|
||||
|
||||
controller_frame::controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error)
|
||||
{
|
||||
memset(memory, 0, sizeof(memory));
|
||||
backing = memory;
|
||||
types[0] = p1;
|
||||
types[1] = p2;
|
||||
set_types(types);
|
||||
}
|
||||
|
||||
controller_frame::controller_frame(unsigned char* mem, porttype_t p1, porttype_t p2) throw(std::runtime_error)
|
||||
{
|
||||
if(!mem)
|
||||
throw std::runtime_error("NULL backing memory not allowed");
|
||||
memset(memory, 0, sizeof(memory));
|
||||
backing = mem;
|
||||
types[0] = p1;
|
||||
types[1] = p2;
|
||||
set_types(types);
|
||||
}
|
||||
|
||||
controller_frame::controller_frame(const controller_frame& obj) throw()
|
||||
{
|
||||
memset(memory, 0, sizeof(memory));
|
||||
backing = memory;
|
||||
set_types(obj.types);
|
||||
memcpy(backing, obj.backing, totalsize);
|
||||
}
|
||||
|
||||
controller_frame& controller_frame::operator=(const controller_frame& obj) throw(std::runtime_error)
|
||||
{
|
||||
set_types(obj.types);
|
||||
memcpy(backing, obj.backing, totalsize);
|
||||
}
|
||||
|
||||
void controller_frame::set_types(const porttype_t* tarr)
|
||||
{
|
||||
for(unsigned i = 0; i < MAX_PORTS; i++) {
|
||||
if(memory != backing && types[i] != tarr[i])
|
||||
throw std::runtime_error("Controller_frame: Type mismatch");
|
||||
if(!porttype_info::lookup(tarr[i]).legal(i))
|
||||
throw std::runtime_error("Illegal port type for port index");
|
||||
}
|
||||
size_t offset = SYSTEM_BYTES;
|
||||
for(unsigned i = 0; i < MAX_PORTS; i++) {
|
||||
offsets[i] = offset;
|
||||
types[i] = tarr[i];
|
||||
pinfo[i] = &porttype_info::lookup(tarr[i]);
|
||||
offset += pinfo[i]->storage_size;
|
||||
}
|
||||
totalsize = offset;
|
||||
}
|
||||
|
||||
size_t controller_frame_vector::walk_helper(size_t frame, bool sflag) throw()
|
||||
{
|
||||
size_t ret = sflag ? frame : 0;
|
||||
if(frame >= frames)
|
||||
return ret;
|
||||
frame++;
|
||||
ret++;
|
||||
size_t page = frame / frames_per_page;
|
||||
size_t offset = frame_size * (frame % frames_per_page);
|
||||
size_t index = frame % frames_per_page;
|
||||
if(cache_page_num != page) {
|
||||
cache_page = &pages[page];
|
||||
cache_page_num = page;
|
||||
}
|
||||
while(frame < frames) {
|
||||
if(index == frames_per_page) {
|
||||
page++;
|
||||
cache_page = &pages[page];
|
||||
cache_page_num = page;
|
||||
}
|
||||
if(controller_frame::sync(cache_page->content + offset))
|
||||
break;
|
||||
index++;
|
||||
offset += frame_size;
|
||||
frame++;
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t controller_frame_vector::count_frames() throw()
|
||||
{
|
||||
size_t ret = 0;
|
||||
if(!frames)
|
||||
return 0;
|
||||
cache_page_num = 0;
|
||||
cache_page = &pages[0];
|
||||
size_t offset = 0;
|
||||
size_t index = 0;
|
||||
for(size_t i = 0; i < frames; i++) {
|
||||
if(index == frames_per_page) {
|
||||
cache_page_num++;
|
||||
cache_page = &cache_page[cache_page_num];
|
||||
index = 0;
|
||||
offset = 0;
|
||||
}
|
||||
if(controller_frame::sync(cache_page->content + offset))
|
||||
ret++;
|
||||
index++;
|
||||
offset += frame_size;
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void controller_frame_vector::clear(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error)
|
||||
{
|
||||
controller_frame check(p1, p2);
|
||||
frame_size = check.size();
|
||||
frames_per_page = CONTROLLER_PAGE_SIZE / frame_size;
|
||||
frames = 0;
|
||||
types[0] = p1;
|
||||
types[1] = p2;
|
||||
clear_cache();
|
||||
pages.clear();
|
||||
}
|
||||
|
||||
controller_frame_vector::~controller_frame_vector() throw()
|
||||
{
|
||||
pages.clear();
|
||||
cache_page = NULL;
|
||||
}
|
||||
|
||||
controller_frame_vector::controller_frame_vector(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error)
|
||||
{
|
||||
clear(p1, p2);
|
||||
}
|
||||
|
||||
void controller_frame_vector::append(controller_frame frame) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
controller_frame check(types[0], types[1]);
|
||||
if(!check.types_match(frame))
|
||||
throw std::runtime_error("controller_frame_vector::append: Type mismatch");
|
||||
if(frames % frames_per_page == 0) {
|
||||
//Create new page.
|
||||
pages[frames / frames_per_page];
|
||||
}
|
||||
//Write the entry.
|
||||
size_t page = frames / frames_per_page;
|
||||
size_t offset = frame_size * (frames % frames_per_page);
|
||||
cache_page_num = page;
|
||||
cache_page = &pages[page];
|
||||
controller_frame(cache_page->content + offset, types[0], types[1]) = frame;
|
||||
frames++;
|
||||
}
|
||||
|
||||
controller_frame_vector::controller_frame_vector(const controller_frame_vector& vector) throw(std::bad_alloc)
|
||||
{
|
||||
clear(vector.types[0], vector.types[1]);
|
||||
*this = vector;
|
||||
}
|
||||
|
||||
controller_frame_vector& controller_frame_vector::operator=(const controller_frame_vector& v)
|
||||
throw(std::bad_alloc)
|
||||
{
|
||||
if(this == &v)
|
||||
return *this;
|
||||
resize(v.frames);
|
||||
clear_cache();
|
||||
|
||||
//Copy the fields.
|
||||
frame_size = v.frame_size;
|
||||
frames_per_page = v.frames_per_page;
|
||||
for(size_t i = 0; i < MAX_PORTS; i++)
|
||||
types[i] = v.types[i];
|
||||
|
||||
//This can't fail anymore. Copy the raw page contents.
|
||||
size_t pagecount = (frames + frames_per_page - 1) / frames_per_page;
|
||||
for(size_t i = 0; i < pagecount; i++) {
|
||||
page& pg = pages[i];
|
||||
const page& pg2 = v.pages.find(i)->second;
|
||||
pg = pg2;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t controller_frame::system_serialize(const unsigned char* buffer, char* textbuf)
|
||||
{
|
||||
char tmp[128];
|
||||
if(buffer[1] || buffer[2] || buffer[3] || buffer[4])
|
||||
sprintf(tmp, "%c%c %i %i", ((buffer[0] & 1) ? 'F' : '.'), ((buffer[0] & 2) ? 'R' : '.'),
|
||||
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
|
||||
else
|
||||
sprintf(tmp, "%c%c", ((buffer[0] & 1) ? 'F' : '.'), ((buffer[0] & 2) ? 'R' : '.'));
|
||||
size_t len = strlen(tmp);
|
||||
memcpy(textbuf, tmp, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t controller_frame::system_deserialize(unsigned char* buffer, const char* textbuf)
|
||||
{
|
||||
buffer[0] = 0;
|
||||
size_t idx = 0;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 1;
|
||||
if(read_button_value(textbuf, idx))
|
||||
buffer[0] |= 2;
|
||||
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
|
||||
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
|
||||
skip_rest_of_field(textbuf, idx, false);
|
||||
return idx;
|
||||
}
|
||||
|
||||
short read_axis_value(const char* buf, size_t& idx) throw()
|
||||
{
|
||||
char ch;
|
||||
//Skip ws.
|
||||
while(is_nonterminator(buf[idx])) {
|
||||
char ch = buf[idx];
|
||||
if(ch != ' ' && ch != '\t')
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
//Read the sign if any.
|
||||
if(!is_nonterminator(buf[idx]))
|
||||
return 0;
|
||||
bool negative = false;
|
||||
if(ch == '-') {
|
||||
negative = true;
|
||||
idx++;
|
||||
}
|
||||
if(ch == '+')
|
||||
idx++;
|
||||
|
||||
//Read numeric value.
|
||||
int numval = 0;
|
||||
while(!is_nonterminator(buf[idx]) && isdigit(static_cast<unsigned char>(ch = buf[idx]))) {
|
||||
numval = numval * 10 + (ch - '0');
|
||||
idx++;
|
||||
}
|
||||
if(negative)
|
||||
numval = -numval;
|
||||
|
||||
return static_cast<short>(numval);
|
||||
}
|
||||
|
||||
void controller_frame_vector::resize(size_t newsize) throw(std::bad_alloc)
|
||||
{
|
||||
clear_cache();
|
||||
if(newsize == 0) {
|
||||
clear();
|
||||
} else if(newsize < frames) {
|
||||
//Shrink movie.
|
||||
size_t current_pages = (frames + frames_per_page - 1) / frames_per_page;
|
||||
size_t pages_needed = (newsize + frames_per_page - 1) / frames_per_page;
|
||||
for(size_t i = pages_needed; i < current_pages; i++)
|
||||
pages.erase(i);
|
||||
//Now zeroize the excess memory.
|
||||
size_t offset = frame_size * (newsize % frames_per_page);
|
||||
memset(pages[pages_needed - 1].content + offset, 0, CONTROLLER_PAGE_SIZE - offset);
|
||||
frames = newsize;
|
||||
} else if(newsize > frames) {
|
||||
//Enlarge movie.
|
||||
size_t current_pages = (frames + frames_per_page - 1) / frames_per_page;
|
||||
size_t pages_needed = (newsize + frames_per_page - 1) / frames_per_page;
|
||||
//Create the needed pages.
|
||||
for(size_t i = current_pages; i < pages_needed; i++) {
|
||||
try {
|
||||
pages[i];
|
||||
} catch(...) {
|
||||
for(size_t i = current_pages; i < pages_needed; i++)
|
||||
if(pages.count(i))
|
||||
pages.erase(i);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
frames = newsize;
|
||||
}
|
||||
}
|
||||
|
||||
controller_frame::controller_frame() throw()
|
||||
{
|
||||
memset(memory, 0, sizeof(memory));
|
||||
backing = memory;
|
||||
for(unsigned i = 0; i < MAX_PORTS; i++) {
|
||||
offsets[i] = SYSTEM_BYTES;
|
||||
types[i] = PT_INVALID;
|
||||
pinfo[i] = NULL;
|
||||
}
|
||||
totalsize = SYSTEM_BYTES;
|
||||
}
|
||||
|
||||
void controller_frame::set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error)
|
||||
{
|
||||
char tmp[MAXIMUM_CONTROLLER_FRAME_SIZE] = {0};
|
||||
if(!porttype_info::lookup(ptype).legal(port))
|
||||
throw std::runtime_error("Illegal port type for port index");
|
||||
if(memory != backing)
|
||||
throw std::runtime_error("Can't set port type on non-dedicated controller frame");
|
||||
if(port >= MAX_PORTS)
|
||||
return;
|
||||
const porttype_info* newpinfo[MAX_PORTS];
|
||||
size_t newoffsets[MAX_PORTS];
|
||||
size_t offset = SYSTEM_BYTES;
|
||||
for(size_t i = 0; i < MAX_PORTS; i++) {
|
||||
if(i != port)
|
||||
newpinfo[i] = pinfo[i];
|
||||
else
|
||||
newpinfo[i] = &porttype_info::lookup(ptype);
|
||||
newoffsets[i] = offset;
|
||||
if(newpinfo[i])
|
||||
offset += newpinfo[i]->storage_size;
|
||||
if(i != port && newpinfo[i] && newpinfo[i]->storage_size)
|
||||
memcpy(tmp + newoffsets[i], backing + offsets[i], newpinfo[i]->storage_size);
|
||||
}
|
||||
memcpy(memory, tmp, MAXIMUM_CONTROLLER_FRAME_SIZE);
|
||||
types[port] = ptype;
|
||||
pinfo[port] = newpinfo[port];
|
||||
}
|
|
@ -6,7 +6,7 @@ lua_function::lua_function(const std::string& name) throw(std::bad_alloc) {}
|
|||
lua_function::~lua_function() throw() {}
|
||||
void lua_callback_do_paint(struct lua_render_context* ctx) throw() {}
|
||||
void lua_callback_do_video(struct lua_render_context* ctx) throw() {}
|
||||
void lua_callback_do_input(controls_t& data, bool subframe) throw() {}
|
||||
void lua_callback_do_input(controller_frame& data, bool subframe) throw() {}
|
||||
void lua_callback_do_reset() throw() {}
|
||||
void lua_callback_do_frame() throw() {}
|
||||
void lua_callback_do_readwrite() throw() {}
|
||||
|
@ -151,7 +151,7 @@ bool get_boolean_argument(lua_State* LS, unsigned argindex, const char* fname)
|
|||
}
|
||||
|
||||
lua_render_context* lua_render_ctx = NULL;
|
||||
controls_t* lua_input_controllerdata = NULL;
|
||||
controller_frame* lua_input_controllerdata = NULL;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -411,7 +411,7 @@ void lua_callback_post_save(const std::string& name, bool is_state) throw()
|
|||
run_lua_cb(2);
|
||||
}
|
||||
|
||||
void lua_callback_do_input(controls_t& data, bool subframe) throw()
|
||||
void lua_callback_do_input(controller_frame& data, bool subframe) throw()
|
||||
{
|
||||
if(!callback_exists("on_input"))
|
||||
return;
|
||||
|
|
|
@ -114,7 +114,7 @@ private:
|
|||
bool default_firmware;
|
||||
} firmwarepath_setting;
|
||||
|
||||
controls_t movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
|
||||
controller_frame movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
if(lua_requests_subframe_paint)
|
||||
redraw_framebuffer();
|
||||
|
@ -175,7 +175,7 @@ controls_t movie_logic::update_controls(bool subframe) throw(std::bad_alloc, std
|
|||
set_curcontrols_reset(pending_reset_cycles);
|
||||
else if(!subframe)
|
||||
set_curcontrols_reset(-1);
|
||||
controls_t tmp = get_current_controls(movb.get_movie().get_current_frame());
|
||||
controller_frame tmp = get_current_controls(movb.get_movie().get_current_frame());
|
||||
lua_callback_do_input(tmp, subframe);
|
||||
return tmp;
|
||||
}
|
||||
|
@ -279,58 +279,21 @@ void update_movie_state()
|
|||
}
|
||||
do_watch_memory();
|
||||
|
||||
controls_t c;
|
||||
controller_frame c;
|
||||
if(movb.get_movie().readonly_mode())
|
||||
c = movb.get_movie().get_controls();
|
||||
else
|
||||
c = get_current_controls(movb.get_movie().get_current_frame());
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
unsigned pindex = controller_index_by_logical(i);
|
||||
unsigned port = pindex >> 2;
|
||||
unsigned dev = pindex & 3;
|
||||
auto ctype = controller_type_by_logical(i);
|
||||
std::ostringstream x;
|
||||
switch(ctype) {
|
||||
case DT_GAMEPAD:
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_LEFT) ? "l" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_RIGHT) ? "r" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_UP) ? "u" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_DOWN) ? "d" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_A) ? "A" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_B) ? "B" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_X) ? "X" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_Y) ? "Y" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_L) ? "L" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_R) ? "R" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_START) ? "S" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JOYPAD_SELECT) ? "s" : " ");
|
||||
break;
|
||||
case DT_MOUSE:
|
||||
x << c(port, dev, SNES_DEVICE_ID_MOUSE_X) << " ";
|
||||
x << c(port, dev, SNES_DEVICE_ID_MOUSE_Y) << " ";
|
||||
x << (c(port, dev, SNES_DEVICE_ID_MOUSE_LEFT) ? "L" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_MOUSE_RIGHT) ? "R" : " ");
|
||||
break;
|
||||
case DT_SUPERSCOPE:
|
||||
x << c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_X) << " ";
|
||||
x << c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_Y) << " ";
|
||||
x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER) ? "T" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_CURSOR) ? "C" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_TURBO) ? "t" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_SUPER_SCOPE_PAUSE) ? "P" : " ");
|
||||
break;
|
||||
case DT_JUSTIFIER:
|
||||
x << c(port, dev, SNES_DEVICE_ID_JUSTIFIER_X) << " ";
|
||||
x << c(port, dev, SNES_DEVICE_ID_JUSTIFIER_Y) << " ";
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JUSTIFIER_START) ? "T" : " ");
|
||||
x << (c(port, dev, SNES_DEVICE_ID_JUSTIFIER_TRIGGER) ? "S" : " ");
|
||||
break;
|
||||
case DT_NONE:
|
||||
devicetype_t dtype = c.devicetype(pindex);
|
||||
if(dtype == DT_NONE)
|
||||
continue;
|
||||
}
|
||||
char buffer[MAX_DISPLAY_LENGTH];
|
||||
c.display(pindex, buffer);
|
||||
char y[3] = {'P', 0, 0};
|
||||
y[1] = 49 + i;
|
||||
_status.set(y, x.str());
|
||||
_status.set(y, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -307,4 +307,4 @@ std::string format_address(void* addr)
|
|||
}
|
||||
|
||||
std::string bsnes_core_version;
|
||||
std::string lsnes_version = "1-β1";
|
||||
std::string lsnes_version = "1-β2";
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
#define FLAG_SYNC CONTROL_FRAME_SYNC
|
||||
|
||||
//std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
|
||||
|
||||
namespace
|
||||
{
|
||||
bool movies_compatible(const std::vector<controls_t>& old_movie, const std::vector<controls_t>& new_movie,
|
||||
bool movies_compatible(controller_frame_vector& old_movie, controller_frame_vector& new_movie,
|
||||
uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
|
||||
const std::string& new_projectid)
|
||||
{
|
||||
|
@ -30,16 +29,15 @@ namespace
|
|||
uint64_t syncs_seen = 0;
|
||||
uint64_t frames_read = 0;
|
||||
while(syncs_seen < frame - 1) {
|
||||
controls_t oldc(true), newc(true);
|
||||
//Due to way subframes are stored, we can ignore syncing when comparing.
|
||||
controller_frame oldc = old_movie.blank_frame(true), newc = new_movie.blank_frame(true);
|
||||
if(frames_read < old_movie.size())
|
||||
oldc = old_movie[frames_read];
|
||||
if(frames_read < new_movie.size())
|
||||
newc = new_movie[frames_read];
|
||||
if(memcmp(oldc.controls, newc.controls, sizeof(oldc.controls)))
|
||||
if(oldc != newc)
|
||||
return false; //Mismatch.
|
||||
frames_read++;
|
||||
if(newc(CONTROL_FRAME_SYNC))
|
||||
if(newc.sync())
|
||||
syncs_seen++;
|
||||
}
|
||||
//We increment the counter one time too many.
|
||||
|
@ -50,14 +48,30 @@ namespace
|
|||
readable_old_subframes = old_movie.size() - frames_read;
|
||||
if(frames_read < new_movie.size())
|
||||
readable_new_subframes = new_movie.size() - frames_read;
|
||||
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
uint32_t p = polls[i] & 0x7FFFFFFFUL;
|
||||
//Compare reset flags of current frame.
|
||||
bool old_reset = false;
|
||||
bool new_reset = false;
|
||||
std::pair<short, short> old_delay = std::make_pair(0, 0);
|
||||
std::pair<short, short> new_delay = std::make_pair(0, 0);
|
||||
if(readable_old_subframes) {
|
||||
old_reset = old_movie[frames_read].reset();
|
||||
old_delay = old_movie[frames_read].delay();
|
||||
}
|
||||
if(readable_new_subframes) {
|
||||
new_reset = new_movie[frames_read].reset();
|
||||
new_delay = new_movie[frames_read].delay();
|
||||
}
|
||||
if(old_reset != new_reset || old_delay != new_delay)
|
||||
return false;
|
||||
//Then rest of the stuff.
|
||||
for(unsigned i = 0; i < MAX_BUTTONS; i++) {
|
||||
uint32_t p = polls[i + 4] & 0x7FFFFFFFUL;
|
||||
short ov = 0, nv = 0;
|
||||
for(uint32_t j = 0; j < p; j++) {
|
||||
if(j < readable_old_subframes)
|
||||
ov = old_movie[j + frames_read](i);
|
||||
ov = old_movie[j + frames_read].axis2(i);
|
||||
if(j < readable_new_subframes)
|
||||
nv = new_movie[j + frames_read](i);
|
||||
nv = new_movie[j + frames_read].axis2(i);
|
||||
if(ov != nv)
|
||||
return false;
|
||||
}
|
||||
|
@ -68,8 +82,7 @@ namespace
|
|||
|
||||
void movie::set_all_DRDY() throw()
|
||||
{
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
|
||||
pollcounters[i] |= 0x80000000UL;
|
||||
pollcounters.set_all_DRDY();
|
||||
}
|
||||
|
||||
std::string movie::rerecord_count() throw(std::bad_alloc)
|
||||
|
@ -97,31 +110,21 @@ bool movie::readonly_mode() throw()
|
|||
return readonly;
|
||||
}
|
||||
|
||||
short movie::next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error)
|
||||
{
|
||||
return next_input(ccindex2(port, controller, index));
|
||||
}
|
||||
|
||||
void movie::set_controls(controls_t controls) throw()
|
||||
void movie::set_controls(controller_frame controls) throw()
|
||||
{
|
||||
current_controls = controls;
|
||||
}
|
||||
|
||||
uint32_t movie::count_changes(uint64_t first_subframe) throw()
|
||||
{
|
||||
if(first_subframe >= movie_data.size())
|
||||
return 0;
|
||||
uint32_t ret = 1;
|
||||
while(first_subframe + ret < movie_data.size() && !movie_data[first_subframe + ret](CONTROL_FRAME_SYNC))
|
||||
ret++;
|
||||
return ret;
|
||||
return movie_data.subframe_count(first_subframe);
|
||||
}
|
||||
|
||||
controls_t movie::get_controls() throw()
|
||||
controller_frame movie::get_controls() throw()
|
||||
{
|
||||
if(!readonly)
|
||||
return current_controls;
|
||||
controls_t c;
|
||||
controller_frame c = movie_data.blank_frame(false);
|
||||
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
|
||||
if(current_frame == 0)
|
||||
return c;
|
||||
|
@ -129,10 +132,14 @@ controls_t movie::get_controls() throw()
|
|||
uint32_t changes = count_changes(current_frame_first_subframe);
|
||||
if(!changes)
|
||||
return c; //End of movie.
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
|
||||
if(pollcounters.get_system()) {
|
||||
c.reset(movie_data[current_frame_first_subframe].reset());
|
||||
c.delay(movie_data[current_frame_first_subframe].delay());
|
||||
}
|
||||
for(size_t i = 0; i < MAX_BUTTONS; i++) {
|
||||
uint32_t polls = pollcounters.get_polls(i);
|
||||
uint32_t index = (changes > polls) ? polls : changes - 1;
|
||||
c(i) = movie_data[current_frame_first_subframe + index](i);
|
||||
c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
@ -155,13 +162,7 @@ uint64_t movie::get_frame_count() throw()
|
|||
void movie::next_frame() throw(std::bad_alloc)
|
||||
{
|
||||
//If all poll counters are zero for all real controls, this frame is lag.
|
||||
bool this_frame_lag = true;
|
||||
for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
|
||||
if(pollcounters[i] & 0x7FFFFFFF)
|
||||
this_frame_lag = false;
|
||||
//Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
|
||||
if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
|
||||
this_frame_lag = false;
|
||||
bool this_frame_lag = !pollcounters.has_polled();
|
||||
//Oh, frame 0 must not be considered lag.
|
||||
if(current_frame && this_frame_lag) {
|
||||
lag_frames++;
|
||||
|
@ -169,16 +170,13 @@ void movie::next_frame() throw(std::bad_alloc)
|
|||
if(!readonly) {
|
||||
//If in read-write mode, write a dummy record for the frame. Force sync flag.
|
||||
//As index should be movie_data.size(), it is correct afterwards.
|
||||
controls_t c = current_controls;
|
||||
c(CONTROL_FRAME_SYNC) = 1;
|
||||
movie_data.push_back(c);
|
||||
movie_data.append(current_controls.copy(true));
|
||||
frames_in_movie++;
|
||||
}
|
||||
}
|
||||
|
||||
//Reset the poll counters and DRDY flags.
|
||||
for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
|
||||
pollcounters[i] = 0;
|
||||
pollcounters.clear();
|
||||
|
||||
//Increment the current frame counter and subframe counter. Note that first subframe is undefined for
|
||||
//frame 0 and 0 for frame 1.
|
||||
|
@ -190,34 +188,20 @@ void movie::next_frame() throw(std::bad_alloc)
|
|||
current_frame++;
|
||||
}
|
||||
|
||||
bool movie::get_DRDY(unsigned controlindex) throw(std::logic_error)
|
||||
bool movie::get_DRDY(unsigned pid, unsigned ctrl) throw(std::logic_error)
|
||||
{
|
||||
if(controlindex >= TOTAL_CONTROLS)
|
||||
throw std::logic_error("movie::get_DRDY: Bad index");
|
||||
return ((pollcounters[controlindex] & 0x80000000UL) != 0);
|
||||
return pollcounters.get_DRDY(pid, ctrl);
|
||||
}
|
||||
|
||||
bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error)
|
||||
short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
|
||||
{
|
||||
return get_DRDY(ccindex2(port, controller, index));
|
||||
}
|
||||
|
||||
short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error)
|
||||
{
|
||||
//Check validity of index.
|
||||
if(controlindex == FLAG_SYNC)
|
||||
return 0;
|
||||
if(controlindex >= TOTAL_CONTROLS)
|
||||
throw std::logic_error("movie::next_input: Invalid control index");
|
||||
|
||||
//Clear the DRDY flag.
|
||||
pollcounters[controlindex] &= 0x7FFFFFFF;
|
||||
pollcounters.clear_DRDY(pid, ctrl);
|
||||
|
||||
if(readonly) {
|
||||
//In readonly mode...
|
||||
//If at the end of the movie, return released / neutral (but also record the poll)...
|
||||
if(current_frame_first_subframe >= movie_data.size()) {
|
||||
pollcounters[controlindex]++;
|
||||
pollcounters.increment_polls(pid, ctrl);
|
||||
return 0;
|
||||
}
|
||||
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
|
||||
|
@ -225,10 +209,10 @@ short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_
|
|||
return 0;
|
||||
//Otherwise find the last valid frame of input.
|
||||
uint32_t changes = count_changes(current_frame_first_subframe);
|
||||
uint32_t polls = (pollcounters[controlindex]++) & 0x7FFFFFFF;
|
||||
uint32_t polls = pollcounters.increment_polls(pid, ctrl);
|
||||
uint32_t index = (changes > polls) ? polls : changes - 1;
|
||||
//debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
|
||||
return movie_data[current_frame_first_subframe + index](controlindex);
|
||||
return movie_data[current_frame_first_subframe + index].axis(pid, ctrl);
|
||||
} else {
|
||||
//Readwrite mode.
|
||||
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
|
||||
|
@ -237,38 +221,32 @@ short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_
|
|||
return 0;
|
||||
//If at movie end, insert complete input with frame sync set (this is the first subframe).
|
||||
if(current_frame_first_subframe >= movie_data.size()) {
|
||||
controls_t c = current_controls;
|
||||
c(CONTROL_FRAME_SYNC) = 1;
|
||||
movie_data.push_back(c);
|
||||
movie_data.append(current_controls.copy(true));
|
||||
//current_frame_first_subframe should be movie_data.size(), so it is right.
|
||||
pollcounters[controlindex]++;
|
||||
pollcounters.increment_polls(pid, ctrl);
|
||||
frames_in_movie++;
|
||||
assert(pollcounters[controlindex] == 1);
|
||||
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
|
||||
return movie_data[current_frame_first_subframe](controlindex);
|
||||
return movie_data[current_frame_first_subframe].axis(pid, ctrl);
|
||||
}
|
||||
short new_value = current_controls(controlindex);
|
||||
short new_value = current_controls.axis(pid, ctrl);
|
||||
//Fortunately, we know this frame is the last one in movie_data.
|
||||
uint32_t pollcounter = pollcounters[controlindex] & 0x7FFFFFFF;
|
||||
uint32_t pollcounter = pollcounters.get_polls(pid, ctrl);
|
||||
uint64_t fetchrow = movie_data.size() - 1;
|
||||
if(current_frame_first_subframe + pollcounter < movie_data.size()) {
|
||||
//The index is within existing size. Change the value and propagate to all subsequent
|
||||
//subframes.
|
||||
for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
|
||||
movie_data[i](controlindex) = new_value;
|
||||
movie_data[i].axis(pid, ctrl, new_value);
|
||||
fetchrow = current_frame_first_subframe + pollcounter;
|
||||
} else if(new_value != movie_data[movie_data.size() - 1](controlindex)) {
|
||||
} else if(new_value != movie_data[movie_data.size() - 1].axis(pid, ctrl)) {
|
||||
//The index is not within existing size and value does not match. We need to create a new
|
||||
//subframes(s), copying the last subframe.
|
||||
while(current_frame_first_subframe + pollcounter >= movie_data.size()) {
|
||||
controls_t c = movie_data[movie_data.size() - 1];
|
||||
c(CONTROL_FRAME_SYNC) = 0;
|
||||
movie_data.push_back(c);
|
||||
}
|
||||
while(current_frame_first_subframe + pollcounter >= movie_data.size())
|
||||
movie_data.append(movie_data[movie_data.size() - 1].copy(false));
|
||||
fetchrow = current_frame_first_subframe + pollcounter;
|
||||
movie_data[current_frame_first_subframe + pollcounter](controlindex) = new_value;
|
||||
movie_data[current_frame_first_subframe + pollcounter].axis(pid, ctrl, new_value);
|
||||
}
|
||||
pollcounters[controlindex]++;
|
||||
pollcounters.increment_polls(pid, ctrl);
|
||||
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
|
||||
return new_value;
|
||||
}
|
||||
|
@ -282,37 +260,33 @@ movie::movie() throw(std::bad_alloc)
|
|||
current_frame = 0;
|
||||
frames_in_movie = 0;
|
||||
current_frame_first_subframe = 0;
|
||||
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
pollcounters[i] = 0;
|
||||
current_controls(i) = 0;
|
||||
}
|
||||
lag_frames = 0;
|
||||
clear_caches();
|
||||
}
|
||||
|
||||
void movie::load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
|
||||
void movie::load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
if(input.size() > 0 && !input[0](CONTROL_FRAME_SYNC))
|
||||
if(input.size() > 0 && !input[0].sync())
|
||||
throw std::runtime_error("First subframe MUST have frame sync flag set");
|
||||
clear_caches();
|
||||
frames_in_movie = 0;
|
||||
for(auto i : input)
|
||||
if(i(CONTROL_FRAME_SYNC))
|
||||
for(size_t i = 0; i < input.size(); i++)
|
||||
if(input[i].sync())
|
||||
frames_in_movie++;
|
||||
readonly = true;
|
||||
rerecords = rerecs;
|
||||
_project_id = project_id;
|
||||
current_frame = 0;
|
||||
current_frame_first_subframe = 0;
|
||||
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
pollcounters[i] = 0;
|
||||
}
|
||||
pollcounters.clear();
|
||||
lag_frames = 0;
|
||||
movie_data = input;
|
||||
//This is to force internal type of current_controls to become correct.
|
||||
current_controls = input.blank_frame(false);
|
||||
}
|
||||
|
||||
std::vector<controls_t> movie::save() throw(std::bad_alloc)
|
||||
controller_frame_vector movie::save() throw(std::bad_alloc)
|
||||
{
|
||||
return movie_data;
|
||||
}
|
||||
|
@ -322,37 +296,21 @@ void movie::commit_reset(long delay) throw(std::bad_alloc)
|
|||
if(readonly || delay < 0)
|
||||
return;
|
||||
//If this frame is lagged, we need to write entry for it.
|
||||
bool this_frame_lag = true;
|
||||
for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
|
||||
if(pollcounters[i] & 0x7FFFFFFF)
|
||||
this_frame_lag = false;
|
||||
//Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
|
||||
if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
|
||||
this_frame_lag = false;
|
||||
if(this_frame_lag) {
|
||||
controls_t c = current_controls;
|
||||
c(CONTROL_FRAME_SYNC) = 1;
|
||||
movie_data.push_back(c);
|
||||
if(!pollcounters.has_polled()) {
|
||||
movie_data.append(current_controls.copy(true));
|
||||
frames_in_movie++;
|
||||
//Current_frame_first_subframe is correct.
|
||||
}
|
||||
//Also set poll counters on reset cycles to avoid special cases elsewhere.
|
||||
pollcounters[CONTROL_SYSTEM_RESET] = 1;
|
||||
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
|
||||
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
|
||||
pollcounters.set_system();
|
||||
//Current frame is always last in rw mode.
|
||||
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET) = 1;
|
||||
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
|
||||
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
|
||||
movie_data[current_frame_first_subframe].reset(true);
|
||||
movie_data[current_frame_first_subframe].delay(std::make_pair(delay / 10000, delay % 10000));
|
||||
}
|
||||
|
||||
unsigned movie::next_poll_number()
|
||||
{
|
||||
unsigned max = 0;
|
||||
for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
|
||||
if(max < (pollcounters[i] & 0x7FFFFFFF))
|
||||
max = (pollcounters[i] & 0x7FFFFFFF);
|
||||
return max + 1;
|
||||
return pollcounters.max_polls() + 1;
|
||||
}
|
||||
|
||||
void movie::readonly_mode(bool enable) throw(std::bad_alloc)
|
||||
|
@ -373,8 +331,7 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
|
|||
if(current_frame_first_subframe >= movie_data.size()) {
|
||||
//Yes, this will insert one extra frame... But we will lose it later if it is not needed.
|
||||
while(frames_in_movie < current_frame) {
|
||||
controls_t c(true);
|
||||
movie_data.push_back(c);
|
||||
movie_data.append(movie_data.blank_frame(true));
|
||||
frames_in_movie++;
|
||||
}
|
||||
current_frame_first_subframe = movie_data.size() - 1;
|
||||
|
@ -384,22 +341,23 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
|
|||
//forward values with smaller poll counters.
|
||||
uint64_t next_frame_first_subframe = current_frame_first_subframe +
|
||||
count_changes(current_frame_first_subframe);
|
||||
uint64_t max_readable_subframes = current_frame_first_subframe;
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
|
||||
if(current_frame_first_subframe + polls >= next_frame_first_subframe)
|
||||
max_readable_subframes = next_frame_first_subframe;
|
||||
else if(current_frame_first_subframe + polls > max_readable_subframes)
|
||||
max_readable_subframes = current_frame_first_subframe + polls;
|
||||
}
|
||||
uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
|
||||
if(max_readable_subframes > next_frame_first_subframe)
|
||||
max_readable_subframes = next_frame_first_subframe;
|
||||
|
||||
movie_data.resize(max_readable_subframes);
|
||||
next_frame_first_subframe = max_readable_subframes;
|
||||
for(size_t i = 1; i < TOTAL_CONTROLS; i++) {
|
||||
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
|
||||
if(!polls)
|
||||
polls = 1;
|
||||
//Propagate RESET. This always has poll count of 0 or 1, which always behaves like 1.
|
||||
for(uint64_t j = current_frame_first_subframe + 1; j < next_frame_first_subframe; j++) {
|
||||
movie_data[j].reset(movie_data[current_frame_first_subframe].reset());
|
||||
movie_data[j].delay(movie_data[current_frame_first_subframe].delay());
|
||||
}
|
||||
//Then the other buttons.
|
||||
for(size_t i = 0; i < MAX_BUTTONS; i++) {
|
||||
uint32_t polls = pollcounters.get_polls(i);
|
||||
polls = polls ? polls : 1;
|
||||
for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
|
||||
movie_data[j](i) = movie_data[current_frame_first_subframe + polls - 1](i);
|
||||
movie_data[j].axis2(i, movie_data[current_frame_first_subframe + polls - 1].axis2(i));
|
||||
}
|
||||
frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
|
||||
}
|
||||
|
@ -409,19 +367,18 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
|
|||
void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
|
||||
throw(std::bad_alloc)
|
||||
{
|
||||
pcounters.resize(TOTAL_CONTROLS);
|
||||
pollcounters.save_state(pcounters);
|
||||
proj_id = _project_id;
|
||||
curframe = current_frame;
|
||||
lagframes = lag_frames;
|
||||
memcpy(&pcounters[0], pollcounters, TOTAL_CONTROLS * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
//Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
|
||||
size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
|
||||
std::vector<controls_t>* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
|
||||
controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
if(pcounters.size() != TOTAL_CONTROLS)
|
||||
if(!pollcounters.check(pcounters))
|
||||
throw std::runtime_error("Wrong number of poll counters");
|
||||
if(old_movie && !movies_compatible(*old_movie, movie_data, curframe, &pcounters[0], old_projectid,
|
||||
_project_id))
|
||||
|
@ -434,7 +391,7 @@ size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vec
|
|||
current_frame = curframe;
|
||||
current_frame_first_subframe = tmp_firstsubframe;
|
||||
lag_frames = lagframe;
|
||||
memcpy(pollcounters, &pcounters[0], TOTAL_CONTROLS * sizeof(uint32_t));
|
||||
pollcounters.load_state(pcounters);
|
||||
readonly_mode(ro);
|
||||
return 0;
|
||||
}
|
||||
|
@ -443,14 +400,12 @@ long movie::get_reset_status() throw()
|
|||
{
|
||||
if(current_frame == 0 || current_frame_first_subframe >= movie_data.size())
|
||||
return -1; //No resets out of range.
|
||||
if(!movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET))
|
||||
if(!movie_data[current_frame_first_subframe].reset())
|
||||
return -1; //Not a reset.
|
||||
//Also set poll counters on reset cycles to avoid special cases elsewhere.
|
||||
pollcounters[CONTROL_SYSTEM_RESET] = 1;
|
||||
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
|
||||
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
|
||||
long hi = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI);
|
||||
long lo = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO);
|
||||
pollcounters.set_system();
|
||||
auto g = movie_data[current_frame_first_subframe].delay();
|
||||
long hi = g.first, lo = g.second;
|
||||
return hi * 10000 + lo;
|
||||
}
|
||||
|
||||
|
@ -472,7 +427,7 @@ void movie::clear_caches() throw()
|
|||
cached_subframe = 0;
|
||||
}
|
||||
|
||||
controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
|
||||
controller_frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
|
||||
{
|
||||
if(frame < cached_frame)
|
||||
clear_caches();
|
||||
|
@ -482,8 +437,9 @@ controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
|
|||
cached_frame = frame;
|
||||
cached_subframe = p;
|
||||
uint64_t max = count_changes(p);
|
||||
if(!max)
|
||||
return controls_t(true);
|
||||
if(!max) {
|
||||
return movie_data.blank_frame(true);
|
||||
}
|
||||
if(max <= subframe)
|
||||
subframe = max - 1;
|
||||
return movie_data[p + subframe];
|
||||
|
@ -502,14 +458,14 @@ movie& movie_logic::get_movie() throw()
|
|||
long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
mov.next_frame();
|
||||
controls_t c = update_controls(false);
|
||||
controller_frame c = update_controls(false);
|
||||
if(!mov.readonly_mode()) {
|
||||
mov.set_controls(c);
|
||||
if(dont_poll)
|
||||
mov.set_all_DRDY();
|
||||
if(c(CONTROL_SYSTEM_RESET)) {
|
||||
long hi = c(CONTROL_SYSTEM_RESET_CYCLES_HI);
|
||||
long lo = c(CONTROL_SYSTEM_RESET_CYCLES_LO);
|
||||
if(c.reset()) {
|
||||
auto g = c.delay();
|
||||
long hi = g.first, lo = g.second;
|
||||
mov.commit_reset(hi * 10000 + lo);
|
||||
}
|
||||
}
|
||||
|
@ -518,13 +474,12 @@ long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::
|
|||
|
||||
short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
if(dev >= MAX_CONTROLLERS_PER_PORT || id >= CONTROLLER_CONTROLS)
|
||||
return 0;
|
||||
if(!mov.get_DRDY(port ? 1 : 0, dev, id)) {
|
||||
unsigned pid = port ? (dev + MAX_CONTROLLERS_PER_PORT) : dev;
|
||||
if(!mov.get_DRDY(pid, id)) {
|
||||
mov.set_controls(update_controls(true));
|
||||
mov.set_all_DRDY();
|
||||
}
|
||||
int16_t in = mov.next_input(port ? 1 : 0, dev, id);
|
||||
int16_t in = mov.next_input(pid, id);
|
||||
//debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
|
||||
// << ") giving " << in << std::endl;
|
||||
//debuglog.flush();
|
||||
|
|
|
@ -207,16 +207,16 @@ void write_authors_file(zip_writer& w, std::vector<std::pair<std::string, std::s
|
|||
}
|
||||
}
|
||||
|
||||
void write_input(zip_writer& w, std::vector<controls_t>& input, porttype_t port1, porttype_t port2)
|
||||
void write_input(zip_writer& w, controller_frame_vector& input, porttype_t port1, porttype_t port2)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
std::vector<cencode::fn_t> encoders;
|
||||
encoders.push_back(port_types[port1].encoder);
|
||||
encoders.push_back(port_types[port2].encoder);
|
||||
std::ostream& m = w.create_file("input");
|
||||
try {
|
||||
for(auto i : input)
|
||||
m << i.tostring(encoders) << std::endl;
|
||||
char buffer[MAX_SERIALIZED_SIZE];
|
||||
for(size_t i = 0; i < input.size(); i++) {
|
||||
input[i].serialize(buffer);
|
||||
m << buffer << std::endl;
|
||||
}
|
||||
if(!m)
|
||||
throw std::runtime_error("Can't write ZIP file member");
|
||||
w.close_file();
|
||||
|
@ -226,19 +226,18 @@ void write_input(zip_writer& w, std::vector<controls_t>& input, porttype_t port1
|
|||
}
|
||||
}
|
||||
|
||||
void read_input(zip_reader& r, std::vector<controls_t>& input, porttype_t port1, porttype_t port2, unsigned version)
|
||||
void read_input(zip_reader& r, controller_frame_vector& input, porttype_t port1, porttype_t port2, unsigned version)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
std::vector<cdecode::fn_t> decoders;
|
||||
decoders.push_back(port_types[port1].decoder);
|
||||
decoders.push_back(port_types[port2].decoder);
|
||||
controller_frame tmp = input.blank_frame(false);
|
||||
std::istream& m = r["input"];
|
||||
try {
|
||||
std::string x;
|
||||
while(std::getline(m, x)) {
|
||||
strip_CR(x);
|
||||
if(x != "") {
|
||||
input.push_back(controls_t(x, decoders, version));
|
||||
tmp.deserialize(x.c_str());
|
||||
input.append(tmp);
|
||||
}
|
||||
}
|
||||
delete &m;
|
||||
|
@ -295,13 +294,14 @@ void write_pollcounters(zip_writer& w, const std::string& file, const std::vecto
|
|||
|
||||
porttype_t parse_controller_type(const std::string& type, bool port) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
porttype_t port1 = PT_INVALID;
|
||||
for(unsigned i = 0; i <= PT_LAST_CTYPE; i++)
|
||||
if(type == port_types[i].name && (port || port_types[i].valid_port1))
|
||||
port1 = static_cast<porttype_t>(i);
|
||||
if(port1 == PT_INVALID)
|
||||
try {
|
||||
const porttype_info& i = porttype_info::lookup(type);
|
||||
if(!i.legal(port ? 1 : 0))
|
||||
throw 42;
|
||||
return i.value;
|
||||
} catch(...) {
|
||||
throw std::runtime_error(std::string("Illegal port") + (port ? "2" : "1") + " device '" + type + "'");
|
||||
return port1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -339,12 +339,13 @@ moviefile::moviefile(const std::string& movie) throw(std::bad_alloc, std::runtim
|
|||
} catch(std::exception& e) {
|
||||
throw std::runtime_error("Illegal game type '" + tmp + "'");
|
||||
}
|
||||
tmp = port_types[PT_GAMEPAD].name;
|
||||
tmp = "gamepad";
|
||||
read_linefile(r, "port1", tmp, true);
|
||||
port1 = port_type::lookup(tmp, false).ptype;
|
||||
tmp = port_types[PT_NONE].name;
|
||||
port1 = porttype_info::lookup(tmp).value;
|
||||
tmp = "none";
|
||||
read_linefile(r, "port2", tmp, true);
|
||||
port2 = port_type::lookup(tmp, true).ptype;
|
||||
port2 = porttype_info::lookup(tmp).value;
|
||||
input.clear(port1, port2);
|
||||
read_linefile(r, "gamename", gamename, true);
|
||||
read_linefile(r, "projectid", projectid);
|
||||
rerecords = read_rrdata(r, c_rrdata);
|
||||
|
@ -397,9 +398,9 @@ void moviefile::save(const std::string& movie, unsigned compression) throw(std::
|
|||
zip_writer w(movie, compression);
|
||||
write_linefile(w, "gametype", gtype::tostring(gametype));
|
||||
if(port1 != PT_GAMEPAD)
|
||||
write_linefile(w, "port1", port_types[port1].name);
|
||||
write_linefile(w, "port1", porttype_info::lookup(port1).name);
|
||||
if(port2 != PT_NONE)
|
||||
write_linefile(w, "port2", port_types[port2].name);
|
||||
write_linefile(w, "port2", porttype_info::lookup(port2).name);
|
||||
write_linefile(w, "gamename", gamename, true);
|
||||
write_linefile(w, "systemid", "lsnes-rr1");
|
||||
write_linefile(w, "controlsversion", "0");
|
||||
|
@ -437,12 +438,7 @@ void moviefile::save(const std::string& movie, unsigned compression) throw(std::
|
|||
|
||||
uint64_t moviefile::get_frame_count() throw()
|
||||
{
|
||||
uint64_t frames = 0;
|
||||
for(size_t i = 0; i < input.size(); i++) {
|
||||
if(input[i](CONTROL_FRAME_SYNC))
|
||||
frames++;
|
||||
}
|
||||
return frames;
|
||||
return input.count_frames();
|
||||
}
|
||||
|
||||
namespace
|
||||
|
|
|
@ -8,9 +8,9 @@ namespace
|
|||
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
|
||||
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
|
||||
short value = get_numeric_argument<short>(LS, 3, fname.c_str());
|
||||
if(controller > 7 || index > 11)
|
||||
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER)
|
||||
return 0;
|
||||
(*lua_input_controllerdata)(controller >> 2, controller & 3, index) = value;
|
||||
lua_input_controllerdata->axis(controller, index, value);
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
@ -19,9 +19,9 @@ namespace
|
|||
return 0;
|
||||
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
|
||||
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
|
||||
if(controller > 7 || index > 11)
|
||||
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER)
|
||||
return 0;
|
||||
lua_pushnumber(LS, (*lua_input_controllerdata)(controller >> 2, controller & 3, index));
|
||||
lua_pushnumber(LS, lua_input_controllerdata->axis(controller, index));
|
||||
return 1;
|
||||
});
|
||||
|
||||
|
@ -34,9 +34,8 @@ namespace
|
|||
return 0;
|
||||
short lo = cycles % 10000;
|
||||
short hi = cycles / 10000;
|
||||
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET) = 1;
|
||||
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET_CYCLES_HI) = hi;
|
||||
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET_CYCLES_LO) = lo;
|
||||
lua_input_controllerdata->reset(true);
|
||||
lua_input_controllerdata->delay(std::make_pair(hi, lo));
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,11 +39,25 @@ namespace
|
|||
uint64_t frame = get_numeric_argument<uint64_t>(LS, 1, "movie.frame_subframes");
|
||||
uint64_t subframe = get_numeric_argument<uint64_t>(LS, 2, "movie.frame_subframes");
|
||||
auto& m = get_movie();
|
||||
controls_t r = m.read_subframe(frame, subframe);
|
||||
controller_frame r = m.read_subframe(frame, subframe);
|
||||
lua_newtable(LS);
|
||||
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
|
||||
lua_pushnumber(LS, i);
|
||||
lua_pushnumber(LS, r(i));
|
||||
|
||||
lua_pushnumber(LS, 0);
|
||||
lua_pushnumber(LS, r.sync() ? 1 : 0);
|
||||
lua_settable(LS, -3);
|
||||
lua_pushnumber(LS, 1);
|
||||
lua_pushnumber(LS, r.reset() ? 1 : 0);
|
||||
lua_settable(LS, -3);
|
||||
lua_pushnumber(LS, 2);
|
||||
lua_pushnumber(LS, r.delay().first);
|
||||
lua_settable(LS, -3);
|
||||
lua_pushnumber(LS, 3);
|
||||
lua_pushnumber(LS, r.delay().second);
|
||||
lua_settable(LS, -3);
|
||||
|
||||
for(size_t i = 0; i < MAX_BUTTONS; i++) {
|
||||
lua_pushnumber(LS, i + 4);
|
||||
lua_pushnumber(LS, r.axis2(i));
|
||||
lua_settable(LS, -3);
|
||||
}
|
||||
return 1;
|
||||
|
|
|
@ -47,9 +47,9 @@ struct moviefile generate_movie_template(std::vector<std::string> cmdline, loade
|
|||
for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
|
||||
std::string o = *i;
|
||||
if(o.length() >= 8 && o.substr(0, 8) == "--port1=")
|
||||
movie.port1 = port_type::lookup(o.substr(8), false).ptype;
|
||||
movie.port1 = porttype_info::lookup(o.substr(8)).value;
|
||||
if(o.length() >= 8 && o.substr(0, 8) == "--port2=")
|
||||
movie.port2 = port_type::lookup(o.substr(8), true).ptype;
|
||||
movie.port2 = porttype_info::lookup(o.substr(8)).value;
|
||||
if(o.length() >= 11 && o.substr(0, 11) == "--gamename=")
|
||||
movie.gamename = o.substr(11);
|
||||
if(o.length() >= 9 && o.substr(0, 9) == "--author=") {
|
||||
|
@ -65,9 +65,8 @@ struct moviefile generate_movie_template(std::vector<std::string> cmdline, loade
|
|||
if(movie.rtc_subsecond < 0 || movie.rtc_subsecond > 3462619485019ULL)
|
||||
throw std::runtime_error("Bad RTC subsecond value (range is 0-3462619485019)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
movie.input.clear(movie.port1, movie.port2);
|
||||
|
||||
return movie;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "core/command.hpp"
|
||||
#include "core/controller.hpp"
|
||||
#include "core/controllerdata.hpp"
|
||||
#include "core/controllerframe.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/framebuffer.hpp"
|
||||
#include "core/framerate.hpp"
|
||||
|
|
|
@ -872,6 +872,7 @@ 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.port1, f.port2);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue