1
0
mirror of https://github.com/blawar/ooot.git synced 2024-07-02 09:03:36 +00:00

TAS support and input cleanup (#154)

* Cleaned up the input stuff.

* Put all the input related classes into the namespace `hid` and did some further cleanup.

* TAS support is now working.
Generates `last-run.tas`.

* Added *.tas to .gitignore.

* Put `hid` into namespace `oot`.

* Added a trivial form of compression for TAS files.

Co-authored-by: DaMarkov <DaMarkovZED@gmail.com>
This commit is contained in:
DaMarkov 2022-02-15 23:16:29 +01:00 committed by GitHub
parent 418dd1b650
commit 0dec26fce0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1622 additions and 1562 deletions

1
.gitignore vendored
View File

@ -27,6 +27,7 @@ docs/doxygen/
out.txt
*.log
*.sav
*.tas
# Tool artifacts

View File

@ -3,6 +3,7 @@
#include <ctime>
#include <fstream>
#include <filesystem>
#include "tas.h"
#include "../options.h"
#ifdef __SWITCH__
@ -11,240 +12,269 @@
#define TAS_DIR "tas"
#endif
namespace sm64::hid
using namespace oot::hid;
N64Controller::State::State()
{
State::State()
{
mouse_x = 0;
mouse_y = 0;
has_mouse = false;
mouse_x = 0;
mouse_y = 0;
has_mouse = false;
reset();
reset();
}
void N64Controller::State::reset()
{
button = 0;
stick_x = 0;
stick_y = 0;
errnum = 0;
r_stick_x = 0;
r_stick_y = 0;
has_mouse = false;
}
N64Controller::N64Controller(bool isLocal) : rawStickX(0), rawStickY(0), stickX(0), stickY(0), stickMag(0),
buttonDown(0), buttonPressed(0), r_rawStickX(0), r_rawStickY(0), r_stickX(0), r_stickY(0), r_stickMag(0),
m_isLocal(isLocal), m_state(), m_motorEnabled(false)
{
}
N64Controller::~N64Controller()
{
if (m_tasFile)//Tas file is open
fclose(m_tasFile);
m_tasFile = nullptr;
}
void N64Controller::merge(const N64Controller& controller)
{
m_state.button |= controller.m_state.button;
if (abs(m_state.stick_x) < abs(controller.m_state.stick_x))
m_state.stick_x = controller.m_state.stick_x;
if (abs(m_state.stick_y) < abs(controller.m_state.stick_y))
m_state.stick_y = controller.m_state.stick_y;
if (abs(m_state.r_stick_x) < abs(controller.m_state.r_stick_x))
m_state.r_stick_x = controller.m_state.r_stick_x;
if (abs(m_state.r_stick_y) < abs(controller.m_state.r_stick_y))
m_state.r_stick_y = controller.m_state.r_stick_y;
if (controller.hasMouse())
{
m_state.mouse_x = controller.m_state.mouse_x;
m_state.mouse_y = controller.m_state.mouse_y;
}
void State::reset()
{
button = 0;
stick_x = 0;
stick_y = 0;
errnum = 0;
r_stick_x = 0;
r_stick_y = 0;
has_mouse = false;
}
m_state.has_mouse |= controller.m_state.has_mouse;
}
Controller::Controller(bool isLocal) :
rawStickX(0), rawStickY(0), stickX(0), stickY(0), stickMag(0), buttonDown(0), buttonPressed(0), r_rawStickX(0), r_rawStickY(0), r_stickX(0), r_stickY(0), r_stickMag(0), m_isLocal(isLocal), m_state(), m_motorEnabled(false)
{
}
void Controller::SendMotorEvent(short time, short level)
{
}
void Controller::SendMotorDecay(short level)
{
}
static std::string getTasFileName()
{
return "last_run.tas";
/*std::error_code error;
std::filesystem::create_directory(TAS_DIR, error);
void Controller::ResetMotorPack()
{
}
time_t now = time(0);
tm* time = localtime(&now);
void Controller::SendMotorVib(int level)
{
}
if (!time)
return TAS_DIR"/record.tas";
void Controller::update()
{
}
char buf[64] = { 0 };
sprintf(buf, TAS_DIR"/%04d.%02d.%02d-%04d.tas", time->tm_year, time->tm_mon + 1, time->tm_mday, time->tm_hour * 60 + time->tm_min);
return buf;*/
}
void Controller::merge(const Controller& controller)
{
m_state.button |= controller.m_state.button;
if(abs(m_state.stick_x) < abs(controller.m_state.stick_x))
static std::ofstream g_tas;
static std::ofstream& stream()
{
/*if (!g_tas)
{
auto name = getTasFileName();
g_tas = std::ofstream(name, std::ofstream::binary);
if (g_tas)
g_tas.write((const char*)&oot::config(), sizeof(oot::config()));
}*/
return g_tas;
}
void N64Controller::resolveInputs()
{
#define SRAM_SIZE 0x8000//But this in ultra_reimplementation.h?
//Recording TAS?
if (!Tas::isTasPlaying() && oot::config().game().recordTas())
{
//stream().write((const char*)&m_state, sizeof(m_state));
//Write TAS data
if (!m_tasFile)//Not open yet?
{
m_state.stick_x = controller.m_state.stick_x;
}
fopen_s(&m_tasFile, "last-run.tas", "wb");
if(abs(m_state.stick_y) < abs(controller.m_state.stick_y))
{
m_state.stick_y = controller.m_state.stick_y;
}
if (!m_tasFile)
return;
if(abs(m_state.r_stick_x) < abs(controller.m_state.r_stick_x))
{
m_state.r_stick_x = controller.m_state.r_stick_x;
}
//Write our current config
//fwrite(&oot::config(), sizeof(oot::config()), 1, m_tasOutput);
if(abs(m_state.r_stick_y) < abs(controller.m_state.r_stick_y))
{
m_state.r_stick_y = controller.m_state.r_stick_y;
}
if(controller.hasMouse())
{
m_state.mouse_x = controller.m_state.mouse_x;
m_state.mouse_y = controller.m_state.mouse_y;
}
m_state.has_mouse |= controller.m_state.has_mouse;
}
bool Controller::hasMouse() const
{
return m_state.has_mouse;
}
static std::string getTasFileName()
{
std::error_code error;
std::filesystem::create_directory(TAS_DIR, error);
time_t now = time(0);
tm* ltm = localtime(&now);
if (!ltm)
{
return TAS_DIR"/record.tas";
}
char buf[64] = { 0 };
sprintf(buf, TAS_DIR"/%04d.%02d.%02d-%04d.tas", ltm->tm_year, ltm->tm_mon + 1, ltm->tm_mday, ltm->tm_hour * 60 + ltm->tm_min);
return buf;
}
#ifdef ENABLE_TAS
static std::ofstream* g_tas = nullptr;
static std::ofstream& stream()
{
if (!g_tas)
{
auto name = getTasFileName();
g_tas = new std::ofstream(name, std::ifstream::binary);
g_tas->write((const char*)&config(), sizeof(config()));
}
return *g_tas;
}
#endif
void Controller::resolveInputs()
{
#ifdef ENABLE_TAS
if (!hid::isTasPlaying() && config().game().recordTas())
{
stream().write((const char*)&m_state, sizeof(m_state));
if (m_state.button)
//Write save data
uint8_t* sram = new uint8_t[SRAM_SIZE];
FILE* save = nullptr;
fopen_s(&save, "oot.sav", "wb");
if (save)
{
int y = 0;
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
fclose(save);
}
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, m_tasFile);
delete[] sram;
}
if (m_tasFile)//Tas file is open
{
//fwrite(&m_state, sizeof(m_state), 1, m_tasFile);
StateCompressed compressed(m_state);//Compress the state
compressed.write(m_tasFile);//Write to TAS file
#ifdef _DEBUG
fflush(m_tasFile);//Flush every frame to keep all the data when the game crashes
#endif
rawStickX = m_state.stick_x;
rawStickY = m_state.stick_y;
r_rawStickX = m_state.r_stick_x;
r_rawStickY = m_state.r_stick_y;
buttonPressed = m_state.button & (m_state.button ^ buttonDown);
// 0.5x A presses are a good meme
buttonDown = m_state.button;
if(oot::config().game().mirror())
{
rawStickX = -rawStickX;
r_rawStickX = -r_rawStickX;
}
// reset the controller's x and y floats.
this->stickX = 0;
this->stickY = 0;
// modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
if(this->rawStickX <= -8)
{
this->stickX = this->rawStickX + 6;
}
if(this->rawStickX >= 8)
{
this->stickX = this->rawStickX - 6;
}
if(this->rawStickY <= -8)
{
this->stickY = this->rawStickY + 6;
}
if(this->rawStickY >= 8)
{
this->stickY = this->rawStickY - 6;
}
// calculate f32 magnitude from the center by vector length.
this->stickMag = sqrtf(this->stickX * this->stickX + this->stickY * this->stickY);
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
// flatten the values down to the allowed maximum value.
if(this->stickMag > 64)
{
this->stickX *= 64 / this->stickMag;
this->stickY *= 64 / this->stickMag;
this->stickMag = 64;
}
this->r_stickX = 0;
this->r_stickY = 0;
if(this->r_rawStickX <= -8)
{
this->r_stickX = this->r_rawStickX + 6;
}
if(this->r_rawStickX >= 8)
{
this->r_stickX = this->r_rawStickX - 6;
}
if(this->r_rawStickY <= -8)
{
this->r_stickY = this->r_rawStickY + 6;
}
if(this->r_rawStickY >= 8)
{
this->r_stickY = this->r_rawStickY - 6;
}
// calculate f32 magnitude from the center by vector length.
this->r_stickMag = sqrtf(this->r_stickX * this->r_stickX + this->r_stickY * this->r_stickY);
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
// flatten the values down to the allowed maximum value.
if(this->r_stickMag > 64)
{
this->r_stickX *= 64 / this->r_stickMag;
this->r_stickY *= 64 / this->r_stickMag;
this->r_stickMag = 64;
}
}
s64 Controller::mouse_x() const
//Playing back a TAS?
else if (Tas::isTasPlaying())
{
return m_state.mouse_x * (oot::config().camera().mousexInvert() ? -1 : 1) * oot::config().camera().mousexScaler();
if (!m_tasFile)
{
m_tasFile = fopen("last-run.tas", "rb");
if (!m_tasFile)
return;
//fread(&oot::config(), 1, sizeof(oot::config()), fp);
uint8_t* sram = new uint8_t[SRAM_SIZE];
fread(sram, sizeof(uint8_t), SRAM_SIZE, m_tasFile);
FILE* save = nullptr;
fopen_s(&save, "oot.sav", "wb");
if (save)
{
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
fclose(save);
}
delete[] sram;
}
if (m_tasFile)
{
//fread(&m_state, sizeof(m_state), 1, m_tasFile);//Uncompressed read
StateCompressed compressed;
compressed.read(m_tasFile);//Read the compressed state
m_state = compressed;//Uncompress
}
}
s64 Controller::mouse_y() const
rawStickX = m_state.stick_x;
rawStickY = m_state.stick_y;
r_rawStickX = m_state.r_stick_x;
r_rawStickY = m_state.r_stick_y;
buttonPressed = m_state.button & (m_state.button ^ buttonDown);
buttonDown = m_state.button;
if (oot::config().game().mirror())
{
return m_state.mouse_y * (oot::config().camera().mouseyInvert() ? -1 : 1) * oot::config().camera().mouseyScaler();
rawStickX = -rawStickX;
r_rawStickX = -r_rawStickX;
}
bool Controller::updateRebind(int input)
// reset the controller's x and y floats.
this->stickX = 0;
this->stickY = 0;
// modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
if (this->rawStickX <= -8)
this->stickX = this->rawStickX + 6;
if (this->rawStickX >= 8)
this->stickX = this->rawStickX - 6;
if (this->rawStickY <= -8)
this->stickY = this->rawStickY + 6;
if (this->rawStickY >= 8)
this->stickY = this->rawStickY - 6;
// calculate f32 magnitude from the center by vector length.
this->stickMag = sqrtf(this->stickX * this->stickX + this->stickY * this->stickY);
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
// flatten the values down to the allowed maximum value.
if (this->stickMag > 64)
{
return false;
this->stickX *= 64 / this->stickMag;
this->stickY *= 64 / this->stickMag;
this->stickMag = 64;
}
} // namespace sm64::hid
this->r_stickX = 0;
this->r_stickY = 0;
if (this->r_rawStickX <= -8)
this->r_stickX = this->r_rawStickX + 6;
if (this->r_rawStickX >= 8)
this->r_stickX = this->r_rawStickX - 6;
if (this->r_rawStickY <= -8)
this->r_stickY = this->r_rawStickY + 6;
if (this->r_rawStickY >= 8)
this->r_stickY = this->r_rawStickY - 6;
// calculate f32 magnitude from the center by vector length.
this->r_stickMag = sqrtf(this->r_stickX * this->r_stickX + this->r_stickY * this->r_stickY);
// magnitude cannot exceed 64.0f: if it does, modify the values appropriately to
// flatten the values down to the allowed maximum value.
if (this->r_stickMag > 64)
{
this->r_stickX *= 64 / this->r_stickMag;
this->r_stickY *= 64 / this->r_stickMag;
this->r_stickMag = 64;
}
}
s64 N64Controller::mouse_x() const
{
return m_state.mouse_x * (oot::config().camera().mousexInvert() ? -1 : 1) * oot::config().camera().mousexScaler();
}
s64 N64Controller::mouse_y() const
{
return m_state.mouse_y * (oot::config().camera().mouseyInvert() ? -1 : 1) * oot::config().camera().mouseyScaler();
}
/*bool N64Controller::updateRebind(int input)
{
return false;
}*/

View File

@ -1,111 +1,130 @@
#pragma once
#include "ultra64/types.h"
#include <stdio.h>
namespace sm64::hid
namespace oot
{
enum Button
namespace hid
{
CONT_A = 0x8000,
CONT_B = 0x4000,
CONT_G = 0x2000,
CONT_START = 0x1000,
CONT_UP = 0x0800,
CONT_DOWN = 0x0400,
CONT_LEFT = 0x0200,
CONT_RIGHT = 0x0100,
CONT_L = 0x0020,
CONT_R = 0x0010,
CONT_E = 0x0008,
CONT_D = 0x0004,
CONT_C = 0x0002,
CONT_F = 0x0001,
A_BUTTON = CONT_A,
B_BUTTON = CONT_B,
L_TRIG = CONT_L,
R_TRIG = CONT_R,
Z_TRIG = CONT_G,
START_BUTTON = CONT_START,
U_JPAD = CONT_UP,
L_JPAD = CONT_LEFT,
R_JPAD = CONT_RIGHT,
D_JPAD = CONT_DOWN,
U_CBUTTONS = CONT_E,
L_CBUTTONS = CONT_C,
R_CBUTTONS = CONT_F,
D_CBUTTONS = CONT_D,
STICK_X_LEFT = 0x10000,
STICK_X_RIGHT = 0x20000,
STICK_X_DOWN = 0x80000,
STICK_X_UP = 0x40000,
WALK_BUTTON = 0x100000
};
class State
{
public:
State();
void reset();
u16 button;
s8 stick_x; /* -80 <= stick_x <= 80 */
s8 stick_y; /* -80 <= stick_y <= 80 */
u8 errnum;
s8 r_stick_x; /* -80 <= stick_x <= 80 */
s8 r_stick_y; /* -80 <= stick_y <= 80 */
s64 mouse_x;
s64 mouse_y;
bool has_mouse;
};
class Controller
{
public:
s16 rawStickX;
s16 rawStickY;
float stickX; // [-64, 64] positive is right
float stickY; // [-64, 64] positive is up
float stickMag; // distance from center [0, 64]
u16 buttonDown;
u16 buttonPressed;
s16 r_rawStickX; //
s16 r_rawStickY; //
float r_stickX; // [-64, 64] positive is right
float r_stickY; // [-64, 64] positive is up
float r_stickMag; // distance from center [0, 64]
s64 mouse_x() const;
s64 mouse_y() const;
Controller(bool isLocal = true);
virtual void update();
virtual void resolveInputs();
virtual bool isLocal() const
class N64Controller
{
return m_isLocal;
}
State& state()
{
return m_state;
}
public:
class State
{
public:
State();
void reset();
virtual void merge(const Controller& controller);
virtual bool hasMouse() const;
u16 button;
s8 stick_x; /* -80 <= stick_x <= 80 */
s8 stick_y; /* -80 <= stick_y <= 80 */
u8 errnum;
s8 r_stick_x; /* -80 <= stick_x <= 80 */
s8 r_stick_y; /* -80 <= stick_y <= 80 */
virtual void SendMotorEvent(short time, short level);
virtual void SendMotorDecay(short level);
virtual void ResetMotorPack();
virtual void SendMotorVib(int level);
s64 mouse_x;
s64 mouse_y;
bool has_mouse;
};
virtual bool updateRebind(int input);
class StateCompressed
{
public:
StateCompressed() = default;
StateCompressed(State state)
{
m_data.button = state.button;
m_data.stick_x = state.stick_x;
m_data.stick_y = state.stick_y;
//Is there any real data?
m_isUsed = m_data.button || m_data.stick_x || m_data.stick_y;
}
operator State ()
{
State ret;
ret.button = m_data.button;
ret.stick_x = m_data.stick_x;
ret.stick_y = m_data.stick_y;
return ret;
}
void read(FILE* in) {
fread(&m_isUsed, sizeof(u8), 1, in);//Read the flag
if (m_isUsed)//Is there even any input?
fread(&m_data, sizeof(InputFrame), 1, in);//Write the full state
}
void write(FILE* out) {
fwrite(&m_isUsed, sizeof(u8), 1, out);//Write if there is data
if (m_isUsed)//Is there even any input?
fwrite(&m_data, sizeof(InputFrame), 1, out);//Write the full state
}
private:
u8 m_isUsed = 0;//0 when all input is zero
struct InputFrame {
u16 button = 0x0000;
s8 stick_x = 0; /* -80 <= stick_x <= 80 */
s8 stick_y = 0; /* -80 <= stick_y <= 80 */
} m_data;
};
s16 rawStickX;
s16 rawStickY;
float stickX; // [-64, 64] positive is right
float stickY; // [-64, 64] positive is up
float stickMag; // distance from center [0, 64]
u16 buttonDown;
u16 buttonPressed;
s16 r_rawStickX; //
s16 r_rawStickY; //
float r_stickX; // [-64, 64] positive is right
float r_stickY; // [-64, 64] positive is up
float r_stickMag; // distance from center [0, 64]
s64 mouse_x() const;
s64 mouse_y() const;
N64Controller(bool isLocal = true);
~N64Controller();
virtual void update() {}
virtual void resolveInputs();
virtual bool isLocal() const {
return m_isLocal;
}
State& state() {
return m_state;
}
const State state() const {
return m_state;
}
virtual void merge(const N64Controller& controller);
virtual bool hasMouse() const { return m_state.has_mouse; }
virtual void SendMotorEvent(short time, short level) {}
virtual void SendMotorDecay(short level) {}
virtual void ResetMotorPack() {}
virtual void SendMotorVib(int level) {}
//virtual bool updateRebind(Button input);
protected:
State m_state;
bool m_isLocal;
bool m_motorEnabled;
};
State m_state;
bool m_isLocal;
bool m_motorEnabled;
bool isTasPlaying();
} // namespace sm64::hid
private:
FILE* m_tasFile = nullptr;
};
}
}

View File

@ -5,138 +5,107 @@
#include <stdexcept>
#include "../options.h"
namespace sm64::hid
using namespace oot::hid;
static InputDeviceManager* g_deviceManager = nullptr;
InputDeviceManager& InputDeviceManager::get()
{
static Controllers* g_controllers;
if (!g_deviceManager)
g_deviceManager = new InputDeviceManager();
return *g_deviceManager;
}
Controllers& controllers()
N64Controller& InputDevice::controller(const u64 index)
{
if (index >= m_controllers.size())
throw std::runtime_error("invalid controller index");
return *m_controllers[index];
}
bool InputDevice::updateRebind(Button input)
{
bool result = 0;
for (auto& controller : m_controllers)
{
if(!g_controllers)
{
g_controllers = new Controllers();
}
return *g_controllers;
controller->state().reset();
//result |= controller->updateRebind(input);
controller->resolveInputs();
}
return result;
}
Driver::Driver()
void InputDevice::update()
{
for(auto& controller : m_controllers)
{
controller->state().reset();
controller->update();
controller->resolveInputs();
}
}
Driver::~Driver()
{
}
const u64 Driver::size() const
{
return m_controllers.size();
}
Controller& Driver::controller(const u64 index)
{
if(index >= m_controllers.size())
{
throw std::runtime_error("invalid controller index");
}
return *m_controllers[index];
}
bool Driver::updateRebind(int input)
{
bool result = 0;
for (auto& controller : m_controllers)
{
controller->state().reset();
result |= controller->updateRebind(input);
controller->resolveInputs();
}
return result;
}
void Driver::update()
{
for(auto& controller : m_controllers)
{
controller->state().reset();
controller->update();
controller->resolveInputs();
}
}
void Driver::scan(class Controllers* controllers)
{
}
Controllers::Controllers() : m_rebindInput(0)
{
m_drivers.push_back(new SDL());
InputDeviceManager::InputDeviceManager() : m_rebindInput(Button::NONE)
{
m_drivers.push_back(new Joypad());
#ifdef ENABLE_MOUSE
m_drivers.push_back(new Keyboard());
m_drivers.push_back(new Keyboard());
#endif
//if (Tas::isTasPlaying())
//m_drivers.push_back(new Tas());
}
}
Controllers::~Controllers()
const u64 InputDeviceManager::size() const
{
u64 count = 0;
for (auto& driver : m_drivers)
count += driver->size();
return count;
}
void InputDeviceManager::update()
{
if (isRebindMode())
{
}
bool result = 0;
const u64 Controllers::size() const
for (auto& driver : m_drivers)
result |= driver->updateRebind(m_rebindInput);
if (result)
m_rebindInput = Button::NONE;
}
else
{
u64 count = 0;
for(auto& driver : m_drivers)
{
count += driver->size();
}
return count;
for (auto& driver : m_drivers)
driver->update();
}
}
void Controllers::update()
void InputDeviceManager::scan()
{
for (auto& driver : m_drivers)
{
if (isRebindMode())
{
bool result = 0;
for (auto& driver : m_drivers)
{
result |= driver->updateRebind(m_rebindInput);
}
if (result)
{
m_rebindInput = 0;
}
}
else
{
for (auto& driver : m_drivers)
{
driver->update();
}
}
//if(!driver->defaultOnly() || !found)
driver->scan();
}
void Controllers::scan()
{
u64 found = 0;
for(auto& driver : m_drivers)
{
//if(!driver->defaultOnly() || !found)
{
driver->scan(this);
found += driver->size();
}
}
}
void Controllers::rebind(int input)
{
m_rebindInput = input;
}
bool Controllers::isRebindMode() const
{
return m_rebindInput > 0;
}
} // namespace sm64::hid
}

View File

@ -1,55 +1,97 @@
#pragma once
#include "ultra64/types.h"
#include "controller.h"
#include <vector>
#include <memory>
namespace sm64::hid
namespace oot
{
class Driver
namespace hid
{
public:
Driver();
virtual ~Driver();
virtual const u64 size() const;
virtual Controller& controller(const u64 index);
virtual void update();
virtual bool updateRebind(int input);
virtual void scan(class Controllers* controllers);
virtual bool defaultOnly()
enum class Button : uint32_t
{
return false;
}
NONE = 0x0000,
CONT_A = 0x8000,
CONT_B = 0x4000,
CONT_G = 0x2000,
CONT_START = 0x1000,
CONT_UP = 0x0800,
CONT_DOWN = 0x0400,
CONT_LEFT = 0x0200,
CONT_RIGHT = 0x0100,
CONT_L = 0x0020,
CONT_R = 0x0010,
CONT_E = 0x0008,
CONT_D = 0x0004,
CONT_C = 0x0002,
CONT_F = 0x0001,
std::vector<std::shared_ptr<Controller>>& controllers()
A_BUTTON = CONT_A,
B_BUTTON = CONT_B,
L_TRIG = CONT_L,
R_TRIG = CONT_R,
Z_TRIG = CONT_G,
START_BUTTON = CONT_START,
U_JPAD = CONT_UP,
L_JPAD = CONT_LEFT,
R_JPAD = CONT_RIGHT,
D_JPAD = CONT_DOWN,
U_CBUTTONS = CONT_E,
L_CBUTTONS = CONT_C,
R_CBUTTONS = CONT_F,
D_CBUTTONS = CONT_D,
STICK_X_LEFT = 0x10000,
STICK_X_RIGHT = 0x20000,
STICK_X_DOWN = 0x80000,
STICK_X_UP = 0x40000,
WALK_BUTTON = 0x100000
};
class InputDevice;
class InputDeviceManager
{
return m_controllers;
}
public:
static InputDeviceManager& get();
protected:
std::vector<std::shared_ptr<Controller>> m_controllers;
};
InputDeviceManager();
virtual ~InputDeviceManager() {}
const u64 size() const;
void update();
void scan();
bool isRebindMode() const { return m_rebindInput != Button::NONE; }
void rebind(Button input) { m_rebindInput = input; }
std::vector<InputDevice*>& drivers() { return m_drivers; }
class Controllers
{
public:
Controllers();
virtual ~Controllers();
const u64 size() const;
void update();
void scan();
bool isRebindMode() const;
void rebind(int input);
std::vector<class Driver*>& drivers()
protected:
std::vector<InputDevice*> m_drivers;
Button m_rebindInput;
};
class InputDevice
{
return m_drivers;
}
public:
InputDevice() = default;
virtual ~InputDevice() {}
protected:
std::vector<class Driver*> m_drivers;
int m_rebindInput;
};
virtual void scan() = 0;
Controllers& controllers();
} // namespace sm64::hid
virtual const u64 size() const { return m_controllers.size(); }
virtual N64Controller& controller(const u64 index);
virtual void update();
virtual bool updateRebind(Button input);
virtual bool defaultOnly() { return false; }
std::vector<std::shared_ptr<N64Controller>>& controllers() { return m_controllers; }
protected:
std::vector<std::shared_ptr<N64Controller>> m_controllers;
};
}
}

View File

@ -15,11 +15,16 @@
#endif
#include "../player/players.h"
#include "../options.h"
#include "tas.h"
extern "C"
{
void set_fullscreen(bool value);
}
using namespace oot::hid;
u8 Get_Language();
void Set_Language(u8 language_id);
@ -41,399 +46,347 @@ bool saveJson(rapidjson::Document& doc, const std::string& jsonFilePath)
}
#endif
namespace sm64::hid
const char* Keyboard::getInputName(Button input)
{
namespace controller
switch (input)
{
const char* getInputName(int input)
{
switch (input)
{
case STICK_X_UP: return "STICK_X_UP";
case STICK_X_LEFT: return "STICK_X_LEFT";
case STICK_X_DOWN: return "STICK_X_DOWN";
case STICK_X_RIGHT: return "STICK_X_RIGHT";
case A_BUTTON: return "A_BUTTON";
case B_BUTTON: return "B_BUTTON";
case Z_TRIG: return "Z_TRIG";
case U_CBUTTONS: return "U_CBUTTONS";
case L_CBUTTONS: return "L_CBUTTONS";
case D_CBUTTONS: return "D_CBUTTONS";
case R_CBUTTONS: return "R_CBUTTONS";
case R_TRIG: return "R_TRIG";
case START_BUTTON: return "START_BUTTON";
case WALK_BUTTON: return "WALK_BUTTON";
}
return "";
}
case Button::STICK_X_UP: return "STICK_X_UP";
case Button::STICK_X_LEFT: return "STICK_X_LEFT";
case Button::STICK_X_DOWN: return "STICK_X_DOWN";
case Button::STICK_X_RIGHT: return "STICK_X_RIGHT";
case Button::A_BUTTON: return "A_BUTTON";
case Button::B_BUTTON: return "B_BUTTON";
case Button::Z_TRIG: return "Z_TRIG";
case Button::U_CBUTTONS: return "U_CBUTTONS";
case Button::L_CBUTTONS: return "L_CBUTTONS";
case Button::D_CBUTTONS: return "D_CBUTTONS";
case Button::R_CBUTTONS: return "R_CBUTTONS";
case Button::R_TRIG: return "R_TRIG";
case Button::START_BUTTON: return "START_BUTTON";
case Button::WALK_BUTTON: return "WALK_BUTTON";
}
return "";
}
int getInputValue(const std::string& input)
{
if (input == "STICK_X_UP") return STICK_X_UP;
if (input == "STICK_X_LEFT") return STICK_X_LEFT;
if (input == "STICK_X_DOWN") return STICK_X_DOWN;
if (input == "STICK_X_RIGHT") return STICK_X_RIGHT;
if (input == "A_BUTTON") return A_BUTTON;
if (input == "B_BUTTON") return B_BUTTON;
if (input == "Z_TRIG") return Z_TRIG;
if (input == "U_CBUTTONS") return U_CBUTTONS;
if (input == "L_CBUTTONS") return L_CBUTTONS;
if (input == "D_CBUTTONS") return D_CBUTTONS;
if (input == "R_CBUTTONS") return R_CBUTTONS;
if (input == "R_TRIG") return R_TRIG;
if (input == "START_BUTTON") return START_BUTTON;
if (input == "WALK_BUTTON") return WALK_BUTTON;
return 0;
}
class Keyboard : public Controller
{
public:
Keyboard() : Controller()
{
memset(m_lastKeyState, 0, sizeof(m_lastKeyState));
m_keyBindings[SDL_SCANCODE_W] = STICK_X_UP;
m_keyBindings[SDL_SCANCODE_A] = STICK_X_LEFT;
m_keyBindings[SDL_SCANCODE_S] = STICK_X_DOWN;
m_keyBindings[SDL_SCANCODE_D] = STICK_X_RIGHT;
m_keyBindings[SDL_SCANCODE_SPACE] = A_BUTTON;
m_keyBindings[SDL_SCANCODE_F] = B_BUTTON;
m_keyBindings[SDL_SCANCODE_O] = A_BUTTON;
m_keyBindings[SDL_SCANCODE_P] = B_BUTTON;
m_keyBindings[SDL_SCANCODE_LSHIFT] = Z_TRIG;
m_keyBindings[SDL_SCANCODE_C] = Z_TRIG;
m_keyBindings[SDL_SCANCODE_I] = U_JPAD;
m_keyBindings[SDL_SCANCODE_J] = L_JPAD;
m_keyBindings[SDL_SCANCODE_K] = D_JPAD;
m_keyBindings[SDL_SCANCODE_L] = R_JPAD;
m_keyBindings[SDL_SCANCODE_UP] = U_CBUTTONS;
m_keyBindings[SDL_SCANCODE_LEFT] = L_CBUTTONS;
m_keyBindings[SDL_SCANCODE_DOWN] = D_CBUTTONS;
m_keyBindings[SDL_SCANCODE_RIGHT] = R_CBUTTONS;
m_keyBindings[SDL_SCANCODE_X] = L_TRIG;
m_keyBindings[SDL_SCANCODE_V] = R_TRIG;
m_keyBindings[SDL_SCANCODE_RSHIFT] = R_TRIG;
m_keyBindings[SDL_SCANCODE_RETURN] = START_BUTTON;
Button Keyboard::getInputValue(const std::string& input)
{
if (input == "STICK_X_UP") return Button::STICK_X_UP;
if (input == "STICK_X_LEFT") return Button::STICK_X_LEFT;
if (input == "STICK_X_DOWN") return Button::STICK_X_DOWN;
if (input == "STICK_X_RIGHT") return Button::STICK_X_RIGHT;
if (input == "A_BUTTON") return Button::A_BUTTON;
if (input == "B_BUTTON") return Button::B_BUTTON;
if (input == "Z_TRIG") return Button::Z_TRIG;
if (input == "U_CBUTTONS") return Button::U_CBUTTONS;
if (input == "L_CBUTTONS") return Button::L_CBUTTONS;
if (input == "D_CBUTTONS") return Button::D_CBUTTONS;
if (input == "R_CBUTTONS") return Button::R_CBUTTONS;
if (input == "R_TRIG") return Button::R_TRIG;
if (input == "START_BUTTON") return Button::START_BUTTON;
if (input == "WALK_BUTTON") return Button::WALK_BUTTON;
return (Button)0;
}
Keyboard::Keyboard() : N64Controller()
{
memset(m_lastKeyState, 0, sizeof(m_lastKeyState));
m_keyBindings[SDL_SCANCODE_W] = Button::STICK_X_UP;
m_keyBindings[SDL_SCANCODE_A] = Button::STICK_X_LEFT;
m_keyBindings[SDL_SCANCODE_S] = Button::STICK_X_DOWN;
m_keyBindings[SDL_SCANCODE_D] = Button::STICK_X_RIGHT;
m_keyBindings[SDL_SCANCODE_SPACE] = Button::A_BUTTON;
m_keyBindings[SDL_SCANCODE_F] = Button::B_BUTTON;
m_keyBindings[SDL_SCANCODE_O] = Button::A_BUTTON;
m_keyBindings[SDL_SCANCODE_P] = Button::B_BUTTON;
m_keyBindings[SDL_SCANCODE_LSHIFT] = Button::Z_TRIG;
m_keyBindings[SDL_SCANCODE_C] = Button::Z_TRIG;
m_keyBindings[SDL_SCANCODE_I] = Button::U_JPAD;
m_keyBindings[SDL_SCANCODE_J] = Button::L_JPAD;
m_keyBindings[SDL_SCANCODE_K] = Button::D_JPAD;
m_keyBindings[SDL_SCANCODE_L] = Button::R_JPAD;
m_keyBindings[SDL_SCANCODE_UP] = Button::U_CBUTTONS;
m_keyBindings[SDL_SCANCODE_LEFT] = Button::L_CBUTTONS;
m_keyBindings[SDL_SCANCODE_DOWN] = Button::D_CBUTTONS;
m_keyBindings[SDL_SCANCODE_RIGHT] = Button::R_CBUTTONS;
m_keyBindings[SDL_SCANCODE_X] = Button::L_TRIG;
m_keyBindings[SDL_SCANCODE_V] = Button::R_TRIG;
m_keyBindings[SDL_SCANCODE_RSHIFT] = Button::R_TRIG;
m_keyBindings[SDL_SCANCODE_RETURN] = Button::START_BUTTON;
#ifndef __SWITCH__
loadKeyBindings();
loadKeyBindings();
#endif
}
}
void loadKeyBindings()
{
void Keyboard::loadKeyBindings()
{
#ifdef ENABLE_JSON
try
try
{
std::ifstream ifs("keyboard1.bindings.json", std::ifstream::in);
if (ifs.is_open())
{
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document d;
d.ParseStream(isw);
if (d.IsObject())
{
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
{
std::ifstream ifs("keyboard1.bindings.json", std::ifstream::in);
if (ifs.is_open())
if (itr->name.IsString() && itr->value.IsString())
{
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document d;
d.ParseStream(isw);
auto key = SDL_GetScancodeFromName(itr->name.GetString());
auto value = getInputValue(itr->value.GetString());
if (d.IsObject())
if (key != SDL_SCANCODE_UNKNOWN && value) {
m_keyBindings[key] = value;
else
{
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
{
if (itr->name.IsString() && itr->value.IsString())
{
auto key = SDL_GetScancodeFromName(itr->name.GetString());
auto value = getInputValue(itr->value.GetString());
if (key != SDL_SCANCODE_UNKNOWN && value)
{
m_keyBindings[key] = value;
}
else
{
// TODO FIX sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
}
}
}
// TODO FIX sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
}
}
}
catch (...)
{
}
#endif
}
}
}
catch (...)
{
}
#endif
}
void saveKeyBindings()
{
void Keyboard::saveKeyBindings()
{
#ifdef ENABLE_JSON
try
{
rapidjson::Document d;
d.SetObject();
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
for (const auto i : m_keyBindings)
{
rapidjson::Value value(getInputName(i.second), allocator);
rapidjson::Value key(SDL_GetScancodeName(i.first), allocator);
d.AddMember(key, value, allocator);
}
saveJson(d, "keyboard1.bindings.json");
}
catch (...)
{
}
#endif
}
void clearRebindMode()
{
}
void resetRebinds()
{
}
bool hasMouse() const
{
return true;
return m_state.has_mouse;
}
int keyboard_buttons_down;
int keyboard_map_scancode(SDL_Scancode scancode)
{
if (m_keyBindings.count(scancode))
{
return m_keyBindings[scancode];
}
return 0;
}
bool on_key_down(SDL_Scancode scancode)
{
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= mapped;
return mapped != 0;
}
bool on_key_up(SDL_Scancode scancode)
{
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~mapped;
return mapped != 0;
}
void on_all_keys_up()
{
keyboard_buttons_down = 0;
}
void enableMouse()
{
this->state().has_mouse = true;
}
bool canRebind(SDL_Scancode scancode, int input)
{
if (m_keyBindings.count(scancode) == 0)
{
return true;
}
auto replacingInput = m_keyBindings[scancode];
u64 count = 0;
for (auto i : m_keyBindings)
{
if (i.second == replacingInput)
{
count++;
}
}
return count != 1;
}
bool updateRebind(int input) override
{
int count = 0;
auto state = SDL_GetKeyboardState(&count);
u64 changed = 0;
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
{
m_lastKeyState[i] = (state[i] ^ m_lastKeyState[i]) & state[i];
}
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
{
if (m_lastKeyState[i] && canRebind((SDL_Scancode)i, input))
{
m_keyBindings[(SDL_Scancode)i] = input;
changed++;
saveKeyBindings();
}
}
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
return changed != 0;
}
void update()
{
bool walk = false;
int count = 0;
auto state = SDL_GetKeyboardState(&count);
if (state[SDL_SCANCODE_F10] && (m_lastKeyState[SDL_SCANCODE_F10] ^ state[SDL_SCANCODE_F10]))
{
oot::config().game().fullscreen() = !oot::config().game().fullscreen();
set_fullscreen(oot::config().game().fullscreen());
}
if(state[SDL_SCANCODE_F9] && (m_lastKeyState[SDL_SCANCODE_F9] ^ state[SDL_SCANCODE_F9]))
{
Set_Language(Get_Language()+1);
}
if (state[SDL_SCANCODE_ESCAPE] && (m_lastKeyState[SDL_SCANCODE_ESCAPE] ^ state[SDL_SCANCODE_ESCAPE]))
{
if (oot::config().game().fullscreen())
{
oot::config().game().fullscreen() = false;
set_fullscreen(oot::config().game().fullscreen());
}
}
//When F5 is pressed. Mark all dpad button as pressed. Used to open map select
if (state[SDL_SCANCODE_F5] && (m_lastKeyState[SDL_SCANCODE_F5] ^ state[SDL_SCANCODE_F5]))
{
m_state.button |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
}
if (hid::isTasPlaying())
{
return;
}
for (const auto x : m_keyBindings)
{
const auto scancode = x.first;
const auto input = x.second;
if (scancode < count)
{
if (state[scancode])
{
if (input > 0xFFFF)
{
switch (input)
{
case STICK_X_DOWN:
m_state.stick_y = -128;
break;
case STICK_X_UP:
m_state.stick_y = 127;
break;
case STICK_X_LEFT:
m_state.stick_x = -128;
break;
case STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case WALK_BUTTON:
walk = true;
break;
}
}
else
{
this->state().button |= input;
}
}
}
}
#ifdef ENABLE_MOUSE
int mouse_delta_x = 0;
int mouse_delta_y = 0;
auto buttons = SDL_GetRelativeMouseState(&mouse_delta_x, &mouse_delta_y);
this->enableMouse();
if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT))
{
m_state.button |= B_BUTTON;
}
if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT))
{
m_state.button |= A_BUTTON;
}
if (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE))
{
walk = true;
}
m_state.mouse_x += mouse_delta_x * 4;
m_state.mouse_y += mouse_delta_y * 4;
if (mouse_delta_x > 10)
{
int yy = 0;
}
#endif
if (walk)
{
m_state.stick_x *= 0.25f;
m_state.stick_y *= 0.25f;
}
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
}
protected:
std::unordered_map<SDL_Scancode, int> m_keyBindings;
u8 m_lastKeyState[MAX_KEY_STATE];
};
} // namespace controller
Keyboard::Keyboard() : Driver()
try
{
}
rapidjson::Document d;
d.SetObject();
Keyboard::~Keyboard()
{
}
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
void Keyboard::scan(class Controllers* controllers)
{
if (!size())
for (const auto i : m_keyBindings)
{
#ifdef ENABLE_MOUSE
//SDL_SetRelativeMouseMode(SDL_TRUE);
#endif
auto controller = std::make_shared<controller::Keyboard>();
#ifdef ENABLE_MOUSE
rapidjson::Value value(getInputName(i.second), allocator);
rapidjson::Value key(SDL_GetScancodeName(i.first), allocator);
d.AddMember(key, value, allocator);
}
controller->enableMouse();
saveJson(d, "keyboard1.bindings.json");
}
catch (...)
{
}
#endif
m_controllers.push_back(controller);
players().attach(controller, 0);
}
bool Keyboard::hasMouse() const
{
return true;
return m_state.has_mouse;
}
Button Keyboard::keyboard_map_scancode(SDL_Scancode scancode)
{
if (m_keyBindings.count(scancode))
return m_keyBindings[scancode];
return Button::NONE;
}
bool Keyboard::on_key_down(SDL_Scancode scancode)
{
Button mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= (int)mapped;
return (int)mapped != 0;
}
bool Keyboard::on_key_up(SDL_Scancode scancode)
{
Button mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~(int)mapped;
return (int)mapped != 0;
}
bool Keyboard::canRebind(SDL_Scancode scancode, Button input)
{
if (m_keyBindings.count(scancode) == 0)
return true;
auto replacingInput = m_keyBindings[scancode];
u64 count = 0;
for (auto i : m_keyBindings)
{
if (i.second == replacingInput)
count++;
}
return count != 1;
}
bool Keyboard::updateRebind(Button input)
{
int count = 0;
auto state = SDL_GetKeyboardState(&count);
u64 changed = 0;
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
m_lastKeyState[i] = (state[i] ^ m_lastKeyState[i]) & state[i];
for (int i = 0; i < std::min(MAX_KEY_STATE, count); i++)
{
if (m_lastKeyState[i] && canRebind((SDL_Scancode)i, input))
{
m_keyBindings[(SDL_Scancode)i] = input;
changed++;
saveKeyBindings();
}
}
void Keyboard::update()
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
return changed != 0;
}
void Keyboard::update()
{
bool walk = false;
int count = 0;
auto state = SDL_GetKeyboardState(&count);
if (state[SDL_SCANCODE_F10] && (m_lastKeyState[SDL_SCANCODE_F10] ^ state[SDL_SCANCODE_F10]))
{
for (auto& keyboard : m_controllers)
oot::config().game().fullscreen() = !oot::config().game().fullscreen();
set_fullscreen(oot::config().game().fullscreen());
}
if (state[SDL_SCANCODE_F9] && (m_lastKeyState[SDL_SCANCODE_F9] ^ state[SDL_SCANCODE_F9]))
Set_Language(Get_Language()+1);
if (state[SDL_SCANCODE_ESCAPE] && (m_lastKeyState[SDL_SCANCODE_ESCAPE] ^ state[SDL_SCANCODE_ESCAPE]))
{
if (oot::config().game().fullscreen())
{
((controller::Keyboard*)keyboard.get())->update();
oot::config().game().fullscreen() = false;
set_fullscreen(oot::config().game().fullscreen());
}
}
} // namespace sm64::hid
//When F5 is pressed. Mark all dpad button as pressed. Used to open map select
if (state[SDL_SCANCODE_F5] && (m_lastKeyState[SDL_SCANCODE_F5] ^ state[SDL_SCANCODE_F5]))
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::R_JPAD;
if (Tas::isTasPlaying())
return;
for (const auto& [scancode, input] : m_keyBindings)
{
if (scancode < count && state[scancode])
{
if ((uint32_t)input <= 0xFFFF)
m_state.button |= (uint32_t)input;
else
{
switch (input)
{
case Button::STICK_X_DOWN:
m_state.stick_y = -128;
break;
case Button::STICK_X_UP:
m_state.stick_y = 127;
break;
case Button::STICK_X_LEFT:
m_state.stick_x = -128;
break;
case Button::STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case Button::WALK_BUTTON:
walk = true;
break;
}
}
}
}
#ifdef ENABLE_MOUSE
int mouse_delta_x = 0;
int mouse_delta_y = 0;
auto buttons = SDL_GetRelativeMouseState(&mouse_delta_x, &mouse_delta_y);
enableMouse();
if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT))
m_state.button |= (uint16_t)Button::B_BUTTON;
if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT))
m_state.button |= (uint16_t)Button::A_BUTTON;
if (buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE))
walk = true;
m_state.mouse_x += mouse_delta_x * 4;
m_state.mouse_y += mouse_delta_y * 4;
#endif
if (walk)
{
m_state.stick_x *= 0.25f;
m_state.stick_y *= 0.25f;
}
memcpy(m_lastKeyState, state, std::min(MAX_KEY_STATE, count));
}
void Keyboard::scan()
{
if (!size())
{
#ifdef ENABLE_MOUSE
//SDL_SetRelativeMouseMode(SDL_TRUE);
#endif
auto controller = std::make_shared<Keyboard>();
#ifdef ENABLE_MOUSE
controller->enableMouse();
#endif
m_controllers.push_back(controller);
Players::get().attach(controller, 0);
}
}
/*void Keyboard::update()
{
for (auto& keyboard : m_controllers)
((Keyboard*)keyboard.get())->update();
}*/

View File

@ -1,23 +1,50 @@
#pragma once
#include <SDL2/SDL.h>
#include <unordered_map>
#include <string>
#include "controllers.h"
#define MAX_KEY_STATE (16*1024)
namespace sm64::hid
{
class Keyboard : public Driver
{
public:
Keyboard();
virtual ~Keyboard();
void scan(class Controllers* controllers) override;
void update() override;
bool defaultOnly() override
{
return true;
}
protected:
};
} // namespace sm64::hid
namespace oot
{
namespace hid
{
class Keyboard : public N64Controller, public InputDevice
{
public:
Keyboard();
virtual ~Keyboard() {}
void scan();
void update() override;
bool defaultOnly() override { return true; }
bool updateRebind(Button input) override;
void clearRebindMode() {}
void resetRebinds() {}
bool hasMouse() const;
void enableMouse() { state().has_mouse = true; }
bool canRebind(SDL_Scancode scancode, Button input);
Button keyboard_map_scancode(SDL_Scancode scancode);
bool on_key_down(SDL_Scancode scancode);
bool on_key_up(SDL_Scancode scancode);
void on_all_keys_up() { keyboard_buttons_down = 0; }
void loadKeyBindings();
void saveKeyBindings();
int keyboard_buttons_down;
private:
const char* getInputName(Button input);
Button getInputValue(const std::string& input);
std::unordered_map<SDL_Scancode, Button> m_keyBindings;
u8 m_lastKeyState[MAX_KEY_STATE];
};
}
}

View File

@ -18,6 +18,7 @@
#include <fstream>
#include "../options.h"
#include "../player/players.h"
#include "tas.h"
#ifndef __SWITCH__
#define DEADZONE 20
@ -27,8 +28,6 @@
#define RDEADZONE 0
#endif
static bool init_ok;
#define INITIAL_PEAK 0x8000
static int g_lstickX_peak = INITIAL_PEAK;
@ -36,510 +35,462 @@ static int g_lstickY_peak = INITIAL_PEAK;
static int g_rstickX_peak = INITIAL_PEAK;
static int g_rstickY_peak = INITIAL_PEAK;
#ifdef DEBUG
#include <time.h>
extern struct Object* gMarioObject;
#endif
#ifdef ENABLE_JSON
bool saveJson(rapidjson::Document& doc, const std::string& jsonFilePath);
#endif
namespace sm64::hid
using namespace oot::hid;
static bool g_haptics = false;
//const char* getInputName(int input);
//int getInputValue(const std::string& input);
Joypad::~Joypad()
{
static bool g_haptics = false;
closeHaptics();
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
}
namespace controller
{
const char* getInputName(int input);
int getInputValue(const std::string& input);
class SDL : public Controller
{
public:
SDL(SDL_GameController* controller) : Controller(), m_context(controller), m_haptic(nullptr)
{
m_motorEnabled = initHaptics();
Joypad::Joypad() : N64Controller()
{
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
m_keyBindings[SDL_CONTROLLER_BUTTON_START] = START_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = Z_TRIG;
m_keyBindings[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = R_TRIG;
m_keyBindings[SDL_CONTROLLER_BUTTON_A] = A_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_X] = B_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_B] = A_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_Y] = B_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = L_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = R_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_UP] = U_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = D_CBUTTONS;
g_haptics = SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0;
#ifndef __SWITCH__
loadKeyBindings();
#endif
}
virtual ~SDL()
{
closeHaptics();
}
void loadKeyBindings()
{
#ifdef ENABLE_JSON
try
{
std::ifstream ifs("gamepad1.bindings.json", std::ifstream::in);
if (ifs.is_open())
{
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document d;
d.ParseStream(isw);
if (d.IsObject())
{
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
{
if (itr->name.IsString() && itr->value.IsString())
{
auto key = SDL_GameControllerGetButtonFromString(itr->name.GetString());
auto value = getInputValue(itr->value.GetString());
if (key != SDL_CONTROLLER_BUTTON_INVALID && value)
{
m_keyBindings[key] = value;
}
else
{
//sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
}
}
}
}
}
}
catch (...)
{
}
#endif
}
void saveKeyBindings()
{
#ifdef ENABLE_JSON
try
{
rapidjson::Document d;
d.SetObject();
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
for (const auto i : m_keyBindings)
{
rapidjson::Value value(getInputName(i.second), allocator);
rapidjson::Value key(SDL_GameControllerGetStringForButton(i.first), allocator);
d.AddMember(key, value, allocator);
}
saveJson(d, "gamepad1.bindings.json");
}
catch (...)
{
}
#endif
}
bool initHaptics()
{
if(!g_haptics)
{
return false;
}
m_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(m_context));
if(!m_haptic)
{
return false;
}
if(SDL_HapticRumbleSupported(m_haptic) != 1)
{
closeHaptics();
return false;
}
if(SDL_HapticRumbleInit(m_haptic) != 0)
{
closeHaptics();
return NULL;
}
return true;
}
void closeHaptics()
{
if(m_haptic)
{
SDL_HapticClose(m_haptic);
m_haptic = nullptr;
}
}
void SendMotorEvent(short time, short level) override
{
if(m_motorEnabled)
{
SDL_HapticRumblePlay(m_haptic, level / 100.0f, time * 10);
}
}
void SendMotorDecay(short level) override
{
}
void ResetMotorPack() override
{
if(m_motorEnabled)
{
SDL_HapticRumbleStop(m_haptic);
}
}
void SendMotorVib(int level) override
{
SendMotorEvent(30, level);
}
static inline int8_t convertToByte(int value, int max)
{
int8_t result = value * 0x7F / max;
return result;
}
static inline int8_t invert(const int8_t value)
{
if(value <= -128)
{
return 127;
}
return -value;
}
inline int16_t readAxis(SDL_GameControllerAxis axis)
{
return SDL_GameControllerGetAxis(m_context, axis);
}
inline int8_t stickLeftX()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTX);
if(abs(value) > g_lstickX_peak)
{
g_lstickX_peak = abs(value);
}
return convertToByte(value, g_lstickX_peak);
}
inline int8_t stickLeftY()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTY);
if(abs(value) > g_lstickY_peak)
{
g_lstickY_peak = abs(value);
}
return convertToByte(value, g_lstickY_peak);
}
inline int8_t stickRightX()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTX);
if(abs(value) > g_rstickX_peak)
{
g_rstickX_peak = abs(value);
}
return convertToByte(value, g_rstickX_peak);
}
inline int8_t stickRightY()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTY);
if(abs(value) > g_rstickY_peak)
{
g_rstickY_peak = abs(value);
}
return convertToByte(value, g_rstickY_peak);
}
bool canRebind(SDL_GameControllerButton button, int input)
{
if (m_keyBindings.count(button) == 0)
{
return true;
}
auto replacingInput = m_keyBindings[button];
u64 count = 0;
if (replacingInput != START_BUTTON && replacingInput != A_BUTTON && replacingInput != B_BUTTON)
{
return true;
}
if (replacingInput == input)
{
return true;
}
for (auto i : m_keyBindings)
{
if (i.second == replacingInput)
{
count++;
}
}
if (count == 1)
{
return false;
}
return count != 1;
}
bool updateRebind(int input) override
{
u8 state[SDL_CONTROLLER_BUTTON_MAX];
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
bool newState = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
state[i] = (m_buttonState[i] ^ newState) & newState;
m_buttonState[i] = newState;
}
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
if (state[i] && canRebind((SDL_GameControllerButton)i, input))
{
m_keyBindings[(SDL_GameControllerButton)i] = input;
saveKeyBindings();
return true;
}
}
return false;
}
void update() override
{
if(!init_ok || !m_context || hid::isTasPlaying())
{
return;
}
bool walk = false;
SDL_GameControllerUpdate();
if(m_context != NULL && !SDL_GameControllerGetAttached(m_context))
{
SDL_GameControllerClose(m_context);
m_context = NULL;
}
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
m_buttonState[i] = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
}
m_state.stick_x = stickLeftX();
m_state.stick_y = invert(stickLeftY());
m_state.r_stick_x = stickRightX();
m_state.r_stick_y = stickRightY();
for (const auto& i : m_keyBindings)
{
if (m_buttonState[i.first])
{
if (i.second > 0xFFFF)
{
switch (i.second)
{
case STICK_X_DOWN:
m_state.stick_y = -128;
break;
case STICK_X_UP:
m_state.stick_y = 127;
break;
case STICK_X_LEFT:
m_state.stick_x = -128;
break;
case STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case WALK_BUTTON:
walk = true;
break;
}
}
else
{
m_state.button |= i.second;
}
}
}
if(oot::config().camera().useClassicCamera())
{
int16_t leftx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
int16_t lefty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
int16_t rightx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
int16_t righty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
if(abs(righty) > 0x7000)
{
if(righty > 0)
{
m_state.button |= D_CBUTTONS;
}
else
{
m_state.button |= U_CBUTTONS;
}
}
if(abs(rightx) > 0x7000)
{
if(rightx > 1)
{
m_state.button |= R_CBUTTONS;
}
else
{
m_state.button |= L_CBUTTONS;
}
}
}
int16_t ltrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
int16_t rtrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
if(ltrig > 30 * 256)
m_state.button |= Z_TRIG;
//if(rtrig > 30 * 256)
//m_state.button |= R_TRIG;
if (walk)
{
m_state.stick_x *= 0.25f;
m_state.stick_y *= 0.25f;
}
uint32_t magnitude_sq = (uint32_t)(m_state.stick_x * m_state.stick_x) + (uint32_t)(m_state.stick_y * m_state.stick_y);
if(magnitude_sq < (uint32_t)(DEADZONE * DEADZONE))
{
m_state.stick_x = 0;
m_state.stick_y = 0;
}
magnitude_sq = (uint32_t)(m_state.r_stick_x * m_state.r_stick_x) + (uint32_t)(m_state.r_stick_y * m_state.r_stick_y);
if(magnitude_sq < (uint32_t)(RDEADZONE * RDEADZONE))
{
m_state.r_stick_x = 0;
m_state.r_stick_y = 0;
}
//When the right trigger is pressed, interpret the c buttons as the dpad
if (rtrig > 30 * 256)
{
if (m_state.button & U_CBUTTONS)
m_state.button |= U_JPAD;
if (m_state.button & D_CBUTTONS)
m_state.button |= D_JPAD;
if (m_state.button & L_CBUTTONS)
m_state.button |= L_JPAD;
if (m_state.button & R_CBUTTONS)
m_state.button |= R_JPAD;
m_state.button &= ~(U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS);//Unpress the c buttons
}
//When the back button is pressed. Mark all dpad button as pressed. Used to open map select
if (m_buttonState[SDL_CONTROLLER_BUTTON_BACK])
m_state.button |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
}
protected:
SDL_GameController* m_context;
SDL_Haptic* m_haptic;
std::unordered_map<SDL_GameControllerButton, int> m_keyBindings;
u8 m_buttonState[SDL_CONTROLLER_BUTTON_MAX];
};
} // namespace controller
SDL::SDL()
{
if(SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
{
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
}
g_haptics = SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0;
}
SDL::~SDL()
{
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
}
void SDL::scan(class Controllers* controllers)
{
#ifdef __SWITCH__
// swap A, B and X, Y to correct positions
SDL_GameControllerAddMapping("53776974636820436F6E74726F6C6C65,Switch Controller,"
"a:b0,b:b1,back:b11,"
"dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,"
"leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,"
"rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,"
"start:b10,x:b2,y:b3");
// swap A, B and X, Y to correct positions
SDL_GameControllerAddMapping("53776974636820436F6E74726F6C6C65,Switch Controller,"
"a:b0,b:b1,back:b11,"
"dpdown:b15,dpleft:b12,dpright:b14,dpup:b13,"
"leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,"
"rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,"
"start:b10,x:b2,y:b3");
#endif
init_ok = true;
for(int i = 0; i < SDL_NumJoysticks(); i++)
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
if (SDL_IsGameController(i))
{
if(SDL_IsGameController(i))
{
auto context = SDL_GameControllerOpen(i);
m_context = SDL_GameControllerOpen(i);
if(context)
{
auto controller = std::make_shared<controller::SDL>(context);
m_controllers.push_back(controller);
players().attach(controller, 0);
}
}
if (m_context)
break;
}
}
#ifdef ENABLE_MOUSE
// temporarily change mouse mode to not take over the cursor
// SDL_SetRelativeMouseMode(m_controllers.size() ? SDL_FALSE : SDL_TRUE);
// temporarily change mouse mode to not take over the cursor
// SDL_SetRelativeMouseMode(m_controllers.size() ? SDL_FALSE : SDL_TRUE);
#endif
}
} // namespace sm64::hid
m_motorEnabled = initHaptics();
m_keyBindings[SDL_CONTROLLER_BUTTON_START] = Button::START_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = Button::Z_TRIG;
m_keyBindings[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = Button::R_TRIG;
m_keyBindings[SDL_CONTROLLER_BUTTON_A] = Button::A_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_X] = Button::B_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_B] = Button::A_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_Y] = Button::B_BUTTON;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = Button::L_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = Button::R_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_UP] = Button::U_CBUTTONS;
m_keyBindings[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = Button::D_CBUTTONS;
#ifndef __SWITCH__
loadKeyBindings();
#endif
}
void Joypad::scan()
{
auto controller = std::make_shared<Joypad>();
m_controllers.push_back(controller);
Players::get().attach(controller, 0);
}
void Joypad::loadKeyBindings()
{
#ifdef ENABLE_JSON
try
{
std::ifstream ifs("gamepad1.bindings.json", std::ifstream::in);
if (ifs.is_open())
{
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document d;
d.ParseStream(isw);
if (d.IsObject())
{
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr)
{
if (itr->name.IsString() && itr->value.IsString())
{
auto key = SDL_GameControllerGetButtonFromString(itr->name.GetString());
auto value = getInputValue(itr->value.GetString());
if (key != SDL_CONTROLLER_BUTTON_INVALID && value)
m_keyBindings[key] = value;
else
{
//sm64::log("could not bind key: \"%s\" -> \"%s\"\n", itr->value.GetString(), itr->name.GetString());
}
}
}
}
}
}
catch (...)
{
}
#endif
}
void Joypad::saveKeyBindings()
{
#ifdef ENABLE_JSON
try
{
rapidjson::Document d;
d.SetObject();
rapidjson::Document::AllocatorType& allocator = d.GetAllocator();
for (const auto i : m_keyBindings)
{
rapidjson::Value value(getInputName(i.second), allocator);
rapidjson::Value key(SDL_GameControllerGetStringForButton(i.first), allocator);
d.AddMember(key, value, allocator);
}
saveJson(d, "gamepad1.bindings.json");
}
catch (...)
{
}
#endif
}
bool Joypad::initHaptics()
{
if (!g_haptics)
return false;
m_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(m_context));
if (!m_haptic)
return false;
if (SDL_HapticRumbleSupported(m_haptic) != 1)
{
closeHaptics();
return false;
}
if (SDL_HapticRumbleInit(m_haptic) != 0)
{
closeHaptics();
return NULL;
}
return true;
}
void Joypad::closeHaptics()
{
if (m_haptic)
{
SDL_HapticClose(m_haptic);
m_haptic = nullptr;
}
}
void Joypad::SendMotorEvent(short time, short level)
{
if (m_motorEnabled)
SDL_HapticRumblePlay(m_haptic, level / 100.0f, time * 10);
}
void Joypad::ResetMotorPack()
{
if (m_motorEnabled)
SDL_HapticRumbleStop(m_haptic);
}
void Joypad::SendMotorVib(int level)
{
SendMotorEvent(30, level);
}
int8_t Joypad::convertToByte(int value, int max)
{
int8_t result = value * 0x7F / max;
return result;
}
int8_t Joypad::invert(const int8_t value)
{
if (value <= -128)
return 127;
return -value;
}
int8_t Joypad::stickLeftX()
{
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
if (abs(value) > g_lstickX_peak)
g_lstickX_peak = abs(value);
return convertToByte(value, g_lstickX_peak);
}
inline int8_t Joypad::stickLeftY()
{
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
if (abs(value) > g_lstickY_peak)
g_lstickY_peak = abs(value);
return convertToByte(value, g_lstickY_peak);
}
inline int8_t Joypad::stickRightX()
{
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
if (abs(value) > g_rstickX_peak)
g_rstickX_peak = abs(value);
return convertToByte(value, g_rstickX_peak);
}
inline int8_t Joypad::stickRightY()
{
auto value = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
if (abs(value) > g_rstickY_peak)
g_rstickY_peak = abs(value);
return convertToByte(value, g_rstickY_peak);
}
bool Joypad::canRebind(SDL_GameControllerButton button, Button input)
{
if (m_keyBindings.count(button) == 0)
return true;
auto replacingInput = m_keyBindings[button];
u64 count = 0;
if (replacingInput != Button::START_BUTTON && replacingInput != Button::A_BUTTON && replacingInput != Button::B_BUTTON)
return true;
if (replacingInput == input)
return true;
for (auto& [input, button] : m_keyBindings)
{
if (button == replacingInput)
count++;
}
if (count == 1)
return false;
return count != 1;
}
bool Joypad::updateRebind(Button input)
{
u8 state[SDL_CONTROLLER_BUTTON_MAX];
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
bool newState = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
state[i] = (m_buttonState[i] ^ newState) & newState;
m_buttonState[i] = newState;
}
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
if (state[i] && canRebind((SDL_GameControllerButton)i, input))
{
m_keyBindings[(SDL_GameControllerButton)i] = input;
saveKeyBindings();
return true;
}
}
return false;
}
void Joypad::update()
{
if (!m_context || Tas::isTasPlaying())
return;
bool walk = false;
SDL_GameControllerUpdate();
if (m_context != NULL && !SDL_GameControllerGetAttached(m_context))
{
SDL_GameControllerClose(m_context);
m_context = NULL;
}
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
m_buttonState[i] = SDL_GameControllerGetButton(m_context, (SDL_GameControllerButton)i);
m_state.stick_x = stickLeftX();
m_state.stick_y = invert(stickLeftY());
m_state.r_stick_x = stickRightX();
m_state.r_stick_y = stickRightY();
for (const auto& [scancode, input] : m_keyBindings)
{
if (m_buttonState[scancode])
{
if ((uint32_t)input <= 0xFFFF)
m_state.button |= (uint16_t)input;
else
{
switch (input)
{
case Button::STICK_X_DOWN:
m_state.stick_y = -128;
break;
case Button::STICK_X_UP:
m_state.stick_y = 127;
break;
case Button::STICK_X_LEFT:
m_state.stick_x = -128;
break;
case Button::STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case Button::WALK_BUTTON:
walk = true;
break;
}
}
}
}
if (oot::config().camera().useClassicCamera())
{
int16_t leftx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTX);
int16_t lefty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_LEFTY);
int16_t rightx = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTX);
int16_t righty = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_RIGHTY);
if (abs(righty) > 0x7000)
{
if (righty > 0)
m_state.button |= (uint16_t)Button::D_CBUTTONS;
else
m_state.button |= (uint16_t)Button::U_CBUTTONS;
}
if (abs(rightx) > 0x7000)
{
if (rightx > 1)
m_state.button |= (uint16_t)Button::R_CBUTTONS;
else
m_state.button |= (uint16_t)Button::L_CBUTTONS;
}
}
int16_t ltrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
int16_t rtrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
if (ltrig > 30 * 256)
m_state.button |= (uint16_t)Button::Z_TRIG;
//if(rtrig > 30 * 256)
//m_state.button |= (uint16_t)Button::R_TRIG;
if (walk)
{
m_state.stick_x *= 0.25f;
m_state.stick_y *= 0.25f;
}
uint32_t magnitude_sq = (uint32_t)(m_state.stick_x * m_state.stick_x) + (uint32_t)(m_state.stick_y * m_state.stick_y);
if (magnitude_sq < (uint32_t)(DEADZONE * DEADZONE))
{
m_state.stick_x = 0;
m_state.stick_y = 0;
}
magnitude_sq = (uint32_t)(m_state.r_stick_x * m_state.r_stick_x) + (uint32_t)(m_state.r_stick_y * m_state.r_stick_y);
if (magnitude_sq < (uint32_t)(RDEADZONE * RDEADZONE))
{
m_state.r_stick_x = 0;
m_state.r_stick_y = 0;
}
//When the right trigger is pressed, interpret the c buttons as the dpad
if (rtrig > 30 * 256)
{
if (m_state.button & (uint16_t)Button::U_CBUTTONS)
m_state.button |= (uint16_t)Button::U_JPAD;
if (m_state.button & (uint16_t)Button::D_CBUTTONS)
m_state.button |= (uint16_t)Button::D_JPAD;
if (m_state.button & (uint16_t)Button::L_CBUTTONS)
m_state.button |= (uint16_t)Button::L_JPAD;
if (m_state.button & (uint16_t)Button::R_CBUTTONS)
m_state.button |= (uint16_t)Button::R_JPAD;
m_state.button &= ~((uint16_t)Button::U_CBUTTONS | (uint16_t)Button::D_CBUTTONS | (uint16_t)Button::L_CBUTTONS | (uint16_t)Button::R_CBUTTONS);//Unpress the c buttons
}
//When the back button is pressed. Mark all dpad button as pressed. Used to open map select
if (m_buttonState[SDL_CONTROLLER_BUTTON_BACK])
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::R_JPAD;
}
#endif

View File

@ -1,16 +1,56 @@
#pragma once
#include <string>
#include <unordered_map>
#include <SDL2/SDL.h>
#include "controllers.h"
namespace sm64::hid
{
class SDL : public Driver
{
public:
SDL();
virtual ~SDL();
void scan(class Controllers* controllers) override;
protected:
};
} // namespace sm64::hid
namespace oot
{
namespace hid
{
class Joypad : public N64Controller, public InputDevice
{
public:
Joypad();
virtual ~Joypad();
virtual void scan() override;
void loadKeyBindings();
void saveKeyBindings();
void ResetMotorPack() override;
void SendMotorVib(int level) override;
static inline int8_t convertToByte(int value, int max);
static inline int8_t invert(const int8_t value);
inline int8_t stickLeftX();
inline int8_t stickLeftY();
inline int8_t stickRightX();
inline int8_t stickRightY();
bool canRebind(SDL_GameControllerButton button, Button input);
bool updateRebind(Button input) override;
void update() override;
private:
bool initHaptics();
void closeHaptics();
void SendMotorEvent(short time, short level) override;
void SendMotorDecay(short level) override {};
SDL_GameController* m_context = nullptr;
SDL_Haptic* m_haptic = nullptr;
std::unordered_map<SDL_GameControllerButton, Button> m_keyBindings;
u8 m_buttonState[SDL_CONTROLLER_BUTTON_MAX];
};
}
}

View File

@ -2,74 +2,83 @@
#include <stdio.h>
#include "../player/players.h"
#include "../options.h"
#include "tas.h"
static u64 g_counter = 0;
static bool g_tasPlaying = false;
static bool g_tasPlaying = false;
namespace sm64::hid
using namespace oot::hid;
bool Tas::isTasPlaying()
{
bool isTasPlaying()
{
return g_tasPlaying;
}
return g_tasPlaying;
}
namespace controller
void Tas::playTas(bool enabled)
{
g_tasPlaying = enabled;
}
Tas::Tas() : N64Controller()
{
fp = fopen("last-run.tas", "rb");
if (fp)
{
class Tas : public Controller
//fread(&oot::config(), 1, sizeof(oot::config()), fp);
#define SRAM_SIZE 0x8000//But this in ultra_reimplementation.h?
uint8_t* sram = new uint8_t[SRAM_SIZE];
fread(sram, sizeof(uint8_t), SRAM_SIZE, fp);
FILE* save = nullptr;
fopen_s(&save, "oot.sav", "wb");
if (save)
{
public:
Tas() : Controller()
{
fp = fopen("cont.tas", "rb");
if(fp != NULL)
{
fread(&oot::config(), 1, sizeof(oot::config()), fp);
g_tasPlaying = true;
}
}
virtual ~Tas()
{
if(fp)
{
fclose(fp);
}
}
void update()
{
if(fp != NULL)
{
auto r = fread(&m_state, 1, sizeof(m_state), fp);
if (m_state.button)
{
int x = 0;
}
}
}
protected:
FILE* fp;
};
} // namespace controller
Tas::Tas() : Driver()
{
}
Tas::~Tas()
{
}
void Tas::scan(class Controllers* controllers)
{
if(!size())
{
auto controller = std::make_shared<controller::Tas>();
m_controllers.push_back(controller);
players().attach(controller, 0);
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
fclose(save);
}
}
} // namespace sm64::hid
delete[] sram;
g_tasPlaying = true;
}
}
Tas::~Tas()
{
if (fp)
fclose(fp);
}
void Tas::update()
{
if (fp)
{
auto r = fread(&m_state, 1, sizeof(m_state), fp);
if (m_state.button)
int x = 0;
}
}
void Tas::scan()
{
if (!size())
{
auto controller = std::make_shared<Tas>();
m_controllers.push_back(controller);
Players::get().attach(controller, 0);
}
}

View File

@ -1,15 +1,26 @@
#pragma once
#include <stdio.h>
#include "controllers.h"
namespace sm64::hid
{
class Tas : public Driver
{
public:
Tas();
virtual ~Tas();
void scan(class Controllers* controllers) override;
protected:
};
} // namespace sm64::hid
namespace oot
{
namespace hid
{
class Tas : public N64Controller, public InputDevice
{
public:
static bool isTasPlaying();
static void playTas(bool enabled);
Tas();
virtual ~Tas();
void scan() override;
void update();
private:
FILE* fp = nullptr;
};
}
}

View File

@ -288,6 +288,11 @@ namespace oot
return m_recordTas;
}
void Game::recordTas(bool enable)
{
m_recordTas = enable;
}
bool& Game::forceMouse()
{
return m_forceMouse;

View File

@ -181,6 +181,7 @@ namespace oot
bool& disableFramePacing();
bool& recordTas();
void recordTas(bool enable);
bool& forceMouse();
protected:

View File

@ -2,6 +2,9 @@
#define ENABLE_OPENGL
#define USE_GLIDEN64
#include "window.h"
#include "options.h"
#include "player/players.h"
#include "controller/tas.h"
static std::unique_ptr<platform::window::Base> gWindow;
@ -287,6 +290,13 @@ void main_func(void)
}
#endif
#ifdef _DEBUG//Record TAS to capture bugs and crashes
oot::hid::Tas::playTas(true);//Uncomment to play back TAS/crash report from end-users
if (!oot::hid::Tas::isTasPlaying())
oot::config().game().recordTas(true);
#endif
//static u64 pool[0x165000 / 8 / 4 * sizeof(void*)];
//main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0]));
//gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
@ -328,6 +338,7 @@ void main_func(void)
inited = 1;
Graph_ThreadEntry(0);
gfx_shutdown();
}

View File

@ -1,72 +1,46 @@
#include "player.h"
#include "../options.h"
namespace sm64
using namespace oot::hid;
void Player::attach(const std::shared_ptr<N64Controller>& controller)
{
Player::Player() : m_rebindInput(0)
m_controllers.push_back(controller);
}
void Player::update()
{
m_controller.state().reset();
if (isRebindMode())
{
bool result = false;
//for (auto& controller : m_controllers)
//result |= controller->updateRebind(m_rebindInput);
if (result)
m_rebindInput = -10;
}
Player::~Player()
else if (m_rebindInput < 0)
m_rebindInput++;
else
{
}
void Player::attach(const std::shared_ptr<hid::Controller>& controller)
{
m_controllers.push_back(controller);
}
void Player::detachControllers()
{
m_controllers.resize(0);
}
void Player::update()
{
m_controller.state().reset();
if (isRebindMode())
for (auto& controller : m_controllers)
{
bool result = 0;
controller->state().reset();
controller->update();
for (auto& controller : m_controllers)
{
result |= controller->updateRebind(m_rebindInput);
}
if (result)
{
m_rebindInput = -10;
}
m_controller.merge(*controller);
}
else if (m_rebindInput < 0)
{
m_rebindInput++;
}
else
{
for (auto& controller : m_controllers)
{
controller->state().reset();
controller->update();
m_controller.merge(*controller);
}
if (m_controllers.size() > 2 && !oot::config().game().forceMouse())
{
m_controller.state().has_mouse = false;
}
}
m_controller.resolveInputs();
if (m_controllers.size() > 2 && !oot::config().game().forceMouse())
m_controller.state().has_mouse = false;
}
void Player::rebind(int input)
{
m_rebindInput = input;
}
bool Player::isRebindMode() const
{
return m_rebindInput > 0;
}
} // namespace sm64
m_controller.resolveInputs();
}

View File

@ -4,33 +4,35 @@
#include <vector>
#include <memory>
namespace sm64
namespace oot
{
class Player
namespace hid
{
class Player
{
public:
Player();
virtual ~Player();
Player() : m_rebindInput(0) {}
virtual ~Player() {}
std::vector<std::shared_ptr<hid::Controller> >& controllers()
{
return m_controllers;
}
void attach(const std::shared_ptr<hid::Controller>& controller);
void detachControllers();
void update();
hid::Controller& controller()
{
return m_controller;
}
std::vector<std::shared_ptr<N64Controller> >& controllers() {
return m_controllers;
}
void attach(const std::shared_ptr<N64Controller>& controller);
void detachControllers() { m_controllers.resize(0); }
void update();
N64Controller& controller() {
return m_controller;
}
void rebind(int input);
bool isRebindMode() const;
void rebind(int input) { m_rebindInput = input; }
bool isRebindMode() const { return m_rebindInput > 0; }
protected:
std::vector<std::shared_ptr<hid::Controller> > m_controllers;
hid::Controller m_controller;
int m_rebindInput;
};
} // namespace sm64
std::vector<std::shared_ptr<N64Controller> > m_controllers;
N64Controller m_controller;
int m_rebindInput;
};
}
}

View File

@ -1,73 +1,82 @@
#include <algorithm>
#include "players.h"
#include "../controller/controllers.h"
#include "../options.h"
#include "z64.h";
#include "padmgr.h"
extern PadMgr gPadMgr;
using namespace oot::hid;
Players g_players;
extern "C" {
void hid_init() {
sm64::hid::controllers().scan();
InputDeviceManager::get().scan();
}
void hid_update() {
sm64::players().update();
Players::Update();
}
}
namespace sm64
Players& Players::get()
{
Players g_players;
return g_players;
}
Players& players()
void Players::Update()
{
g_players.update();
}
const N64Controller* Players::GetController()
{
if (g_players[0].controllers().size() == 0)
return nullptr;
return g_players[0].controllers()[0].get();
}
void Players::update()
{
memset(&gPadMgr.ctrlrIsConnected[0], 0, sizeof(gPadMgr.ctrlrIsConnected));
int i = 0;
for (auto& player : m_players)
{
return g_players;
player.update();
gPadMgr.inputs[i].cur.button = player.controller().state().button;
gPadMgr.inputs[i].cur.stick_x = player.controller().state().stick_x;
gPadMgr.inputs[i].cur.stick_y = player.controller().state().stick_y;
gPadMgr.ctrlrIsConnected[i] = true;
i++;
}
Player& player(const u64 i)
gPadMgr.nControllers = i;
}
void Players::attach(const std::shared_ptr<N64Controller>& controller, const u8 playerId)
{
if (playerId == MAX_PLAYERS)
m_players[m_size++].attach(controller);
else
{
return g_players[i];
m_players[playerId].attach(controller);
m_size = std::max((u64)playerId + 1, m_size);
}
Players::Players() : m_players(), m_size(0)
{
}
const u64 Players::size() const
{
return m_size;
}
void Players::update()
{
int i=0;
memset(&gPadMgr.ctrlrIsConnected[0], 0, sizeof(gPadMgr.ctrlrIsConnected));
for(auto& player : m_players)
{
player.update();
gPadMgr.inputs[i].cur.button = player.controller().state().button;
gPadMgr.inputs[i].cur.stick_x = player.controller().state().stick_x;
gPadMgr.inputs[i].cur.stick_y = player.controller().state().stick_y;
gPadMgr.ctrlrIsConnected[i] = true;
i++;
}
gPadMgr.nControllers = i;
}
void Players::attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId)
{
if(playerId == MAX_PLAYERS)
{
m_players[m_size++].attach(controller);
}
else
{
m_players[playerId].attach(controller);
m_size = std::max((u64)playerId + 1, m_size);
}
}
} // namespace sm64
}

View File

@ -1,30 +1,36 @@
#pragma once
#include "ultra64/types.h"
#include "player.h"
#include <vector>
#include "player.h"
#include "../controller/sdl.h"
namespace sm64
static const u8 MAX_PLAYERS = 0xFF;
namespace oot
{
static const u8 MAX_PLAYERS = 0xFF;
class Players
namespace hid
{
public:
static const u32 MAX_PLAYERS = 1;
Players();
const u64 size() const;
void update();
Player& operator[](u32 i)
class Players
{
return m_players[i];
}
void attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId = MAX_PLAYERS);
public:
static const u32 MAX_PLAYERS = 1;
protected:
Player m_players[MAX_PLAYERS];
u64 m_size;
};
static Players& get();
static void Update();
static const N64Controller* GetController();
Players& players();
Player& player(const u64 i);
} // namespace sm64
Players() = default;
const u64 size() const { return m_size; }
void update();
void attach(const std::shared_ptr<N64Controller>& controller, const u8 playerId = MAX_PLAYERS);
Player& operator[](u32 i) { return m_players[i]; }
private:
Player m_players[MAX_PLAYERS];
u64 m_size = 0;
};
}
}