Add lua functions to manipulate emulator settings
[lsnes.git] / controllerdata.cpp
bloba4d376fe394685a9d9072a494195bd63c507c509
1 #include "lsnes.hpp"
2 #include "controllerdata.hpp"
3 #include <sstream>
4 #include <cctype>
5 #include <iostream>
6 #include <cstring>
9 namespace
11 inline unsigned ccindex(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
13 if(port >= MAX_PORTS || controller >= MAX_CONTROLLERS_PER_PORT || control >= CONTROLLER_CONTROLS) {
14 std::ostringstream x;
15 x << "ccindex: Invalid (port, controller, control) tuple (" << port << "," << controller
16 << "," << control << ")";
17 throw std::logic_error(x.str());
19 return MAX_SYSTEM_CONTROLS + port * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT +
20 CONTROLLER_CONTROLS * controller + control;
23 bool parse_button_ctrl(const std::string& str, size_t& pos) throw()
25 if(pos >= str.length())
26 return false;
27 switch(str[pos]) {
28 case '.':
29 case ' ':
30 case '\t':
31 pos++;
32 case '|':
33 return false;
34 default:
35 pos++;
36 return true;
40 short parse_number_ctrl(const std::string& str, size_t& pos) throw()
42 char ch;
43 //Skip ws.
44 while(pos < str.length()) {
45 char ch = str[pos];
46 if(ch != ' ' && ch != '\t')
47 break;
48 pos++;
50 //Read the sign if any.
51 if(pos >= str.length() || (ch = str[pos]) == '|')
52 return 0;
53 bool negative = false;
54 if(ch == '-') {
55 negative = true;
56 pos++;
58 if(ch == '+')
59 pos++;
61 //Read numeric value.
62 int numval = 0;
63 while(pos < str.length() && isdigit(static_cast<unsigned char>(ch = str[pos]))) {
64 numval = numval * 10 + (ch - '0');
65 pos++;
67 if(negative)
68 numval = -numval;
70 return static_cast<short>(numval);
73 void parse_end_of_field(const std::string& str, size_t& pos) throw()
75 while(pos < str.length() && str[pos] != '|')
76 pos++;
80 size_t cdecode::system(const std::string& line, size_t pos, short* controls, unsigned version) throw(std::bad_alloc,
81 std::runtime_error)
83 controls[0] = parse_button_ctrl(line, pos); //Frame sync.
84 controls[1] = parse_button_ctrl(line, pos); //Reset.
85 controls[2] = parse_number_ctrl(line, pos); //Reset cycles hi.
86 controls[3] = parse_number_ctrl(line, pos); //Reset cycles lo.
87 parse_end_of_field(line, pos);
88 return pos;
91 size_t cdecode::none(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
92 std::runtime_error)
94 return pos;
97 size_t cdecode::gamepad(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
98 std::runtime_error)
100 for(unsigned i = 0; i < 12; i++)
101 controls[ccindex(port, 0, i)] = parse_button_ctrl(line, pos);
102 parse_end_of_field(line, pos);
103 return pos;
106 size_t cdecode::multitap(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
107 std::runtime_error)
109 for(unsigned j = 0; j < 4; j++) {
110 for(unsigned i = 0; i < 12; i++)
111 controls[ccindex(port, j, i)] = parse_button_ctrl(line, pos);
112 parse_end_of_field(line, pos);
113 pos++;
115 return pos;
118 size_t cdecode::mouse(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
119 std::runtime_error)
121 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
122 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
123 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
124 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
125 parse_end_of_field(line, pos);
126 return pos;
129 size_t cdecode::superscope(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
130 std::runtime_error)
132 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
133 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
134 controls[ccindex(port, 0, 4)] = parse_button_ctrl(line, pos);
135 controls[ccindex(port, 0, 5)] = parse_button_ctrl(line, pos);
136 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
137 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
138 parse_end_of_field(line, pos);
139 return pos;
142 size_t cdecode::justifier(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
143 std::runtime_error)
145 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
146 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
147 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
148 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
149 parse_end_of_field(line, pos);
150 return pos;
153 size_t cdecode::justifiers(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
154 std::runtime_error)
156 for(unsigned i = 0; i < 2; i++) {
157 controls[ccindex(port, i, 2)] = parse_button_ctrl(line, pos);
158 controls[ccindex(port, i, 3)] = parse_button_ctrl(line, pos);
159 controls[ccindex(port, i, 0)] = parse_number_ctrl(line, pos);
160 controls[ccindex(port, i, 1)] = parse_number_ctrl(line, pos);
161 parse_end_of_field(line, pos);
162 pos++;
164 return pos;
167 size_t cencode::system(char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
169 buffer[bufferpos++] = controls[0] ? 'F' : '.';
170 buffer[bufferpos++] = controls[1] ? 'R' : '.';
171 if(controls[2] || controls[3]) {
172 bufferpos += sprintf(buffer + bufferpos, " %i %i", static_cast<int>(controls[2]),
173 static_cast<int>(controls[3]));
175 return bufferpos;
178 size_t cencode::none(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
180 return ENCODE_SPECIAL_NO_OUTPUT;
183 size_t cencode::gamepad(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
185 static const char* characters = "BYsSudlrAXLR";
186 for(unsigned i = 0; i < 12; i++)
187 buffer[bufferpos++] = controls[ccindex(port, 0, i)] ? characters[i] : '.';
188 return bufferpos;
191 size_t cencode::multitap(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
193 static const char* characters = "BYsSudlrAXLR";
194 for(unsigned j = 0; j < 4; j++) {
195 for(unsigned i = 0; i < 12; i++)
196 buffer[bufferpos++] = controls[ccindex(port, j, i)] ? characters[i] : '.';
197 buffer[bufferpos++] = '|';
199 bufferpos--; //Eat the last '|', it shouldn't be there.
200 return bufferpos;
203 size_t cencode::mouse(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
205 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'L' : '.',
206 controls[ccindex(port, 0, 3)] ? 'R' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
207 static_cast<int>(controls[ccindex(port, 0, 1)]));
208 return bufferpos;
211 size_t cencode::superscope(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
213 bufferpos += sprintf(buffer + bufferpos, "%c%c%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
214 controls[ccindex(port, 0, 3)] ? 'C' : '.', controls[ccindex(port, 0, 4)] ? 'U' : '.',
215 controls[ccindex(port, 0, 5)] ? 'P' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
216 static_cast<int>(controls[ccindex(port, 0, 1)]));
217 return bufferpos;
220 size_t cencode::justifier(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
222 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
223 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
224 static_cast<int>(controls[ccindex(port, 0, 1)]));
225 return bufferpos;
228 size_t cencode::justifiers(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
230 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
231 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
232 static_cast<int>(controls[ccindex(port, 0, 1)]));
233 buffer[bufferpos++] = '|';
234 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
235 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
236 static_cast<int>(controls[ccindex(port, 0, 1)]));
237 return bufferpos;
241 unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
243 return ccindex(port, controller, control);
247 controls_t controls_t::operator^(controls_t other) throw()
249 controls_t x;
250 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
251 x.controls[i] = controls[i] ^ ((i < MAX_SYSTEM_CONTROLS) ? 0 : other.controls[i]);
252 return x;
255 controls_t::controls_t(bool sync) throw()
257 memset(controls, 0, sizeof(controls));
258 if(sync)
259 controls[CONTROL_FRAME_SYNC] = 1;
262 const short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error)
264 return controls[ccindex(port, controller, control)];
267 const short& controls_t::operator()(unsigned control) const throw(std::logic_error)
269 if(control >= TOTAL_CONTROLS)
270 throw std::logic_error("controls_t::operator(): Invalid control index");
271 return controls[control];
274 short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
276 return controls[ccindex(port, controller, control)];
279 short& controls_t::operator()(unsigned control) throw(std::logic_error)
281 if(control >= TOTAL_CONTROLS)
282 throw std::logic_error("controls_t::operator(): Invalid control index");
283 return controls[control];
286 controls_t::controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
287 throw(std::bad_alloc, std::runtime_error)
289 memset(controls, 0, sizeof(controls));
290 size_t position = 0;
291 position = cdecode::system(line, position, controls, version);
292 for(unsigned i = 0; i < decoders.size(); i++) {
293 if(position < line.length() && line[position] == '|')
294 position++;
295 position = decoders[i](i, line, position, controls);
299 std::string controls_t::tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc)
301 char buffer[1024];
302 size_t linelen = 0, tmp;
303 tmp = cencode::system(buffer, linelen, controls);
304 for(unsigned i = 0; i < encoders.size(); i++) {
305 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
306 buffer[(linelen = tmp)++] = '|';
307 tmp = encoders[i](i, buffer, linelen, controls);
309 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
310 linelen = tmp;
311 return std::string(buffer, buffer + linelen);
314 bool controls_t::operator==(const controls_t& c) const throw()
316 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
317 if(controls[i] != c.controls[i])
318 return false;
319 return true;
323 const port_type& port_type::lookup(const std::string& name, bool port2) throw(std::bad_alloc,
324 std::runtime_error)
326 for(unsigned i = 0; i <= PT_LAST_CTYPE; i++) {
327 if(name != port_types[i].name)
328 continue;
329 if(!port2 && !port_types[i].valid_port1)
330 throw std::runtime_error("Can't connect " + name + " to port #1");
331 return port_types[i];
333 throw std::runtime_error("Unknown port type '" + name + "'");
336 port_type port_types[] = {
337 { "none", cdecode::none, cencode::none, PT_NONE, 0, DT_NONE, true },
338 { "gamepad", cdecode::gamepad, cencode::gamepad, PT_GAMEPAD, 1, DT_GAMEPAD, true },
339 { "multitap", cdecode::multitap, cencode::multitap, PT_MULTITAP, 4, DT_GAMEPAD, true },
340 { "mouse", cdecode::mouse, cencode::mouse, PT_MOUSE, 1, DT_MOUSE, true },
341 { "superscope", cdecode::superscope, cencode::superscope, PT_SUPERSCOPE, 1, DT_SUPERSCOPE, false },
342 { "justifier", cdecode::justifier, cencode::justifier, PT_JUSTIFIER, 1, DT_JUSTIFIER, false },
343 { "justifiers", cdecode::justifiers, cencode::justifiers, PT_JUSTIFIERS, 2, DT_JUSTIFIER, false }