348 lines
11 KiB
C++
348 lines
11 KiB
C++
#include "lsnes.hpp"
|
|
#include "controllerdata.hpp"
|
|
#include <sstream>
|
|
#include <cctype>
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <snes/snes.hpp>
|
|
#include <ui-libsnes/libsnes.hpp>
|
|
|
|
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 }
|
|
};
|