Add BSNES patches for v085
[lsnes.git] / src / core / controllerdata.cpp
blob3366c4b8740e739f0b99324be790069e87d8f2c2
1 #include "lsnes.hpp"
2 #include <snes/snes.hpp>
3 #include <ui-libsnes/libsnes.hpp>
5 #include "core/controllerdata.hpp"
7 #include <sstream>
8 #include <cctype>
9 #include <iostream>
10 #include <cstring>
12 namespace
14 inline unsigned ccindex(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
16 if(port >= MAX_PORTS || controller >= MAX_CONTROLLERS_PER_PORT || control >= CONTROLLER_CONTROLS) {
17 std::ostringstream x;
18 x << "ccindex: Invalid (port, controller, control) tuple (" << port << "," << controller
19 << "," << control << ")";
20 throw std::logic_error(x.str());
22 return MAX_SYSTEM_CONTROLS + port * CONTROLLER_CONTROLS * MAX_CONTROLLERS_PER_PORT +
23 CONTROLLER_CONTROLS * controller + control;
26 bool parse_button_ctrl(const std::string& str, size_t& pos) throw()
28 if(pos >= str.length())
29 return false;
30 switch(str[pos]) {
31 case '.':
32 case ' ':
33 case '\t':
34 pos++;
35 case '|':
36 return false;
37 default:
38 pos++;
39 return true;
43 short parse_number_ctrl(const std::string& str, size_t& pos) throw()
45 char ch;
46 //Skip ws.
47 while(pos < str.length()) {
48 char ch = str[pos];
49 if(ch != ' ' && ch != '\t')
50 break;
51 pos++;
53 //Read the sign if any.
54 if(pos >= str.length() || (ch = str[pos]) == '|')
55 return 0;
56 bool negative = false;
57 if(ch == '-') {
58 negative = true;
59 pos++;
61 if(ch == '+')
62 pos++;
64 //Read numeric value.
65 int numval = 0;
66 while(pos < str.length() && isdigit(static_cast<unsigned char>(ch = str[pos]))) {
67 numval = numval * 10 + (ch - '0');
68 pos++;
70 if(negative)
71 numval = -numval;
73 return static_cast<short>(numval);
76 void parse_end_of_field(const std::string& str, size_t& pos) throw()
78 while(pos < str.length() && str[pos] != '|')
79 pos++;
83 size_t cdecode::system(const std::string& line, size_t pos, short* controls, unsigned version) throw(std::bad_alloc,
84 std::runtime_error)
86 controls[0] = parse_button_ctrl(line, pos); //Frame sync.
87 controls[1] = parse_button_ctrl(line, pos); //Reset.
88 controls[2] = parse_number_ctrl(line, pos); //Reset cycles hi.
89 controls[3] = parse_number_ctrl(line, pos); //Reset cycles lo.
90 parse_end_of_field(line, pos);
91 return pos;
94 size_t cdecode::none(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
95 std::runtime_error)
97 return pos;
100 size_t cdecode::gamepad(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
101 std::runtime_error)
103 for(unsigned i = 0; i < 12; i++)
104 controls[ccindex(port, 0, i)] = parse_button_ctrl(line, pos);
105 parse_end_of_field(line, pos);
106 return pos;
109 size_t cdecode::multitap(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
110 std::runtime_error)
112 for(unsigned j = 0; j < 4; j++) {
113 for(unsigned i = 0; i < 12; i++)
114 controls[ccindex(port, j, i)] = parse_button_ctrl(line, pos);
115 parse_end_of_field(line, pos);
116 pos++;
118 return pos;
121 size_t cdecode::mouse(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
122 std::runtime_error)
124 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
125 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
126 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
127 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
128 parse_end_of_field(line, pos);
129 return pos;
132 size_t cdecode::superscope(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
133 std::runtime_error)
135 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
136 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
137 controls[ccindex(port, 0, 4)] = parse_button_ctrl(line, pos);
138 controls[ccindex(port, 0, 5)] = parse_button_ctrl(line, pos);
139 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
140 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
141 parse_end_of_field(line, pos);
142 return pos;
145 size_t cdecode::justifier(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
146 std::runtime_error)
148 controls[ccindex(port, 0, 2)] = parse_button_ctrl(line, pos);
149 controls[ccindex(port, 0, 3)] = parse_button_ctrl(line, pos);
150 controls[ccindex(port, 0, 0)] = parse_number_ctrl(line, pos);
151 controls[ccindex(port, 0, 1)] = parse_number_ctrl(line, pos);
152 parse_end_of_field(line, pos);
153 return pos;
156 size_t cdecode::justifiers(unsigned port, const std::string& line, size_t pos, short* controls) throw(std::bad_alloc,
157 std::runtime_error)
159 for(unsigned i = 0; i < 2; i++) {
160 controls[ccindex(port, i, 2)] = parse_button_ctrl(line, pos);
161 controls[ccindex(port, i, 3)] = parse_button_ctrl(line, pos);
162 controls[ccindex(port, i, 0)] = parse_number_ctrl(line, pos);
163 controls[ccindex(port, i, 1)] = parse_number_ctrl(line, pos);
164 parse_end_of_field(line, pos);
165 pos++;
167 return pos;
170 size_t cencode::system(char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
172 buffer[bufferpos++] = controls[0] ? 'F' : '.';
173 buffer[bufferpos++] = controls[1] ? 'R' : '.';
174 if(controls[2] || controls[3]) {
175 bufferpos += sprintf(buffer + bufferpos, " %i %i", static_cast<int>(controls[2]),
176 static_cast<int>(controls[3]));
178 return bufferpos;
181 size_t cencode::none(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
183 return ENCODE_SPECIAL_NO_OUTPUT;
186 size_t cencode::gamepad(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
188 static const char* characters = "BYsSudlrAXLR";
189 for(unsigned i = 0; i < 12; i++)
190 buffer[bufferpos++] = controls[ccindex(port, 0, i)] ? characters[i] : '.';
191 return bufferpos;
194 size_t cencode::multitap(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
196 static const char* characters = "BYsSudlrAXLR";
197 for(unsigned j = 0; j < 4; j++) {
198 for(unsigned i = 0; i < 12; i++)
199 buffer[bufferpos++] = controls[ccindex(port, j, i)] ? characters[i] : '.';
200 buffer[bufferpos++] = '|';
202 bufferpos--; //Eat the last '|', it shouldn't be there.
203 return bufferpos;
206 size_t cencode::mouse(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
208 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'L' : '.',
209 controls[ccindex(port, 0, 3)] ? 'R' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
210 static_cast<int>(controls[ccindex(port, 0, 1)]));
211 return bufferpos;
214 size_t cencode::superscope(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
216 bufferpos += sprintf(buffer + bufferpos, "%c%c%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
217 controls[ccindex(port, 0, 3)] ? 'C' : '.', controls[ccindex(port, 0, 4)] ? 'U' : '.',
218 controls[ccindex(port, 0, 5)] ? 'P' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
219 static_cast<int>(controls[ccindex(port, 0, 1)]));
220 return bufferpos;
223 size_t cencode::justifier(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
225 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
226 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
227 static_cast<int>(controls[ccindex(port, 0, 1)]));
228 return bufferpos;
231 size_t cencode::justifiers(unsigned port, char* buffer, size_t bufferpos, const short* controls) throw(std::bad_alloc)
233 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
234 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
235 static_cast<int>(controls[ccindex(port, 0, 1)]));
236 buffer[bufferpos++] = '|';
237 bufferpos += sprintf(buffer + bufferpos, "%c%c %i %i", controls[ccindex(port, 0, 2)] ? 'T' : '.',
238 controls[ccindex(port, 0, 3)] ? 'S' : '.', static_cast<int>(controls[ccindex(port, 0, 0)]),
239 static_cast<int>(controls[ccindex(port, 0, 1)]));
240 return bufferpos;
244 unsigned ccindex2(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
246 return ccindex(port, controller, control);
250 controls_t controls_t::operator^(controls_t other) throw()
252 controls_t x;
253 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
254 x.controls[i] = controls[i] ^ ((i < MAX_SYSTEM_CONTROLS) ? 0 : other.controls[i]);
255 return x;
258 controls_t::controls_t(bool sync) throw()
260 memset(controls, 0, sizeof(controls));
261 if(sync)
262 controls[CONTROL_FRAME_SYNC] = 1;
265 const short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) const throw(std::logic_error)
267 return controls[ccindex(port, controller, control)];
270 const short& controls_t::operator()(unsigned control) const throw(std::logic_error)
272 if(control >= TOTAL_CONTROLS)
273 throw std::logic_error("controls_t::operator(): Invalid control index");
274 return controls[control];
277 short& controls_t::operator()(unsigned port, unsigned controller, unsigned control) throw(std::logic_error)
279 return controls[ccindex(port, controller, control)];
282 short& controls_t::operator()(unsigned control) throw(std::logic_error)
284 if(control >= TOTAL_CONTROLS)
285 throw std::logic_error("controls_t::operator(): Invalid control index");
286 return controls[control];
289 controls_t::controls_t(const std::string& line, const std::vector<cdecode::fn_t>& decoders, unsigned version)
290 throw(std::bad_alloc, std::runtime_error)
292 memset(controls, 0, sizeof(controls));
293 size_t position = 0;
294 position = cdecode::system(line, position, controls, version);
295 for(unsigned i = 0; i < decoders.size(); i++) {
296 if(position < line.length() && line[position] == '|')
297 position++;
298 position = decoders[i](i, line, position, controls);
302 std::string controls_t::tostring(const std::vector<cencode::fn_t>& encoders) const throw(std::bad_alloc)
304 char buffer[1024];
305 size_t linelen = 0, tmp;
306 tmp = cencode::system(buffer, linelen, controls);
307 for(unsigned i = 0; i < encoders.size(); i++) {
308 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
309 buffer[(linelen = tmp)++] = '|';
310 tmp = encoders[i](i, buffer, linelen, controls);
312 if(tmp != ENCODE_SPECIAL_NO_OUTPUT)
313 linelen = tmp;
314 return std::string(buffer, buffer + linelen);
317 bool controls_t::operator==(const controls_t& c) const throw()
319 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
320 if(controls[i] != c.controls[i])
321 return false;
322 return true;
326 const port_type& port_type::lookup(const std::string& name, bool port2) throw(std::bad_alloc,
327 std::runtime_error)
329 for(unsigned i = 0; i <= PT_LAST_CTYPE; i++) {
330 if(name != port_types[i].name)
331 continue;
332 if(!port2 && !port_types[i].valid_port1)
333 throw std::runtime_error("Can't connect " + name + " to port #1");
334 return port_types[i];
336 throw std::runtime_error("Unknown port type '" + name + "'");
339 port_type port_types[] = {
340 { "none", cdecode::none, cencode::none, PT_NONE, 0, DT_NONE, true, SNES_DEVICE_NONE },
341 { "gamepad", cdecode::gamepad, cencode::gamepad, PT_GAMEPAD, 1, DT_GAMEPAD, true, SNES_DEVICE_JOYPAD },
342 { "multitap", cdecode::multitap, cencode::multitap, PT_MULTITAP, 4, DT_GAMEPAD, true, SNES_DEVICE_MULTITAP },
343 { "mouse", cdecode::mouse, cencode::mouse, PT_MOUSE, 1, DT_MOUSE, true, SNES_DEVICE_MOUSE },
344 { "superscope", cdecode::superscope, cencode::superscope, PT_SUPERSCOPE, 1, DT_SUPERSCOPE, false,
345 SNES_DEVICE_SUPER_SCOPE },
346 { "justifier", cdecode::justifier, cencode::justifier, PT_JUSTIFIER, 1, DT_JUSTIFIER, false,
347 SNES_DEVICE_JUSTIFIER },
348 { "justifiers", cdecode::justifiers, cencode::justifiers, PT_JUSTIFIERS, 2, DT_JUSTIFIER, false,
349 SNES_DEVICE_JUSTIFIERS }