From ec20694f7b13d508e646833ccb53717b1201c29e Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Mon, 9 Jan 2012 21:55:55 +0200 Subject: [PATCH] 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). --- include/core/controller.hpp | 4 +- include/core/controllerdata.hpp | 427 ----------------- include/core/controllerframe.hpp | 935 +++++++++++++++++++++++++++++++++++++ include/core/lua-int.hpp | 2 +- include/core/lua.hpp | 4 +- include/core/movie.hpp | 56 +-- include/core/moviefile.hpp | 4 +- manual.lyx | 16 + manual.txt | 8 + src/core/controller.cpp | 63 +-- src/core/controller_gamepad.cpp | 82 ++++ src/core/controller_justifier.cpp | 99 ++++ src/core/controller_justifiers.cpp | 113 +++++ src/core/controller_mouse.cpp | 99 ++++ src/core/controller_multitap.cpp | 96 ++++ src/core/controller_none.cpp | 56 +++ src/core/controller_superscope.cpp | 109 +++++ src/core/controllerdata.cpp | 350 -------------- src/core/controllerframe.cpp | 449 ++++++++++++++++++ src/core/lua.cpp | 6 +- src/core/mainloop.cpp | 53 +-- src/core/misc.cpp | 2 +- src/core/movie.cpp | 255 +++++----- src/core/moviefile.cpp | 54 +-- src/lua/input.cpp | 15 +- src/lua/movie.cpp | 22 +- src/plat-sdl/main.cpp | 7 +- src/plat-wxwidgets/mainwindow.cpp | 2 +- src/plat-wxwidgets/romselect.cpp | 1 + 29 files changed, 2289 insertions(+), 1100 deletions(-) delete mode 100644 include/core/controllerdata.hpp create mode 100644 include/core/controllerframe.hpp create mode 100644 src/core/controller_gamepad.cpp create mode 100644 src/core/controller_justifier.cpp create mode 100644 src/core/controller_justifiers.cpp create mode 100644 src/core/controller_mouse.cpp create mode 100644 src/core/controller_multitap.cpp create mode 100644 src/core/controller_none.cpp create mode 100644 src/core/controller_superscope.cpp delete mode 100644 src/core/controllerdata.cpp create mode 100644 src/core/controllerframe.cpp diff --git a/include/core/controller.hpp b/include/core/controller.hpp index 5ab719a9..cb368a65 100644 --- a/include/core/controller.hpp +++ b/include/core/controller.hpp @@ -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. diff --git a/include/core/controllerdata.hpp b/include/core/controllerdata.hpp deleted file mode 100644 index c42b6e5c..00000000 --- a/include/core/controllerdata.hpp +++ /dev/null @@ -1,427 +0,0 @@ -#ifndef _controllerdata__hpp__included__ -#define _controllerdata__hpp__included__ - -#include -#include - -#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& 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& 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 diff --git a/include/core/controllerframe.hpp b/include/core/controllerframe.hpp new file mode 100644 index 00000000..999e7385 --- /dev/null +++ b/include/core/controllerframe.hpp @@ -0,0 +1,935 @@ +#ifndef _controllerframe__hpp__included__ +#define _controllerframe__hpp__included__ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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(val) >> 8; + buf[1] = static_cast(val); +} + +/** + * Serialize short. + */ +inline short unserialize_short(const unsigned char* buf) +{ + return static_cast((static_cast(buf[0]) << 8) | static_cast(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& mem) throw(std::bad_alloc); +/** + * Load state from memory block. + * + * Parameter mem: The block from restore from. + */ + void load_state(const std::vector& mem) throw(); +/** + * Check if state can be loaded without errors. + * + * Returns: True if load is possible, false otherwise. + */ + bool check(const std::vector& 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 x) throw() + { + backing[1] = static_cast(x.first) >> 8; + backing[2] = static_cast(x.first); + backing[3] = static_cast(x.second) >> 8; + backing[4] = static_cast(x.second); + } +/** + * Get the reset delay. + * + * Return value: Value of reset delay. + */ + std::pair delay() throw() + { + short x, y; + x = static_cast(static_cast(backing[1]) << 8); + x |= static_cast(static_cast(backing[2])); + y = static_cast(static_cast(backing[3]) << 8); + y |= static_cast(static_cast(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 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 diff --git a/include/core/lua-int.hpp b/include/core/lua-int.hpp index 3228974f..68957db3 100644 --- a/include/core/lua-int.hpp +++ b/include/core/lua-int.hpp @@ -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 diff --git a/include/core/lua.hpp b/include/core/lua.hpp index ebf5f68e..494b99a2 100644 --- a/include/core/lua.hpp +++ b/include/core/lua.hpp @@ -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(); diff --git a/include/core/movie.hpp b/include/core/movie.hpp index 0dd9b834..a0f56df7 100644 --- a/include/core/movie.hpp +++ b/include/core/movie.hpp @@ -4,7 +4,7 @@ #include #include #include -#include "controllerdata.hpp" +#include "controllerframe.hpp" /** * Movie being played back or recorded @@ -101,25 +101,12 @@ 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 @@ -127,40 +114,29 @@ public: 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& 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 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& pcounters, bool ro, - std::vector* 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 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; }; diff --git a/include/core/moviefile.hpp b/include/core/moviefile.hpp index 0d5f70bc..ab9cfff0 100644 --- a/include/core/moviefile.hpp +++ b/include/core/moviefile.hpp @@ -5,7 +5,7 @@ #include #include #include -#include "controllerdata.hpp" +#include "controllerframe.hpp" #include "rom.hpp" @@ -143,7 +143,7 @@ struct moviefile /** * Input for each (sub)frame. */ - std::vector input; //Input for each frame. + controller_frame_vector input; //Input for each frame. /** * Current RTC second. */ diff --git a/manual.lyx b/manual.lyx index 03a032e3..22289923 100644 --- a/manual.lyx +++ b/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 diff --git a/manual.txt b/manual.txt index eac0e17f..a85df9bb 100644 --- a/manual.txt +++ b/manual.txt @@ -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. + diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 797a9e9b..d7bc8fda 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -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 autofire_pattern; + controller_frame curcontrols; + controller_frame autoheld_controls; + std::vector 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 new_autofire_pattern; + std::vector 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) diff --git a/src/core/controller_gamepad.cpp b/src/core/controller_gamepad.cpp new file mode 100644 index 00000000..ea7417ca --- /dev/null +++ b/src/core/controller_gamepad.cpp @@ -0,0 +1,82 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_justifier.cpp b/src/core/controller_justifier.cpp new file mode 100644 index 00000000..77429bb9 --- /dev/null +++ b/src/core/controller_justifier.cpp @@ -0,0 +1,99 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_justifiers.cpp b/src/core/controller_justifiers.cpp new file mode 100644 index 00000000..c591a85c --- /dev/null +++ b/src/core/controller_justifiers.cpp @@ -0,0 +1,113 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_mouse.cpp b/src/core/controller_mouse.cpp new file mode 100644 index 00000000..ef48d20c --- /dev/null +++ b/src/core/controller_mouse.cpp @@ -0,0 +1,99 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_multitap.cpp b/src/core/controller_multitap.cpp new file mode 100644 index 00000000..c1e466b4 --- /dev/null +++ b/src/core/controller_multitap.cpp @@ -0,0 +1,96 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_none.cpp b/src/core/controller_none.cpp new file mode 100644 index 00000000..51a05410 --- /dev/null +++ b/src/core/controller_none.cpp @@ -0,0 +1,56 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controller_superscope.cpp b/src/core/controller_superscope.cpp new file mode 100644 index 00000000..5245f836 --- /dev/null +++ b/src/core/controller_superscope.cpp @@ -0,0 +1,109 @@ +#include "lsnes.hpp" +#include +#include + +#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; +} diff --git a/src/core/controllerdata.cpp b/src/core/controllerdata.cpp deleted file mode 100644 index 3366c4b8..00000000 --- a/src/core/controllerdata.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include "lsnes.hpp" -#include -#include - -#include "core/controllerdata.hpp" - -#include -#include -#include -#include - -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(ch = str[pos]))) { - numval = numval * 10 + (ch - '0'); - pos++; - } - if(negative) - numval = -numval; - - return static_cast(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(controls[2]), - static_cast(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(controls[ccindex(port, 0, 0)]), - static_cast(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(controls[ccindex(port, 0, 0)]), - static_cast(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(controls[ccindex(port, 0, 0)]), - static_cast(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(controls[ccindex(port, 0, 0)]), - static_cast(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(controls[ccindex(port, 0, 0)]), - static_cast(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& 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& 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 } -}; diff --git a/src/core/controllerframe.cpp b/src/core/controllerframe.cpp new file mode 100644 index 00000000..ecf2414e --- /dev/null +++ b/src/core/controllerframe.cpp @@ -0,0 +1,449 @@ +#include "core/controllerframe.hpp" + +#define SYSTEM_BYTES 5 + +namespace +{ + std::set& porttypes() + { + static std::set 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& 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& 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& 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(ch = buf[idx]))) { + numval = numval * 10 + (ch - '0'); + idx++; + } + if(negative) + numval = -numval; + + return static_cast(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]; +} diff --git a/src/core/lua.cpp b/src/core/lua.cpp index 60eb2b02..bd4f3212 100644 --- a/src/core/lua.cpp +++ b/src/core/lua.cpp @@ -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; diff --git a/src/core/mainloop.cpp b/src/core/mainloop.cpp index eda6c961..68b6a4bc 100644 --- a/src/core/mainloop.cpp +++ b/src/core/mainloop.cpp @@ -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); } } diff --git a/src/core/misc.cpp b/src/core/misc.cpp index 3304dae7..aab6fead 100644 --- a/src/core/misc.cpp +++ b/src/core/misc.cpp @@ -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"; diff --git a/src/core/movie.cpp b/src/core/movie.cpp index bcf7c86c..c16d6e16 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -9,13 +9,12 @@ #include #include -#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& old_movie, const std::vector& 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 old_delay = std::make_pair(0, 0); + std::pair 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) -{ - if(controlindex >= TOTAL_CONTROLS) - throw std::logic_error("movie::get_DRDY: Bad index"); - return ((pollcounters[controlindex] & 0x80000000UL) != 0); -} - -bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error) +bool movie::get_DRDY(unsigned pid, unsigned ctrl) throw(std::logic_error) { - return get_DRDY(ccindex2(port, controller, index)); + return pollcounters.get_DRDY(pid, ctrl); } -short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error) +short movie::next_input(unsigned pid, unsigned ctrl) 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& 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 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& 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& pcounters, bool ro, - std::vector* 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(); diff --git a/src/core/moviefile.cpp b/src/core/moviefile.cpp index a31a57ac..fb2f1fc1 100644 --- a/src/core/moviefile.cpp +++ b/src/core/moviefile.cpp @@ -207,16 +207,16 @@ void write_authors_file(zip_writer& w, std::vector& 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 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& input, porttype_t port1 } } -void read_input(zip_reader& r, std::vector& 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 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(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 diff --git a/src/lua/input.cpp b/src/lua/input.cpp index fa3a3e10..9f58385e 100644 --- a/src/lua/input.cpp +++ b/src/lua/input.cpp @@ -8,9 +8,9 @@ namespace unsigned controller = get_numeric_argument(LS, 1, fname.c_str()); unsigned index = get_numeric_argument(LS, 2, fname.c_str()); short value = get_numeric_argument(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(LS, 1, fname.c_str()); unsigned index = get_numeric_argument(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; }); -} \ No newline at end of file +} diff --git a/src/lua/movie.cpp b/src/lua/movie.cpp index 0e2666dd..873ef6f6 100644 --- a/src/lua/movie.cpp +++ b/src/lua/movie.cpp @@ -39,11 +39,25 @@ namespace uint64_t frame = get_numeric_argument(LS, 1, "movie.frame_subframes"); uint64_t subframe = get_numeric_argument(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; diff --git a/src/plat-sdl/main.cpp b/src/plat-sdl/main.cpp index 6c0f6af3..3eadb158 100644 --- a/src/plat-sdl/main.cpp +++ b/src/plat-sdl/main.cpp @@ -47,9 +47,9 @@ struct moviefile generate_movie_template(std::vector 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 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; } diff --git a/src/plat-wxwidgets/mainwindow.cpp b/src/plat-wxwidgets/mainwindow.cpp index 23f03b60..fb6369b9 100644 --- a/src/plat-wxwidgets/mainwindow.cpp +++ b/src/plat-wxwidgets/mainwindow.cpp @@ -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" diff --git a/src/plat-wxwidgets/romselect.cpp b/src/plat-wxwidgets/romselect.cpp index fb30f610..88f5d7a0 100644 --- a/src/plat-wxwidgets/romselect.cpp +++ b/src/plat-wxwidgets/romselect.cpp @@ -872,6 +872,7 @@ struct moviefile wxwin_project::make_movie() f.movie_rtc_subsecond = f.rtc_subsecond = boost::lexical_cast(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; } -- 2.11.4.GIT