1
0
mirror of https://github.com/blawar/ooot.git synced 2024-07-04 18:13:37 +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,9 +12,10 @@
#define TAS_DIR "tas"
#endif
namespace sm64::hid
{
State::State()
using namespace oot::hid;
N64Controller::State::State()
{
mouse_x = 0;
mouse_y = 0;
@ -22,7 +24,7 @@ namespace sm64::hid
reset();
}
void State::reset()
void N64Controller::State::reset()
{
button = 0;
stick_x = 0;
@ -33,54 +35,38 @@ namespace sm64::hid
has_mouse = false;
}
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)
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)
{
}
void Controller::SendMotorEvent(short time, short level)
N64Controller::~N64Controller()
{
if (m_tasFile)//Tas file is open
fclose(m_tasFile);
m_tasFile = nullptr;
}
void Controller::SendMotorDecay(short level)
{
}
void Controller::ResetMotorPack()
{
}
void Controller::SendMotorVib(int level)
{
}
void Controller::update()
{
}
void Controller::merge(const Controller& controller)
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())
{
@ -91,66 +77,128 @@ namespace sm64::hid
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;
return "last_run.tas";
/*std::error_code error;
std::filesystem::create_directory(TAS_DIR, error);
time_t now = time(0);
tm* ltm = localtime(&now);
tm* time = localtime(&now);
if (!ltm)
{
if (!time)
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;
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;*/
}
#ifdef ENABLE_TAS
static std::ofstream* g_tas = nullptr;
static std::ofstream g_tas;
static std::ofstream& stream()
{
if (!g_tas)
/*if (!g_tas)
{
auto name = getTasFileName();
g_tas = new std::ofstream(name, std::ifstream::binary);
g_tas->write((const char*)&config(), sizeof(config()));
g_tas = std::ofstream(name, std::ofstream::binary);
if (g_tas)
g_tas.write((const char*)&oot::config(), sizeof(oot::config()));
}*/
return g_tas;
}
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?
{
fopen_s(&m_tasFile, "last-run.tas", "wb");
if (!m_tasFile)
return;
//Write our current config
//fwrite(&oot::config(), sizeof(oot::config()), 1, m_tasOutput);
//Write save data
uint8_t* sram = new uint8_t[SRAM_SIZE];
FILE* save = nullptr;
fopen_s(&save, "oot.sav", "wb");
if (save)
{
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
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)
{
int y = 0;
}
}
#endif
//Playing back a TAS?
else if (Tas::isTasPlaying())
{
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
}
}
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())
@ -165,24 +213,16 @@ namespace sm64::hid
// 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);
@ -200,24 +240,16 @@ namespace sm64::hid
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);
@ -232,19 +264,17 @@ namespace sm64::hid
}
}
s64 Controller::mouse_x() const
s64 N64Controller::mouse_x() const
{
return m_state.mouse_x * (oot::config().camera().mousexInvert() ? -1 : 1) * oot::config().camera().mousexScaler();
}
s64 Controller::mouse_y() const
s64 N64Controller::mouse_y() const
{
return m_state.mouse_y * (oot::config().camera().mouseyInvert() ? -1 : 1) * oot::config().camera().mouseyScaler();
}
bool Controller::updateRebind(int input)
/*bool N64Controller::updateRebind(int input)
{
return false;
}
} // namespace sm64::hid
}*/

View File

@ -1,46 +1,16 @@
#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 N64Controller
{
public:
class State
{
public:
@ -59,9 +29,53 @@ namespace sm64::hid
bool has_mouse;
};
class Controller
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
@ -79,33 +93,38 @@ namespace sm64::hid
s64 mouse_x() const;
s64 mouse_y() const;
Controller(bool isLocal = true);
virtual void update();
N64Controller(bool isLocal = true);
~N64Controller();
virtual void update() {}
virtual void resolveInputs();
virtual bool isLocal() const
{
virtual bool isLocal() const {
return m_isLocal;
}
State& state()
{
State& state() {
return m_state;
}
const State state() const {
return m_state;
}
virtual void merge(const Controller& controller);
virtual bool hasMouse() const;
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 void SendMotorEvent(short time, short level) {}
virtual void SendMotorDecay(short level) {}
virtual void ResetMotorPack() {}
virtual void SendMotorVib(int level) {}
virtual bool updateRebind(int input);
//virtual bool updateRebind(Button input);
protected:
State m_state;
bool m_isLocal;
bool m_motorEnabled;
};
bool isTasPlaying();
} // namespace sm64::hid
private:
FILE* m_tasFile = nullptr;
};
}
}

View File

@ -5,54 +5,45 @@
#include <stdexcept>
#include "../options.h"
namespace sm64::hid
{
static Controllers* g_controllers;
Controllers& controllers()
using namespace oot::hid;
static InputDeviceManager* g_deviceManager = nullptr;
InputDeviceManager& InputDeviceManager::get()
{
if(!g_controllers)
{
g_controllers = new Controllers();
}
return *g_controllers;
if (!g_deviceManager)
g_deviceManager = new InputDeviceManager();
return *g_deviceManager;
}
Driver::Driver()
{
}
Driver::~Driver()
{
}
const u64 Driver::size() const
{
return m_controllers.size();
}
Controller& Driver::controller(const u64 index)
N64Controller& InputDevice::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 InputDevice::updateRebind(Button input)
{
bool result = 0;
for (auto& controller : m_controllers)
{
controller->state().reset();
result |= controller->updateRebind(input);
//result |= controller->updateRebind(input);
controller->resolveInputs();
}
return result;
}
void Driver::update()
void InputDevice::update()
{
for(auto& controller : m_controllers)
{
@ -62,81 +53,59 @@ namespace sm64::hid
}
}
void Driver::scan(class Controllers* controllers)
{
}
Controllers::Controllers() : m_rebindInput(0)
InputDeviceManager::InputDeviceManager() : m_rebindInput(Button::NONE)
{
m_drivers.push_back(new SDL());
m_drivers.push_back(new Joypad());
#ifdef ENABLE_MOUSE
m_drivers.push_back(new Keyboard());
#endif
//if (Tas::isTasPlaying())
//m_drivers.push_back(new Tas());
}
Controllers::~Controllers()
{
}
const u64 Controllers::size() const
const u64 InputDeviceManager::size() const
{
u64 count = 0;
for (auto& driver : m_drivers)
{
count += driver->size();
}
return count;
}
void Controllers::update()
void InputDeviceManager::update()
{
if (isRebindMode())
{
bool result = 0;
for (auto& driver : m_drivers)
{
result |= driver->updateRebind(m_rebindInput);
}
if (result)
{
m_rebindInput = 0;
}
m_rebindInput = Button::NONE;
}
else
{
for (auto& driver : m_drivers)
{
driver->update();
}
}
}
void Controllers::scan()
void InputDeviceManager::scan()
{
u64 found = 0;
for (auto& driver : m_drivers)
{
//if(!driver->defaultOnly() || !found)
{
driver->scan(this);
found += driver->size();
driver->scan();
}
}
}
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
{
class Driver
{
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()
{
return false;
}
std::vector<std::shared_ptr<Controller>>& controllers()
{
return m_controllers;
}
protected:
std::vector<std::shared_ptr<Controller>> m_controllers;
namespace oot
{
namespace hid
{
enum class Button : uint32_t
{
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,
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 Controllers
class InputDevice;
class InputDeviceManager
{
public:
Controllers();
virtual ~Controllers();
static InputDeviceManager& get();
InputDeviceManager();
virtual ~InputDeviceManager() {}
const u64 size() const;
void update();
void scan();
bool isRebindMode() const;
void rebind(int input);
std::vector<class Driver*>& drivers()
{
return m_drivers;
}
bool isRebindMode() const { return m_rebindInput != Button::NONE; }
void rebind(Button input) { m_rebindInput = input; }
std::vector<InputDevice*>& drivers() { return m_drivers; }
protected:
std::vector<class Driver*> m_drivers;
int m_rebindInput;
std::vector<InputDevice*> m_drivers;
Button m_rebindInput;
};
Controllers& controllers();
} // namespace sm64::hid
class InputDevice
{
public:
InputDevice() = default;
virtual ~InputDevice() {}
virtual void scan() = 0;
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,87 +46,87 @@ bool saveJson(rapidjson::Document& doc, const std::string& jsonFilePath)
}
#endif
namespace sm64::hid
{
namespace controller
{
const char* getInputName(int input)
const char* Keyboard::getInputName(Button 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";
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;
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;
}
class Keyboard : public Controller
{
public:
Keyboard() : Controller()
Keyboard::Keyboard() : N64Controller()
{
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;
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();
#endif
}
void loadKeyBindings()
void Keyboard::loadKeyBindings()
{
#ifdef ENABLE_JSON
try
@ -144,10 +149,8 @@ namespace sm64::hid
auto key = SDL_GetScancodeFromName(itr->name.GetString());
auto value = getInputValue(itr->value.GetString());
if (key != SDL_SCANCODE_UNKNOWN && value)
{
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());
@ -163,7 +166,9 @@ namespace sm64::hid
#endif
}
void saveKeyBindings()
void Keyboard::saveKeyBindings()
{
#ifdef ENABLE_JSON
try
@ -188,62 +193,47 @@ namespace sm64::hid
#endif
}
void clearRebindMode()
{
}
void resetRebinds()
{
}
bool hasMouse() const
bool Keyboard::hasMouse() const
{
return true;
return m_state.has_mouse;
}
int keyboard_buttons_down;
int keyboard_map_scancode(SDL_Scancode scancode)
Button Keyboard::keyboard_map_scancode(SDL_Scancode scancode)
{
if (m_keyBindings.count(scancode))
{
return m_keyBindings[scancode];
}
return 0;
return Button::NONE;
}
bool on_key_down(SDL_Scancode scancode)
bool Keyboard::on_key_down(SDL_Scancode scancode)
{
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= mapped;
return mapped != 0;
Button mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= (int)mapped;
return (int)mapped != 0;
}
bool on_key_up(SDL_Scancode scancode)
bool Keyboard::on_key_up(SDL_Scancode scancode)
{
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~mapped;
return mapped != 0;
Button mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~(int)mapped;
return (int)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)
bool Keyboard::canRebind(SDL_Scancode scancode, Button input)
{
if (m_keyBindings.count(scancode) == 0)
{
return true;
}
auto replacingInput = m_keyBindings[scancode];
u64 count = 0;
@ -251,24 +241,22 @@ namespace sm64::hid
for (auto i : m_keyBindings)
{
if (i.second == replacingInput)
{
count++;
}
}
return count != 1;
}
bool updateRebind(int input) override
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++)
{
@ -284,7 +272,9 @@ namespace sm64::hid
return changed != 0;
}
void update()
void Keyboard::update()
{
bool walk = false;
int count = 0;
@ -297,9 +287,7 @@ namespace sm64::hid
}
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]))
{
@ -312,50 +300,38 @@ 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 |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
}
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::R_JPAD;
if (hid::isTasPlaying())
{
if (Tas::isTasPlaying())
return;
}
for (const auto x : m_keyBindings)
for (const auto& [scancode, input] : m_keyBindings)
{
const auto scancode = x.first;
const auto input = x.second;
if (scancode < count)
if (scancode < count && state[scancode])
{
if (state[scancode])
{
if (input > 0xFFFF)
if ((uint32_t)input <= 0xFFFF)
m_state.button |= (uint32_t)input;
else
{
switch (input)
{
case STICK_X_DOWN:
case Button::STICK_X_DOWN:
m_state.stick_y = -128;
break;
case STICK_X_UP:
case Button::STICK_X_UP:
m_state.stick_y = 127;
break;
case STICK_X_LEFT:
case Button::STICK_X_LEFT:
m_state.stick_x = -128;
break;
case STICK_X_RIGHT:
case Button::STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case WALK_BUTTON:
case Button::WALK_BUTTON:
walk = true;
break;
}
}
else
{
this->state().button |= input;
}
}
}
}
@ -364,30 +340,19 @@ namespace sm64::hid
int mouse_delta_y = 0;
auto buttons = SDL_GetRelativeMouseState(&mouse_delta_x, &mouse_delta_y);
this->enableMouse();
enableMouse();
if (buttons & SDL_BUTTON(SDL_BUTTON_LEFT))
{
m_state.button |= B_BUTTON;
}
m_state.button |= (uint16_t)Button::B_BUTTON;
if (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT))
{
m_state.button |= A_BUTTON;
}
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;
if (mouse_delta_x > 10)
{
int yy = 0;
}
#endif
if (walk)
@ -395,45 +360,33 @@ namespace sm64::hid
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()
{
}
Keyboard::~Keyboard()
{
}
void Keyboard::scan(class Controllers* controllers)
void Keyboard::scan()
{
if (!size())
{
#ifdef ENABLE_MOUSE
//SDL_SetRelativeMouseMode(SDL_TRUE);
#endif
auto controller = std::make_shared<controller::Keyboard>();
auto controller = std::make_shared<Keyboard>();
#ifdef ENABLE_MOUSE
controller->enableMouse();
#endif
m_controllers.push_back(controller);
players().attach(controller, 0);
Players::get().attach(controller, 0);
}
}
void Keyboard::update()
/*void Keyboard::update()
{
for (auto& keyboard : m_controllers)
{
((controller::Keyboard*)keyboard.get())->update();
}
}
} // namespace sm64::hid
((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
namespace oot
{
class Keyboard : public Driver
namespace hid
{
class Keyboard : public N64Controller, public InputDevice
{
public:
Keyboard();
virtual ~Keyboard();
void scan(class Controllers* controllers) override;
virtual ~Keyboard() {}
void scan();
void update() override;
bool defaultOnly() override
{
return true;
}
bool defaultOnly() override { return true; }
bool updateRebind(Button input) override;
protected:
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];
};
} // namespace sm64::hid
}
}

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,56 +35,93 @@ 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;
namespace controller
{
const char* getInputName(int input);
int getInputValue(const std::string& input);
class SDL : public Controller
//const char* getInputName(int input);
//int getInputValue(const std::string& input);
Joypad::~Joypad()
{
public:
SDL(SDL_GameController* controller) : Controller(), m_context(controller), m_haptic(nullptr)
closeHaptics();
SDL_QuitSubSystem(SDL_INIT_HAPTIC);
}
Joypad::Joypad() : N64Controller()
{
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0)
fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
g_haptics = SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0;
#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");
#endif
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
if (SDL_IsGameController(i))
{
m_context = SDL_GameControllerOpen(i);
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);
#endif
m_motorEnabled = initHaptics();
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;
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
}
virtual ~SDL()
void Joypad::scan()
{
closeHaptics();
auto controller = std::make_shared<Joypad>();
m_controllers.push_back(controller);
Players::get().attach(controller, 0);
}
void loadKeyBindings()
void Joypad::loadKeyBindings()
{
#ifdef ENABLE_JSON
try
@ -109,9 +145,7 @@ namespace sm64::hid
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());
@ -127,7 +161,9 @@ namespace sm64::hid
#endif
}
void saveKeyBindings()
void Joypad::saveKeyBindings()
{
#ifdef ENABLE_JSON
try
@ -152,19 +188,17 @@ namespace sm64::hid
#endif
}
bool initHaptics()
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)
{
@ -181,7 +215,9 @@ namespace sm64::hid
return true;
}
void closeHaptics()
void Joypad::closeHaptics()
{
if (m_haptic)
{
@ -190,137 +226,122 @@ namespace sm64::hid
}
}
void SendMotorEvent(short time, short level) override
void Joypad::SendMotorEvent(short time, short level)
{
if (m_motorEnabled)
{
SDL_HapticRumblePlay(m_haptic, level / 100.0f, time * 10);
}
}
void SendMotorDecay(short level) override
{
}
void ResetMotorPack() override
void Joypad::ResetMotorPack()
{
if (m_motorEnabled)
{
SDL_HapticRumbleStop(m_haptic);
}
}
void SendMotorVib(int level) override
void Joypad::SendMotorVib(int level)
{
SendMotorEvent(30, level);
}
static inline int8_t convertToByte(int value, int max)
int8_t Joypad::convertToByte(int value, int max)
{
int8_t result = value * 0x7F / max;
return result;
}
static inline int8_t invert(const int8_t value)
int8_t Joypad::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()
int8_t Joypad::stickLeftX()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTX);
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 stickLeftY()
inline int8_t Joypad::stickLeftY()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_LEFTY);
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 stickRightX()
inline int8_t Joypad::stickRightX()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTX);
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 stickRightY()
inline int8_t Joypad::stickRightY()
{
auto value = readAxis(SDL_CONTROLLER_AXIS_RIGHTY);
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 canRebind(SDL_GameControllerButton button, int input)
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 != START_BUTTON && replacingInput != A_BUTTON && replacingInput != B_BUTTON)
{
if (replacingInput != Button::START_BUTTON && replacingInput != Button::A_BUTTON && replacingInput != Button::B_BUTTON)
return true;
}
if (replacingInput == input)
{
return true;
}
for (auto i : m_keyBindings)
{
if (i.second == replacingInput)
for (auto& [input, button] : m_keyBindings)
{
if (button == replacingInput)
count++;
}
}
if (count == 1)
{
return false;
}
return count != 1;
}
bool updateRebind(int input) override
bool Joypad::updateRebind(Button input)
{
u8 state[SDL_CONTROLLER_BUTTON_MAX];
@ -344,12 +365,12 @@ namespace sm64::hid
return false;
}
void update() override
{
if(!init_ok || !m_context || hid::isTasPlaying())
void Joypad::update()
{
if (!m_context || Tas::isTasPlaying())
return;
}
bool walk = false;
SDL_GameControllerUpdate();
@ -361,47 +382,44 @@ namespace sm64::hid
}
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)
for (const auto& [scancode, input] : m_keyBindings)
{
if (m_buttonState[i.first])
if (m_buttonState[scancode])
{
if (i.second > 0xFFFF)
if ((uint32_t)input <= 0xFFFF)
m_state.button |= (uint16_t)input;
else
{
switch (i.second)
switch (input)
{
case STICK_X_DOWN:
case Button::STICK_X_DOWN:
m_state.stick_y = -128;
break;
case STICK_X_UP:
case Button::STICK_X_UP:
m_state.stick_y = 127;
break;
case STICK_X_LEFT:
case Button::STICK_X_LEFT:
m_state.stick_x = -128;
break;
case STICK_X_RIGHT:
case Button::STICK_X_RIGHT:
m_state.stick_x = 127;
break;
case WALK_BUTTON:
case Button::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);
@ -412,25 +430,17 @@ namespace sm64::hid
if (abs(righty) > 0x7000)
{
if (righty > 0)
{
m_state.button |= D_CBUTTONS;
}
m_state.button |= (uint16_t)Button::D_CBUTTONS;
else
{
m_state.button |= U_CBUTTONS;
}
m_state.button |= (uint16_t)Button::U_CBUTTONS;
}
if (abs(rightx) > 0x7000)
{
if (rightx > 1)
{
m_state.button |= R_CBUTTONS;
}
m_state.button |= (uint16_t)Button::R_CBUTTONS;
else
{
m_state.button |= L_CBUTTONS;
}
m_state.button |= (uint16_t)Button::L_CBUTTONS;
}
}
@ -438,9 +448,9 @@ namespace sm64::hid
int16_t rtrig = SDL_GameControllerGetAxis(m_context, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
if (ltrig > 30 * 256)
m_state.button |= Z_TRIG;
m_state.button |= (uint16_t)Button::Z_TRIG;
//if(rtrig > 30 * 256)
//m_state.button |= R_TRIG;
//m_state.button |= (uint16_t)Button::R_TRIG;
if (walk)
{
@ -467,79 +477,20 @@ namespace sm64::hid
//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;
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 &= ~(U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS);//Unpress the c buttons
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 |= U_JPAD | D_JPAD | L_JPAD | R_JPAD;
m_state.button |= (uint16_t)Button::U_JPAD | (uint16_t)Button::D_JPAD | (uint16_t)Button::L_JPAD | (uint16_t)Button::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");
#endif
init_ok = true;
for(int i = 0; i < SDL_NumJoysticks(); i++)
{
if(SDL_IsGameController(i))
{
auto context = SDL_GameControllerOpen(i);
if(context)
{
auto controller = std::make_shared<controller::SDL>(context);
m_controllers.push_back(controller);
players().attach(controller, 0);
}
}
}
#ifdef ENABLE_MOUSE
// temporarily change mouse mode to not take over the cursor
// SDL_SetRelativeMouseMode(m_controllers.size() ? SDL_FALSE : SDL_TRUE);
#endif
}
} // namespace sm64::hid
#endif

View File

@ -1,16 +1,56 @@
#pragma once
#include <string>
#include <unordered_map>
#include <SDL2/SDL.h>
#include "controllers.h"
namespace sm64::hid
namespace oot
{
class SDL : public Driver
namespace hid
{
class Joypad : public N64Controller, public InputDevice
{
public:
SDL();
virtual ~SDL();
void scan(class Controllers* controllers) override;
Joypad();
virtual ~Joypad();
protected:
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];
};
} // namespace sm64::hid
}
}

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;
namespace sm64::hid
{
bool isTasPlaying()
using namespace oot::hid;
bool Tas::isTasPlaying()
{
return g_tasPlaying;
}
namespace controller
{
class Tas : public Controller
{
public:
Tas() : Controller()
{
fp = fopen("cont.tas", "rb");
if(fp != NULL)
void Tas::playTas(bool enabled)
{
fread(&oot::config(), 1, sizeof(oot::config()), fp);
g_tasPlaying = enabled;
}
Tas::Tas() : N64Controller()
{
fp = fopen("last-run.tas", "rb");
if (fp)
{
//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)
{
fwrite(sram, sizeof(uint8_t), SRAM_SIZE, save);
fclose(save);
}
delete[] sram;
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()
{
if (fp)
fclose(fp);
}
void Tas::scan(class Controllers* controllers)
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<controller::Tas>();
auto controller = std::make_shared<Tas>();
m_controllers.push_back(controller);
players().attach(controller, 0);
Players::get().attach(controller, 0);
}
}
} // namespace sm64::hid

View File

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

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

View File

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

View File

@ -1,49 +1,58 @@
#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 g_players;
Players& players()
Players& Players::get()
{
return g_players;
}
Player& player(const u64 i)
void Players::Update()
{
return g_players[i];
g_players.update();
}
Players::Players() : m_players(), m_size(0)
const N64Controller* Players::GetController()
{
if (g_players[0].controllers().size() == 0)
return nullptr;
return g_players[0].controllers()[0].get();
}
const u64 Players::size() const
{
return m_size;
}
void Players::update()
{
int i=0;
memset(&gPadMgr.ctrlrIsConnected[0], 0, sizeof(gPadMgr.ctrlrIsConnected));
int i = 0;
for (auto& player : m_players)
{
player.update();
@ -55,19 +64,19 @@ namespace sm64
gPadMgr.ctrlrIsConnected[i] = true;
i++;
}
gPadMgr.nControllers = i;
}
void Players::attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId)
void Players::attach(const std::shared_ptr<N64Controller>& 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
{
namespace hid
{
class Players
{
public:
static const u32 MAX_PLAYERS = 1;
Players();
const u64 size() const;
static Players& get();
static void Update();
static const N64Controller* GetController();
Players() = default;
const u64 size() const { return m_size; }
void update();
Player& operator[](u32 i)
{
return m_players[i];
}
void attach(const std::shared_ptr<hid::Controller>& controller, const u8 playerId = MAX_PLAYERS);
void attach(const std::shared_ptr<N64Controller>& controller, const u8 playerId = MAX_PLAYERS);
protected:
Player& operator[](u32 i) { return m_players[i]; }
private:
Player m_players[MAX_PLAYERS];
u64 m_size;
u64 m_size = 0;
};
Players& players();
Player& player(const u64 i);
} // namespace sm64
}
}