Make joysticks actually work
[lsnes.git] / controllerdata.cpp
bloba04ab75fad91232789bd04d8af96a9e164bf8c75
1 #include "lsnes.hpp"
2 #include "controllerdata.hpp"
3 #include <sstream>
4 #include <cctype>
5 #include <iostream>
6 #include <cstring>
7 #include <snes/snes.hpp>
8 #include <ui-libsnes/libsnes.hpp>
10 namespace
12 inline unsigned ccindex(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
14 if(port >= MAX_PORTS || controller >= MAX_CONTROLLERS_PER_PORT || control >= CONTROLLER_CONTROLS) {
15 std::ostringstream x;
16 x << "ccindex: Invalid (port, controller, control) tuple (" << port << "," << controller
17 << "," << control << ")";
18 throw std::logic_error(x.str());
20 return MAX_SYSTEM_CONTROLS + port * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT +
21 CONTROLLER_CONTROLS * controller + control;
24 bool parse_button_ctrl(const std::string& str, size_t& pos) throw()
26 if(pos >= str.length())
27 return false;
28 switch(str[pos]) {
29 case '.':
30 case ' ':
31 case '\t':
32 pos++;
33 case '|':
34 return false;
35 default:
36 pos++;
37 return true;
41 short parse_number_ctrl(const std::string& str, size_t& pos) throw()
43 char ch;
44 //Skip ws.
45 while(pos < str.length()) {
46 char ch = str[pos];
47 if(ch != ' ' && ch != '\t')
48 break;
49 pos++;
51 //Read the sign if any.
52 if(pos >= str.length() || (ch = str[pos]) == '|')
53 return 0;
54 bool negative = false;
55 if(ch == '-') {
56 negative = true;
57 pos++;
59 if(ch == '+')
60 pos++;
62 //Read numeric value.
63 int numval = 0;
64 while(pos < str.length() && isdigit(static_cast<unsigned char>(ch = str[pos]))) {
65 numval = numval * 10 + (ch - '0');
66 pos++;
68 if(negative)
69 numval = -numval;
71 return static_cast<short>(numval);
74 void parse_end_of_field(const std::string& str, size_t& pos) throw()
76 while(pos < str.length() && str[pos] != '|')
77 pos++;
81 size_t cdecode::system(const std::string& line, size_t pos, short* controls, unsigned version) throw(std::bad_alloc,
82 std::runtime_error)
84 controls[0] = parse_button_ctrl(line, pos); //Frame sync.
85 controls[1] = parse_button_ctrl(line, pos); //Reset.
86 controls[2] = parse_number_ctrl(line, pos); //Reset cycles hi.
87 controls[3] = parse_number_ctrl(line, pos); //Reset cycles lo.
88 parse_end_of_field(line, pos);
89 return pos;
92 size_t cdecode::none(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
93 std::runtime_error)
95 return pos;
98 size_t cdecode::gamepad(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
99 std::runtime_error)
101 for(unsigned i = 0; i < 12; i++)
102 controls[ccindex(port, 0, i)] = parse_button_ctrl(line, pos);
103 parse_end_of_field(line, pos);
104 return pos;
107 size_t cdecode::multitap(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
108 std::runtime_error)
110 for(unsigned j = 0; j < 4; j++) {
111 for(unsigned i = 0; i < 12; i++)
112 controls[ccindex(port, j, i)] = parse_button_ctrl(line, pos);
113 parse_end_of_field(line, pos);
114 pos++;
116 return pos;
119 size_t cdecode::mouse(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
120 std::runtime_error)
122 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
123 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
124 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
125 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
126 parse_end_of_field(line, pos);
127 return pos;
130 size_t cdecode::superscope(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
131 std::runtime_error)
133 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
134 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
135 controls[ccindex(port, 0, 4)] = parse_button_ctrl(line, pos);
136 controls[ccindex(port, 0, 5)] = parse_button_ctrl(line, pos);
137 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
138 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
139 parse_end_of_field(line, pos);
140 return pos;
143 size_t cdecode::justifier(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
144 std::runtime_error)
146 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
147 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
148 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
149 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
150 parse_end_of_field(line, pos);
151 return pos;
154 size_t cdecode::justifiers(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
155 std::runtime_error)
157 for(unsigned i = 0; i < 2; i++) {
158 controls[ccindex(port, i, 2)] = parse_button_ctrl(line, pos);
159 controls[ccindex(port, i, 3)] = parse_button_ctrl(line, pos);
160 controls[ccindex(port, i, 0)] = parse_number_ctrl(line, pos);
161 controls[ccindex(port, i, 1)] = parse_number_ctrl(line, pos);
162 parse_end_of_field(line, pos);
163 pos++;
165 return pos;
168 size_t cencode::system(char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
170 buffer[bufferpos++] = controls[0] ? 'F' : '.';
171 buffer[bufferpos++] = controls[1] ? 'R' : '.';
172 if(controls[2] || controls[3]) {
173 bufferpos += sprintf(buffer + bufferpos, " %i %i", static_cast<int>(controls[2]),
174 static_cast<int>(controls[3]));
176 return bufferpos;
179 size_t cencode::none(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
181 return ENCODE_SPECIAL_NO_OUTPUT;
184 size_t cencode::gamepad(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
186 static const char* characters = "BYsSudlrAXLR";
187 for(unsigned i = 0; i < 12; i++)
188 buffer[bufferpos++] = controls[ccindex(port, 0, i)] ? characters[i] : '.';
189 return bufferpos;
192 size_t cencode::multitap(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
194 static const char* characters = "BYsSudlrAXLR";
195 for(unsigned j = 0; j < 4; j++) {
196 for(unsigned i = 0; i < 12; i++)
197 buffer[bufferpos++] = controls[ccindex(port, j, i)] ? characters[i] : '.';
198 buffer[bufferpos++] = '|';
200 bufferpos--; //Eat the last '|', it shouldn't be there.
201 return bufferpos;
204 size_t cencode::mouse(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
206 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'L' : '.',
207 controls[ccindex(port, 0, 3)] ? 'R' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
208 static_cast<int>(controls[ccindex(port, 0, 1)]));
209 return bufferpos;
212 size_t cencode::superscope(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
214 bufferpos += sprintf(buffer + bufferpos, "%c%c%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
215 controls[ccindex(port, 0, 3)] ? 'C' : '.', controls[ccindex(port, 0, 4)] ? 'U' : '.',
216 controls[ccindex(port, 0, 5)] ? 'P' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
217 static_cast<int>(controls[ccindex(port, 0, 1)]));
218 return bufferpos;
221 size_t cencode::justifier(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
223 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
224 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
225 static_cast<int>(controls[ccindex(port, 0, 1)]));
226 return bufferpos;
229 size_t cencode::justifiers(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
231 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
232 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
233 static_cast<int>(controls[ccindex(port, 0, 1)]));
234 buffer[bufferpos++] = '|';
235 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
236 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
237 static_cast<int>(controls[ccindex(port, 0, 1)]));
238 return bufferpos;
242 unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
244 return ccindex(port, controller, control);
248 controls_t controls_t::operator^(controls_t other) throw()
250 controls_t x;
251 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
252 x.controls[i] = controls[i] ^ ((i < MAX_SYSTEM_CONTROLS) ? 0 : other.controls[i]);
253 return x;
256 controls_t::controls_t(bool sync) throw()
258 memset(controls, 0, sizeof(controls));
259 if(sync)
260 controls[CONTROL_FRAME_SYNC] = 1;
263 const short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error)
265 return controls[ccindex(port, controller, control)];
268 const short& controls_t::operator()(unsigned control) const throw(std::logic_error)
270 if(control >= TOTAL_CONTROLS)
271 throw std::logic_error("controls_t::operator(): Invalid control index");
272 return controls[control];
275 short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
277 return controls[ccindex(port, controller, control)];
280 short& controls_t::operator()(unsigned control) throw(std::logic_error)
282 if(control >= TOTAL_CONTROLS)
283 throw std::logic_error("controls_t::operator(): Invalid control index");
284 return controls[control];
287 controls_t::controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
288 throw(std::bad_alloc, std::runtime_error)
290 memset(controls, 0, sizeof(controls));
291 size_t position = 0;
292 position = cdecode::system(line, position, controls, version);
293 for(unsigned i = 0; i < decoders.size(); i++) {
294 if(position < line.length() && line[position] == '|')
295 position++;
296 position = decoders[i](i, line, position, controls);
300 std::string controls_t::tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc)
302 char buffer[1024];
303 size_t linelen = 0, tmp;
304 tmp = cencode::system(buffer, linelen, controls);
305 for(unsigned i = 0; i < encoders.size(); i++) {
306 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
307 buffer[(linelen = tmp)++] = '|';
308 tmp = encoders[i](i, buffer, linelen, controls);
310 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
311 linelen = tmp;
312 return std::string(buffer, buffer + linelen);
315 bool controls_t::operator==(const controls_t& c) const throw()
317 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
318 if(controls[i] != c.controls[i])
319 return false;
320 return true;
324 const port_type& port_type::lookup(const std::string& name, bool port2) throw(std::bad_alloc,
325 std::runtime_error)
327 for(unsigned i = 0; i <= PT_LAST_CTYPE; i++) {
328 if(name != port_types[i].name)
329 continue;
330 if(!port2 && !port_types[i].valid_port1)
331 throw std::runtime_error("Can't connect " + name + " to port #1");
332 return port_types[i];
334 throw std::runtime_error("Unknown port type '" + name + "'");
337 port_type port_types[] = {
338 { "none", cdecode::none, cencode::none, PT_NONE, 0, DT_NONE, true, SNES_DEVICE_NONE },
339 { "gamepad", cdecode::gamepad, cencode::gamepad, PT_GAMEPAD, 1, DT_GAMEPAD, true, SNES_DEVICE_JOYPAD },
340 { "multitap", cdecode::multitap, cencode::multitap, PT_MULTITAP, 4, DT_GAMEPAD, true, SNES_DEVICE_MULTITAP },
341 { "mouse", cdecode::mouse, cencode::mouse, PT_MOUSE, 1, DT_MOUSE, true, SNES_DEVICE_MOUSE },
342 { "superscope", cdecode::superscope, cencode::superscope, PT_SUPERSCOPE, 1, DT_SUPERSCOPE, false,
343 SNES_DEVICE_SUPER_SCOPE },
344 { "justifier", cdecode::justifier, cencode::justifier, PT_JUSTIFIER, 1, DT_JUSTIFIER, false,
345 SNES_DEVICE_JUSTIFIER },
346 { "justifiers", cdecode::justifiers, cencode::justifiers, PT_JUSTIFIERS, 2, DT_JUSTIFIER, false,
347 SNES_DEVICE_JUSTIFIERS }