Pack movie data in memory

Instead of using full 2*100 bytes for each subframe of movie data, pack
it in controller-dependent way, reducing the memory usage to 7-20 bytes
per subframe (90-96% reduction).
This commit is contained in:
Ilari Liusvaara 2012-01-09 21:55:55 +02:00
parent 95122ae109
commit ec20694f7b
29 changed files with 2289 additions and 1100 deletions

View file

@ -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.

View file

@ -1,427 +0,0 @@
#ifndef _controllerdata__hpp__included__
#define _controllerdata__hpp__included__
#include <vector>
#include <stdexcept>
#define ENCODE_SPECIAL_NO_OUTPUT 0xFFFFFFFFU
/**
* What version to write as control version?
*/
#define WRITE_CONTROL_VERSION 0
/**
* System control: Frame sync flag
*/
#define CONTROL_FRAME_SYNC 0
/**
* System control: System reset button
*/
#define CONTROL_SYSTEM_RESET 1
/**
* High part of cycle count for system reset (multiplier 10000).
*/
#define CONTROL_SYSTEM_RESET_CYCLES_HI 2
/**
* Low part of cycle count for system reset (multiplier 1).
*/
#define CONTROL_SYSTEM_RESET_CYCLES_LO 3
/**
* Number of system controls.
*/
#define MAX_SYSTEM_CONTROLS 4
/**
* SNES has 2 controller ports.
*/
#define MAX_PORTS 2
/**
* Multitap can connect 4 controllers to a single port.
*/
#define MAX_CONTROLLERS_PER_PORT 4
/**
* Ordinary gamepad has 12 buttons/axis total (more than anything else supported).
*/
#define CONTROLLER_CONTROLS 12
/**
* The total number of controls (currently 100).
*/
#define TOTAL_CONTROLS (MAX_SYSTEM_CONTROLS + MAX_PORTS * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT)
struct controls_t;
/**
* Decoders
*/
class cdecode
{
public:
/**
* This is type of functions that perform decoding of port fields.
*
* parameter port: The number of port to decode.
* parameter line: The line to decode from.
* parameter pos: Position on the line to start from.
* parameter controls: Buffer to place the read controls to.
* returns: End of fields (end of string or on '|') or start of next field (otherwise).
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Bad input.
*/
typedef size_t (*fn_t)(unsigned port, const std::string& line, size_t pos, short* controls);
/**
* This is a decoder for the system field. Note that this is not compatible with fn_t as parameters are diffrent.
*
* parameter port: The number of port to decode.
* parameter line: The line to decode from.
* parameter pos: Position on the line to start from.
* parameter controls: Buffer to place the read controls to.
* parameter version: The version of control structure to read.
* returns: End of fields or start of next field.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Bad input.
*/
static size_t system(const std::string& line, size_t pos, short* controls, unsigned version)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type none (see fn_t).
*/
static size_t none(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type gamepad (see fn_t).
*/
static size_t gamepad(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type multitap (see fn_t).
*/
static size_t multitap(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type mouse (see fn_t).
*/
static size_t mouse(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type superscope (see fn_t).
*/
static size_t superscope(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type justifier (see fn_t).
*/
static size_t justifier(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
/**
* This is a port decoder for port type justifiers (see fn_t).
*/
static size_t justifiers(unsigned port, const std::string& line, size_t pos, short* controls)
throw(std::bad_alloc, std::runtime_error);
};
/**
* Encoders
*/
class cencode
{
public:
/**
* This is the type of functions that perform encoding of port fields.
*
* parameter port: To number of port to encode.
* parameter buffer: Buffer to store the encoded data to.
* parameter pos: Position to start writing to.
* parameter controls: Buffer to read the controls from.
* returns: Position after written data, or ENCODE_SPECIAL_NO_OUTPUT if one wants no output, suppressing even the
* field terminator.
* throws std::bad_alloc: Not enough memory.
*/
typedef size_t (*fn_t)(unsigned port, char* buffer, size_t pos, const short* controls);
/**
* This is encoder for the system field. Note that the parameters are bit diffrent and this can't be put into fn_t.
*
* parameter buffer: Buffer to store the encoded data to.
* parameter pos: Position to start writing to.
* parameter controls: Buffer to read the controls from.
* returns: Position after written data, or ENCODE_SPECIAL_NO_OUTPUT if one wants no output, suppressing even the
* field terminator.
* throws std::bad_alloc: Not enough memory.
*/
static size_t system(char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type none. See fn_t.
*/
static size_t none(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type gamepad. See fn_t.
*/
static size_t gamepad(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type multitap. See fn_t.
*/
static size_t multitap(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type mouse. See fn_t.
*/
static size_t mouse(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type superscope. See fn_t.
*/
static size_t superscope(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type justifier. See fn_t.
*/
static size_t justifier(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
/**
* This is a port encoder for port type justifiers. See fn_t.
*/
static size_t justifiers(unsigned port, char* buffer, size_t pos, const short* controls) throw(std::bad_alloc);
};
/**
* This structure holds controls for single (sub)frame or instant of time.
*/
struct controls_t
{
/**
* Creates new controls structure. All buttons are released and all axes are 0 (neutral).
*
* parameter sync: If true, write 1 (pressed) to frame sync subfield, else write 0 (released). Default false.
*/
explicit controls_t(bool sync = false) throw();
/**
* This constructor takes in a line of input, port decoders and system field version and decodes the controls.
*
* parameter line: The line to decode.
* parameter decoders: The decoders for each port.
* parameter version: Version for the system field.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid input line.
*/
controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
throw(std::bad_alloc, std::runtime_error);
/**
* This method takes in port encoders and encodes the controls.
*
* parameter encoders: The encoders for each port.
* returns: The encoded controls.
* throws std::bad_alloc: Not enough memory.
*/
std::string tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc);
/**
* This method takes in controller (port, controller, control) tuple and returns reference to the value of that
* control.
*
* parameter port: The port number
* parameter controller: The controller number within that port.
* parameter control: The control number within that controller.
* returns: Reference to control value.
* throws std::logic_error: port, controller or control is invalid.
*/
const short& operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error);
/**
* This method takes in control index and returns reference to the value of that control.
*
* parameter control: The control index number.
* returns: Reference to control value.
* throws std::logic_error: control index is invalid.
*/
const short& operator()(unsigned control) const throw(std::logic_error);
/**
* This method takes in controller (port, controller, control) tuple and returns reference to the value of that
* control.
*
* parameter port: The port number
* parameter controller: The controller number within that port.
* parameter control: The control number within that controller.
* returns: Reference to control value.
* throws std::logic_error: port, controller or control is invalid.
*/
short& operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error);
/**
* This method takes in control index and returns reference to the value of that control.
*
* parameter control: The control index number.
* returns: Reference to control value.
* throws std::logic_error: control index is invalid.
*/
short& operator()(unsigned control) throw(std::logic_error);
/**
* Perform XOR per-control.
*
* parameter other: The othe field to XOR with.
* returns: The XOR result.
*/
controls_t operator^(controls_t other) throw();
/**
* This field contains the raw controller data. Avoid manipulating directly.
*/
short controls[TOTAL_CONTROLS];
/**
* Equality
*/
bool operator==(const controls_t& c) const throw();
};
/**
* This enumeration gives the type of port.
*/
enum porttype_t
{
/**
* No device
*/
PT_NONE = 0, //Nothing connected to port.
/**
* Gamepad
*/
PT_GAMEPAD = 1,
/**
* Multitap (with 4 gamepads connected)
*/
PT_MULTITAP = 2,
/**
* Mouse
*/
PT_MOUSE = 3,
/**
* Superscope (only allowed for port 2).
*/
PT_SUPERSCOPE = 4,
/**
* Justifier (only allowed for port 2).
*/
PT_JUSTIFIER = 5,
/**
* 2 Justifiers (only allowed for port 2).
*/
PT_JUSTIFIERS = 6,
/**
* Number of controller types.
*/
PT_LAST_CTYPE = 6,
/**
* Invalid controller type.
*/
PT_INVALID = PT_LAST_CTYPE + 1
};
/**
* This enumeration gives the type of device.
*/
enum devicetype_t
{
/**
* No device
*/
DT_NONE = 0,
/**
* Gamepad (note that multitap controllers are gamepads)
*/
DT_GAMEPAD = 1,
/**
* Mouse
*/
DT_MOUSE = 3,
/**
* Superscope
*/
DT_SUPERSCOPE = 4,
/**
* Justifier (note that justifiers is two of these).
*/
DT_JUSTIFIER = 5
};
/**
* Information about port type.
*/
struct port_type
{
/**
* Name of type.
*/
const char* name;
/**
* Decoder function.
*/
cdecode::fn_t decoder;
/**
* Encoder function.
*/
cencode::fn_t encoder;
/**
* Port type value.
*/
porttype_t ptype;
/**
* Number of devices.
*/
unsigned devices;
/**
* Type of each connected device.
*/
devicetype_t dtype;
/**
* True if valid for port1&2, false if valid for only for port 2.
*/
bool valid_port1;
/**
* BSNES controller type ID.
*/
unsigned bsnes_type;
/**
* Lookup port type by name.
*
* parameter name: Name of the port type to look up.
* parameter port2: True if controller is for port 2, false if for port 1.
* returns: The port type structure
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid port type.
*/
static const port_type& lookup(const std::string& name, bool port2 = true) throw(std::bad_alloc,
std::runtime_error);
};
/**
* Information about port types, index by port type value (porttype_t).
*/
extern port_type port_types[];
/**
* This method takes in controller (port, controller, control) tuple and returns the system index corresponding to
* that control.
*
* parameter port: The port number
* parameter controller: The controller number within that port.
* parameter control: The control number within that controller.
* returns: The control index.
* throws std::logic_error: port, controller or control is invalid.
*/
unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error);
#endif

View file

@ -0,0 +1,935 @@
#ifndef _controllerframe__hpp__included__
#define _controllerframe__hpp__included__
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include <set>
/**
* For now, reserve 20 bytes, for:
*
* - 5 bytes for system.
* - 6 bytes for port 1 (multitap).
* - 9 bytes for port 2 (justifiers).
*/
#define MAXIMUM_CONTROLLER_FRAME_SIZE 20
/**
* Maximum amount of data controller_frame::display() can write.
*/
#define MAX_DISPLAY_LENGTH 128
/**
* Maximum amount of data controller_frame::serialize() can write.
*/
#define MAX_SERIALIZED_SIZE 256
/**
* Maximum number of ports.
*/
#define MAX_PORTS 2
/**
* Maximum number of controllers per one port.
*/
#define MAX_CONTROLLERS_PER_PORT 4
/**
* Maximum numbers of controls per one controller.
*/
#define MAX_CONTROLS_PER_CONTROLLER 12
/**
* Number of button controls.
*/
#define MAX_BUTTONS MAX_PORTS * MAX_CONTROLLERS_PER_PORT * MAX_CONTROLS_PER_CONTROLLER
/**
* Size of controller page.
*/
#define CONTROLLER_PAGE_SIZE 65500
/**
* Special return value for deserialize() indicating no input was taken.
*/
#define DESERIALIZE_SPECIAL_BLANK 0xFFFFFFFFUL
/**
* This enumeration gives the type of port.
*/
enum porttype_t
{
/**
* No device
*/
PT_NONE = 0, //Nothing connected to port.
/**
* Gamepad
*/
PT_GAMEPAD = 1,
/**
* Multitap (with 4 gamepads connected)
*/
PT_MULTITAP = 2,
/**
* Mouse
*/
PT_MOUSE = 3,
/**
* Superscope (only allowed for port 2).
*/
PT_SUPERSCOPE = 4,
/**
* Justifier (only allowed for port 2).
*/
PT_JUSTIFIER = 5,
/**
* 2 Justifiers (only allowed for port 2).
*/
PT_JUSTIFIERS = 6,
/**
* Number of controller types.
*/
PT_LAST_CTYPE = 6,
/**
* Invalid controller type.
*/
PT_INVALID = PT_LAST_CTYPE + 1
};
/**
* This enumeration gives the type of device.
*/
enum devicetype_t
{
/**
* No device
*/
DT_NONE = 0,
/**
* Gamepad (note that multitap controllers are gamepads)
*/
DT_GAMEPAD = 1,
/**
* Mouse
*/
DT_MOUSE = 3,
/**
* Superscope
*/
DT_SUPERSCOPE = 4,
/**
* Justifier (note that justifiers is two of these).
*/
DT_JUSTIFIER = 5
};
/**
* Is not field terminator.
*
* Parameter ch: The character.
* Returns: True if character is not terminator, false if character is terminator.
*/
inline bool is_nonterminator(char ch) throw()
{
return (ch != '|' && ch != '\r' && ch != '\n' && ch != '\0');
}
/**
* Read button value.
*
* Parameter buf: Buffer to read from.
* Parameter idx: Index to buffer. Updated.
* Returns: The read value.
*/
inline bool read_button_value(const char* buf, size_t& idx) throw()
{
char ch = buf[idx];
if(is_nonterminator(ch))
idx++;
return (ch != '|' && ch != '\r' && ch != '\n' && ch != '\0' && ch != '.' && ch != ' ' && ch != '\t');
}
/**
* Read axis value.
*
* Parameter buf: Buffer to read from.
* Parameter idx: Index to buffer. Updated.
* Returns: The read value.
*/
short read_axis_value(const char* buf, size_t& idx) throw();
/**
* Skip whitespace.
*
* Parameter buf: Buffer to read from.
* Parameter idx: Index to buffer. Updated.
*/
inline void skip_field_whitespace(const char* buf, size_t& idx) throw()
{
while(buf[idx] == ' ' || buf[idx] == '\t')
idx++;
}
/**
* Skip rest of the field.
*
* Parameter buf: Buffer to read from.
* Parameter idx: Index to buffer. Updated.
* Parameter include_pipe: If true, also skip the '|'.
*/
inline void skip_rest_of_field(const char* buf, size_t& idx, bool include_pipe) throw()
{
while(is_nonterminator(buf[idx]))
idx++;
if(include_pipe && buf[idx] == '|')
idx++;
}
/**
* Serialize short.
*/
inline void serialize_short(unsigned char* buf, short val)
{
buf[0] = static_cast<unsigned short>(val) >> 8;
buf[1] = static_cast<unsigned short>(val);
}
/**
* Serialize short.
*/
inline short unserialize_short(const unsigned char* buf)
{
return static_cast<short>((static_cast<unsigned short>(buf[0]) << 8) | static_cast<unsigned short>(buf[1]));
}
/**
* Information about port type.
*/
struct porttype_info
{
/**
* Look up information about port type.
*
* Parameter p: The port type.
* Returns: Infor about port type.
* Throws std::runtime_error: Invalid port type.
*/
static const porttype_info& lookup(porttype_t p) throw(std::runtime_error);
/**
* Look up information about port type.
*
* Parameter p: The port type string.
* Returns: Infor about port type.
* Throws std::runtime_error: Invalid port type.
*/
static const porttype_info& lookup(const std::string& p) throw(std::runtime_error);
/**
* Register port type.
*
* Parameter ptype: Type value for port type.
* Parameter pname: The name of port type.
* Parameter psize: The size of storage for this type.
* Throws std::bad_alloc: Not enough memory.
*/
porttype_info(porttype_t ptype, const std::string& pname, size_t psize) throw(std::bad_alloc);
/**
* Unregister port type.
*/
~porttype_info() throw();
/**
* Writes controller data into compressed representation.
*
* Parameter buffer: The buffer storing compressed representation of controller state.
* Parameter idx: Index of controller.
* Parameter ctrl: The control to manipulate.
* Parameter x: New value for control. Only zero/nonzero matters for buttons.
*/
virtual void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw() = 0;
/**
* Read controller data from compressed representation.
*
* Parameter buffer: The buffer storing compressed representation of controller state.
* Parameter idx: Index of controller.
* Parameter ctrl: The control to query.
* Returns: The value of control. Buttons return 0 or 1.
*/
virtual short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw() = 0;
/**
* Format compressed controller data into input display.
*
* Parameter buffer: The buffer storing compressed representation of controller state.
* Parameter idx: Index of controller.
* Parameter buf: The buffer to write NUL-terminated display string to. Assumed to be MAX_DISPLAY_LENGTH bytes in size.
*/
virtual void display(const unsigned char* buffer, unsigned idx, char* buf) const throw() = 0;
/**
* Take compressed controller data and serialize it into textual representation.
*
* - The initial '|' is also written.
*
* Parameter buffer: The buffer storing compressed representation of controller state.
* Parameter textbuf: The text buffer to write to.
* Returns: Number of bytes written.
*/
virtual size_t serialize(const unsigned char* buffer, char* textbuf) const throw() = 0;
/**
* Unserialize textual representation into compressed controller state.
*
* - Only stops reading on '|', NUL, CR or LF in the final read field. That byte is not read.
*
* Parameter buffer: The buffer storing compressed representation of controller state.
* Parameter textbuf: The text buffer to read.
* Returns: Number of bytes read.
* Throws std::runtime_error: Bad serialization.
*/
virtual size_t deserialize(unsigned char* buffer, const char* textbuf) const throw() = 0;
/**
* Return device type for given index.
*
* Parameter idx: The index of controller.
* Returns: The type of device.
*/
virtual devicetype_t devicetype(unsigned idx) const throw() = 0;
/**
* Number of controllers connected to this port.
*/
virtual unsigned controllers() const throw() = 0;
/**
* Internal type value for port.
*/
virtual unsigned internal_type() const throw() = 0;
/**
* Return if type is legal for port.
*
* Parameter port: Number of port.
* Returns: True if legal, false if not.
*/
virtual bool legal(unsigned port) const throw() = 0;
/**
* Port type value.
*/
porttype_t value;
/**
* Number of bytes it takes to store this.
*/
size_t storage_size;
/**
* Name of port type.
*/
std::string name;
private:
porttype_info(const porttype_info&);
porttype_info& operator=(const porttype_info&);
};
/**
* Poll counter vector.
*/
class pollcounter_vector
{
public:
/**
* Create new pollcounter vector filled with all zeroes and all DRDY bits clear.
*/
pollcounter_vector() throw();
/**
* Zero all poll counters and clear all DRDY bits. System flag is cleared.
*/
void clear() throw();
/**
* Set all DRDY bits.
*/
void set_all_DRDY() throw();
/**
* Clear specified DRDY bit.
*
* Parameter pid: The physical controller id.
* Parameter ctrl: The control id.
*/
void clear_DRDY(unsigned pid, unsigned ctrl) throw();
/**
* Get state of DRDY bit.
*
* Parameter pid: The physical controller id.
* Parameter ctrl: The control id.
* Returns: The DRDY state.
*/
bool get_DRDY(unsigned pid, unsigned ctrl) throw();
/**
* Get state of DRDY bit.
*
* Parameter idx: The control index.
* Returns: The DRDY state.
*/
bool get_DRDY(unsigned idx) throw()
{
return get_DRDY(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/**
* Is any poll count nonzero or is system flag set?
*
* Returns: True if at least one poll count is nonzero or if system flag is set. False otherwise.
*/
bool has_polled() throw();
/**
* Read the actual poll count on specified control.
*
* Parameter pid: The physical controller id.
* Parameter ctrl: The control id.
* Return: The poll count.
*/
uint32_t get_polls(unsigned pid, unsigned ctrl) throw();
/**
* Read the actual poll count on specified control.
*
* Parameter idx: The control index.
* Return: The poll count.
*/
uint32_t get_polls(unsigned idx) throw()
{
return get_polls(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/**
* Increment poll count on specified control.
*
* Parameter pid: The physical controller id.
* Parameter ctrl: The control id.
* Return: The poll count pre-increment.
*/
uint32_t increment_polls(unsigned pid, unsigned ctrl) throw();
/**
* Set the system flag.
*/
void set_system() throw();
/**
* Get the system flag.
*
* Returns: The state of system flag.
*/
bool get_system() throw();
/**
* Get highest poll counter value.
*
* - System flag counts as 1 poll.
*
* Returns: The maximum poll count (at least 1 if system flag is set).
*/
uint32_t max_polls() throw();
/**
* Save state to memory block.
*
* Parameter mem: The memory block to save to.
* Throws std::bad_alloc: Not enough memory.
*/
void save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc);
/**
* Load state from memory block.
*
* Parameter mem: The block from restore from.
*/
void load_state(const std::vector<uint32_t>& mem) throw();
/**
* Check if state can be loaded without errors.
*
* Returns: True if load is possible, false otherwise.
*/
bool check(const std::vector<uint32_t>& mem) throw();
private:
uint32_t ctrs[MAX_BUTTONS];
bool system_flag;
};
/**
* Single (sub)frame of controls.
*/
class controller_frame
{
public:
/**
* Default constructor. Invalid port types, dedicated memory.
*/
controller_frame() throw();
/**
* Create subframe of controls with specified controller types and dedicated memory.
*
* Parameter p1: Type of port1.
* Parameter p2: Type of port2.
*
* Throws std::runtime_error: Invalid port type.
*/
controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error);
/**
* Create subframe of controls with specified controller types and specified memory.
*
* Parameter memory: The backing memory.
* Parameter p1: Type of port1.
* Parameter p2: Type of port2.
*
* Throws std::runtime_error: Invalid port type or NULL memory.
*/
controller_frame(unsigned char* memory, porttype_t p1 = PT_GAMEPAD, porttype_t p2 = PT_NONE)
throw(std::runtime_error);
/**
* Copy construct a frame. The memory will be dedicated.
*
* Parameter obj: The object to copy.
*/
controller_frame(const controller_frame& obj) throw();
/**
* Assign a frame. The types must either match or memory must be dedicated.
*
* Parameter obj: The object to copy.
* Returns: Reference to this.
* Throws std::runtime_error: The types don't match and memory is not dedicated.
*/
controller_frame& operator=(const controller_frame& obj) throw(std::runtime_error);
/**
* Get type of port.
*
* Parameter port: Number of port.
* Returns: The type of port.
*/
porttype_t get_port_type(unsigned port) throw()
{
return (port < MAX_PORTS) ? types[port] : PT_NONE;
}
/**
* Get blank dedicated frame of same port types.
*
* Return blank frame.
*/
controller_frame blank_frame() throw()
{
return controller_frame(types[0], types[1]);
}
/**
* Set type of port. Input for that port is zeroized.
*
* Parameter port: Number of port.
* Parameter type: The new type.
* Throws std::runtime_error: Bad port type or non-dedicated memory.
*/
void set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error);
/**
* Check that types match.
*
* Parameter obj: Another object.
* Returns: True if types match, false otherwise.
*/
bool types_match(const controller_frame& obj) const throw()
{
for(size_t i = 0; i < MAX_PORTS; i++)
if(types[i] != obj.types[i])
return false;
return true;
}
/**
* Perform XOR between controller frames.
*
* Parameter another: The another object.
* Returns: The XOR result (dedicated memory).
* Throws std::runtime_error: Type mismatch.
*/
controller_frame operator^(const controller_frame& another) throw(std::runtime_error)
{
controller_frame x(*this);
for(size_t i = 0; i < MAX_PORTS; i++)
if(types[i] != another.types[i])
throw std::runtime_error("controller_frame::operator^: Type mismatch");
for(size_t i = 0; i < totalsize; i++)
x.backing[i] ^= another.backing[i];
return x;
}
/**
* Set the sync flag.
*
* Parameter x: The value to set the sync flag to.
*/
void sync(bool x) throw()
{
if(x)
backing[0] |= 1;
else
backing[0] &= ~1;
}
/**
* Get the sync flag.
*
* Return value: Value of sync flag.
*/
bool sync() throw()
{
return ((backing[0] & 1) != 0);
}
/**
* Quick get sync flag for buffer.
*/
static bool sync(const unsigned char* mem) throw()
{
return ((mem[0] & 1) != 0);
}
/**
* Set the reset flag.
*
* Parameter x: The value to set the reset flag to.
*/
void reset(bool x) throw()
{
if(x)
backing[0] |= 2;
else
backing[0] &= ~2;
}
/**
* Get the reset flag.
*
* Return value: Value of resset flag.
*/
bool reset() throw()
{
return ((backing[0] & 2) != 0);
}
/**
* Set the reset delay.
*
* Parameter x: The value to set reset delay to.
*/
void delay(std::pair<short, short> x) throw()
{
backing[1] = static_cast<unsigned short>(x.first) >> 8;
backing[2] = static_cast<unsigned short>(x.first);
backing[3] = static_cast<unsigned short>(x.second) >> 8;
backing[4] = static_cast<unsigned short>(x.second);
}
/**
* Get the reset delay.
*
* Return value: Value of reset delay.
*/
std::pair<short, short> delay() throw()
{
short x, y;
x = static_cast<short>(static_cast<unsigned short>(backing[1]) << 8);
x |= static_cast<short>(static_cast<unsigned short>(backing[2]));
y = static_cast<short>(static_cast<unsigned short>(backing[3]) << 8);
y |= static_cast<short>(static_cast<unsigned short>(backing[4]));
return std::make_pair(x, y);
}
/**
* Get size of frame.
*
* Returns: The number of bytes it takes to store frame of this type.
*/
size_t size()
{
return totalsize;
}
/**
* Set axis/button value.
*
* Parameter pid: Physical controller id.
* Parameter ctrl: The control id.
* Parameter x: The new value.
*/
void axis(unsigned pid, unsigned ctrl, short x) throw()
{
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
pinfo[port]->write(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, ctrl, x);
}
/**
* Set axis/button value.
*
* Parameter idx: Control index.
* Parameter x: The new value.
*/
void axis2(unsigned idx, short x) throw()
{
axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER, x);
}
/**
* Get axis/button value.
*
* Parameter pid: Physical controller id.
* Parameter ctrl: The control id.
* Return value: The axis value.
*/
short axis(unsigned pid, unsigned ctrl) throw()
{
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
return pinfo[port]->read(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, ctrl);
}
/**
* Get axis/button value.
*
* Parameter idx: Index of control.
* Return value: The axis value.
*/
short axis2(unsigned idx) throw()
{
return axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/**
* Get controller display.
*
* Parameter pid: Physical controller id.
* Parameter buf: Buffer to write nul-terminated display to.
*/
void display(unsigned pid, char* buf) throw()
{
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
return pinfo[port]->display(backing + offsets[port], pid % MAX_CONTROLLERS_PER_PORT, buf);
}
/**
* Get device type.
*
* Parameter pid: Physical controller id.
* Returns: Device type.
*/
devicetype_t devicetype(unsigned pid) throw()
{
unsigned port = (pid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS;
return pinfo[port]->devicetype(pid % MAX_CONTROLLERS_PER_PORT);
}
/**
* Deserialize frame from text format.
*
* Parameter buf: The buffer containing text representation. Terminated by NUL, CR or LF.
* Throws std::runtime_error: Bad serialized representation.
*/
void deserialize(const char* buf) throw(std::runtime_error)
{
size_t offset = 0;
offset += system_deserialize(backing, buf);
if(buf[offset] == '|')
offset++;
for(size_t i = 0; i < MAX_PORTS; i++) {
size_t s = pinfo[i]->deserialize(backing + offsets[i], buf + offset);
if(s != DESERIALIZE_SPECIAL_BLANK) {
offset += s;
if(buf[offset] == '|')
offset++;
}
}
}
/**
* Serialize frame to text format.
*
* Parameter buf: The buffer to write NUL-terminated text representation to.
*/
void serialize(char* buf) throw()
{
size_t offset = 0;
offset += system_serialize(backing, buf);
for(size_t i = 0; i < MAX_PORTS; i++) {
offset += pinfo[i]->serialize(backing + offsets[i], buf + offset);
buf[offset++] = (i < MAX_PORTS - 1) ? '|' : '\0';
}
}
/**
* Return copy with dedicated memory.
*
* Parameter sync: If set, the frame will have sync flag set, otherwise it will have sync flag clear.
* Returns: Copy of this frame.
*/
controller_frame copy(bool sync)
{
controller_frame c(*this);
c.sync(sync);
return c;
}
/**
* Compare two frames.
*
* Parameter obj: Another frame.
* Returns: True if equal, false if not.
*/
bool operator==(const controller_frame& obj) const throw()
{
if(!types_match(obj))
return false;
return !memcmp(backing, obj.backing, totalsize);
}
/**
* Compare two frames.
*
* Parameter obj: Another frame.
* Returns: True if not equal, false if equal.
*/
bool operator!=(const controller_frame& obj) const throw()
{
return !(*this == obj);
}
private:
size_t totalsize;
unsigned char memory[MAXIMUM_CONTROLLER_FRAME_SIZE];
unsigned char* backing;
porttype_t types[MAX_PORTS];
size_t offsets[MAX_PORTS];
const porttype_info* pinfo[MAX_PORTS];
static size_t system_serialize(const unsigned char* buffer, char* textbuf);
static size_t system_deserialize(unsigned char* buffer, const char* textbuf);
void set_types(const porttype_t* tarr);
};
/**
* Vector of controller frames.
*/
class controller_frame_vector
{
public:
/**
* Construct new controller frame vector.
*
* Parameter p1: Type of port 1.
* Parameter p2: Type of port 2.
* Throws std::runtime_error: Illegal port types.
*/
controller_frame_vector(enum porttype_t p1 = PT_GAMEPAD, enum porttype_t p2 = PT_NONE)
throw(std::runtime_error);
/**
* Destroy controller frame vector
*/
~controller_frame_vector() throw();
/**
* Copy controller frame vector.
*
* Parameter obj: The object to copy.
* Throws std::bad_alloc: Not enough memory.
*/
controller_frame_vector(const controller_frame_vector& vector) throw(std::bad_alloc);
/**
* Assign controller frame vector.
*
* Parameter obj: The object to copy.
* Returns: Reference to this.
* Throws std::bad_alloc: Not enough memory.
*/
controller_frame_vector& operator=(const controller_frame_vector& vector) throw(std::bad_alloc);
/**
* Blank vector and change the type of ports.
*
* Parameter p1: Type of port 1.
* Parameter p2: Type of port 2.
* Throws std::runtime_error: Illegal port types.
*/
void clear(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error);
/**
* Blank vector.
*/
void clear() throw()
{
clear(types[0], types[1]);
}
/**
* Get number of subframes.
*/
size_t size()
{
return frames;
}
/**
* Access specified subframe.
*
* Parameter x: The frame number.
* Returns: The controller frame.
* Throws std::runtime_error: Invalid frame index.
*/
controller_frame operator[](size_t x)
{
size_t page = x / frames_per_page;
size_t pageoffset = frame_size * (x % frames_per_page);
if(x >= frames)
throw std::runtime_error("controller_frame_vector::operator[]: Illegal index");
if(page != cache_page_num) {
cache_page = &pages[page];
cache_page_num = page;
}
return controller_frame(cache_page->content + pageoffset, types[0], types[1]);
}
/**
* Append a subframe.
*
* Parameter frame: The frame to append.
* Throws std::bad_alloc: Not enough memory.
* Throws std::runtime_error: Port type mismatch.
*/
void append(controller_frame frame) throw(std::bad_alloc, std::runtime_error);
/**
* Change length of vector.
*
* - Reducing length of vector will discard extra elements.
* - Extending length of vector will add all-zero elements.
*
* Parameter newsize: New size of vector.
* Throws std::bad_alloc: Not enough memory.
*/
void resize(size_t newsize) throw(std::bad_alloc);
/**
* Walk the indexes of sync subframes.
*
* - If frame is in range and there is at least one more sync subframe after it, the index of first sync subframe
* after given frame.
* - If frame is in range, but there are no more sync subframes after it, the length of vector is returned.
* - If frame is out of range, the given frame is returned.
*
* Parameter frame: The frame number to start search from.
* Returns: Index of next sync frame.
*/
size_t walk_sync(size_t frame) throw()
{
return walk_helper(frame, true);
}
/**
* Get number of subframes in frame. The given subframe is assumed to be sync subframe.
*
* - The return value is the same as (walk_sync(frame) - frame).
*
* Parameter frame: The frame number to start search from.
* Returns: Number of subframes in this frame.
*/
size_t subframe_count(size_t frame) throw()
{
return walk_helper(frame, false);
}
/**
* Count number of subframes in vector with sync flag set.
*
* Returns: The number of frames.
*/
size_t count_frames() throw();
/**
* Return blank controller frame with correct type and dedicated memory.
*
* Parameter sync: If set, the frame will have sync flag set, otherwise it will have sync flag clear.
* Returns: Blank frame.
*/
controller_frame blank_frame(bool sync)
{
controller_frame c(types[0], types[1]);
c.sync(sync);
return c;
}
private:
class page
{
public:
page() { memset(content, 0, CONTROLLER_PAGE_SIZE); }
unsigned char content[CONTROLLER_PAGE_SIZE];
};
size_t frames_per_page;
size_t frame_size;
size_t frames;
porttype_t types[MAX_PORTS];
size_t cache_page_num;
page* cache_page;
std::map<size_t, page> pages;
size_t walk_helper(size_t frame, bool sflag) throw();
void clear_cache()
{
cache_page_num = 0;
cache_page_num--;
cache_page = NULL;
}
};
#endif

View file

@ -12,7 +12,7 @@ extern "C"
std::string get_string_argument(lua_State* LS, unsigned argindex, const char* fname);
bool get_boolean_argument(lua_State* LS, unsigned argindex, const char* fname);
extern lua_render_context* lua_render_ctx;
extern controls_t* lua_input_controllerdata;
extern controller_frame* lua_input_controllerdata;
template<typename T>

View file

@ -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();

View file

@ -4,7 +4,7 @@
#include <string>
#include <cstdint>
#include <stdexcept>
#include "controllerdata.hpp"
#include "controllerframe.hpp"
/**
* Movie being played back or recorded
@ -101,66 +101,42 @@ public:
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
* flag is unset.
*
* parameter controlindex: The index of control to read it for.
* parameter pid: Physical controller id.
* parameter index: Control ID.
* returns: The read value.
* throws std::logic_error: Invalid control index.
*/
bool get_DRDY(unsigned controlindex) throw(std::logic_error);
/**
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
* flag is unset.
*
* This differs from get_DRDY(unsigned) in that this takes (port, controller,index) tuple.
*
* parameter port: The port controller is connected to (0 or 1)
* parameter controller: The controller number within port (0 to 3)
* parameter index: The index of control in controller (0 to 11)
* returns: The read value.
* throws std::logic_error: Invalid control index.
*/
bool get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error);
bool get_DRDY(unsigned pid, unsigned index) throw(std::logic_error);
/**
* Set all data ready flags
*/
void set_all_DRDY() throw();
/**
* Poll a control. Note that index 0 (sync flag) always reads as released.
*
* parameter controlindex: The index
* returns: The read value
* throws std::bad_alloc: Not enough memory.
* throws std::logic_error: Invalid control index or before movie start.
*/
short next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error);
/**
* Poll a control by (port, controller, index) tuple.
*
* parameter port: The port controller is connected to (0 or 1)
* parameter controller: The controller number within port (0 to 3)
* parameter pid: Physical controller ID.
* parameter index: The index of control in controller (0 to 11)
* returns: The read value
* throws std::bad_alloc: Not enough memory.
* throws std::logic_error: Invalid port, controller or index or before movie start.
*/
short next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error);
short next_input(unsigned pid, unsigned index) throw(std::bad_alloc, std::logic_error);
/**
* Set current control values. These are read in readwrite mode.
*
* parameter controls: The new controls.
*/
void set_controls(controls_t controls) throw();
void set_controls(controller_frame controls) throw();
/**
* Get current control values in effect.
*
* returns: Controls
*/
controls_t get_controls() throw();
controller_frame get_controls() throw();
/**
* Loads a movie plus some other parameters. The playback pointer is positioned to start of movie and readonly
@ -172,7 +148,7 @@ public:
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Bad movie data.
*/
void load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
void load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
throw(std::bad_alloc, std::runtime_error);
/**
@ -181,7 +157,7 @@ public:
* returns: The movie data.
* throws std::bad_alloc: Not enough memory.
*/
std::vector<controls_t> save() throw(std::bad_alloc);
controller_frame_vector save() throw(std::bad_alloc);
/**
* This method serializes the state of movie code.
@ -209,7 +185,7 @@ public:
* Throws std::runtime_error: Movie check failure.
*/
size_t restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
std::vector<controls_t>* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
std::runtime_error);
/**
@ -248,7 +224,7 @@ public:
* returns: The controls for subframe. If subframe is too great, reads last present subframe. If frame is outside
* movie, reads all released.
*/
controls_t read_subframe(uint64_t frame, uint64_t subframe) throw();
controller_frame read_subframe(uint64_t frame, uint64_t subframe) throw();
private:
//TRUE if readonly mode is active.
bool readonly;
@ -257,15 +233,15 @@ private:
//Project ID.
std::string _project_id;
//The actual controller data.
std::vector<controls_t> movie_data;
controller_frame_vector movie_data;
//Current frame + 1 (0 before next_frame() has been called.
uint64_t current_frame;
//First subframe in current frame (movie_data.size() if no subframes have been stored).
uint64_t current_frame_first_subframe;
//How many times has each control been polled (bit 31 is data ready bit)?
uint32_t pollcounters[TOTAL_CONTROLS];
pollcounter_vector pollcounters;
//Current state of buttons.
controls_t current_controls;
controller_frame current_controls;
//Number of known lag frames.
uint64_t lag_frames;
//Number of frames in movie.
@ -320,7 +296,7 @@ public:
*
* parameter subframe: True if this is for subframe update, false if for frame update.
*/
controls_t update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error);
controller_frame update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error);
private:
movie mov;
};

View file

@ -5,7 +5,7 @@
#include <vector>
#include <stdexcept>
#include <map>
#include "controllerdata.hpp"
#include "controllerframe.hpp"
#include "rom.hpp"
@ -143,7 +143,7 @@ struct moviefile
/**
* Input for each (sub)frame.
*/
std::vector<controls_t> input; //Input for each frame.
controller_frame_vector input; //Input for each frame.
/**
* Current RTC second.
*/

View file

@ -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

View file

@ -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.

View file

@ -36,9 +36,9 @@ namespace
int analog_indices[3] = {-1, -1, -1};
bool analog_is_mouse[3];
//Current controls.
controls_t curcontrols;
controls_t autoheld_controls;
std::vector<controls_t> autofire_pattern;
controller_frame curcontrols;
controller_frame autoheld_controls;
std::vector<controller_frame> autofire_pattern;
void update_analog_indices() throw()
{
@ -86,7 +86,7 @@ namespace
}
//Do button action.
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor, controls_t& c)
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor, controller_frame& c)
{
enum devicetype_t p = controller_type_by_logical(ui_id);
int x = controller_index_by_logical(ui_id);
@ -145,9 +145,9 @@ namespace
break;
};
if(do_xor)
c((x & 4) ? 1 : 0, x & 3, bid) ^= newstate;
c.axis(x, bid, c.axis(x, bid) ^ newstate);
else
c((x & 4) ? 1 : 0, x & 3, bid) = newstate;
c.axis(x, bid, newstate);
}
//Do button action.
@ -169,14 +169,14 @@ namespace
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
if(!t)
throw std::runtime_error("Need at least one frame for autofire");
std::vector<controls_t> new_autofire_pattern;
std::vector<controller_frame> new_autofire_pattern;
init_buttonmap();
while(t) {
std::string fpattern = t;
if(fpattern == "-")
new_autofire_pattern.push_back(controls_t());
new_autofire_pattern.push_back(curcontrols.blank_frame());
else {
controls_t c;
controller_frame c(curcontrols.blank_frame());
while(fpattern != "") {
size_t split = fpattern.find_first_of(",");
std::string button = fpattern;
@ -273,8 +273,8 @@ namespace
int controller_index_by_logical(unsigned lid) throw()
{
unsigned p1devs = port_types[porttypes[0]].devices;
unsigned p2devs = port_types[porttypes[1]].devices;
unsigned p1devs = porttype_info::lookup(porttypes[0]).controllers();
unsigned p2devs = porttype_info::lookup(porttypes[1]).controllers();
if(lid >= p1devs + p2devs)
return -1;
//Exceptional: If p1 is none, map all to p2.
@ -310,23 +310,27 @@ devicetype_t controller_type_by_logical(unsigned lid) throw()
int x = controller_index_by_logical(lid);
if(x < 0)
return DT_NONE;
enum porttype_t rawtype = porttypes[x >> 2];
if((x & 3) < port_types[rawtype].devices)
return port_types[rawtype].dtype;
else
return DT_NONE;
enum porttype_t rawtype = porttypes[x / MAX_CONTROLLERS_PER_PORT];
return porttype_info::lookup(rawtype).devicetype(x % MAX_CONTROLLERS_PER_PORT);
}
void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core) throw()
{
if(set_core && ptype != PT_INVALID)
snes_set_controller_port_device(port != 0, port_types[ptype].bsnes_type);
snes_set_controller_port_device(port != 0, porttype_info::lookup(ptype).internal_type());
porttype_t oldtype = curcontrols.get_port_type(port);
if(oldtype != ptype) {
curcontrols.set_port_type(port, ptype);
autoheld_controls.set_port_type(port, ptype);
//The old autofire pattern no longer applies.
autofire_pattern.clear();
}
porttypes[port] = ptype;
update_analog_indices();
information_dispatch::do_autohold_reconfigure();
}
controls_t get_current_controls(uint64_t frame)
controller_frame get_current_controls(uint64_t frame)
{
if(autofire_pattern.size())
return curcontrols ^ autoheld_controls ^ autofire_pattern[frame % autofire_pattern.size()];
@ -350,29 +354,26 @@ void send_analog_input(int32_t x, int32_t y, unsigned index)
messages << "No analog controller in slot #" << (index + 1) << std::endl;
return;
}
curcontrols(aindex >> 2, aindex & 3, 0) = x;
curcontrols(aindex >> 2, aindex & 3, 1) = y;
curcontrols.axis(aindex, 0, x);
curcontrols.axis(aindex, 1, y);
}
void set_curcontrols_reset(int32_t delay)
{
if(delay >= 0) {
curcontrols(CONTROL_SYSTEM_RESET) = 1;
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
curcontrols.reset(true);
curcontrols.delay(std::make_pair(delay / 10000, delay % 10000));
} else {
curcontrols(CONTROL_SYSTEM_RESET) = 0;
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = 0;
curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = 0;
curcontrols.reset(false);
curcontrols.delay(std::make_pair(0, 0));
}
}
void change_autohold(unsigned pid, unsigned idx, bool newstate)
{
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= MAX_CONTROLS_PER_CONTROLLER)
return;
autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) = (newstate ? 1 : 0);
autoheld_controls.axis(pid, idx, newstate ? 1 : 0);
information_dispatch::do_autohold_update(pid, idx, newstate);
update_movie_state();
information_dispatch::do_status_update();
@ -380,9 +381,9 @@ void change_autohold(unsigned pid, unsigned idx, bool newstate)
bool get_autohold(unsigned pid, unsigned idx)
{
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= MAX_CONTROLS_PER_CONTROLLER)
return false;
return (autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) != 0);
return (autoheld_controls.axis(pid, idx) != 0);
}
std::string get_button_name(unsigned lidx)

View file

@ -0,0 +1,82 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
const char* buttons = "BYsSudlrAXLR";
struct porttype_gamepad : public porttype_info
{
porttype_gamepad() : porttype_info(PT_GAMEPAD, "gamepad", 2) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 12 || idx > 0)
return;
if(x)
buffer[ctrl / 8] |= (1 << (ctrl % 8));
else
buffer[ctrl / 8] &= ~(1 << (ctrl % 8));
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 12 || idx > 0)
return 0;
return ((buffer[ctrl / 8] & (1 << (ctrl % 8))) != 0);
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx > 0) {
buf[0] = '\0';
return;
}
for(unsigned i = 0; i < 12; i++)
buf[i] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i] : ' ';
buf[12] = '\0';
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
textbuf[0] = '|';
for(unsigned i = 0; i < 12; i++)
textbuf[i + 1] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i] : '.';
return 13;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
buffer[1] = 0;
size_t idx = 0;
for(unsigned i = 0; i < 12; i++)
if(read_button_value(textbuf, idx))
buffer[i / 8] |= (1 << (i % 8));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx == 0) ? DT_GAMEPAD : DT_NONE;
}
unsigned controllers() const throw()
{
return 1;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_JOYPAD;
}
bool legal(unsigned port) const throw()
{
return true;
}
} gamepad;
}

View file

@ -0,0 +1,99 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
struct porttype_justifier : public porttype_info
{
porttype_justifier() : porttype_info(PT_JUSTIFIER, "justifier", 5) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 4 || idx > 0)
return;
switch(ctrl) {
case 0:
case 1:
serialize_short(buffer + 2 * ctrl + 1, x);
break;
case 2:
case 3:
if(x)
buffer[0] |= (1 << (ctrl - 2));
else
buffer[0] &= ~(1 << (ctrl - 2));
break;
}
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 4 || idx > 0)
return 0;
switch(ctrl) {
case 0:
case 1:
return unserialize_short(buffer + 2 * ctrl + 1);
case 2:
case 3:
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
}
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx > 0) {
buf[0] = '\0';
return;
}
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
((buffer[0] & 1) ? 'T' : '-'), ((buffer[0] & 2) ? 'S' : '-'));
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
char tmp[128];
sprintf(tmp, "|%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'), ((buffer[0] & 2) ? 'S' : '.'),
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
size_t len = strlen(tmp);
memcpy(textbuf, tmp, len);
return len;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
size_t idx = 0;
if(read_button_value(textbuf, idx))
buffer[0] |= 1;
if(read_button_value(textbuf, idx))
buffer[0] |= 2;
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx == 0) ? DT_JUSTIFIER : DT_NONE;
}
unsigned controllers() const throw()
{
return 1;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_JUSTIFIER;
}
bool legal(unsigned port) const throw()
{
return (port > 0);
}
} justifier;
}

View file

@ -0,0 +1,113 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
struct porttype_justifiers : public porttype_info
{
porttype_justifiers() : porttype_info(PT_JUSTIFIERS, "justifiers", 9) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 4 || idx > 1)
return;
switch(ctrl) {
case 0:
case 1:
serialize_short(buffer + 4 * idx + 2 * ctrl + 1, x);
break;
case 2:
case 3:
if(x)
buffer[0] |= (1 << (2 * idx + ctrl - 2));
else
buffer[0] &= ~(1 << (2 * idx + ctrl - 2));
break;
}
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 4 || idx > 1)
return 0;
switch(ctrl) {
case 0:
case 1:
return unserialize_short(buffer + 4 * idx + 2 * ctrl + 1);
case 2:
case 3:
return ((buffer[0] & (1 << (2 * idx + ctrl - 2))) != 0);
}
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx == 0)
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1),
unserialize_short(buffer + 3), ((buffer[0] & 1) ? 'T' : '-'),
((buffer[0] & 2) ? 'S' : '-'));
else if(idx == 1)
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 5),
unserialize_short(buffer + 7), ((buffer[0] & 4) ? 'T' : '-'),
((buffer[0] & 8) ? 'S' : '-'));
else
buf[0] = '\0';
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
char tmp[128];
sprintf(tmp, "|%c%c %i %i|%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'),
((buffer[0] & 2) ? 'S' : '.'), unserialize_short(buffer + 1),
unserialize_short(buffer + 3), ((buffer[0] & 4) ? 'T' : '.'),
((buffer[0] & 8) ? 'S' : '.'), unserialize_short(buffer + 5),
unserialize_short(buffer + 7));
size_t len = strlen(tmp);
memcpy(textbuf, tmp, len);
return len;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
size_t idx = 0;
if(read_button_value(textbuf, idx))
buffer[0] |= 1;
if(read_button_value(textbuf, idx))
buffer[0] |= 2;
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, true);
if(read_button_value(textbuf, idx))
buffer[0] |= 4;
if(read_button_value(textbuf, idx))
buffer[0] |= 8;
serialize_short(buffer + 5, read_axis_value(textbuf, idx));
serialize_short(buffer + 7, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx < 2) ? DT_JUSTIFIER : DT_NONE;
}
unsigned controllers() const throw()
{
return 2;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_JUSTIFIERS;
}
bool legal(unsigned port) const throw()
{
return (port > 0);
}
} justifiers;
}

View file

@ -0,0 +1,99 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
struct porttype_mouse : public porttype_info
{
porttype_mouse() : porttype_info(PT_MOUSE, "mouse", 5) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 4 || idx > 0)
return;
switch(ctrl) {
case 0:
case 1:
serialize_short(buffer + 2 * ctrl + 1, x);
break;
case 2:
case 3:
if(x)
buffer[0] |= (1 << (ctrl - 2));
else
buffer[0] &= ~(1 << (ctrl - 2));
break;
}
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 4 || idx > 0)
return 0;
switch(ctrl) {
case 0:
case 1:
return unserialize_short(buffer + 2 * ctrl + 1);
case 2:
case 3:
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
}
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx > 0) {
buf[0] = '\0';
return;
}
sprintf(buf, "%i %i %c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
((buffer[0] & 1) ? 'L' : '-'), ((buffer[0] & 2) ? 'R' : '-'));
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
char tmp[128];
sprintf(tmp, "|%c%c %i %i", ((buffer[0] & 1) ? 'L' : '.'), ((buffer[0] & 2) ? 'R' : '.'),
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
size_t len = strlen(tmp);
memcpy(textbuf, tmp, len);
return len;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
size_t idx = 0;
if(read_button_value(textbuf, idx))
buffer[0] |= 1;
if(read_button_value(textbuf, idx))
buffer[0] |= 2;
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx == 0) ? DT_MOUSE : DT_NONE;
}
unsigned controllers() const throw()
{
return 1;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_MOUSE;
}
bool legal(unsigned port) const throw()
{
return true;
}
} mouse;
}

View file

@ -0,0 +1,96 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
const char* buttons = "BYsSudlrAXLR";
struct porttype_multitap : public porttype_info
{
porttype_multitap() : porttype_info(PT_MULTITAP, "multitap", 6) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 12 || idx > 4)
return;
unsigned i = idx * 12 + ctrl;
if(x)
buffer[i / 8] |= (1 << (i % 8));
else
buffer[i / 8] &= ~(1 << (i % 8));
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 12 || idx > 4)
return 0;
unsigned i = idx * 12 + ctrl;
return ((buffer[i / 8] & (1 << (i % 8))) != 0);
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx > 3) {
buf[0] = '\0';
return;
}
unsigned j = idx * 12;
for(unsigned i = 0; i < 12; i++)
buf[i] = (buffer[(i + j) / 8] & (1 << ((i + j) % 8))) ? buttons[i % 12] : ' ';
buf[12] = '\0';
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
unsigned j = 0;
for(unsigned i = 0; i < 48; i++) {
if(i % 12 == 0)
textbuf[j++] = '|';
textbuf[j++] = (buffer[i / 8] & (1 << (i % 8))) ? buttons[i % 12] : '.';
}
return j;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
buffer[4] = 0;
buffer[5] = 0;
const char* orig_texbuf = textbuf;
size_t index = 0;
for(unsigned i = 0; i < 48; i++) {
if(read_button_value(textbuf, index))
buffer[i / 8] |= (1 << (i % 8));
if(i % 12 == 0 && i > 0)
skip_rest_of_field(textbuf, index, true);
}
skip_rest_of_field(textbuf, index, false);
return index;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx < 4) ? DT_GAMEPAD : DT_NONE;
}
unsigned controllers() const throw()
{
return 4;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_MULTITAP;
}
bool legal(unsigned port) const throw()
{
return true;
}
} multitap;
}

View file

@ -0,0 +1,56 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
struct porttype_none : public porttype_info
{
porttype_none() : porttype_info(PT_NONE, "none", 0) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
return 0;
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
buf[0] = '\0';
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
return 0;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
return DESERIALIZE_SPECIAL_BLANK;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return DT_NONE;
}
unsigned controllers() const throw()
{
return 0;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_NONE;
}
bool legal(unsigned port) const throw()
{
return true;
}
} none;
}

View file

@ -0,0 +1,109 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerframe.hpp"
namespace
{
struct porttype_superscope : public porttype_info
{
porttype_superscope() : porttype_info(PT_SUPERSCOPE, "superscope", 5) {}
void write(unsigned char* buffer, unsigned idx, unsigned ctrl, short x) const throw()
{
if(ctrl >= 6 || idx > 0)
return;
switch(ctrl) {
case 0:
case 1:
serialize_short(buffer + 2 * ctrl + 1, x);
break;
case 2:
case 3:
case 4:
case 5:
if(x)
buffer[0] |= (1 << (ctrl - 2));
else
buffer[0] &= ~(1 << (ctrl - 2));
break;
}
}
short read(const unsigned char* buffer, unsigned idx, unsigned ctrl) const throw()
{
if(ctrl >= 6 || idx > 0)
return 0;
switch(ctrl) {
case 0:
case 1:
return unserialize_short(buffer + 2 * ctrl + 1);
case 2:
case 3:
case 4:
case 5:
return ((buffer[0] & (1 << (ctrl - 2))) != 0);
}
}
void display(const unsigned char* buffer, unsigned idx, char* buf) const throw()
{
if(idx > 0) {
buf[0] = '\0';
return;
}
sprintf(buf, "%i %i %c%c%c%c", unserialize_short(buffer + 1), unserialize_short(buffer + 3),
((buffer[0] & 1) ? 'T' : '-'), ((buffer[0] & 2) ? 'C' : '-'),
((buffer[0] & 4) ? 'U' : '-'), ((buffer[0] & 8) ? 'P' : '-'));
}
size_t serialize(const unsigned char* buffer, char* textbuf) const throw()
{
char tmp[128];
sprintf(tmp, "|%c%c%c%c %i %i", ((buffer[0] & 1) ? 'T' : '.'), ((buffer[0] & 2) ? 'C' : '.'),
((buffer[0] & 4) ? 'U' : '.'), ((buffer[0] & 8) ? 'P' : '.'),
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
size_t len = strlen(tmp);
memcpy(textbuf, tmp, len);
return len;
}
size_t deserialize(unsigned char* buffer, const char* textbuf) const throw()
{
buffer[0] = 0;
size_t idx = 0;
if(read_button_value(textbuf, idx))
buffer[0] |= 1;
if(read_button_value(textbuf, idx))
buffer[0] |= 2;
if(read_button_value(textbuf, idx))
buffer[0] |= 4;
if(read_button_value(textbuf, idx))
buffer[0] |= 8;
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
devicetype_t devicetype(unsigned idx) const throw()
{
return (idx == 0) ? DT_SUPERSCOPE : DT_NONE;
}
unsigned controllers() const throw()
{
return 1;
}
unsigned internal_type() const throw()
{
return SNES_DEVICE_SUPER_SCOPE;
}
bool legal(unsigned port) const throw()
{
return (port > 0);
}
} superscope;
}

View file

@ -1,350 +0,0 @@
#include "lsnes.hpp"
#include <snes/snes.hpp>
#include <ui-libsnes/libsnes.hpp>
#include "core/controllerdata.hpp"
#include <sstream>
#include <cctype>
#include <iostream>
#include <cstring>
namespace
{
inline unsigned ccindex(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
{
if(port >= MAX_PORTS || controller >= MAX_CONTROLLERS_PER_PORT || control >= CONTROLLER_CONTROLS) {
std::ostringstream x;
x << "ccindex: Invalid (port, controller, control) tuple (" << port << "," << controller
<< "," << control << ")";
throw std::logic_error(x.str());
}
return MAX_SYSTEM_CONTROLS + port * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT +
CONTROLLER_CONTROLS * controller + control;
}
bool parse_button_ctrl(const std::string& str, size_t& pos) throw()
{
if(pos >= str.length())
return false;
switch(str[pos]) {
case '.':
case ' ':
case '\t':
pos++;
case '|':
return false;
default:
pos++;
return true;
}
}
short parse_number_ctrl(const std::string& str, size_t& pos) throw()
{
char ch;
//Skip ws.
while(pos < str.length()) {
char ch = str[pos];
if(ch != ' ' && ch != '\t')
break;
pos++;
}
//Read the sign if any.
if(pos >= str.length() || (ch = str[pos]) == '|')
return 0;
bool negative = false;
if(ch == '-') {
negative = true;
pos++;
}
if(ch == '+')
pos++;
//Read numeric value.
int numval = 0;
while(pos < str.length() && isdigit(static_cast<unsigned char>(ch = str[pos]))) {
numval = numval * 10 + (ch - '0');
pos++;
}
if(negative)
numval = -numval;
return static_cast<short>(numval);
}
void parse_end_of_field(const std::string& str, size_t& pos) throw()
{
while(pos < str.length() && str[pos] != '|')
pos++;
}
}
size_t cdecode::system(const std::string& line, size_t pos, short* controls, unsigned version) throw(std::bad_alloc,
std::runtime_error)
{
controls[0] = parse_button_ctrl(line, pos); //Frame sync.
controls[1] = parse_button_ctrl(line, pos); //Reset.
controls[2] = parse_number_ctrl(line, pos); //Reset cycles hi.
controls[3] = parse_number_ctrl(line, pos); //Reset cycles lo.
parse_end_of_field(line, pos);
return pos;
}
size_t cdecode::none(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
return pos;
}
size_t cdecode::gamepad(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
for(unsigned i = 0; i < 12; i++)
controls[ccindex(port, 0, i)] = parse_button_ctrl(line, pos);
parse_end_of_field(line, pos);
return pos;
}
size_t cdecode::multitap(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
for(unsigned j = 0; j < 4; j++) {
for(unsigned i = 0; i < 12; i++)
controls[ccindex(port, j, i)] = parse_button_ctrl(line, pos);
parse_end_of_field(line, pos);
pos++;
}
return pos;
}
size_t cdecode::mouse(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
parse_end_of_field(line, pos);
return pos;
}
size_t cdecode::superscope(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 4)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 5)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
parse_end_of_field(line, pos);
return pos;
}
size_t cdecode::justifier(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
parse_end_of_field(line, pos);
return pos;
}
size_t cdecode::justifiers(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
std::runtime_error)
{
for(unsigned i = 0; i < 2; i++) {
controls[ccindex(port, i, 2)] = parse_button_ctrl(line, pos);
controls[ccindex(port, i, 3)] = parse_button_ctrl(line, pos);
controls[ccindex(port, i, 0)] = parse_number_ctrl(line, pos);
controls[ccindex(port, i, 1)] = parse_number_ctrl(line, pos);
parse_end_of_field(line, pos);
pos++;
}
return pos;
}
size_t cencode::system(char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
buffer[bufferpos++] = controls[0] ? 'F' : '.';
buffer[bufferpos++] = controls[1] ? 'R' : '.';
if(controls[2] || controls[3]) {
bufferpos += sprintf(buffer + bufferpos, " %i %i", static_cast<int>(controls[2]),
static_cast<int>(controls[3]));
}
return bufferpos;
}
size_t cencode::none(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
return ENCODE_SPECIAL_NO_OUTPUT;
}
size_t cencode::gamepad(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
static const char* characters = "BYsSudlrAXLR";
for(unsigned i = 0; i < 12; i++)
buffer[bufferpos++] = controls[ccindex(port, 0, i)] ? characters[i] : '.';
return bufferpos;
}
size_t cencode::multitap(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
static const char* characters = "BYsSudlrAXLR";
for(unsigned j = 0; j < 4; j++) {
for(unsigned i = 0; i < 12; i++)
buffer[bufferpos++] = controls[ccindex(port, j, i)] ? characters[i] : '.';
buffer[bufferpos++] = '|';
}
bufferpos--; //Eat the last '|', it shouldn't be there.
return bufferpos;
}
size_t cencode::mouse(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'L' : '.',
controls[ccindex(port, 0, 3)] ? 'R' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
static_cast<int>(controls[ccindex(port, 0, 1)]));
return bufferpos;
}
size_t cencode::superscope(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
bufferpos += sprintf(buffer + bufferpos, "%c%c%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
controls[ccindex(port, 0, 3)] ? 'C' : '.', controls[ccindex(port, 0, 4)] ? 'U' : '.',
controls[ccindex(port, 0, 5)] ? 'P' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
static_cast<int>(controls[ccindex(port, 0, 1)]));
return bufferpos;
}
size_t cencode::justifier(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
static_cast<int>(controls[ccindex(port, 0, 1)]));
return bufferpos;
}
size_t cencode::justifiers(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
{
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
static_cast<int>(controls[ccindex(port, 0, 1)]));
buffer[bufferpos++] = '|';
bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
static_cast<int>(controls[ccindex(port, 0, 1)]));
return bufferpos;
}
unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
{
return ccindex(port, controller, control);
}
controls_t controls_t::operator^(controls_t other) throw()
{
controls_t x;
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
x.controls[i] = controls[i] ^ ((i < MAX_SYSTEM_CONTROLS) ? 0 : other.controls[i]);
return x;
}
controls_t::controls_t(bool sync) throw()
{
memset(controls, 0, sizeof(controls));
if(sync)
controls[CONTROL_FRAME_SYNC] = 1;
}
const short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error)
{
return controls[ccindex(port, controller, control)];
}
const short& controls_t::operator()(unsigned control) const throw(std::logic_error)
{
if(control >= TOTAL_CONTROLS)
throw std::logic_error("controls_t::operator(): Invalid control index");
return controls[control];
}
short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
{
return controls[ccindex(port, controller, control)];
}
short& controls_t::operator()(unsigned control) throw(std::logic_error)
{
if(control >= TOTAL_CONTROLS)
throw std::logic_error("controls_t::operator(): Invalid control index");
return controls[control];
}
controls_t::controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
throw(std::bad_alloc, std::runtime_error)
{
memset(controls, 0, sizeof(controls));
size_t position = 0;
position = cdecode::system(line, position, controls, version);
for(unsigned i = 0; i < decoders.size(); i++) {
if(position < line.length() && line[position] == '|')
position++;
position = decoders[i](i, line, position, controls);
}
}
std::string controls_t::tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc)
{
char buffer[1024];
size_t linelen = 0, tmp;
tmp = cencode::system(buffer, linelen, controls);
for(unsigned i = 0; i < encoders.size(); i++) {
if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
buffer[(linelen = tmp)++] = '|';
tmp = encoders[i](i, buffer, linelen, controls);
}
if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
linelen = tmp;
return std::string(buffer, buffer + linelen);
}
bool controls_t::operator==(const controls_t& c) const throw()
{
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
if(controls[i] != c.controls[i])
return false;
return true;
}
const port_type& port_type::lookup(const std::string& name, bool port2) throw(std::bad_alloc,
std::runtime_error)
{
for(unsigned i = 0; i <= PT_LAST_CTYPE; i++) {
if(name != port_types[i].name)
continue;
if(!port2 && !port_types[i].valid_port1)
throw std::runtime_error("Can't connect " + name + " to port #1");
return port_types[i];
}
throw std::runtime_error("Unknown port type '" + name + "'");
}
port_type port_types[] = {
{ "none", cdecode::none, cencode::none, PT_NONE, 0, DT_NONE, true, SNES_DEVICE_NONE },
{ "gamepad", cdecode::gamepad, cencode::gamepad, PT_GAMEPAD, 1, DT_GAMEPAD, true, SNES_DEVICE_JOYPAD },
{ "multitap", cdecode::multitap, cencode::multitap, PT_MULTITAP, 4, DT_GAMEPAD, true, SNES_DEVICE_MULTITAP },
{ "mouse", cdecode::mouse, cencode::mouse, PT_MOUSE, 1, DT_MOUSE, true, SNES_DEVICE_MOUSE },
{ "superscope", cdecode::superscope, cencode::superscope, PT_SUPERSCOPE, 1, DT_SUPERSCOPE, false,
SNES_DEVICE_SUPER_SCOPE },
{ "justifier", cdecode::justifier, cencode::justifier, PT_JUSTIFIER, 1, DT_JUSTIFIER, false,
SNES_DEVICE_JUSTIFIER },
{ "justifiers", cdecode::justifiers, cencode::justifiers, PT_JUSTIFIERS, 2, DT_JUSTIFIER, false,
SNES_DEVICE_JUSTIFIERS }
};

View file

@ -0,0 +1,449 @@
#include "core/controllerframe.hpp"
#define SYSTEM_BYTES 5
namespace
{
std::set<porttype_info*>& porttypes()
{
static std::set<porttype_info*> p;
return p;
}
}
const porttype_info& porttype_info::lookup(porttype_t p) throw(std::runtime_error)
{
for(auto i : porttypes())
if(p == i->value)
return *i;
throw std::runtime_error("Bad port type");
}
const porttype_info& porttype_info::lookup(const std::string& p) throw(std::runtime_error)
{
for(auto i : porttypes())
if(p == i->name)
return *i;
throw std::runtime_error("Bad port type");
}
porttype_info::~porttype_info() throw()
{
porttypes().erase(this);
}
porttype_info::porttype_info(porttype_t ptype, const std::string& pname, size_t psize) throw(std::bad_alloc)
{
value = ptype;
name = pname;
storage_size = psize;
porttypes().insert(this);
}
pollcounter_vector::pollcounter_vector() throw()
{
clear();
}
void pollcounter_vector::clear() throw()
{
system_flag = false;
memset(ctrs, 0, sizeof(ctrs));
}
void pollcounter_vector::set_all_DRDY() throw()
{
for(size_t i = 0; i < MAX_BUTTONS ; i++)
ctrs[i] |= 0x80000000UL;
}
#define INDEXOF(pid, ctrl) ((pid) * MAX_CONTROLS_PER_CONTROLLER + (ctrl))
void pollcounter_vector::clear_DRDY(unsigned pid, unsigned ctrl) throw()
{
ctrs[INDEXOF(pid, ctrl)] &= 0x7FFFFFFFUL;
}
bool pollcounter_vector::get_DRDY(unsigned pid, unsigned ctrl) throw()
{
return ((ctrs[INDEXOF(pid, ctrl)] & 0x80000000UL) != 0);
}
bool pollcounter_vector::has_polled() throw()
{
uint32_t res = system_flag ? 1 : 0;
for(size_t i = 0; i < MAX_BUTTONS ; i++)
res |= ctrs[i];
return ((res & 0x7FFFFFFFUL) != 0);
}
uint32_t pollcounter_vector::get_polls(unsigned pid, unsigned ctrl) throw()
{
return ctrs[INDEXOF(pid, ctrl)] & 0x7FFFFFFFUL;
}
uint32_t pollcounter_vector::increment_polls(unsigned pid, unsigned ctrl) throw()
{
size_t i = INDEXOF(pid, ctrl);
uint32_t x = ctrs[i] & 0x7FFFFFFFUL;
++ctrs[i];
return x;
}
void pollcounter_vector::set_system() throw()
{
system_flag = true;
}
bool pollcounter_vector::get_system() throw()
{
return system_flag;
}
uint32_t pollcounter_vector::max_polls() throw()
{
uint32_t max = system_flag ? 1 : 0;
for(unsigned i = 0; i < MAX_BUTTONS; i++) {
uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL;
max = (max < tmp) ? tmp : max;
}
return max;
}
void pollcounter_vector::save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc)
{
mem.resize(4 + MAX_BUTTONS );
//Compatiblity fun.
mem[0] = 0x80000000UL;
mem[1] = system_flag ? 1 : 0x80000000UL;
mem[2] = system_flag ? 1 : 0x80000000UL;
mem[3] = system_flag ? 1 : 0x80000000UL;
for(size_t i = 0; i < MAX_BUTTONS ; i++)
mem[4 + i] = ctrs[i];
}
void pollcounter_vector::load_state(const std::vector<uint32_t>& mem) throw()
{
system_flag = (mem[1] | mem[2] | mem[3]) & 0x7FFFFFFFUL;
for(size_t i = 0; i < MAX_BUTTONS ; i++)
ctrs[i] = mem[i + 4];
}
bool pollcounter_vector::check(const std::vector<uint32_t>& mem) throw()
{
return (mem.size() == MAX_BUTTONS + 4);
}
controller_frame::controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error)
{
memset(memory, 0, sizeof(memory));
backing = memory;
types[0] = p1;
types[1] = p2;
set_types(types);
}
controller_frame::controller_frame(unsigned char* mem, porttype_t p1, porttype_t p2) throw(std::runtime_error)
{
if(!mem)
throw std::runtime_error("NULL backing memory not allowed");
memset(memory, 0, sizeof(memory));
backing = mem;
types[0] = p1;
types[1] = p2;
set_types(types);
}
controller_frame::controller_frame(const controller_frame& obj) throw()
{
memset(memory, 0, sizeof(memory));
backing = memory;
set_types(obj.types);
memcpy(backing, obj.backing, totalsize);
}
controller_frame& controller_frame::operator=(const controller_frame& obj) throw(std::runtime_error)
{
set_types(obj.types);
memcpy(backing, obj.backing, totalsize);
}
void controller_frame::set_types(const porttype_t* tarr)
{
for(unsigned i = 0; i < MAX_PORTS; i++) {
if(memory != backing && types[i] != tarr[i])
throw std::runtime_error("Controller_frame: Type mismatch");
if(!porttype_info::lookup(tarr[i]).legal(i))
throw std::runtime_error("Illegal port type for port index");
}
size_t offset = SYSTEM_BYTES;
for(unsigned i = 0; i < MAX_PORTS; i++) {
offsets[i] = offset;
types[i] = tarr[i];
pinfo[i] = &porttype_info::lookup(tarr[i]);
offset += pinfo[i]->storage_size;
}
totalsize = offset;
}
size_t controller_frame_vector::walk_helper(size_t frame, bool sflag) throw()
{
size_t ret = sflag ? frame : 0;
if(frame >= frames)
return ret;
frame++;
ret++;
size_t page = frame / frames_per_page;
size_t offset = frame_size * (frame % frames_per_page);
size_t index = frame % frames_per_page;
if(cache_page_num != page) {
cache_page = &pages[page];
cache_page_num = page;
}
while(frame < frames) {
if(index == frames_per_page) {
page++;
cache_page = &pages[page];
cache_page_num = page;
}
if(controller_frame::sync(cache_page->content + offset))
break;
index++;
offset += frame_size;
frame++;
ret++;
}
return ret;
}
size_t controller_frame_vector::count_frames() throw()
{
size_t ret = 0;
if(!frames)
return 0;
cache_page_num = 0;
cache_page = &pages[0];
size_t offset = 0;
size_t index = 0;
for(size_t i = 0; i < frames; i++) {
if(index == frames_per_page) {
cache_page_num++;
cache_page = &cache_page[cache_page_num];
index = 0;
offset = 0;
}
if(controller_frame::sync(cache_page->content + offset))
ret++;
index++;
offset += frame_size;
}
return ret;
}
void controller_frame_vector::clear(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error)
{
controller_frame check(p1, p2);
frame_size = check.size();
frames_per_page = CONTROLLER_PAGE_SIZE / frame_size;
frames = 0;
types[0] = p1;
types[1] = p2;
clear_cache();
pages.clear();
}
controller_frame_vector::~controller_frame_vector() throw()
{
pages.clear();
cache_page = NULL;
}
controller_frame_vector::controller_frame_vector(enum porttype_t p1, enum porttype_t p2) throw(std::runtime_error)
{
clear(p1, p2);
}
void controller_frame_vector::append(controller_frame frame) throw(std::bad_alloc, std::runtime_error)
{
controller_frame check(types[0], types[1]);
if(!check.types_match(frame))
throw std::runtime_error("controller_frame_vector::append: Type mismatch");
if(frames % frames_per_page == 0) {
//Create new page.
pages[frames / frames_per_page];
}
//Write the entry.
size_t page = frames / frames_per_page;
size_t offset = frame_size * (frames % frames_per_page);
cache_page_num = page;
cache_page = &pages[page];
controller_frame(cache_page->content + offset, types[0], types[1]) = frame;
frames++;
}
controller_frame_vector::controller_frame_vector(const controller_frame_vector& vector) throw(std::bad_alloc)
{
clear(vector.types[0], vector.types[1]);
*this = vector;
}
controller_frame_vector& controller_frame_vector::operator=(const controller_frame_vector& v)
throw(std::bad_alloc)
{
if(this == &v)
return *this;
resize(v.frames);
clear_cache();
//Copy the fields.
frame_size = v.frame_size;
frames_per_page = v.frames_per_page;
for(size_t i = 0; i < MAX_PORTS; i++)
types[i] = v.types[i];
//This can't fail anymore. Copy the raw page contents.
size_t pagecount = (frames + frames_per_page - 1) / frames_per_page;
for(size_t i = 0; i < pagecount; i++) {
page& pg = pages[i];
const page& pg2 = v.pages.find(i)->second;
pg = pg2;
}
return *this;
}
size_t controller_frame::system_serialize(const unsigned char* buffer, char* textbuf)
{
char tmp[128];
if(buffer[1] || buffer[2] || buffer[3] || buffer[4])
sprintf(tmp, "%c%c %i %i", ((buffer[0] & 1) ? 'F' : '.'), ((buffer[0] & 2) ? 'R' : '.'),
unserialize_short(buffer + 1), unserialize_short(buffer + 3));
else
sprintf(tmp, "%c%c", ((buffer[0] & 1) ? 'F' : '.'), ((buffer[0] & 2) ? 'R' : '.'));
size_t len = strlen(tmp);
memcpy(textbuf, tmp, len);
return len;
}
size_t controller_frame::system_deserialize(unsigned char* buffer, const char* textbuf)
{
buffer[0] = 0;
size_t idx = 0;
if(read_button_value(textbuf, idx))
buffer[0] |= 1;
if(read_button_value(textbuf, idx))
buffer[0] |= 2;
serialize_short(buffer + 1, read_axis_value(textbuf, idx));
serialize_short(buffer + 3, read_axis_value(textbuf, idx));
skip_rest_of_field(textbuf, idx, false);
return idx;
}
short read_axis_value(const char* buf, size_t& idx) throw()
{
char ch;
//Skip ws.
while(is_nonterminator(buf[idx])) {
char ch = buf[idx];
if(ch != ' ' && ch != '\t')
break;
idx++;
}
//Read the sign if any.
if(!is_nonterminator(buf[idx]))
return 0;
bool negative = false;
if(ch == '-') {
negative = true;
idx++;
}
if(ch == '+')
idx++;
//Read numeric value.
int numval = 0;
while(!is_nonterminator(buf[idx]) && isdigit(static_cast<unsigned char>(ch = buf[idx]))) {
numval = numval * 10 + (ch - '0');
idx++;
}
if(negative)
numval = -numval;
return static_cast<short>(numval);
}
void controller_frame_vector::resize(size_t newsize) throw(std::bad_alloc)
{
clear_cache();
if(newsize == 0) {
clear();
} else if(newsize < frames) {
//Shrink movie.
size_t current_pages = (frames + frames_per_page - 1) / frames_per_page;
size_t pages_needed = (newsize + frames_per_page - 1) / frames_per_page;
for(size_t i = pages_needed; i < current_pages; i++)
pages.erase(i);
//Now zeroize the excess memory.
size_t offset = frame_size * (newsize % frames_per_page);
memset(pages[pages_needed - 1].content + offset, 0, CONTROLLER_PAGE_SIZE - offset);
frames = newsize;
} else if(newsize > frames) {
//Enlarge movie.
size_t current_pages = (frames + frames_per_page - 1) / frames_per_page;
size_t pages_needed = (newsize + frames_per_page - 1) / frames_per_page;
//Create the needed pages.
for(size_t i = current_pages; i < pages_needed; i++) {
try {
pages[i];
} catch(...) {
for(size_t i = current_pages; i < pages_needed; i++)
if(pages.count(i))
pages.erase(i);
throw;
}
}
frames = newsize;
}
}
controller_frame::controller_frame() throw()
{
memset(memory, 0, sizeof(memory));
backing = memory;
for(unsigned i = 0; i < MAX_PORTS; i++) {
offsets[i] = SYSTEM_BYTES;
types[i] = PT_INVALID;
pinfo[i] = NULL;
}
totalsize = SYSTEM_BYTES;
}
void controller_frame::set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error)
{
char tmp[MAXIMUM_CONTROLLER_FRAME_SIZE] = {0};
if(!porttype_info::lookup(ptype).legal(port))
throw std::runtime_error("Illegal port type for port index");
if(memory != backing)
throw std::runtime_error("Can't set port type on non-dedicated controller frame");
if(port >= MAX_PORTS)
return;
const porttype_info* newpinfo[MAX_PORTS];
size_t newoffsets[MAX_PORTS];
size_t offset = SYSTEM_BYTES;
for(size_t i = 0; i < MAX_PORTS; i++) {
if(i != port)
newpinfo[i] = pinfo[i];
else
newpinfo[i] = &porttype_info::lookup(ptype);
newoffsets[i] = offset;
if(newpinfo[i])
offset += newpinfo[i]->storage_size;
if(i != port && newpinfo[i] && newpinfo[i]->storage_size)
memcpy(tmp + newoffsets[i], backing + offsets[i], newpinfo[i]->storage_size);
}
memcpy(memory, tmp, MAXIMUM_CONTROLLER_FRAME_SIZE);
types[port] = ptype;
pinfo[port] = newpinfo[port];
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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";

View file

@ -9,13 +9,12 @@
#include <cstring>
#include <fstream>
#define FLAG_SYNC CONTROL_FRAME_SYNC
//std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
namespace
{
bool movies_compatible(const std::vector<controls_t>& old_movie, const std::vector<controls_t>& new_movie,
bool movies_compatible(controller_frame_vector& old_movie, controller_frame_vector& new_movie,
uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
const std::string& new_projectid)
{
@ -30,16 +29,15 @@ namespace
uint64_t syncs_seen = 0;
uint64_t frames_read = 0;
while(syncs_seen < frame - 1) {
controls_t oldc(true), newc(true);
//Due to way subframes are stored, we can ignore syncing when comparing.
controller_frame oldc = old_movie.blank_frame(true), newc = new_movie.blank_frame(true);
if(frames_read < old_movie.size())
oldc = old_movie[frames_read];
if(frames_read < new_movie.size())
newc = new_movie[frames_read];
if(memcmp(oldc.controls, newc.controls, sizeof(oldc.controls)))
if(oldc != newc)
return false; //Mismatch.
frames_read++;
if(newc(CONTROL_FRAME_SYNC))
if(newc.sync())
syncs_seen++;
}
//We increment the counter one time too many.
@ -50,14 +48,30 @@ namespace
readable_old_subframes = old_movie.size() - frames_read;
if(frames_read < new_movie.size())
readable_new_subframes = new_movie.size() - frames_read;
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
uint32_t p = polls[i] & 0x7FFFFFFFUL;
//Compare reset flags of current frame.
bool old_reset = false;
bool new_reset = false;
std::pair<short, short> old_delay = std::make_pair(0, 0);
std::pair<short, short> new_delay = std::make_pair(0, 0);
if(readable_old_subframes) {
old_reset = old_movie[frames_read].reset();
old_delay = old_movie[frames_read].delay();
}
if(readable_new_subframes) {
new_reset = new_movie[frames_read].reset();
new_delay = new_movie[frames_read].delay();
}
if(old_reset != new_reset || old_delay != new_delay)
return false;
//Then rest of the stuff.
for(unsigned i = 0; i < MAX_BUTTONS; i++) {
uint32_t p = polls[i + 4] & 0x7FFFFFFFUL;
short ov = 0, nv = 0;
for(uint32_t j = 0; j < p; j++) {
if(j < readable_old_subframes)
ov = old_movie[j + frames_read](i);
ov = old_movie[j + frames_read].axis2(i);
if(j < readable_new_subframes)
nv = new_movie[j + frames_read](i);
nv = new_movie[j + frames_read].axis2(i);
if(ov != nv)
return false;
}
@ -68,8 +82,7 @@ namespace
void movie::set_all_DRDY() throw()
{
for(size_t i = 0; i < TOTAL_CONTROLS; i++)
pollcounters[i] |= 0x80000000UL;
pollcounters.set_all_DRDY();
}
std::string movie::rerecord_count() throw(std::bad_alloc)
@ -97,31 +110,21 @@ bool movie::readonly_mode() throw()
return readonly;
}
short movie::next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error)
{
return next_input(ccindex2(port, controller, index));
}
void movie::set_controls(controls_t controls) throw()
void movie::set_controls(controller_frame controls) throw()
{
current_controls = controls;
}
uint32_t movie::count_changes(uint64_t first_subframe) throw()
{
if(first_subframe >= movie_data.size())
return 0;
uint32_t ret = 1;
while(first_subframe + ret < movie_data.size() && !movie_data[first_subframe + ret](CONTROL_FRAME_SYNC))
ret++;
return ret;
return movie_data.subframe_count(first_subframe);
}
controls_t movie::get_controls() throw()
controller_frame movie::get_controls() throw()
{
if(!readonly)
return current_controls;
controls_t c;
controller_frame c = movie_data.blank_frame(false);
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
if(current_frame == 0)
return c;
@ -129,10 +132,14 @@ controls_t movie::get_controls() throw()
uint32_t changes = count_changes(current_frame_first_subframe);
if(!changes)
return c; //End of movie.
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
if(pollcounters.get_system()) {
c.reset(movie_data[current_frame_first_subframe].reset());
c.delay(movie_data[current_frame_first_subframe].delay());
}
for(size_t i = 0; i < MAX_BUTTONS; i++) {
uint32_t polls = pollcounters.get_polls(i);
uint32_t index = (changes > polls) ? polls : changes - 1;
c(i) = movie_data[current_frame_first_subframe + index](i);
c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i));
}
return c;
}
@ -155,13 +162,7 @@ uint64_t movie::get_frame_count() throw()
void movie::next_frame() throw(std::bad_alloc)
{
//If all poll counters are zero for all real controls, this frame is lag.
bool this_frame_lag = true;
for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
if(pollcounters[i] & 0x7FFFFFFF)
this_frame_lag = false;
//Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
this_frame_lag = false;
bool this_frame_lag = !pollcounters.has_polled();
//Oh, frame 0 must not be considered lag.
if(current_frame && this_frame_lag) {
lag_frames++;
@ -169,16 +170,13 @@ void movie::next_frame() throw(std::bad_alloc)
if(!readonly) {
//If in read-write mode, write a dummy record for the frame. Force sync flag.
//As index should be movie_data.size(), it is correct afterwards.
controls_t c = current_controls;
c(CONTROL_FRAME_SYNC) = 1;
movie_data.push_back(c);
movie_data.append(current_controls.copy(true));
frames_in_movie++;
}
}
//Reset the poll counters and DRDY flags.
for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
pollcounters[i] = 0;
pollcounters.clear();
//Increment the current frame counter and subframe counter. Note that first subframe is undefined for
//frame 0 and 0 for frame 1.
@ -190,34 +188,20 @@ void movie::next_frame() throw(std::bad_alloc)
current_frame++;
}
bool movie::get_DRDY(unsigned controlindex) throw(std::logic_error)
bool movie::get_DRDY(unsigned pid, unsigned ctrl) throw(std::logic_error)
{
if(controlindex >= TOTAL_CONTROLS)
throw std::logic_error("movie::get_DRDY: Bad index");
return ((pollcounters[controlindex] & 0x80000000UL) != 0);
return pollcounters.get_DRDY(pid, ctrl);
}
bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error)
short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
{
return get_DRDY(ccindex2(port, controller, index));
}
short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error)
{
//Check validity of index.
if(controlindex == FLAG_SYNC)
return 0;
if(controlindex >= TOTAL_CONTROLS)
throw std::logic_error("movie::next_input: Invalid control index");
//Clear the DRDY flag.
pollcounters[controlindex] &= 0x7FFFFFFF;
pollcounters.clear_DRDY(pid, ctrl);
if(readonly) {
//In readonly mode...
//If at the end of the movie, return released / neutral (but also record the poll)...
if(current_frame_first_subframe >= movie_data.size()) {
pollcounters[controlindex]++;
pollcounters.increment_polls(pid, ctrl);
return 0;
}
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
@ -225,10 +209,10 @@ short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_
return 0;
//Otherwise find the last valid frame of input.
uint32_t changes = count_changes(current_frame_first_subframe);
uint32_t polls = (pollcounters[controlindex]++) & 0x7FFFFFFF;
uint32_t polls = pollcounters.increment_polls(pid, ctrl);
uint32_t index = (changes > polls) ? polls : changes - 1;
//debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
return movie_data[current_frame_first_subframe + index](controlindex);
return movie_data[current_frame_first_subframe + index].axis(pid, ctrl);
} else {
//Readwrite mode.
//Before the beginning? Somebody screwed up (but return released / neutral anyway)...
@ -237,38 +221,32 @@ short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_
return 0;
//If at movie end, insert complete input with frame sync set (this is the first subframe).
if(current_frame_first_subframe >= movie_data.size()) {
controls_t c = current_controls;
c(CONTROL_FRAME_SYNC) = 1;
movie_data.push_back(c);
movie_data.append(current_controls.copy(true));
//current_frame_first_subframe should be movie_data.size(), so it is right.
pollcounters[controlindex]++;
pollcounters.increment_polls(pid, ctrl);
frames_in_movie++;
assert(pollcounters[controlindex] == 1);
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
return movie_data[current_frame_first_subframe](controlindex);
return movie_data[current_frame_first_subframe].axis(pid, ctrl);
}
short new_value = current_controls(controlindex);
short new_value = current_controls.axis(pid, ctrl);
//Fortunately, we know this frame is the last one in movie_data.
uint32_t pollcounter = pollcounters[controlindex] & 0x7FFFFFFF;
uint32_t pollcounter = pollcounters.get_polls(pid, ctrl);
uint64_t fetchrow = movie_data.size() - 1;
if(current_frame_first_subframe + pollcounter < movie_data.size()) {
//The index is within existing size. Change the value and propagate to all subsequent
//subframes.
for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
movie_data[i](controlindex) = new_value;
movie_data[i].axis(pid, ctrl, new_value);
fetchrow = current_frame_first_subframe + pollcounter;
} else if(new_value != movie_data[movie_data.size() - 1](controlindex)) {
} else if(new_value != movie_data[movie_data.size() - 1].axis(pid, ctrl)) {
//The index is not within existing size and value does not match. We need to create a new
//subframes(s), copying the last subframe.
while(current_frame_first_subframe + pollcounter >= movie_data.size()) {
controls_t c = movie_data[movie_data.size() - 1];
c(CONTROL_FRAME_SYNC) = 0;
movie_data.push_back(c);
}
while(current_frame_first_subframe + pollcounter >= movie_data.size())
movie_data.append(movie_data[movie_data.size() - 1].copy(false));
fetchrow = current_frame_first_subframe + pollcounter;
movie_data[current_frame_first_subframe + pollcounter](controlindex) = new_value;
movie_data[current_frame_first_subframe + pollcounter].axis(pid, ctrl, new_value);
}
pollcounters[controlindex]++;
pollcounters.increment_polls(pid, ctrl);
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
return new_value;
}
@ -282,37 +260,33 @@ movie::movie() throw(std::bad_alloc)
current_frame = 0;
frames_in_movie = 0;
current_frame_first_subframe = 0;
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
pollcounters[i] = 0;
current_controls(i) = 0;
}
lag_frames = 0;
clear_caches();
}
void movie::load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
void movie::load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
throw(std::bad_alloc, std::runtime_error)
{
if(input.size() > 0 && !input[0](CONTROL_FRAME_SYNC))
if(input.size() > 0 && !input[0].sync())
throw std::runtime_error("First subframe MUST have frame sync flag set");
clear_caches();
frames_in_movie = 0;
for(auto i : input)
if(i(CONTROL_FRAME_SYNC))
for(size_t i = 0; i < input.size(); i++)
if(input[i].sync())
frames_in_movie++;
readonly = true;
rerecords = rerecs;
_project_id = project_id;
current_frame = 0;
current_frame_first_subframe = 0;
for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
pollcounters[i] = 0;
}
pollcounters.clear();
lag_frames = 0;
movie_data = input;
//This is to force internal type of current_controls to become correct.
current_controls = input.blank_frame(false);
}
std::vector<controls_t> movie::save() throw(std::bad_alloc)
controller_frame_vector movie::save() throw(std::bad_alloc)
{
return movie_data;
}
@ -322,37 +296,21 @@ void movie::commit_reset(long delay) throw(std::bad_alloc)
if(readonly || delay < 0)
return;
//If this frame is lagged, we need to write entry for it.
bool this_frame_lag = true;
for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
if(pollcounters[i] & 0x7FFFFFFF)
this_frame_lag = false;
//Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
this_frame_lag = false;
if(this_frame_lag) {
controls_t c = current_controls;
c(CONTROL_FRAME_SYNC) = 1;
movie_data.push_back(c);
if(!pollcounters.has_polled()) {
movie_data.append(current_controls.copy(true));
frames_in_movie++;
//Current_frame_first_subframe is correct.
}
//Also set poll counters on reset cycles to avoid special cases elsewhere.
pollcounters[CONTROL_SYSTEM_RESET] = 1;
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
pollcounters.set_system();
//Current frame is always last in rw mode.
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET) = 1;
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
movie_data[current_frame_first_subframe].reset(true);
movie_data[current_frame_first_subframe].delay(std::make_pair(delay / 10000, delay % 10000));
}
unsigned movie::next_poll_number()
{
unsigned max = 0;
for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
if(max < (pollcounters[i] & 0x7FFFFFFF))
max = (pollcounters[i] & 0x7FFFFFFF);
return max + 1;
return pollcounters.max_polls() + 1;
}
void movie::readonly_mode(bool enable) throw(std::bad_alloc)
@ -373,8 +331,7 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
if(current_frame_first_subframe >= movie_data.size()) {
//Yes, this will insert one extra frame... But we will lose it later if it is not needed.
while(frames_in_movie < current_frame) {
controls_t c(true);
movie_data.push_back(c);
movie_data.append(movie_data.blank_frame(true));
frames_in_movie++;
}
current_frame_first_subframe = movie_data.size() - 1;
@ -384,22 +341,23 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
//forward values with smaller poll counters.
uint64_t next_frame_first_subframe = current_frame_first_subframe +
count_changes(current_frame_first_subframe);
uint64_t max_readable_subframes = current_frame_first_subframe;
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
if(current_frame_first_subframe + polls >= next_frame_first_subframe)
max_readable_subframes = next_frame_first_subframe;
else if(current_frame_first_subframe + polls > max_readable_subframes)
max_readable_subframes = current_frame_first_subframe + polls;
}
uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
if(max_readable_subframes > next_frame_first_subframe)
max_readable_subframes = next_frame_first_subframe;
movie_data.resize(max_readable_subframes);
next_frame_first_subframe = max_readable_subframes;
for(size_t i = 1; i < TOTAL_CONTROLS; i++) {
uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
if(!polls)
polls = 1;
//Propagate RESET. This always has poll count of 0 or 1, which always behaves like 1.
for(uint64_t j = current_frame_first_subframe + 1; j < next_frame_first_subframe; j++) {
movie_data[j].reset(movie_data[current_frame_first_subframe].reset());
movie_data[j].delay(movie_data[current_frame_first_subframe].delay());
}
//Then the other buttons.
for(size_t i = 0; i < MAX_BUTTONS; i++) {
uint32_t polls = pollcounters.get_polls(i);
polls = polls ? polls : 1;
for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
movie_data[j](i) = movie_data[current_frame_first_subframe + polls - 1](i);
movie_data[j].axis2(i, movie_data[current_frame_first_subframe + polls - 1].axis2(i));
}
frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
}
@ -409,19 +367,18 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
throw(std::bad_alloc)
{
pcounters.resize(TOTAL_CONTROLS);
pollcounters.save_state(pcounters);
proj_id = _project_id;
curframe = current_frame;
lagframes = lag_frames;
memcpy(&pcounters[0], pollcounters, TOTAL_CONTROLS * sizeof(uint32_t));
}
//Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
std::vector<controls_t>* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
std::runtime_error)
{
if(pcounters.size() != TOTAL_CONTROLS)
if(!pollcounters.check(pcounters))
throw std::runtime_error("Wrong number of poll counters");
if(old_movie && !movies_compatible(*old_movie, movie_data, curframe, &pcounters[0], old_projectid,
_project_id))
@ -434,7 +391,7 @@ size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vec
current_frame = curframe;
current_frame_first_subframe = tmp_firstsubframe;
lag_frames = lagframe;
memcpy(pollcounters, &pcounters[0], TOTAL_CONTROLS * sizeof(uint32_t));
pollcounters.load_state(pcounters);
readonly_mode(ro);
return 0;
}
@ -443,14 +400,12 @@ long movie::get_reset_status() throw()
{
if(current_frame == 0 || current_frame_first_subframe >= movie_data.size())
return -1; //No resets out of range.
if(!movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET))
if(!movie_data[current_frame_first_subframe].reset())
return -1; //Not a reset.
//Also set poll counters on reset cycles to avoid special cases elsewhere.
pollcounters[CONTROL_SYSTEM_RESET] = 1;
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
long hi = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI);
long lo = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO);
pollcounters.set_system();
auto g = movie_data[current_frame_first_subframe].delay();
long hi = g.first, lo = g.second;
return hi * 10000 + lo;
}
@ -472,7 +427,7 @@ void movie::clear_caches() throw()
cached_subframe = 0;
}
controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
controller_frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
{
if(frame < cached_frame)
clear_caches();
@ -482,8 +437,9 @@ controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
cached_frame = frame;
cached_subframe = p;
uint64_t max = count_changes(p);
if(!max)
return controls_t(true);
if(!max) {
return movie_data.blank_frame(true);
}
if(max <= subframe)
subframe = max - 1;
return movie_data[p + subframe];
@ -502,14 +458,14 @@ movie& movie_logic::get_movie() throw()
long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error)
{
mov.next_frame();
controls_t c = update_controls(false);
controller_frame c = update_controls(false);
if(!mov.readonly_mode()) {
mov.set_controls(c);
if(dont_poll)
mov.set_all_DRDY();
if(c(CONTROL_SYSTEM_RESET)) {
long hi = c(CONTROL_SYSTEM_RESET_CYCLES_HI);
long lo = c(CONTROL_SYSTEM_RESET_CYCLES_LO);
if(c.reset()) {
auto g = c.delay();
long hi = g.first, lo = g.second;
mov.commit_reset(hi * 10000 + lo);
}
}
@ -518,13 +474,12 @@ long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::
short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
{
if(dev >= MAX_CONTROLLERS_PER_PORT || id >= CONTROLLER_CONTROLS)
return 0;
if(!mov.get_DRDY(port ? 1 : 0, dev, id)) {
unsigned pid = port ? (dev + MAX_CONTROLLERS_PER_PORT) : dev;
if(!mov.get_DRDY(pid, id)) {
mov.set_controls(update_controls(true));
mov.set_all_DRDY();
}
int16_t in = mov.next_input(port ? 1 : 0, dev, id);
int16_t in = mov.next_input(pid, id);
//debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
// << ") giving " << in << std::endl;
//debuglog.flush();

View file

@ -207,16 +207,16 @@ void write_authors_file(zip_writer& w, std::vector<std::pair<std::string, std::s
}
}
void write_input(zip_writer& w, std::vector<controls_t>& input, porttype_t port1, porttype_t port2)
void write_input(zip_writer& w, controller_frame_vector& input, porttype_t port1, porttype_t port2)
throw(std::bad_alloc, std::runtime_error)
{
std::vector<cencode::fn_t> encoders;
encoders.push_back(port_types[port1].encoder);
encoders.push_back(port_types[port2].encoder);
std::ostream& m = w.create_file("input");
try {
for(auto i : input)
m << i.tostring(encoders) << std::endl;
char buffer[MAX_SERIALIZED_SIZE];
for(size_t i = 0; i < input.size(); i++) {
input[i].serialize(buffer);
m << buffer << std::endl;
}
if(!m)
throw std::runtime_error("Can't write ZIP file member");
w.close_file();
@ -226,19 +226,18 @@ void write_input(zip_writer& w, std::vector<controls_t>& input, porttype_t port1
}
}
void read_input(zip_reader& r, std::vector<controls_t>& input, porttype_t port1, porttype_t port2, unsigned version)
void read_input(zip_reader& r, controller_frame_vector& input, porttype_t port1, porttype_t port2, unsigned version)
throw(std::bad_alloc, std::runtime_error)
{
std::vector<cdecode::fn_t> decoders;
decoders.push_back(port_types[port1].decoder);
decoders.push_back(port_types[port2].decoder);
controller_frame tmp = input.blank_frame(false);
std::istream& m = r["input"];
try {
std::string x;
while(std::getline(m, x)) {
strip_CR(x);
if(x != "") {
input.push_back(controls_t(x, decoders, version));
tmp.deserialize(x.c_str());
input.append(tmp);
}
}
delete &m;
@ -295,13 +294,14 @@ void write_pollcounters(zip_writer& w, const std::string& file, const std::vecto
porttype_t parse_controller_type(const std::string& type, bool port) throw(std::bad_alloc, std::runtime_error)
{
porttype_t port1 = PT_INVALID;
for(unsigned i = 0; i <= PT_LAST_CTYPE; i++)
if(type == port_types[i].name && (port || port_types[i].valid_port1))
port1 = static_cast<porttype_t>(i);
if(port1 == PT_INVALID)
try {
const porttype_info& i = porttype_info::lookup(type);
if(!i.legal(port ? 1 : 0))
throw 42;
return i.value;
} catch(...) {
throw std::runtime_error(std::string("Illegal port") + (port ? "2" : "1") + " device '" + type + "'");
return port1;
}
}
@ -339,12 +339,13 @@ moviefile::moviefile(const std::string& movie) throw(std::bad_alloc, std::runtim
} catch(std::exception& e) {
throw std::runtime_error("Illegal game type '" + tmp + "'");
}
tmp = port_types[PT_GAMEPAD].name;
tmp = "gamepad";
read_linefile(r, "port1", tmp, true);
port1 = port_type::lookup(tmp, false).ptype;
tmp = port_types[PT_NONE].name;
port1 = porttype_info::lookup(tmp).value;
tmp = "none";
read_linefile(r, "port2", tmp, true);
port2 = port_type::lookup(tmp, true).ptype;
port2 = porttype_info::lookup(tmp).value;
input.clear(port1, port2);
read_linefile(r, "gamename", gamename, true);
read_linefile(r, "projectid", projectid);
rerecords = read_rrdata(r, c_rrdata);
@ -397,9 +398,9 @@ void moviefile::save(const std::string& movie, unsigned compression) throw(std::
zip_writer w(movie, compression);
write_linefile(w, "gametype", gtype::tostring(gametype));
if(port1 != PT_GAMEPAD)
write_linefile(w, "port1", port_types[port1].name);
write_linefile(w, "port1", porttype_info::lookup(port1).name);
if(port2 != PT_NONE)
write_linefile(w, "port2", port_types[port2].name);
write_linefile(w, "port2", porttype_info::lookup(port2).name);
write_linefile(w, "gamename", gamename, true);
write_linefile(w, "systemid", "lsnes-rr1");
write_linefile(w, "controlsversion", "0");
@ -437,12 +438,7 @@ void moviefile::save(const std::string& movie, unsigned compression) throw(std::
uint64_t moviefile::get_frame_count() throw()
{
uint64_t frames = 0;
for(size_t i = 0; i < input.size(); i++) {
if(input[i](CONTROL_FRAME_SYNC))
frames++;
}
return frames;
return input.count_frames();
}
namespace

View file

@ -8,9 +8,9 @@ namespace
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
short value = get_numeric_argument<short>(LS, 3, fname.c_str());
if(controller > 7 || index > 11)
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER)
return 0;
(*lua_input_controllerdata)(controller >> 2, controller & 3, index) = value;
lua_input_controllerdata->axis(controller, index, value);
return 0;
});
@ -19,9 +19,9 @@ namespace
return 0;
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
if(controller > 7 || index > 11)
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER)
return 0;
lua_pushnumber(LS, (*lua_input_controllerdata)(controller >> 2, controller & 3, index));
lua_pushnumber(LS, lua_input_controllerdata->axis(controller, index));
return 1;
});
@ -34,9 +34,8 @@ namespace
return 0;
short lo = cycles % 10000;
short hi = cycles / 10000;
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET) = 1;
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET_CYCLES_HI) = hi;
(*lua_input_controllerdata)(CONTROL_SYSTEM_RESET_CYCLES_LO) = lo;
lua_input_controllerdata->reset(true);
lua_input_controllerdata->delay(std::make_pair(hi, lo));
return 0;
});
}
}

View file

@ -39,11 +39,25 @@ namespace
uint64_t frame = get_numeric_argument<uint64_t>(LS, 1, "movie.frame_subframes");
uint64_t subframe = get_numeric_argument<uint64_t>(LS, 2, "movie.frame_subframes");
auto& m = get_movie();
controls_t r = m.read_subframe(frame, subframe);
controller_frame r = m.read_subframe(frame, subframe);
lua_newtable(LS);
for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
lua_pushnumber(LS, i);
lua_pushnumber(LS, r(i));
lua_pushnumber(LS, 0);
lua_pushnumber(LS, r.sync() ? 1 : 0);
lua_settable(LS, -3);
lua_pushnumber(LS, 1);
lua_pushnumber(LS, r.reset() ? 1 : 0);
lua_settable(LS, -3);
lua_pushnumber(LS, 2);
lua_pushnumber(LS, r.delay().first);
lua_settable(LS, -3);
lua_pushnumber(LS, 3);
lua_pushnumber(LS, r.delay().second);
lua_settable(LS, -3);
for(size_t i = 0; i < MAX_BUTTONS; i++) {
lua_pushnumber(LS, i + 4);
lua_pushnumber(LS, r.axis2(i));
lua_settable(LS, -3);
}
return 1;

View file

@ -47,9 +47,9 @@ struct moviefile generate_movie_template(std::vector<std::string> cmdline, loade
for(auto i = cmdline.begin(); i != cmdline.end(); i++) {
std::string o = *i;
if(o.length() >= 8 && o.substr(0, 8) == "--port1=")
movie.port1 = port_type::lookup(o.substr(8), false).ptype;
movie.port1 = porttype_info::lookup(o.substr(8)).value;
if(o.length() >= 8 && o.substr(0, 8) == "--port2=")
movie.port2 = port_type::lookup(o.substr(8), true).ptype;
movie.port2 = porttype_info::lookup(o.substr(8)).value;
if(o.length() >= 11 && o.substr(0, 11) == "--gamename=")
movie.gamename = o.substr(11);
if(o.length() >= 9 && o.substr(0, 9) == "--author=") {
@ -65,9 +65,8 @@ struct moviefile generate_movie_template(std::vector<std::string> cmdline, loade
if(movie.rtc_subsecond < 0 || movie.rtc_subsecond > 3462619485019ULL)
throw std::runtime_error("Bad RTC subsecond value (range is 0-3462619485019)");
}
}
movie.input.clear(movie.port1, movie.port2);
return movie;
}

View file

@ -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"

View file

@ -872,6 +872,7 @@ struct moviefile wxwin_project::make_movie()
f.movie_rtc_subsecond = f.rtc_subsecond = boost::lexical_cast<int64_t>(tostdstring(rtc_subsec->GetValue()));
if(f.movie_rtc_subsecond < 0)
throw std::runtime_error("RTC subsecond must be positive");
f.input.clear(f.port1, f.port2);
return f;
}