implement cpad stuff

This commit is contained in:
ManDude
2021-08-14 21:00:50 +01:00
parent af5a9f0951
commit af823cdb82
14 changed files with 576 additions and 52 deletions
+1
View File
@@ -34,6 +34,7 @@ set(RUNTIME_SOURCE
system/IOP_Kernel.cpp
system/iop_thread.cpp
system/Deci2Server.cpp
system/newpad.cpp
sce/libcdvd_ee.cpp
sce/libscf.cpp
sce/libdma.cpp
+15 -9
View File
@@ -8,9 +8,11 @@
#include "common/log/log.h"
/* ****************************** */
/* Internal functions */
/* ****************************** */
/*
********************************
* Internal functions
********************************
*/
namespace {
@@ -28,9 +30,11 @@ void set_main_display(std::shared_ptr<GfxDisplay> display) {
} // namespace
/* ****************************** */
/* GfxDisplay */
/* ****************************** */
/*
********************************
* GfxDisplay
********************************
*/
GfxDisplay::GfxDisplay(GLFWwindow* a_window) {
set_renderer(GfxPipeline::OpenGL);
@@ -81,9 +85,11 @@ void GfxDisplay::render_graphics() {
m_renderer->render_display(this);
}
/* ****************************** */
/* DISPLAY */
/* ****************************** */
/*
********************************
* DISPLAY
********************************
*/
namespace Display {
+21 -4
View File
@@ -6,11 +6,12 @@
#include <functional>
#include "gfx.h"
#include "display.h"
#include "pipelines/opengl.h"
#include "common/log/log.h"
#include "game/runtime.h"
#include "display.h"
#include "pipelines/opengl.h"
#include "game/system/newpad.h"
namespace {
@@ -29,7 +30,14 @@ void InitSettings(GfxSettings& settings) {
// debug for now
settings.debug = true;
return;
// we're not setting controller info
settings.pad_mapping_info.input_mode = false;
// use buffered input mode
settings.pad_mapping_info.buffer_mode = true;
// debug input settings
settings.pad_mapping_info.debug = true;
// use a default mapping
Pad::DefaultMapping(settings.pad_mapping_info);
}
} // namespace
@@ -57,6 +65,8 @@ u32 Init() {
lg::info("GFX Init");
// initialize settings
InitSettings(g_settings);
// guarantee we have no keys detected by pad
Pad::ForceClearKeys();
if (g_settings.renderer->init(g_settings)) {
lg::error("Gfx::Init error");
@@ -75,6 +85,9 @@ u32 Init() {
void Loop(std::function<bool()> f) {
lg::info("GFX Loop");
while (f()) {
// clean the inputs
Pad::ClearKeys();
// check if we have a display
if (Display::GetMainDisplay()) {
// lg::debug("run display");
@@ -116,4 +129,8 @@ void texture_relocate(u32 destination, u32 source, u32 format) {
}
}
int PadIsPressed(Pad::Button button, int port) {
return Pad::IsPressed(g_settings.pad_mapping_info, button, port);
}
} // namespace Gfx
+8 -1
View File
@@ -7,8 +7,10 @@
#include <functional>
#include <memory>
#include "common/common_types.h"
#include "game/kernel/kboot.h"
#include "game/system/newpad.h"
// forward declarations
struct GfxSettings;
@@ -39,12 +41,15 @@ struct GfxRendererModule {
struct GfxSettings {
// current version of the settings. this should be set up so that newer versions are always higher
// than older versions
static constexpr u64 CURRENT_VERSION = 0x0000'0000'00001'0001;
static constexpr u64 CURRENT_VERSION = 0x0000'0000'0002'0001;
u64 version; // the version of this settings struct
const GfxRendererModule* renderer; // which rendering pipeline to use.
int vsync; // (temp) number of screen update per frame
bool debug; // graphics debugging
Pad::MappingInfo pad_mapping_info; // button mapping
Pad::MappingInfo pad_mapping_info_backup; // button mapping backup (see newpad.h)
};
namespace Gfx {
@@ -64,4 +69,6 @@ void send_chain(const void* data, u32 offset);
void texture_upload_now(const u8* tpage, int mode, u32 s7_ptr);
void texture_relocate(u32 destination, u32 source, u32 format);
int PadIsPressed(Pad::Button button, int port);
} // namespace Gfx
+3
View File
@@ -14,6 +14,7 @@
#include "game/graphics/opengl_renderer/OpenGLRenderer.h"
#include "game/graphics/texture/TexturePool.h"
#include "game/graphics/dma/dma_copy.h"
#include "game/system/newpad.h"
#include "common/log/log.h"
#include "common/goal_constants.h"
#include "game/runtime.h"
@@ -50,8 +51,10 @@ void SetDisplayCallbacks(GLFWwindow* d) {
glfwSetKeyCallback(d, [](GLFWwindow* /*window*/, int key, int scancode, int action, int mods) {
if (action == GlfwKeyAction::Press) {
lg::debug("KEY PRESS: key: {} scancode: {} mods: {:X}", key, scancode, mods);
Pad::OnKeyPress(key);
} else if (action == GlfwKeyAction::Release) {
lg::debug("KEY RELEASE: key: {} scancode: {} mods: {:X}", key, scancode, mods);
Pad::OnKeyRelease(key);
}
});
}
+113 -12
View File
@@ -409,27 +409,128 @@ void CacheFlush(void* mem, int size) {
* Prints an error if it fails to open.
*/
u64 CPadOpen(u64 cpad_info, s32 pad_number) {
auto info = Ptr<CpadInfo>(cpad_info).c();
if (info->cpad_file == 0) {
auto cpad = Ptr<CPadInfo>(cpad_info).c();
if (cpad->cpad_file == 0) {
// not open, so we will open it
info->cpad_file =
cpad->cpad_file =
ee::scePadPortOpen(pad_number, 0, pad_dma_buf + pad_number * SCE_PAD_DMA_BUFFER_SIZE);
if (info->cpad_file < 1) {
MsgErr("dkernel: !open cpad #%d (%d)\n", pad_number, info->cpad_file);
if (cpad->cpad_file < 1) {
MsgErr("dkernel: !open cpad #%d (%d)\n", pad_number, cpad->cpad_file);
}
info->new_pad = 1;
info->state = 0;
cpad->new_pad = 1;
cpad->state = 0;
}
return cpad_info;
}
// TODO CPadGetData
void CPadGetData() {
static bool warned = false;
if (!warned) {
lg::warn("ignoring calls to CPadGetData");
warned = true;
Ptr<CPadInfo> CPadGetData(u64 cpad_info) {
auto cpad = Ptr<CPadInfo>(cpad_info).c();
auto pad_state = scePadGetState(cpad->number, 0);
if (pad_state == scePadStateDiscon) {
cpad->state = 0;
}
cpad->valid = pad_state | 0x80;
switch (cpad->state) {
// case 99: // functional
default: // controller is functioning as normal
if (pad_state == scePadStateStable || pad_state == scePadStateFindCTP1) {
scePadRead(cpad->number, 0, (u8*)cpad);
// ps2 controllers would send an enabled bit if the button was NOT pressed, but we don't do
// that here. removed code that flipped the bits.
if (cpad->change_time != 0) {
scePadSetActDirect(cpad->number, 0, cpad->direct);
}
cpad->valid = pad_state;
}
break;
case 0: // unavailable
if (pad_state == scePadStateStable || pad_state == scePadStateFindCTP1) {
auto pad_mode = scePadInfoMode(cpad->number, 0, InfoModeCurID, 0);
if (pad_mode != 0) {
auto vibration_mode = scePadInfoMode(cpad->number, 0, InfoModeCurExID, 0);
if (vibration_mode > 0) {
// vibration supported
pad_mode = vibration_mode;
}
if (pad_mode == 4) {
// controller mode
cpad->state = 40;
} else if (pad_mode == 7) {
// dualshock mode
cpad->state = 70;
} else {
// who knows mode
cpad->state = 90;
}
}
}
break;
case 40: // controller mode - check for extra modes
cpad->change_time = 0;
if (scePadInfoMode(cpad->number, 0, InfoModeIdTable, -1) == 0) {
// no controller modes
cpad->state = 90;
return make_ptr<CPadInfo>(cpad);
}
cpad->state = 41;
case 41: // controller mode - change to dualshock mode!
// try to enter the 2nd controller mode (dualshock for ds2's)
if (scePadSetMainMode(cpad->number, 0, 1, 3) == 1) {
cpad->state = 42;
}
break;
case 42: // controller mode change check
if (scePadGetReqState(cpad->number, 0) == scePadReqStateFailed) {
// failed to change to DS2
cpad->state = 41;
}
if (scePadGetReqState(cpad->number, 0) == scePadReqStateComplete) {
// change successful. go back to the beginning.
cpad->state = 0;
}
break;
case 70: // dualshock mode - check vibration
// get number of actuators (2 for DS2)
if (scePadInfoAct(cpad->number, 0, -1, 0) < 1) {
// no actuators means no vibration. skip to end!
cpad->change_time = 0;
cpad->state = 99;
} else {
// we have actuators to use.
cpad->change_time = 1; // remember to update vibration.
cpad->state = 75;
}
break;
case 75: // set actuator vib param info
if (scePadSetActAlign(cpad->number, 0, cpad->align) != 0) {
if (scePadInfoPressMode(cpad->number, 0) == 1) {
// pressure buttons supported
cpad->state = 76;
} else {
// no pressure buttons, done with controller setup
cpad->state = 99;
}
}
break;
case 76: // enter pressure mode
if (scePadEnterPressMode(cpad->number, 0) == 1) {
cpad->state = 78;
}
break;
case 78: // pressure mode request check
if (scePadGetReqState(cpad->number, 0) == 1) {
cpad->state = 76;
}
if (scePadGetReqState(cpad->number, 0) == 0) {
cpad->state = 99;
}
break;
case 90:
break; // unsupported controller. too bad!
}
return make_ptr<CPadInfo>(cpad);
}
// TODO InstallHandler
+19 -7
View File
@@ -89,21 +89,33 @@ void CacheFlush(void* mem, int size);
void InitMachineScheme();
//! Mirror of cpad-info
struct CpadInfo {
struct CPadInfo {
u8 valid;
u8 status;
s16 button0;
u8 rx;
u8 ry;
u8 lx;
u8 ly;
u16 button0;
u8 rightx;
u8 righty;
u8 leftx;
u8 lefty;
u8 abutton[12];
u8 dummy[12];
s32 number;
s32 cpad_file;
u8 _pad0[36];
u32 button0_abs[3];
u32 button0_shadow_abs[1];
u32 button0_rel[3];
float stick0_dir;
float stick0_speed;
s32 new_pad;
s32 state;
u8 align[6];
u8 direct[6];
u8 buzz_val[2];
u8 __pad[2];
u64 buzz_time[2];
u32 buzz;
s32 buzz_act;
s32 change_time; // actually u64 in goal!
};
struct FileStream {
-5
View File
@@ -5,9 +5,6 @@
* Stub implementation of the EE CD/DVD library
*/
#ifndef JAK1_LIBCDVD_EE_H
#define JAK1_LIBCDVD_EE_H
// for sceCdInit
#define SCECdINIT 0x00
@@ -34,5 +31,3 @@ int sceCdMmode(int media);
int sceCdDiskReady(int mode);
int sceCdGetDiskType();
} // namespace ee
#endif // JAK1_LIBCDVD_EE_H
+119
View File
@@ -1,7 +1,16 @@
#include "common/util/assert.h"
#include "libpad.h"
#include "game/kernel/kmachine.h"
#include "game/graphics/gfx.h"
/*!
* @file libpad.h
* Stub implementation of the EE pad (controller) library
*/
namespace ee {
int scePadPortOpen(int port, int slot, void*) {
// we are expected to return a non-zero file descriptor.
// we return the port + 1 and succeed always.
@@ -9,4 +18,114 @@ int scePadPortOpen(int port, int slot, void*) {
assert(slot == 0);
return port + 1;
}
int scePadGetState(int port, int slot) {
// pretend we always have a controller connected
return scePadStateStable;
}
// controller mode array for DS2s
// the game will try very hard to force into dualshock mode so let's not even try
static const int libpad_DualShock2_ModeIDs[2] = {
(int)PadMode::Controller, // no vibration or pressure sensitive buttons
(int)PadMode::DualShock2 // vibration + pressure sensitive buttons
};
int scePadInfoMode(int port, int slot, int term, int offs) {
if (term == InfoModeCurExID) {
// return vibration mode ID. that's just dualshock mode.
return libpad_DualShock2_ModeIDs[1];
} else if (term == InfoModeCurID) {
// return mode ID. just dualshock.
return libpad_DualShock2_ModeIDs[1];
} else if (term == InfoModeCurExOffs) {
// offset of current mode ID
return 1;
} else if (term == InfoModeIdTable) {
if (offs == -1)
return 2;
return libpad_DualShock2_ModeIDs[offs]; // ?
}
// invalid controller or some other error
return 0;
}
// order of pressure sensitive buttons in memory (not the same as their bit order...).
static const Pad::Button libpad_PadPressureButtons[] = {
Pad::Button::Right, Pad::Button::Left, Pad::Button::Up, Pad::Button::Down,
Pad::Button::Triangle, Pad::Button::Circle, Pad::Button::X, Pad::Button::Square,
Pad::Button::L1, Pad::Button::R1, Pad::Button::L2, Pad::Button::R2};
// reads controller data and writes it to a buffer in rdata (must be at least 32 bytes large).
// returns buffer size (32) or 0 on error.
int scePadRead(int port, int slot, u8* rdata) {
auto cpad = (CPadInfo*)(rdata);
cpad->valid = 0; // success
cpad->status = 0x70 /* (dualshock2) */ | (20 / 2); /* (dualshock2 data size) */
cpad->rightx = 0;
cpad->righty = 0;
cpad->leftx = 0;
cpad->lefty = 0;
// pressure sensitivity. ignore for now.
for (int i = 0; i < 12; ++i) {
cpad->abutton[i] = Gfx::PadIsPressed(libpad_PadPressureButtons[i], port) * 255;
}
cpad->button0 = 0;
for (int i = 0; i < 16; ++i) {
cpad->button0 |= Gfx::PadIsPressed((Pad::Button)i, port) << i;
}
return 32;
}
// buzzer control. We don't care right now, return success.
int scePadSetActDirect(int port, int slot, const u8* data) {
return 1;
}
int scePadSetActAlign(int port, int slot, const u8* data) {
return 1;
}
// we also don't care
int scePadSetMainMode(int port, int slot, int offs, int lock) {
return 1;
}
// async pad functions are gonna be synchronous so this always succeeds
int scePadGetReqState(int port, int slot) {
return scePadReqStateComplete;
}
int scePadInfoAct(int port, int slot, int actno, int term) {
if (actno == -1)
return 2; // i think?
if (actno < 2) {
if (term == InfoActSub) {
return 1; // whatever
} else if (term == InfoActFunc) {
return 1; // whatever
} else if (term == InfoActSize) {
return 0; // whatever
} else if (term == InfoActCurr) {
return 1; // whatever
}
}
return 0;
}
int scePadInfoPressMode(int port, int slot) {
return 0; // we do NOT support pressure sensitive buttons right now
}
int scePadEnterPressMode(int port, int slot) {
return 1; // we dont support pressure button, but if we did this would work straight away
}
} // namespace ee
+56 -1
View File
@@ -1,7 +1,62 @@
#pragma once
/*!
* @file libpad.h
* Stub implementation of the EE pad (controller) library
*/
#include "common/common_types.h"
#define SCE_PAD_DMA_BUFFER_SIZE 0x100
// pad status
#define scePadStateDiscon 0
#define scePadStateFindPad 1
#define scePadStateFindCTP1 2
#define scePadStateExecCmd 5
#define scePadStateStable 6
#define scePadStateError 7
#define scePadStateClosed 99
// pad mode info checks
#define InfoModeCurID 1
#define InfoModeCurExID 2
#define InfoModeCurExOffs 3
#define InfoModeIdTable 4
// pad async request states
#define scePadReqStateComplete 0
#define scePadReqStateFaild 1 // lol
#define scePadReqStateFailed 1
#define scePadReqStateBusy 2
// pad actuator info checks
#define InfoActFunc 1
#define InfoActSub 2
#define InfoActSize 3
#define InfoActCurr 4
namespace ee {
// controller modes (not in the lib)
enum PadMode {
Controller = 4,
DualShock = 7,
DualShock2 = DualShock,
NeGcon = 2,
Joystick = 5,
NamcoGun = 6
};
int scePadPortOpen(int port, int slot, void* data);
}
int scePadGetState(int port, int slot);
int scePadInfoMode(int port, int slot, int term, int offs);
int scePadRead(int port, int slot, u8* rdata);
int scePadSetActDirect(int port, int slot, const u8* data);
int scePadSetActAlign(int port, int slot, const u8* data);
int scePadSetMainMode(int port, int slot, int offs, int lock);
int scePadGetReqState(int port, int slot);
int scePadInfoAct(int port, int slot, int actno, int term);
int scePadInfoPressMode(int port, int slot);
int scePadEnterPressMode(int port, int slot);
}
+114
View File
@@ -0,0 +1,114 @@
/*!
* @file newpad.cpp
* PC-port specific cpad implementation on the C kernel. Monitors button inputs.
* Actual input detection is done through window events and is gfx pipeline-dependent.
*/
#include "newpad.h"
#include "common/log/log.h"
#include "game/graphics/pipelines/opengl.h" // for GLFW macros
namespace Pad {
/*
********************************
* Key checking
********************************
*/
std::unordered_map<int, int> g_key_status;
std::unordered_map<int, int> g_buffered_key_status;
void ForceClearKeys() {
g_key_status.clear();
g_buffered_key_status.clear();
}
void ClearKeys() {
for (auto& key : g_key_status) {
key.second = false;
}
for (auto& key : g_buffered_key_status) {
key.second = false;
}
}
void OnKeyPress(int key) {
// set absolute key status
if (g_key_status.find(key) == g_key_status.end()) {
g_key_status.insert(std::make_pair(key, 1));
} else {
g_key_status.at(key) = 1;
}
// set buffered key status
if (g_buffered_key_status.find(key) == g_buffered_key_status.end()) {
g_buffered_key_status.insert(std::make_pair(key, 1));
} else {
g_buffered_key_status.at(key) = 1;
}
}
void OnKeyRelease(int key) {
// set absolute key status
// no bounds checking for now in order to catch bugs
g_key_status.at(key) = 0;
}
/*
********************************
* Pad checking
********************************
*/
static int CheckPadIdx(int pad) {
if (pad < 0 || pad > CONTROLLER_COUNT) {
lg::error("Invalid pad {}, returning pad 0", pad);
}
return 0;
}
// returns 1 if button is pressed. returns 0 if invalid or not pressed.
int IsPressed(MappingInfo& mapping, Button button, int pad = 0) {
auto key = mapping.pad_mapping[CheckPadIdx(pad)][(int)button];
if (key == -1)
return 0;
auto& keymap = mapping.buffer_mode ? g_buffered_key_status : g_key_status;
if (keymap.find(key) == keymap.end())
return 0;
return keymap.at(key);
}
// map a button on a pad to a key
void MapButton(MappingInfo& mapping, Button button, int pad, int key) {
// check if pad is valid. dont map buttons with invalid pads.
if (CheckPadIdx(pad) != pad)
return;
mapping.pad_mapping[pad][(int)button] = key;
}
// reset button mappings
void DefaultMapping(MappingInfo& mapping) {
// make every button invalid
for (int p = 0; p < CONTROLLER_COUNT; ++p) {
for (int i = 0; i < (int)Button::Max; ++i) {
MapButton(mapping, (Button)i, p, -1);
}
}
// face buttons
MapButton(mapping, Button::Ecks, 0, GLFW_KEY_Z);
MapButton(mapping, Button::Square, 0, GLFW_KEY_X);
MapButton(mapping, Button::Triangle, 0, GLFW_KEY_S);
MapButton(mapping, Button::Circle, 0, GLFW_KEY_A);
// dpad
MapButton(mapping, Button::Up, 0, GLFW_KEY_UP);
MapButton(mapping, Button::Right, 0, GLFW_KEY_RIGHT);
MapButton(mapping, Button::Down, 0, GLFW_KEY_DOWN);
MapButton(mapping, Button::Left, 0, GLFW_KEY_LEFT);
}
}; // namespace Pad
+74
View File
@@ -0,0 +1,74 @@
#pragma once
/*!
* @file newpad.h
* PC-port specific cpad implementation on the C kernel. Monitors button inputs.
* Actual input detection is done through window events and is gfx pipeline-dependent.
*/
/* NOTE ABOUT KEY VALUES!
* I am using the renderer-dependent key value macros here (at least for now). This means that the
* button mapping may be renderer-dependent. When changing renderers, make sure to backup the
* original button mapping or something so that the user can reset it afterwards. Eventually we
* should fix this (or maybe it's not even a problem).
*/
#include <unordered_map>
namespace Pad {
static constexpr int CONTROLLER_COUNT = 2; // support 2 controllers.
// mirrors goal enum pad-buttons. used as indices to an array!
enum class Button {
Select = 0,
L3 = 1,
R3 = 2,
Start = 3,
Up = 4,
Right = 5,
Down = 6,
Left = 7,
L2 = 8,
R2 = 9,
L1 = 10,
R1 = 11,
Triangle = 12,
Circle = 13,
X = 14,
Square = 15,
Max = 16,
// aliases
Ecks = X,
Cross = X,
O = Circle
};
struct MappingInfo {
bool debug = true; // debug mode
bool input_mode = false; // input mode for controller mapping
bool buffer_mode = true; // use buffered inputs
int pad_mapping[CONTROLLER_COUNT][(int)Pad::Button::Max]; // controller button mapping
// TODO complex button mapping & key macros (e.g. shift+x for l2+r2 press etc.)
};
// key-down status of any detected key.
extern std::unordered_map<int, int> g_key_status;
// key-down status of any detected key. this is buffered for the remainder of a frame.
extern std::unordered_map<int, int> g_buffered_key_status;
void OnKeyPress(int key);
void OnKeyRelease(int key);
void ForceClearKeys();
void ClearKeys();
void DefaultMapping(MappingInfo& mapping);
int IsPressed(MappingInfo& mapping, Button button, int pad);
void MapButton(MappingInfo& mapping, Button button, int pad, int key);
} // namespace Pad
-1
View File
@@ -214,7 +214,6 @@
(defmethod link-art! art-group ((obj art-group))
"Links the elements of this art-group."
;; this check seems superfluous
(when obj
(countdown (s5-0 (-> obj length))
(let* ((art-elt (-> obj data s5-0))
+33 -12
View File
@@ -30,7 +30,15 @@
(x 14)
(square 15)
)
(defenum pad-type
(normal 4)
(analog 5)
(dualshock 7)
(negcon 2)
(namco-gun 6)
)
;; these forward declarations should probably go somewhere else...
(define-extern get-current-time (function uint))
(define-extern get-integral-current-time (function uint))
@@ -38,16 +46,25 @@
;; this gets set to #f later on.
(define *cheat-mode* #t)
;; data that comes directly from hardware.
;; data that comes directly from hardware. it's 32 bytes + type tag (ignored in C kernel).
(deftype hw-cpad (basic)
((valid uint8 :offset-assert 4)
(status uint8 :offset-assert 5)
(button0 uint16 :offset-assert 6)
(rightx uint8 :offset-assert 8)
(righty uint8 :offset-assert 9)
(leftx uint8 :offset-assert 10)
(lefty uint8 :offset-assert 11)
(abutton uint8 12 :offset-assert 12)
(;; BASIC CONTROLLER data
;; status = 0x40 | (data length / 2)
(valid uint8 :offset-assert 4) ;; 0 if success, 255 if fail
(status uint8 :offset-assert 5) ;; depends on controller
(button0 uint16 :offset-assert 6) ;; binary button states!
;; DUALSHOCK or JOYSTICK data
;; status (dualshock) = 0x70 | (data length / 2)
;; status (joystick) = 0x50 | (data length / 2)
(rightx uint8 :offset-assert 8) ;; right stick xdir
(righty uint8 :offset-assert 9) ;; right stick ydir
(leftx uint8 :offset-assert 10) ;; left stick xdir
(lefty uint8 :offset-assert 11) ;; left stick ydir
;; DUALSHOCK 2 data
;; status = 0x70 | (data length / 2)
(abutton uint8 12 :offset-assert 12) ;; pressure sensitivity information
;; pad buffer needs to be 32 bytes large.
(dummy uint8 12 :offset-assert 24)
)
:method-count-assert 9
@@ -57,7 +74,7 @@
;; data from hardware + additional info calculated here.
(deftype cpad-info (hw-cpad)
((number int32 :offset-assert 36)
((number int32 :offset-assert 36) ;; controller port number
(cpad-file int32 :offset-assert 40)
(button0-abs pad-buttons 3 :offset-assert 44) ;; bitmask of buttons, pressed or not, with history
(button0-shadow-abs pad-buttons 1 :offset-assert 56) ;; modify this to change button history in the future.
@@ -66,7 +83,7 @@
(stick0-speed float :offset-assert 76)
(new-pad int32 :offset-assert 80)
(state int32 :offset-assert 84)
(align uint8 6 :offset-assert 88)
(align uint8 6 :offset-assert 88) ;; hardware control of buzzing.
(direct uint8 6 :offset-assert 94) ;; hardware control of buzzing.
(buzz-val uint8 2 :offset-assert 100) ;; intensity for buzzing
(buzz-time uint64 2 :offset-assert 104) ;; when to stop buzzing
@@ -82,6 +99,10 @@
:flag-assert #x900000088
)
(defmacro cpad-type? (type)
`(= (shr (-> pad status) 4) (cpad-type ,type))
)
(defun cpad-invalid! ((pad cpad-info))
"Reset all data in a cpad-info"
(logior! (-> pad valid) 128)