Merge pull request #2294 from ergo720/lightgun

Added support for the lightgun input device
This commit is contained in:
RadWolfie 2021-11-29 12:33:23 -06:00 committed by GitHub
commit c9fe07ab98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 890 additions and 138 deletions

View File

@ -104,9 +104,10 @@ file (GLOB CXBXR_HEADER_GUIv1
"${CXBXR_ROOT_DIR}/src/common/input/Button.h"
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.h"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgLibusbControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.h"
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.h"
@ -272,9 +273,10 @@ file (GLOB CXBXR_SOURCE_GUIv1
"${CXBXR_ROOT_DIR}/src/common/input/Button.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.cpp"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgLibusbControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgDukeControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.cpp"
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.cpp"

@ -1 +1 @@
Subproject commit 0298a65f228b0c76915bb115d0134675777a0e3d
Subproject commit 02353e8aa552f3db60804626e29838406f206443

View File

@ -516,6 +516,10 @@ bool Settings::LoadConfig()
lambda(dev_num_buttons[device], button_sbc_names);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
lambda(dev_num_buttons[device], button_lightgun_names);
break;
}
}
@ -688,6 +692,9 @@ bool Settings::Save(std::string file_path)
lambda(dev_num_buttons[device], button_sbc_names);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
lambda(dev_num_buttons[device], button_lightgun_names);
break;
}
}

View File

@ -78,7 +78,6 @@ LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
Button *button = reinterpret_cast<Button *>(dwRefData);
if (wParam & MK_SHIFT) {
static_cast<DukeInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
}
else if (!(wParam & ~MK_RBUTTON)) {
button->ClearText();
@ -99,7 +98,7 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
{
switch (uMsg)
{
// Remove the window subclass when this window is destroyed
// Remove the window subclass when this window is destroyed
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
}
@ -109,7 +108,6 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
Button *button = reinterpret_cast<Button *>(dwRefData);
if (wParam & MK_SHIFT) {
static_cast<SbcInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
}
else if (!(wParam & ~MK_RBUTTON)) {
button->ClearText();
@ -122,3 +120,25 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
// Remove the window subclass when this window is destroyed
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
}
break;
case WM_RBUTTONDOWN: {
Button *button = reinterpret_cast<Button *>(dwRefData);
button->ClearText();
static_cast<LightgunInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

View File

@ -31,6 +31,7 @@
#include <Commctrl.h>
#include <string>
#define LIGHTGUN_NUM_BUTTONS 17
#define XBOX_CTRL_NUM_BUTTONS 25
#define SBC_NUM_BUTTONS 56
#define HIGHEST_NUM_BUTTONS SBC_NUM_BUTTONS
@ -62,3 +63,4 @@ private:
LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

View File

@ -31,7 +31,8 @@
#include "gui/resource/ResCxbx.h"
static char *tooltip_text = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
static char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
{
@ -45,7 +46,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
for (size_t i = 0; i < ARRAY_SIZE(button_xbox_ctrl_id); i++) {
m_buttons.push_back(new Button(button_xbox_ctrl_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_xbox_ctrl_id[i]), ButtonDukeSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
@ -56,7 +57,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
for (size_t i = 0; i < ARRAY_SIZE(button_sbc_id); i++) {
m_buttons.push_back(new Button(button_sbc_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
@ -64,6 +65,17 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
for (size_t i = 0; i < ARRAY_SIZE(button_lightgun_id); i++) {
m_buttons.push_back(new Button(button_lightgun_id[i], i, hwnd, wnd));
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
// Install the subclass for the button control
SetWindowSubclass(GetDlgItem(hwnd, button_lightgun_id[i]), ButtonLightgunSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
}
}
break;
}
}
@ -122,3 +134,4 @@ void EmuDevice::CreateTooltipWindow()
}
template void EmuDevice::BindDefault(const std::array<const char*, XBOX_CTRL_NUM_BUTTONS>& arr);
template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);

View File

@ -54,3 +54,4 @@ private:
};
extern template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
extern template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);

View File

@ -53,8 +53,8 @@ std::string GetInputDeviceName(int dev_type)
str = "MS Gamepad S";
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
str = "Light gun";
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
str = "EMS TopGun II";
break;
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
@ -178,7 +178,7 @@ const auto InputDevice::FindPort(std::string_view Port) const
});
}
void InputDevice::SetPort(std::string_view Port, bool Connect)
void InputDevice::SetPort2(std::string_view Port, bool Connect)
{
if (Connect) {
m_XboxPort.emplace_back(Port);

View File

@ -49,7 +49,7 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
DEVICE_INVALID = -1,
MS_CONTROLLER_DUKE,
MS_CONTROLLER_S,
LIGHT_GUN,
LIGHTGUN,
STEERING_WHEEL,
MEMORY_UNIT,
IR_DONGLE,
@ -122,7 +122,8 @@ public:
// retrieves the port this device is attached to
bool GetPort(std::string_view Port) const;
// sets the port this device is attached to
void SetPort(std::string_view Port, bool Connect);
// NOTE: using SetPort2 to avoid a collision with the SetPort macro provided by Windows headers
void SetPort2(std::string_view Port, bool Connect);
// retuns true if it is a libusb device, false otherwise
virtual bool IsLibusb() const { return false; };

View File

@ -57,7 +57,7 @@
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_S
0,
LIGHTGUN_NUM_BUTTONS, // LIGHTGUN
0,
0,
0,
@ -132,6 +132,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
@ -272,7 +273,7 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack)
else {
auto host_dev = g_InputDeviceManager.FindDevice(port);
if (host_dev != nullptr) {
host_dev->SetPort(port, false);
host_dev->SetPort2(port, false);
}
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
if (type != to_underlying(dev->type)) {
@ -309,7 +310,7 @@ void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view por
}
auto host_dev = g_InputDeviceManager.FindDevice(port);
if (host_dev != nullptr) {
host_dev->SetPort(port, false);
host_dev->SetPort2(port, false);
}
}
@ -328,7 +329,7 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
auto dev = FindDevice(std::string(dev_name));
if (dev != nullptr) {
dev->SetPort(port, true);
dev->SetPort2(port, true);
}
return;
}
@ -355,7 +356,7 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
});
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str);
}
dev->SetPort(port, true);
dev->SetPort2(port, true);
}
}
@ -379,6 +380,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
has_changed = UpdateInputLightgun(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
m_Mtx.unlock();
@ -394,7 +400,6 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
m_Mtx.unlock();
return has_changed;
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
@ -472,6 +477,130 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
return true;
}
bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl *> bindings = Device->GetBindings(Port);
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN)]));
// NOTE: the output state is not supported
if (Direction == DIRECTION_IN) {
if (!Device->UpdateInput()) {
return false;
}
// We change the toggle buttons only when a press -> release input transaction is completed
// 0 -> Turbo left
// 1 -> Turbo right
// 2 -> Laser
XpadInput *in_buf = reinterpret_cast<XpadInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
g_devs[Port_num].info.ligthgun.last_turbo = g_devs[Port_num].info.ligthgun.turbo;
for (int i = 14, j = 0; i < 17; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint8_t curr_state = static_cast<uint8_t>(!!state);
if ((~curr_state) & ((g_devs[Port_num].info.ligthgun.last_in_state >> j) & 1)) {
switch (j)
{
case 0:
if (g_devs[Port_num].info.ligthgun.turbo != 2) {
g_devs[Port_num].info.ligthgun.turbo += 1;
}
break;
case 1:
if (g_devs[Port_num].info.ligthgun.turbo != 0) {
g_devs[Port_num].info.ligthgun.turbo -= 1;
}
break;
case 2:
g_devs[Port_num].info.ligthgun.laser ^= 1;
break;
}
}
(g_devs[Port_num].info.ligthgun.last_in_state &= ~(1 << j)) |= (curr_state << j);
}
in_buf->wButtons = XINPUT_LIGHTGUN_ONSCREEN;
for (int i = 0; i < 6; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
if (state) {
in_buf->wButtons |= (1 << i);
}
else {
in_buf->wButtons &= ~(1 << i);
}
}
if (g_devs[Port_num].info.ligthgun.turbo) {
ControlState trigger_state = (bindings[6] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[6])->GetState() : 0.0;
if (trigger_state) {
// Turbo mode 1
in_buf->bAnalogButtons[0] ^= 0xFF;
int start_idx = 7;
if (g_devs[Port_num].info.ligthgun.turbo == 2) {
// Turbo mode 2
start_idx = 8;
++g_devs[Port_num].info.ligthgun.turbo_delay;
if (g_devs[Port_num].info.ligthgun.last_turbo != g_devs[Port_num].info.ligthgun.turbo) {
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
if (g_devs[Port_num].info.ligthgun.turbo_delay == LIGHTGUN_GRIP_DELAY) {
in_buf->bAnalogButtons[1] ^= 0xFF;
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
}
}
for (int i = start_idx, j = start_idx - 6; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
else {
// Turbo active but trigger not pressed
in_buf->bAnalogButtons[0] = 0;
for (int i = 7, j = 1; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
}
else {
// Turbo mode 0 (no turbo)
for (int i = 6, j = 0; i < 10; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
}
}
in_buf->bAnalogButtons[4] = 0;
in_buf->bAnalogButtons[5] = 0;
in_buf->bAnalogButtons[6] = 0;
in_buf->bAnalogButtons[7] = 0;
for (int i = 10; i < 14; i += 2) {
ControlState state_plus = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i + 1])->GetState() : 0.0;
ControlState state = state_plus ? state_plus * 0x7FFF : state_minus ? -state_minus * 0x8000 : 0.0;
switch (i)
{
case 10: {
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_x : g_devs[Port_num].info.ligthgun.offset_x;
in_buf->sThumbLX = static_cast<int16_t>(state) + offset;
}
break;
case 12: {
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_y : g_devs[Port_num].info.ligthgun.offset_y;
in_buf->sThumbLY = static_cast<int16_t>(state) + offset;
}
break;
}
}
in_buf->sThumbRX = in_buf->sThumbRY = 0;
}
return true;
}
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port)
{
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
@ -495,7 +624,6 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
// 8 -> TunerDial Down
// 9 -> GearLever Up
// 10 -> GearLever Down
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
SBCInput *in_buf = reinterpret_cast<SBCInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
for (int i = 0; i < 4; i++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
@ -511,10 +639,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 4, j = 0; i < 6; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[0] ^= (1 << i);
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 6; i < 34; i++) {
@ -531,10 +659,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 34, j = 2; i < 39; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
in_buf->wButtons[2] ^= (1 << (i % 16));
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
for (int i = 39; i < 49; i += 2) {
@ -593,7 +721,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
for (int i = 52, j = 7; i < 56; i++, j++) {
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
switch (i)
{
case 52:
@ -621,7 +749,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
break;
}
}
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
}
}
@ -772,3 +900,35 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
}
}
}
ImVec2 InputDeviceManager::CalcLaserPos(int port)
{
static ImVec2 laser_coord[XBOX_NUM_PORTS] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
// If somebody else is currently holding the lock, we won't wait and instead we report the last known laser position
if (m_Mtx.try_lock()) {
// NOTE: even when switching to faux fullscreen, imgui will still use the original window size. Because of this, we only need to do this once.
// If in the future the above is fixed, then this code will have to recalculate the new window size after a resizing has occured.
static long width, height = -1;
if (height == -1) {
RECT rect;
GetClientRect(m_hwnd, &rect);
width = std::max(rect.right - rect.left, 1l);
height = std::max(rect.bottom - rect.top, 1l);
}
// We convert the laser input coordinates given by xinput (in the sThumbLXY members of XpadInput) with linear interpolation y = y0 + (x - x0) * (y1 - y0) / (x1 - x0)
// For laser_x x0 = -32768; x1 = 32767; y0 = 0; y1 = width
// For laser_y x0 = -32768; x1 = 32767; y0 = height; y1 = 0
int16_t laser_x = g_devs[port].info.buff.ctrl.InBuffer.sThumbLX;
int16_t laser_y = g_devs[port].info.buff.ctrl.InBuffer.sThumbLY;
laser_coord[port].x = ((laser_x + 32768) * width) / 65535.0f;
laser_coord[port].y = height - ((laser_y + 32768) * height) / 65535.0f;
m_Mtx.unlock();
}
return laser_coord[port];
}

View File

@ -31,11 +31,7 @@
#include <thread>
#include "InputDevice.h"
#include "EmuDevice.h"
// Prevent a collision with the SetPort provided by Windows
#ifdef WIN32
#undef SetPort
#endif
#include <imgui.h>
#define PORT_INVALID -1
#define PORT_1 0
@ -54,12 +50,15 @@
#define XID_PACKET_HEADER 2
#define LIGHTGUN_GRIP_DELAY 30
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
inline XBOX_INPUT_DEVICE input_support_list[] = {
XBOX_INPUT_DEVICE::DEVICE_INVALID,
XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE,
XBOX_INPUT_DEVICE::MS_CONTROLLER_S,
XBOX_INPUT_DEVICE::LIGHTGUN,
XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER,
XBOX_INPUT_DEVICE::ARCADE_STICK,
XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER,
@ -156,6 +155,22 @@ struct XidSBCOutput {
#pragma pack()
struct LightGunData {
xbox::short_xt offset_x;
xbox::short_xt offset_y;
xbox::short_xt offset_upp_x;
xbox::short_xt offset_upp_y;
uint8_t last_in_state;
uint8_t last_turbo;
uint8_t turbo_delay;
uint8_t turbo;
uint8_t laser;
};
struct SbcData {
uint16_t last_in_state;
};
union InputBuff {
XidGamepadInput ctrl;
XidSBCInput sbc;
@ -169,8 +184,13 @@ struct DeviceInfo {
uint8_t ucSubType; // xapi subtype
uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
uint32_t dwPacketNumber;
InputBuff buff;
uint32_t dwPacketNumber; // increases by one when the input state changes
InputBuff buff; // input buffer
// device-specific additional data
union {
LightGunData ligthgun;
SbcData sbc;
};
};
struct DeviceState {
@ -214,11 +234,15 @@ public:
void UpdateOpt(bool is_gui);
// device hotplug event handler
void HotplugHandler(bool is_sdl);
// converts xinput -> screen coordinates to display the lightgun laser on the rendering window
ImVec2 CalcLaserPos(int port);
private:
// update input for an xbox controller
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
// update input for a lightgun
bool UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a Steel Battalion controller
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port);
// update input for a passthrough xbox device

View File

@ -150,7 +150,23 @@ InputDevice::Input* InputWindow::DetectInput(InputDevice* const Device, int ms)
return nullptr; // no input
}
void InputWindow::BindButton(int ControlID)
int InputWindow::EnableDefaultButton()
{
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return XINPUT_DEFAULT;
}
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return DINPUT_DEFAULT;
}
else {
EnableWindow(m_hwnd_default, FALSE);
return -1;
}
}
void InputWindow::BindButton(int ControlID, bool auto_swap)
{
// Check if binding thread is still active
// testcase: spacebar and enter keys; without this fix will cause repeat binding result.
@ -163,7 +179,7 @@ void InputWindow::BindButton(int ControlID)
m_bIsBinding = true;
// Don't block the message processing loop
std::thread([this, dev, ControlID]() {
std::thread([this, dev, ControlID, auto_swap]() {
EnableWindow(m_hwnd_window, FALSE);
char current_text[HOST_BUTTON_NAME_LENGTH];
Button* xbox_button = m_DeviceConfig->FindButtonById(ControlID);
@ -173,6 +189,9 @@ void InputWindow::BindButton(int ControlID)
InputDevice::Input* dev_button = fut.get();
if (dev_button) {
xbox_button->UpdateText(dev_button->GetName().c_str());
if (auto_swap) {
SwapMoCursorAxis(xbox_button);
}
m_bHasChanges = true;
}
else {
@ -201,7 +220,6 @@ void InputWindow::UpdateProfile(const std::string &name, int command)
break;
case BUTTON_CLEAR:
case BUTTON_SWAP:
m_bHasChanges = true;
break;
}
@ -348,6 +366,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Cursor X-");
}
m_bHasChanges = true;
break;
case 'Y':
@ -357,6 +376,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Cursor Y+");
}
m_bHasChanges = true;
break;
}
@ -374,6 +394,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Axis X-");
}
m_bHasChanges = true;
break;
case 'Y':
@ -383,6 +404,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
else {
button->UpdateText("Axis Y+");
}
m_bHasChanges = true;
break;
}

View File

@ -39,7 +39,6 @@
#define RUMBLE_TEST 6
#define RUMBLE_CLEAR 7
#define BUTTON_CLEAR 8
#define BUTTON_SWAP 9
#define XINPUT_DEFAULT 0
#define DINPUT_DEFAULT 1
@ -56,7 +55,7 @@ public:
virtual void Initialize(HWND hwnd, int port_num, int dev_type) = 0;
~InputWindow();
virtual void UpdateDeviceList();
void BindButton(int ControlID);
void BindButton(int ControlID, bool auto_swap = false);
virtual void ClearBindings() = 0;
virtual void UpdateProfile(const std::string& name, int command);
void UpdateCurrentDevice();
@ -73,8 +72,8 @@ protected:
void DeleteProfile(const std::string& name);
void OverwriteProfile(const std::string& name);
void LoadDefaultProfile();
virtual int EnableDefaultButton() = 0;
virtual void SaveSlotConfig() = 0;
virtual int EnableDefaultButton();
// xbox device under configuration
EmuDevice* m_DeviceConfig;
@ -84,6 +83,8 @@ protected:
HWND m_hwnd_device_list;
// handle of the profile list combobox
HWND m_hwnd_profile_list;
// handle of the default bindings button
HWND m_hwnd_default;
// number of devices displayed in the device list combobox
int m_num_devices;
// type of the device
@ -109,15 +110,12 @@ public:
void BindDefault();
void ClearBindings() override;
void UpdateProfile(const std::string &name, int command) override;
void SaveSlotConfig() override;
void SaveSlotConfig();
private:
int EnableDefaultButton() override;
void DetectOutput(int ms);
// handle of the default bindings button
HWND m_hwnd_default;
// handle of the rumble window
HWND m_hwnd_rumble;
// handle of the rumble combobox
@ -132,8 +130,8 @@ class SbcInputWindow : public InputWindow
{
public:
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~SbcInputWindow();
void ClearBindings() override;
void SaveSlotConfig() override;
private:
@ -143,10 +141,9 @@ private:
class LibusbInputWindow : public InputWindow
{
public:
~LibusbInputWindow();
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~LibusbInputWindow();
void ClearBindings() override;
void SaveSlotConfig() override;
void UpdateDeviceList() override;
void TestInput();
@ -157,3 +154,12 @@ private:
// handle of the test button
HWND m_hwnd_device_test;
};
class LightgunInputWindow : public InputWindow
{
public:
void Initialize(HWND hwnd, int port_num, int dev_type) override;
~LightgunInputWindow();
void BindDefault();
void ClearBindings() override;
};

View File

@ -118,6 +118,27 @@ inline int button_sbc_id[SBC_NUM_BUTTONS] = {
IDC_GEAR_UP,
IDC_GEAR_DOWN,
};
// Must have the same button order as defined in button_lightgun_names
inline int button_lightgun_id[LIGHTGUN_NUM_BUTTONS] = {
IDC_LG_STICK_UP,
IDC_LG_STICK_DOWN,
IDC_LG_STICK_LEFT,
IDC_LG_STICK_RIGHT,
IDC_LG_START,
IDC_LG_SEBA,
IDC_LG_TRIGGER,
IDC_LG_GRIP,
IDC_LG_A,
IDC_LG_B,
IDC_LG_AIM_POSX,
IDC_LG_AIM_NEGX,
IDC_LG_AIM_POSY,
IDC_LG_AIM_NEGY,
IDC_TURBO_LEFT,
IDC_TURBO_RIGHT,
IDC_LASER,
};
#endif
inline constexpr const char* button_xbox_ctrl_names[XBOX_CTRL_NUM_BUTTONS] = {
@ -207,6 +228,26 @@ inline constexpr const char *button_sbc_names[SBC_NUM_BUTTONS] = {
"GearLever Down",
};
inline constexpr const char *button_lightgun_names[LIGHTGUN_NUM_BUTTONS] = {
"Stick Up",
"Stick Down",
"Stick Left",
"Stick Right",
"START",
"SE/BA",
"Trigger",
"Grip",
"A",
"B",
"Aim X+",
"Aim X-",
"Aim Y+",
"Aim Y-",
"Turbo Left",
"Turbo Right",
"Laser",
};
constexpr bool check_button_name_size(unsigned max_num_buttons)
{
switch (max_num_buttons)

View File

@ -152,6 +152,7 @@ EmuShared::EmuShared()
m_bEmulating_status = false;
m_bFirstLaunch = false;
m_bClipCursor = false;
m_LightgunLaser = 0xF; // laser on by default on all ports
std::memset(m_DeviceControlNames, '\0', sizeof(m_DeviceControlNames));
std::memset(m_DeviceName, '\0', sizeof(m_DeviceName));

View File

@ -257,6 +257,12 @@ class EmuShared : public Mutex
void GetClipCursorFlag(bool *value) { Lock(); *value = m_bClipCursor; Unlock(); }
void SetClipCursorFlag(const bool value) { Lock(); m_bClipCursor = value; Unlock(); }
// ******************************************************************
// * LightgunLaser flag Accessors
// ******************************************************************
void GetLightgunLaser(uint8_t *value, int port) { Lock(); *value = (m_LightgunLaser >> port) & 1; Unlock(); }
void SetLightgunLaser(const uint8_t *value, int port) { Lock(); (m_LightgunLaser &= ~(1 << port)) |= ((*value) << port); Unlock(); }
// ******************************************************************
// * ImGui Accessors
// ******************************************************************
@ -364,6 +370,7 @@ class EmuShared : public Mutex
#else
unsigned int m_Reserved;
#endif
uint8_t m_LightgunLaser;
bool m_bFirstLaunch;
bool m_bClipCursor;
unsigned int m_dwKrnlProcID; // Only used for kernel mode level.

View File

@ -14,6 +14,13 @@
#include "core/kernel/init/CxbxKrnl.h"
#include "core/hle/D3D8/XbVertexBuffer.h"
const ImColor ImGuiVideo::m_laser_col[4] = {
ImColor(ImVec4(1.0f, 0.0f, 0.0f, 1.0f)), // player1: red
ImColor(ImVec4(0.0f, 1.0f, 0.0f, 1.0f)), // player2: green
ImColor(ImVec4(0.0f, 0.0f, 1.0f, 1.0f)), // player3: blue
ImColor(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)) // player4: yellow
};
bool ImGuiVideo::Initialize()
{
g_EmuShared->GetImGuiVideoWindows(&m_windows);
@ -46,4 +53,13 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
ImGui::End();
}
}
// Render the lightgun laser
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN && g_devs[port].info.ligthgun.laser) {
ImGui::Begin("Laser", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoDecoration);
ImGui::GetForegroundDrawList()->AddCircleFilled(g_InputDeviceManager.CalcLaserPos(port), 5, m_laser_col[port], 0);
ImGui::End();
}
}
}

View File

@ -20,4 +20,5 @@ public:
protected:
imgui_video_windows m_windows;
static const ImColor m_laser_col[4];
};

View File

@ -346,6 +346,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("XInputGetState", xbox::EMUPATCH(XInputGetState), PATCH_HLE_OHCI),
PATCH_ENTRY("XInputOpen", xbox::EMUPATCH(XInputOpen), PATCH_HLE_OHCI),
PATCH_ENTRY("XInputPoll", xbox::EMUPATCH(XInputPoll), PATCH_HLE_OHCI),
PATCH_ENTRY("XInputSetLightgunCalibration", xbox::EMUPATCH(XInputSetLightgunCalibration), PATCH_HLE_OHCI),
PATCH_ENTRY("XInputSetState", xbox::EMUPATCH(XInputSetState), PATCH_HLE_OHCI),
// XAPI

View File

@ -59,7 +59,7 @@ std::atomic<bool> g_bIsDevicesEmulating = false;
std::atomic<bool> g_bXppGuard = false;
// Allocate enough memory for the max number of devices we can support simultaneously
// 4 duke / S / sbc / arcade joystick (mutually exclusive) + 8 memory units
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
DeviceState g_devs[MAX_DEVS];
xbox::ulong_xt g_Mounted_MUs = 0;
@ -114,6 +114,7 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
{
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
case XBOX_INPUT_DEVICE::LIGHTGUN:
case XBOX_INPUT_DEVICE::ARCADE_STICK:
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER: {
if (XppType == g_DeviceType_Gamepad) {
@ -137,7 +138,6 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
}
break;
case XBOX_INPUT_DEVICE::LIGHT_GUN:
case XBOX_INPUT_DEVICE::STEERING_WHEEL:
case XBOX_INPUT_DEVICE::IR_DONGLE:
default:
@ -154,6 +154,7 @@ void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view p
{
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
case XBOX_INPUT_DEVICE::LIGHTGUN:
case XBOX_INPUT_DEVICE::ARCADE_STICK:
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER:
xpp = g_DeviceType_Gamepad;
@ -256,6 +257,20 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
dev->info.ucFeedbackSize = sizeof(XpadOutput);
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
dev->type = XBOX_INPUT_DEVICE::LIGHTGUN;
dev->info.bAutoPollDefault = true;
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_LIGHTGUN;
dev->info.ucInputStateSize = sizeof(XpadInput);
dev->info.ucFeedbackSize = sizeof(XpadOutput);
dev->info.ligthgun.offset_x = dev->info.ligthgun.offset_y = 0;
dev->info.ligthgun.offset_upp_x = dev->info.ligthgun.offset_upp_x = 0;
dev->info.ligthgun.last_in_state = dev->info.ligthgun.turbo_delay = 0;
dev->info.ligthgun.turbo = dev->info.ligthgun.last_turbo = 0;
g_EmuShared->GetLightgunLaser(&dev->info.ligthgun.laser, port_num);
break;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
dev->type = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER;
@ -267,6 +282,7 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
dev->info.ucInputStateSize = sizeof(SBCInput);
dev->info.ucFeedbackSize = sizeof(SBCOutput);
dev->info.sbc.last_in_state = 0;
if (type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER)) {
dev->type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
char dev_name[50];
@ -325,6 +341,7 @@ void DestructHleInputDevice(DeviceState *dev)
{
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
case XBOX_INPUT_DEVICE::LIGHTGUN:
dev->info.bAutoPollDefault = false;
dev->info.ucType = 0;
dev->info.ucSubType = 0;
@ -480,7 +497,7 @@ xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE
}
if constexpr (!IsXInputPoll) {
std::memcpy((void *)&pState->Gamepad, reinterpret_cast<uint8_t *>(&g_devs[port].info.buff) + 2, g_devs[port].info.ucInputStateSize);
std::memcpy((void *)&pState->Gamepad, reinterpret_cast<uint8_t *>(&g_devs[port].info.buff) + XID_PACKET_HEADER, g_devs[port].info.ucInputStateSize);
pState->dwPacketNumber = g_devs[port].info.dwPacketNumber;
}
@ -787,6 +804,88 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetState)
RETURN(pFeedback->Header.dwStatus);
}
// ******************************************************************
// * patch: XGetDeviceEnumerationStatus
// ******************************************************************
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDeviceEnumerationStatus)()
{
LOG_FUNC();
dword_xt ret = (g_bIsDevicesInitializing || g_bIsDevicesEmulating) ? XDEVICE_ENUMERATION_BUSY : XDEVICE_ENUMERATION_IDLE;
RETURN(ret);
}
// ******************************************************************
// * patch: XInputGetDeviceDescription
// ******************************************************************
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetDeviceDescription)
(
HANDLE hDevice,
PXINPUT_DEVICE_DESCRIPTION pDescription
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(hDevice)
LOG_FUNC_ARG(pDescription)
LOG_FUNC_END;
dword_xt ret;
int port = static_cast<DeviceState *>(hDevice)->port_idx;
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
// These values are those reported in the device descriptor for the EMS TopGun II documented in the xboxdevwiki
pDescription->wVendorID = 0x0b9a;
pDescription->wProductID = 0x016b;
pDescription->wVersion = 0x457;
ret = ERROR_SUCCESS;
}
else {
// NOTE: Phantasy star online also calls this on the keyboard device
ret = ERROR_NOT_SUPPORTED;
}
}
else {
ret = ERROR_DEVICE_NOT_CONNECTED;
}
RETURN(ret);
}
// ******************************************************************
// * patch: XInputSetLightgunCalibration
// ******************************************************************
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration)
(
HANDLE hDevice,
PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS pCalibrationOffsets
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(hDevice)
LOG_FUNC_ARG(pCalibrationOffsets)
LOG_FUNC_END;
dword_xt ret;
int port = static_cast<DeviceState *>(hDevice)->port_idx;
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
g_devs[port].info.ligthgun.offset_x = pCalibrationOffsets->wCenterX;
g_devs[port].info.ligthgun.offset_y = pCalibrationOffsets->wCenterY;
g_devs[port].info.ligthgun.offset_upp_x = pCalibrationOffsets->wUpperLeftX;
g_devs[port].info.ligthgun.offset_upp_y = pCalibrationOffsets->wUpperLeftY;
ret = ERROR_SUCCESS;
}
else {
ret = ERROR_NOT_SUPPORTED;
}
}
else {
ret = ERROR_DEVICE_NOT_CONNECTED;
}
RETURN(ret);
}
// ******************************************************************
// * patch: SetThreadPriorityBoost
@ -1394,42 +1493,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMUA)
RETURN(ERROR_SUCCESS);
}
// ******************************************************************
// * patch: XGetDeviceEnumerationStatus
// ******************************************************************
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDeviceEnumerationStatus)()
{
LOG_FUNC();
dword_xt ret = (g_bIsDevicesInitializing || g_bIsDevicesEmulating) ? XDEVICE_ENUMERATION_BUSY : XDEVICE_ENUMERATION_IDLE;
RETURN(ret);
}
// ******************************************************************
// * patch: XInputGetDeviceDescription
// ******************************************************************
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetDeviceDescription)
(
HANDLE hDevice,
PVOID pDescription
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(hDevice)
LOG_FUNC_ARG(pDescription)
LOG_FUNC_END;
// TODO: Lightgun support?
LOG_UNIMPLEMENTED();
RETURN(ERROR_NOT_SUPPORTED); // ERROR_DEVICE_NOT_CONNECTED;
}
// ******************************************************************
// * patch: XMountMURootA
// ******************************************************************

View File

@ -270,6 +270,35 @@ typedef struct _XINPUT_FEEDBACK
}
XINPUT_FEEDBACK, *PXINPUT_FEEDBACK;
#pragma pack()
// ******************************************************************
// * XINPUT_DEVICE_DESCRIPTION
// ******************************************************************
typedef struct _XINPUT_DEVICE_DESCRIPTION
{
xbox::ushort_xt wVendorID;
xbox::ushort_xt wProductID;
xbox::ushort_xt wVersion;
}
XINPUT_DEVICE_DESCRIPTION, *PXINPUT_DEVICE_DESCRIPTION;
// ******************************************************************
// * XINPUT_LIGHTGUN_CALIBRATION_OFFSETS
// ******************************************************************
typedef struct _XINPUT_LIGHTGUN_CALIBRATION_OFFSETS
{
xbox::short_xt wCenterX;
xbox::short_xt wCenterY;
xbox::short_xt wUpperLeftX;
xbox::short_xt wUpperLeftY;
}
XINPUT_LIGHTGUN_CALIBRATION_OFFSETS, *PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS;
// ******************************************************************
// * Lightgun-specific flags
// ******************************************************************
#define XINPUT_LIGHTGUN_ONSCREEN 0x2000
// ******************************************************************
// * RTL_HEAP_PARAMETERS
// ******************************************************************
@ -439,6 +468,28 @@ xbox::dword_xt WINAPI EMUPATCH(XInputSetState)
IN OUT PXINPUT_FEEDBACK pFeedback
);
// ******************************************************************
// * patch: XGetDeviceEnumerationStatus
// ******************************************************************
xbox::dword_xt WINAPI EMUPATCH(XGetDeviceEnumerationStatus)();
// ******************************************************************
// * patch: XInputGetDeviceDescription
// ******************************************************************
xbox::dword_xt WINAPI EMUPATCH(XInputGetDeviceDescription)
(
HANDLE hDevice,
PXINPUT_DEVICE_DESCRIPTION pDescription
);
// ******************************************************************
// * patch: XInputSetLightgunCalibration
// ******************************************************************
xbox::dword_xt WINAPI EMUPATCH(XInputSetLightgunCalibration)
(
HANDLE hDevice,
PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS pCalibrationOffsets
);
// ******************************************************************
// * patch: CreateMutex
@ -732,25 +783,11 @@ xbox::bool_xt WINAPI EMUPATCH(MoveFileA)
LPCSTR lpNewFileName
);
// ******************************************************************
// * patch: XGetDeviceEnumerationStatus
// ******************************************************************
xbox::dword_xt WINAPI EMUPATCH(XGetDeviceEnumerationStatus)();
// ******************************************************************
// * patch: SwitchToThread
// ******************************************************************
xbox::bool_xt WINAPI EMUPATCH(SwitchToThread)();
// ******************************************************************
// * patch: XInputGetDeviceDescription
// ******************************************************************
xbox::dword_xt WINAPI EMUPATCH(XInputGetDeviceDescription)
(
HANDLE hDevice,
PVOID pDescription
);
// ******************************************************************
// * patch: ReadFileEx
// ******************************************************************

View File

@ -535,6 +535,12 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi
#endif
}
// Save lightgun laser status
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
g_EmuShared->SetLightgunLaser(&g_devs[port].info.ligthgun.laser, port);
}
}
std::string TitlePath = xbox::LaunchDataPage->Header.szLaunchPath;

View File

@ -26,9 +26,11 @@
// ******************************************************************
#include "windows.h"
#include "controllers/DlgDukeControllerConfig.h"
#include "controllers/DlgSBControllerConfig.h"
#include "controllers/DlgLibusbControllerConfig.h"
#include "input/DlgDukeControllerConfig.h"
#include "input/DlgSBControllerConfig.h"
#include "input/DlgLibusbControllerConfig.h"
#include "input/DlgLightgunConfig.h"
#include "DlgInputConfig.h"
#include "resource/ResCxbx.h"
#include "input\InputManager.h"
#include "Logging.h"
@ -211,6 +213,12 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
}
break;
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_LIGHTGUN_CFG), hWndDlg, DlgLightgunConfigProc,
(DeviceType << 8) | port);
}
break;
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_SBC_CFG), hWndDlg, DlgSBControllerConfigProc,
(DeviceType << 8) | port);

View File

@ -181,22 +181,6 @@ void DukeInputWindow::BindDefault()
}
}
int DukeInputWindow::EnableDefaultButton()
{
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return XINPUT_DEFAULT;
}
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
EnableWindow(m_hwnd_default, TRUE);
return DINPUT_DEFAULT;
}
else {
EnableWindow(m_hwnd_default, FALSE);
return -1;
}
}
void DukeInputWindow::ClearBindings()
{
m_DeviceConfig->ClearButtons();

View File

@ -72,11 +72,6 @@ void LibusbInputWindow::ClearBindings()
// There are no profiles for libusb devices, so this is a nop
}
void LibusbInputWindow::SaveSlotConfig()
{
// There are no slots for libusb devices, so this is a nop
}
int LibusbInputWindow::EnableDefaultButton()
{
// The libusb window does not have a default button, so we return a dummy value here

View File

@ -0,0 +1,223 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::GUI
#include "Windows.h"
#include "gui\resource\ResCxbx.h"
#include "input\InputWindow.h"
#include "gui\DlgInputConfig.h"
#include "common\Logging.h"
static constexpr std::array<std::array<const char *, LIGHTGUN_NUM_BUTTONS>, 2> button_lightgun_default = { {
{ "Pad N", "Pad S", "Pad W", "Pad E", "Start", "Back", "Button A", "Button B", "Button X", "Button Y", "Left X+", "Left X-",
"Left Y+", "Left Y-", "Shoulder L", "Shoulder R", "Thumb R" },
{ "UP", "DOWN", "LEFT", "RIGHT", "RETURN", "SPACE", "Click 0", "Click 1", "W", "E", "Cursor X+", "Cursor X-", "Cursor Y+", "Cursor Y-", "S", "D", "C"}
} };
static LightgunInputWindow *g_InputWindow = nullptr;
LightgunInputWindow::~LightgunInputWindow()
{
g_Settings->m_input_port[m_port_num].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
g_Settings->m_input_port[m_port_num].SlotType[SLOT_BOTTOM] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
}
void LightgunInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
{
// Save window/device specific variables
m_hwnd_window = hwnd;
m_hwnd_device_list = GetDlgItem(m_hwnd_window, IDC_DEVICE_LIST);
m_hwnd_profile_list = GetDlgItem(m_hwnd_window, IDC_PROFILE_NAME);
m_hwnd_default = GetDlgItem(m_hwnd_window, IDC_DEFAULT);
m_dev_type = dev_type;
m_max_num_buttons = dev_num_buttons[dev_type];
m_port_num = port_num;
m_bHasChanges = false;
m_bIsBinding = false;
m_num_devices = 0;
// Set window icon
SetClassLong(m_hwnd_window, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX)));
// Set window title
std::string title = (GetInputDeviceName(m_dev_type) + " at port ");
SendMessage(m_hwnd_window, WM_SETTEXT, 0,
reinterpret_cast<LPARAM>((title + PortUserFormat(std::to_string(m_port_num))).c_str()));
// Set the maximum profile name lenght the user can enter in the profile combobox
SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0);
// construct emu device
m_DeviceConfig = new EmuDevice(m_dev_type, m_hwnd_window, this);
// Enumerate devices
UpdateDeviceList();
// Load currently saved profile for this port/device type
LoadDefaultProfile();
// Load currently selected host device
UpdateCurrentDevice();
// Install the subclass for the profile combobox
SetWindowSubclass(GetWindow(m_hwnd_profile_list, GW_CHILD), ProfileNameSubclassProc, 0, 0);
}
void LightgunInputWindow::ClearBindings()
{
m_DeviceConfig->ClearButtons();
m_bHasChanges = true;
}
void LightgunInputWindow::BindDefault()
{
int api = EnableDefaultButton();
if (api != -1) {
m_DeviceConfig->BindDefault<LIGHTGUN_NUM_BUTTONS>(button_lightgun_default[api]);
m_bHasChanges = true;
}
}
INT_PTR CALLBACK DlgLightgunConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
int port_num = lParam & 0xFF;
int dev_type = (lParam & 0xFF00) >> 8;
// Ensure that port_num is a valid xbox port
assert(port_num >= PORT_1 && port_num <= PORT_4);
// Ensure that the controller type is valid
assert(dev_type == to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN));
g_InputWindow = new LightgunInputWindow;
g_InputWindow->Initialize(hWndDlg, port_num, dev_type);
}
break;
case WM_CLOSE:
{
if (g_InputWindow->IsProfileSaved()) {
delete g_InputWindow;
g_InputWindow = nullptr;
EndDialog(hWndDlg, wParam);
}
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_DEVICE_LIST: {
if (HIWORD(wParam) == CBN_SELCHANGE) {
g_InputWindow->UpdateCurrentDevice();
}
}
break;
case IDC_PROFILE_NAME: {
if (HIWORD(wParam) == CBN_SELCHANGE) {
char name[50];
HWND hwnd = GetDlgItem(hWndDlg, IDC_PROFILE_NAME);
LRESULT str_idx = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
if (str_idx != CB_ERR) {
SendMessage(hwnd, CB_GETLBTEXT, str_idx, reinterpret_cast<LPARAM>(name));
g_InputWindow->UpdateProfile(std::string(name), PROFILE_LOAD);
}
}
}
break;
case IDC_PROFILE_SAVE:
case IDC_PROFILE_DELETE: {
if (HIWORD(wParam) == BN_CLICKED) {
char name[50];
SendMessage(GetDlgItem(hWndDlg, IDC_PROFILE_NAME), WM_GETTEXT,
sizeof(name), reinterpret_cast<LPARAM>(name));
g_InputWindow->UpdateProfile(std::string(name), (LOWORD(wParam) == IDC_PROFILE_SAVE) ? PROFILE_SAVE : PROFILE_DELETE);
}
}
break;
case IDC_DEFAULT: {
if (HIWORD(wParam) == BN_CLICKED) {
g_InputWindow->BindDefault();
}
}
break;
case IDC_CLEAR: {
if (HIWORD(wParam) == BN_CLICKED) {
if (PopupQuestionEx(hWndDlg, LOG_LEVEL::WARNING, PopupButtons::YesNo, PopupReturn::No, "Are you sure you want to remove all button bindings?") == PopupReturn::Yes) {
g_InputWindow->ClearBindings();
}
}
}
break;
case IDC_REFRESH_DEVICES: {
if (HIWORD(wParam) == BN_CLICKED) {
g_InputWindow->UpdateDeviceList();
}
}
break;
case IDC_LG_STICK_UP:
case IDC_LG_STICK_DOWN:
case IDC_LG_STICK_LEFT:
case IDC_LG_STICK_RIGHT:
case IDC_LG_START:
case IDC_LG_SEBA:
case IDC_LG_TRIGGER:
case IDC_LG_GRIP:
case IDC_LG_A:
case IDC_LG_B:
case IDC_LG_AIM_POSX:
case IDC_LG_AIM_NEGX:
case IDC_LG_AIM_POSY:
case IDC_LG_AIM_NEGY:
case IDC_TURBO_LEFT:
case IDC_TURBO_RIGHT:
case IDC_LASER: {
if (HIWORD(wParam) == BN_CLICKED) {
g_InputWindow->BindButton(LOWORD(wParam), true);
}
}
break;
}
}
break;
}
return FALSE;
}

View File

@ -0,0 +1,30 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
INT_PTR CALLBACK DlgLightgunConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

View File

@ -36,6 +36,12 @@
static SbcInputWindow *g_InputWindow = nullptr;
SbcInputWindow::~SbcInputWindow()
{
g_Settings->m_input_port[m_port_num].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
g_Settings->m_input_port[m_port_num].SlotType[SLOT_BOTTOM] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
}
void SbcInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
{
// Save window/device specific variables
@ -88,13 +94,6 @@ int SbcInputWindow::EnableDefaultButton()
return -1;
}
void SbcInputWindow::SaveSlotConfig()
{
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
g_Settings->m_input_port[m_port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
}
}
INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
@ -118,7 +117,6 @@ INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPara
case WM_CLOSE:
{
if (g_InputWindow->IsProfileSaved()) {
g_InputWindow->SaveSlotConfig();
delete g_InputWindow;
g_InputWindow = nullptr;
EndDialog(hWndDlg, wParam);

View File

@ -36,6 +36,10 @@ BEGIN
BEGIN
END
IDD_LIGHTGUN_CFG, DIALOG
BEGIN
END
IDD_SBC_CFG, DIALOG
BEGIN
BOTTOMMARGIN, 269
@ -202,6 +206,60 @@ BEGIN
PUSHBUTTON "Test",IDC_RUMBLE_TEST,95,11,45,14,BS_FLAT
END
IDD_LIGHTGUN_CFG DIALOGEX 0, 0, 529, 225
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "Verdana", 0, 0, 0x1
BEGIN
GROUPBOX "Device",IDC_DEVICE,12,10,175,35,WS_GROUP
COMBOBOX IDC_DEVICE_LIST,21,23,101,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Refresh",IDC_REFRESH_DEVICES,128,23,50,14,BS_FLAT
GROUPBOX "Profile",IDC_PROFILE,197,10,320,35,WS_GROUP
COMBOBOX IDC_PROFILE_NAME,207,24,194,10,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Save",IDC_PROFILE_SAVE,405,23,50,14,BS_FLAT
PUSHBUTTON "Delete",IDC_PROFILE_DELETE,459,23,50,14,BS_FLAT
GROUPBOX "Buttons",IDC_BUTTONS,12,65,121,121,WS_GROUP
PUSHBUTTON "",IDC_LG_A,59,110,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_B,59,127,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_TRIGGER,59,76,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_GRIP,59,93,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_SEBA,59,144,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_START,59,161,57,14,BS_FLAT
LTEXT "A",IDC_STATIC,28,110,20,14,SS_CENTERIMAGE
LTEXT "B",IDC_STATIC,28,127,20,14,SS_CENTERIMAGE
LTEXT "Trigger",IDC_STATIC,28,76,27,14,SS_CENTERIMAGE
LTEXT "Grip",IDC_STATIC,28,93,20,14,SS_CENTERIMAGE
LTEXT "SE/BA",IDC_STATIC,28,144,25,14,SS_CENTERIMAGE
LTEXT "START",IDC_STATIC,28,161,27,14,SS_CENTERIMAGE
GROUPBOX "Aim",IDC_LSTICK,140,65,121,121,WS_GROUP
PUSHBUTTON "",IDC_LG_AIM_POSY,187,75,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_AIM_NEGY,187,93,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_AIM_NEGX,187,111,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_AIM_POSX,187,129,57,14,BS_FLAT
LTEXT "Up",IDC_STATIC,156,75,20,14,SS_CENTERIMAGE
LTEXT "Down",IDC_STATIC,156,93,20,14,SS_CENTERIMAGE
LTEXT "Left",IDC_STATIC,156,111,20,14,SS_CENTERIMAGE
LTEXT "Right",IDC_STATIC,156,129,20,14,SS_CENTERIMAGE
GROUPBOX "Stick",IDC_DPAD,268,65,121,121,WS_GROUP
PUSHBUTTON "",IDC_LG_STICK_UP,315,75,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_STICK_DOWN,315,93,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_STICK_LEFT,315,111,57,14,BS_FLAT
PUSHBUTTON "",IDC_LG_STICK_RIGHT,315,129,57,14,BS_FLAT
LTEXT "Up",IDC_STATIC,285,75,20,14,SS_CENTERIMAGE
LTEXT "Down",IDC_STATIC,285,93,20,14,SS_CENTERIMAGE
LTEXT "Left",IDC_STATIC,285,111,20,14,SS_CENTERIMAGE
LTEXT "Right",IDC_STATIC,285,129,20,14,SS_CENTERIMAGE
PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,200,69,14,BS_FLAT
PUSHBUTTON "Clear",IDC_CLEAR,443,200,50,14,BS_FLAT
GROUPBOX "Turbo switch",IDC_POWER_SWITCH,396,66,121,52,WS_GROUP
PUSHBUTTON "",IDC_TURBO_LEFT,443,76,57,14,BS_FLAT
PUSHBUTTON "",IDC_TURBO_RIGHT,443,94,57,14,BS_FLAT
LTEXT "Left",IDC_STATIC,412,76,20,14,SS_CENTERIMAGE
LTEXT "Right",IDC_STATIC,412,94,20,14,SS_CENTERIMAGE
GROUPBOX "Power switch",IDC_TURBO,396,121,121,66,WS_GROUP
PUSHBUTTON "",IDC_LASER,443,132,57,14,BS_FLAT
LTEXT "Laser",IDC_STATIC,412,132,20,14,SS_CENTERIMAGE
END
IDD_LIBUSB_CFG DIALOGEX 0, 0, 250, 35
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "Verdana", 0, 0, 0x1
@ -590,6 +648,11 @@ BEGIN
0
END
IDD_LIGHTGUN_CFG AFX_DIALOG_LAYOUT
BEGIN
0
END
/////////////////////////////////////////////////////////////////////////////
//

View File

@ -22,6 +22,7 @@
#define IDD_RUMBLE_CFG 135
#define IDD_NETWORK_CFG 136
#define IDD_LIBUSB_CFG 137
#define IDD_LIGHTGUN_CFG 138
#define IDC_LOG_CANCEL 892
#define IDC_LOG_ACCEPT 893
#define IDC_LOG_ENABLE_GENERAL 894
@ -275,6 +276,25 @@
#define IDC_SIGHT_CHANGE_POSX 1322
#define IDC_LIBUSB_LIST 1323
#define IDC_LIBUSB_TEST 1324
#define IDC_LG_A 1325
#define IDC_LG_B 1326
#define IDC_LG_TRIGGER 1327
#define IDC_LG_GRIP 1328
#define IDC_LG_SEBA 1329
#define IDC_LG_START 1330
#define IDC_LG_AIM_POSY 1331
#define IDC_LG_AIM_NEGY 1332
#define IDC_LG_AIM_NEGX 1333
#define IDC_LG_AIM_POSX 1334
#define IDC_LG_STICK_UP 1335
#define IDC_LG_STICK_DOWN 1336
#define IDC_LG_STICK_LEFT 1337
#define IDC_LG_STICK_RIGHT 1338
#define IDC_TURBO_LEFT 1339
#define IDC_TURBO_RIGHT 1340
#define IDC_TURBO 1341
#define IDC_POWER_SWITCH 1342
#define IDC_LASER 1343
#define ID_FILE_EXIT 40005
#define ID_HELP_ABOUT 40008
#define ID_EMULATION_START 40009
@ -340,7 +360,7 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 137
#define _APS_NEXT_RESOURCE_VALUE 139
#define _APS_NEXT_COMMAND_VALUE 40117
#define _APS_NEXT_CONTROL_VALUE 1308
#define _APS_NEXT_SYMED_VALUE 109