207 lines
4.9 KiB
C++
207 lines
4.9 KiB
C++
#pragma once
|
|
|
|
// Base controller class
|
|
// by eien86
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <sstream>
|
|
|
|
namespace quickNES
|
|
{
|
|
|
|
class Controller
|
|
{
|
|
public:
|
|
|
|
enum controller_t { none, joypad, fourscore1, fourscore2 };
|
|
|
|
typedef uint32_t port_t;
|
|
|
|
struct input_t
|
|
{
|
|
bool power = false;
|
|
bool reset = false;
|
|
port_t port1 = 0;
|
|
port_t port2 = 0;
|
|
};
|
|
|
|
inline bool parseInputString(const std::string& input, input_t* decoded) const
|
|
{
|
|
// Parse valid flag
|
|
bool isValid = true;
|
|
|
|
// Converting input into a stream for parsing
|
|
std::istringstream ss(input);
|
|
|
|
// Start separator
|
|
if (ss.get() != '|') isValid = false;
|
|
|
|
// Parsing console inputs
|
|
isValid &= parseConsoleInputs(decoded->reset, decoded->power, ss);
|
|
|
|
// Parsing controller 1 inputs
|
|
isValid &= parseControllerInputs(_controller1Type, decoded->port1, ss);
|
|
|
|
// Parsing controller 1 inputs
|
|
isValid &= parseControllerInputs(_controller2Type, decoded->port2, ss);
|
|
|
|
// End separator
|
|
if (ss.get() != '|') isValid = false;
|
|
|
|
// If its not the end of the stream, then extra values remain and its invalid
|
|
ss.get();
|
|
if (ss.eof() == false) isValid = false;
|
|
|
|
// Returning valid flag
|
|
return isValid;
|
|
};
|
|
|
|
inline void setController1Type(const controller_t type) { _controller1Type = type; }
|
|
inline void setController2Type(const controller_t type) { _controller2Type = type; }
|
|
|
|
private:
|
|
|
|
static bool parseJoyPadInput(uint8_t& code, std::istringstream& ss)
|
|
{
|
|
// Currently read character
|
|
char c;
|
|
|
|
// Cleaning code
|
|
code = 0;
|
|
|
|
// Up
|
|
c = ss.get();
|
|
if (c != '.' && c != 'U') return false;
|
|
if (c == 'U') code |= 0b00010000;
|
|
|
|
// Down
|
|
c = ss.get();
|
|
if (c != '.' && c != 'D') return false;
|
|
if (c == 'D') code |= 0b00100000;
|
|
|
|
// Left
|
|
c = ss.get();
|
|
if (c != '.' && c != 'L') return false;
|
|
if (c == 'L') code |= 0b01000000;
|
|
|
|
// Right
|
|
c = ss.get();
|
|
if (c != '.' && c != 'R') return false;
|
|
if (c == 'R') code |= 0b10000000;
|
|
|
|
// Start
|
|
c = ss.get();
|
|
if (c != '.' && c != 'S') return false;
|
|
if (c == 'S') code |= 0b00001000;
|
|
|
|
// Select
|
|
c = ss.get();
|
|
if (c != '.' && c != 's') return false;
|
|
if (c == 's') code |= 0b00000100;
|
|
|
|
// B
|
|
c = ss.get();
|
|
if (c != '.' && c != 'B') return false;
|
|
if (c == 'B') code |= 0b00000010;
|
|
|
|
// A
|
|
c = ss.get();
|
|
if (c != '.' && c != 'A') return false;
|
|
if (c == 'A') code |= 0b00000001;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool parseControllerInputs(const controller_t type, port_t& port, std::istringstream& ss)
|
|
{
|
|
// Parse valid flag
|
|
bool isValid = true;
|
|
|
|
// If no controller assigned then, its port is all zeroes.
|
|
if (type == controller_t::none) { port = 0; return true; }
|
|
|
|
// Controller separator
|
|
if (ss.get() != '|') isValid = false;
|
|
|
|
// If normal joypad, parse its code now
|
|
if (type == controller_t::joypad)
|
|
{
|
|
// Storage for joypad's code
|
|
uint8_t code = 0;
|
|
|
|
// Parsing joypad code
|
|
isValid &= parseJoyPadInput(code, ss);
|
|
|
|
// Pushing input code into the port
|
|
port = code;
|
|
|
|
// Adding joypad signature
|
|
// Per https://www.nesdev.org/wiki/Standard_controller, the joypad reports 1s after the first 8 bits
|
|
port |= ~0xFF;
|
|
}
|
|
|
|
// If its fourscore, its like two joypads separated by a |
|
|
if (type == controller_t::fourscore1 || type == controller_t::fourscore2)
|
|
{
|
|
// Storage for joypad's code
|
|
uint8_t code1 = 0;
|
|
uint8_t code2 = 0;
|
|
|
|
// Parsing joypad code1
|
|
isValid &= parseJoyPadInput(code1, ss);
|
|
|
|
// Separator
|
|
if (ss.get() != '|') return false;
|
|
|
|
// Parsing joypad code1
|
|
isValid &= parseJoyPadInput(code2, ss);
|
|
|
|
// Creating code
|
|
port = code1;
|
|
port |= (uint32_t)0 | code2 << 8;
|
|
if (type == controller_t::fourscore1) port |= (uint32_t)0 | 1 << 19;
|
|
if (type == controller_t::fourscore2) port |= (uint32_t)0 | 1 << 18;
|
|
port |= (uint32_t)0 | 1 << 24;
|
|
port |= (uint32_t)0 | 1 << 25;
|
|
port |= (uint32_t)0 | 1 << 26;
|
|
port |= (uint32_t)0 | 1 << 27;
|
|
port |= (uint32_t)0 | 1 << 28;
|
|
port |= (uint32_t)0 | 1 << 29;
|
|
port |= (uint32_t)0 | 1 << 30;
|
|
port |= (uint32_t)0 | 1 << 31;
|
|
}
|
|
// Return valid flag
|
|
return isValid;
|
|
}
|
|
|
|
static bool parseConsoleInputs(bool& reset, bool& power, std::istringstream& ss)
|
|
{
|
|
// Parse valid flag
|
|
bool isValid = true;
|
|
|
|
// Currently read character
|
|
char c;
|
|
|
|
// Power trigger
|
|
c = ss.get();
|
|
if (c != '.' && c != 'P') isValid = false;
|
|
if (c == 'P') power = true;
|
|
if (c == '.') power = false;
|
|
|
|
// Reset trigger
|
|
c = ss.get();
|
|
if (c != '.' && c != 'r') isValid = false;
|
|
if (c == 'r') reset = true;
|
|
if (c == '.') reset = false;
|
|
|
|
// Return valid flag
|
|
return isValid;
|
|
}
|
|
|
|
controller_t _controller1Type;
|
|
controller_t _controller2Type;
|
|
|
|
}; // class Controller
|
|
|
|
} // namespace quickNES
|