Skip to content

Commit a800f73

Browse files
authored
Merge pull request stenzek#516 from ggrtk/negcon
Controller: Add NeGcon support
2 parents bf6faaf + ee3a960 commit a800f73

File tree

6 files changed

+350
-4
lines changed

6 files changed

+350
-4
lines changed

src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ add_library(core
5454
memory_card.h
5555
namco_guncon.cpp
5656
namco_guncon.h
57+
negcon.cpp
58+
negcon.h
5759
pad.cpp
5860
pad.h
5961
playstation_mouse.cpp

src/core/controller.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "digital_controller.h"
55
#include "namco_guncon.h"
66
#include "playstation_mouse.h"
7+
#include "negcon.h"
78

89
Controller::Controller() = default;
910

@@ -54,6 +55,9 @@ std::unique_ptr<Controller> Controller::Create(System* system, ControllerType ty
5455
case ControllerType::PlayStationMouse:
5556
return PlayStationMouse::Create(system);
5657

58+
case ControllerType::NeGcon:
59+
return NeGcon::Create();
60+
5761
case ControllerType::None:
5862
default:
5963
return {};
@@ -86,6 +90,9 @@ Controller::AxisList Controller::GetAxisNames(ControllerType type)
8690
case ControllerType::PlayStationMouse:
8791
return PlayStationMouse::StaticGetAxisNames();
8892

93+
case ControllerType::NeGcon:
94+
return NeGcon::StaticGetAxisNames();
95+
8996
case ControllerType::None:
9097
default:
9198
return {};
@@ -108,6 +115,9 @@ Controller::ButtonList Controller::GetButtonNames(ControllerType type)
108115
case ControllerType::PlayStationMouse:
109116
return PlayStationMouse::StaticGetButtonNames();
110117

118+
case ControllerType::NeGcon:
119+
return NeGcon::StaticGetButtonNames();
120+
111121
case ControllerType::None:
112122
default:
113123
return {};
@@ -130,6 +140,9 @@ u32 Controller::GetVibrationMotorCount(ControllerType type)
130140
case ControllerType::PlayStationMouse:
131141
return PlayStationMouse::StaticGetVibrationMotorCount();
132142

143+
case ControllerType::NeGcon:
144+
return NeGcon::StaticGetVibrationMotorCount();
145+
133146
case ControllerType::None:
134147
default:
135148
return 0;
@@ -152,6 +165,9 @@ std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::strin
152165
case ControllerType::PlayStationMouse:
153166
return PlayStationMouse::StaticGetAxisCodeByName(axis_name);
154167

168+
case ControllerType::NeGcon:
169+
return NeGcon::StaticGetAxisCodeByName(axis_name);
170+
155171
case ControllerType::None:
156172
default:
157173
return std::nullopt;
@@ -174,6 +190,9 @@ std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::str
174190
case ControllerType::PlayStationMouse:
175191
return PlayStationMouse::StaticGetButtonCodeByName(button_name);
176192

193+
case ControllerType::NeGcon:
194+
return NeGcon::StaticGetButtonCodeByName(button_name);
195+
177196
case ControllerType::None:
178197
default:
179198
return std::nullopt;

src/core/negcon.cpp

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#include "negcon.h"
2+
#include "common/assert.h"
3+
#include "common/log.h"
4+
#include "common/state_wrapper.h"
5+
#include <array>
6+
#include <cmath>
7+
8+
NeGcon::NeGcon()
9+
{
10+
m_axis_state.fill(0x00);
11+
m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
12+
}
13+
14+
NeGcon::~NeGcon() = default;
15+
16+
ControllerType NeGcon::GetType() const
17+
{
18+
return ControllerType::NeGcon;
19+
}
20+
21+
std::optional<s32> NeGcon::GetAxisCodeByName(std::string_view axis_name) const
22+
{
23+
return StaticGetAxisCodeByName(axis_name);
24+
}
25+
26+
std::optional<s32> NeGcon::GetButtonCodeByName(std::string_view button_name) const
27+
{
28+
return StaticGetButtonCodeByName(button_name);
29+
}
30+
31+
void NeGcon::Reset()
32+
{
33+
m_transfer_state = TransferState::Idle;
34+
}
35+
36+
bool NeGcon::DoState(StateWrapper& sw)
37+
{
38+
if (!Controller::DoState(sw))
39+
return false;
40+
41+
sw.Do(&m_button_state);
42+
sw.Do(&m_transfer_state);
43+
return true;
44+
}
45+
46+
void NeGcon::SetAxisState(s32 axis_code, float value)
47+
{
48+
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
49+
return;
50+
51+
// Steering Axis: -1..1 -> 0..255
52+
if (axis_code == static_cast<s32>(Axis::Steering))
53+
{
54+
const u8 u8_value = static_cast<u8>(std::clamp(((value + 1.0f) / 2.0f) * 255.0f, 0.0f, 255.0f));
55+
56+
SetAxisState(static_cast<Axis>(axis_code), u8_value);
57+
58+
return;
59+
}
60+
61+
// I, II, L: 0..1 -> 0..255 or -1..0 -> 0..255 to support negative axis ranges,
62+
// e.g. if bound to analog stick instead of trigger
63+
const u8 u8_value = static_cast<u8>(std::clamp(std::abs(value) * 255.0f, 0.0f, 255.0f));
64+
65+
SetAxisState(static_cast<Axis>(axis_code), u8_value);
66+
}
67+
68+
void NeGcon::SetAxisState(Axis axis, u8 value)
69+
{
70+
m_axis_state[static_cast<u8>(axis)] = value;
71+
}
72+
73+
void NeGcon::SetButtonState(s32 button_code, bool pressed)
74+
{
75+
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
76+
return;
77+
78+
SetButtonState(static_cast<Button>(button_code), pressed);
79+
}
80+
81+
void NeGcon::SetButtonState(Button button, bool pressed)
82+
{
83+
// Mapping of Button to index of corresponding bit in m_button_state
84+
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {3, 4, 5, 6, 7, 11, 12, 13};
85+
86+
if (pressed)
87+
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
88+
else
89+
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
90+
}
91+
92+
void NeGcon::ResetTransferState()
93+
{
94+
m_transfer_state = TransferState::Idle;
95+
}
96+
97+
bool NeGcon::Transfer(const u8 data_in, u8* data_out)
98+
{
99+
static constexpr u16 ID = 0x5A23;
100+
101+
switch (m_transfer_state)
102+
{
103+
case TransferState::Idle:
104+
{
105+
if (data_in == 0x42)
106+
{
107+
*data_out = Truncate8(ID);
108+
m_transfer_state = TransferState::IDMSB;
109+
return true;
110+
}
111+
else
112+
{
113+
*data_out = 0xFF;
114+
return (data_in == 0x01);
115+
}
116+
}
117+
118+
case TransferState::IDMSB:
119+
{
120+
*data_out = Truncate8(ID >> 8);
121+
m_transfer_state = TransferState::ButtonsLSB;
122+
return true;
123+
}
124+
125+
case TransferState::ButtonsLSB:
126+
{
127+
*data_out = Truncate8(m_button_state);
128+
m_transfer_state = TransferState::ButtonsMSB;
129+
return true;
130+
}
131+
132+
case TransferState::ButtonsMSB:
133+
{
134+
*data_out = Truncate8(m_button_state >> 8);
135+
m_transfer_state = TransferState::AnalogSteering;
136+
return true;
137+
}
138+
139+
case TransferState::AnalogSteering:
140+
{
141+
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::Steering)]);
142+
m_transfer_state = TransferState::AnalogI;
143+
return true;
144+
}
145+
146+
case TransferState::AnalogI:
147+
{
148+
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::I)]);
149+
m_transfer_state = TransferState::AnalogII;
150+
return true;
151+
}
152+
153+
case TransferState::AnalogII:
154+
{
155+
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::II)]);
156+
m_transfer_state = TransferState::AnalogL;
157+
return true;
158+
}
159+
160+
case TransferState::AnalogL:
161+
{
162+
*data_out = Truncate8(m_axis_state[static_cast<u8>(Axis::L)]);
163+
m_transfer_state = TransferState::Idle;
164+
return false;
165+
}
166+
167+
default:
168+
{
169+
UnreachableCode();
170+
return false;
171+
}
172+
}
173+
}
174+
175+
std::unique_ptr<NeGcon> NeGcon::Create()
176+
{
177+
return std::make_unique<NeGcon>();
178+
}
179+
180+
std::optional<s32> NeGcon::StaticGetAxisCodeByName(std::string_view axis_name)
181+
{
182+
#define AXIS(name) \
183+
if (axis_name == #name) \
184+
{ \
185+
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
186+
}
187+
188+
AXIS(Steering);
189+
AXIS(I);
190+
AXIS(II);
191+
AXIS(L);
192+
193+
return std::nullopt;
194+
195+
#undef AXIS
196+
}
197+
198+
std::optional<s32> NeGcon::StaticGetButtonCodeByName(std::string_view button_name)
199+
{
200+
#define BUTTON(name) \
201+
if (button_name == #name) \
202+
{ \
203+
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
204+
}
205+
206+
BUTTON(Up);
207+
BUTTON(Down);
208+
BUTTON(Left);
209+
BUTTON(Right);
210+
BUTTON(A);
211+
BUTTON(B);
212+
BUTTON(R);
213+
BUTTON(Start);
214+
215+
return std::nullopt;
216+
217+
#undef BUTTON
218+
}
219+
220+
Controller::AxisList NeGcon::StaticGetAxisNames()
221+
{
222+
#define A(n) \
223+
{ \
224+
#n, static_cast <s32>(Axis::n) \
225+
}
226+
227+
return {A(Steering), A(I), A(II), A(L)};
228+
229+
#undef A
230+
}
231+
232+
Controller::ButtonList NeGcon::StaticGetButtonNames()
233+
{
234+
#define B(n) \
235+
{ \
236+
#n, static_cast <s32>(Button::n) \
237+
}
238+
239+
return {B(Up), B(Down), B(Left), B(Right), B(A), B(B), B(R), B(Start)};
240+
#undef B
241+
}
242+
243+
u32 NeGcon::StaticGetVibrationMotorCount()
244+
{
245+
return 0;
246+
}

0 commit comments

Comments
 (0)