From 8d4a47d40cd2e23a9119ad5fc7e6b8da9a0d3702 Mon Sep 17 00:00:00 2001 From: skidau Date: Wed, 10 Dec 2014 20:45:45 +1100 Subject: [PATCH] Added GameCube Adapter support. The libusb driver must be installed on the adapter (e.g. zadig can be used to install the driver in Windows). GameCube pad controllers are supported and will override the current input device assigned to the port. GameCube controller buttons are auto-configured and cannot be re-assigned. Rumble is supported. Hotplug is supported while playing a game. If a controller is unplugged from the adapter, Dolphin will fallback to using the host input device on that port. If a port on the adapter is unused, Dolphin will use the host input device for that port, allowing a mixture of host input devices and controllers connected to the adapter. The adapter support can be disabled in the Controllers config if the OS driver is preferred (allowing the pad buttons to be reconfigured). One adapter per system is supported. --- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/ConfigManager.cpp | 2 + Source/Core/Core/ConfigManager.h | 1 + Source/Core/Core/Core.vcxproj | 10 +- Source/Core/Core/HW/SI.cpp | 102 +++---- Source/Core/Core/HW/SI.h | 50 +++ Source/Core/Core/HW/SI_GCAdapter.cpp | 284 ++++++++++++++++++ Source/Core/Core/HW/SI_GCAdapter.h | 29 ++ .../Core/DolphinWX/ControllerConfigDiag.cpp | 51 +++- Source/Core/DolphinWX/ControllerConfigDiag.h | 5 + Source/Core/UICommon/UICommon.cpp | 10 +- 11 files changed, 484 insertions(+), 63 deletions(-) create mode 100644 Source/Core/Core/HW/SI_GCAdapter.cpp create mode 100644 Source/Core/Core/HW/SI_GCAdapter.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 536acd73f7..4824bc8b7e 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -247,7 +247,8 @@ set(LIBS if(LIBUSB_FOUND) # Using shared LibUSB set(LIBS ${LIBS} ${LIBUSB_LIBRARIES}) - set(SRCS ${SRCS} IPC_HLE/WII_IPC_HLE_Device_hid.cpp) + set(SRCS ${SRCS} IPC_HLE/WII_IPC_HLE_Device_hid.cpp + HW/SI_GCAdapter.cpp) endif(LIBUSB_FOUND) set(LIBS ${LIBS} ${POLARSSL_LIBRARY}) diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 0305746cf4..7afb655a44 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -341,6 +341,7 @@ void SConfig::SaveCoreSettings(IniFile& ini) core->Set("FrameSkip", m_FrameSkip); core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend); core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode); + core->Set("GameCubeAdapter", m_GameCubeAdapter); } void SConfig::SaveMovieSettings(IniFile& ini) @@ -574,6 +575,7 @@ void SConfig::LoadCoreSettings(IniFile& ini) core->Get("FrameSkip", &m_FrameSkip, 0); core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, ""); core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto"); + core->Get("GameCubeAdapter", &m_GameCubeAdapter, true); } void SConfig::LoadMovieSettings(IniFile& ini) diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 7c14d9dfb8..badf3fe04b 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -107,6 +107,7 @@ struct SConfig : NonCopyable // Input settings bool m_BackgroundInput; + bool m_GameCubeAdapter; SysConf* m_SYSCONF; diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 611658f65f..ae775bcc5a 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -147,6 +147,13 @@ + + + 4200;%(DisableSpecificWarnings) + @@ -176,7 +183,7 @@ 4200;%(DisableSpecificWarnings) @@ -345,6 +352,7 @@ + diff --git a/Source/Core/Core/HW/SI.cpp b/Source/Core/Core/HW/SI.cpp index 0f6b14917c..736853b06b 100644 --- a/Source/Core/Core/HW/SI.cpp +++ b/Source/Core/Core/HW/SI.cpp @@ -15,6 +15,9 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI.h" #include "Core/HW/SI_DeviceGBA.h" +#if defined(__LIBUSB__) || defined (_WIN32) +#include "Core/HW/SI_GCAdapter.h" +#endif #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" @@ -56,56 +59,6 @@ enum SI_EXI_CLOCK_COUNT = 0x3C, }; -// SI Channel Output -union USIChannelOut -{ - u32 Hex; - struct - { - u32 OUTPUT1 : 8; - u32 OUTPUT0 : 8; - u32 CMD : 8; - u32 : 8; - }; -}; - -// SI Channel Input High u32 -union USIChannelIn_Hi -{ - u32 Hex; - struct - { - u32 INPUT3 : 8; - u32 INPUT2 : 8; - u32 INPUT1 : 8; - u32 INPUT0 : 6; - u32 ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR. - u32 ERRSTAT : 1; // 0: no error 1: error on last transfer - }; -}; - -// SI Channel Input Low u32 -union USIChannelIn_Lo -{ - u32 Hex; - struct - { - u32 INPUT7 : 8; - u32 INPUT6 : 8; - u32 INPUT5 : 8; - u32 INPUT4 : 8; - }; -}; - -// SI Channel -struct SSIChannel -{ - USIChannelOut m_Out; - USIChannelIn_Hi m_InHi; - USIChannelIn_Lo m_InLo; - ISIDevice* m_pDevice; -}; - // SI Poll: Controls how often a device is polled union USIPoll { @@ -384,7 +337,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) g_Channel[1].m_pDevice->SendCommand(g_Channel[1].m_Out.Hex, g_Poll.EN1); g_Channel[2].m_pDevice->SendCommand(g_Channel[2].m_Out.Hex, g_Poll.EN2); g_Channel[3].m_pDevice->SendCommand(g_Channel[3].m_Out.Hex, g_Poll.EN3); - +#if defined(__LIBUSB__) || defined (_WIN32) + SI_GCAdapter::Output(g_Channel); +#endif g_StatusReg.WR = 0; g_StatusReg.WRST0 = 0; g_StatusReg.WRST1 = 0; @@ -473,22 +428,30 @@ static void SetNoResponse(u32 channel) void ChangeDeviceCallback(u64 userdata, int cyclesLate) { u8 channel = (u8)(userdata >> 32); + SIDevices device = (SIDevices)(u32)userdata; - g_Channel[channel].m_Out.Hex = 0; - g_Channel[channel].m_InHi.Hex = 0; - g_Channel[channel].m_InLo.Hex = 0; + // Skip redundant (spammed) device changes + if (GetDeviceType(channel) != device) + { + g_Channel[channel].m_Out.Hex = 0; + g_Channel[channel].m_InHi.Hex = 0; + g_Channel[channel].m_InLo.Hex = 0; - SetNoResponse(channel); + SetNoResponse(channel); - AddDevice((SIDevices)(u32)userdata, channel); + AddDevice(device, channel); + } } void ChangeDevice(SIDevices device, int channel) { // Called from GUI, so we need to make it thread safe. // Let the hardware see no device for .5b cycles - CoreTiming::ScheduleEvent_Threadsafe(0, changeDevice, ((u64)channel << 32) | SIDEVICE_NONE); - CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | device); + if (GetDeviceType(channel) != device) + { + CoreTiming::ScheduleEvent_Threadsafe(0, changeDevice, ((u64)channel << 32) | SIDEVICE_NONE); + CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | device); + } } void UpdateDevices() @@ -499,6 +462,29 @@ void UpdateDevices() g_StatusReg.RDST2 = !!g_Channel[2].m_pDevice->GetData(g_Channel[2].m_InHi.Hex, g_Channel[2].m_InLo.Hex); g_StatusReg.RDST3 = !!g_Channel[3].m_pDevice->GetData(g_Channel[3].m_InHi.Hex, g_Channel[3].m_InLo.Hex); + // Check for connected GC Adapter +#if defined(__LIBUSB__) || defined (_WIN32) + if (SConfig::GetInstance().m_GameCubeAdapter) + { + g_StatusReg.RDST0 |= (SI_GCAdapter::GetDeviceType(0) != SIDEVICE_NONE); + g_StatusReg.RDST1 |= (SI_GCAdapter::GetDeviceType(1) != SIDEVICE_NONE); + g_StatusReg.RDST2 |= (SI_GCAdapter::GetDeviceType(2) != SIDEVICE_NONE); + g_StatusReg.RDST3 |= (SI_GCAdapter::GetDeviceType(3) != SIDEVICE_NONE); + + for (int chan = 0; chan < MAX_SI_CHANNELS; chan++) + { + SIDevices connected_device = SI_GCAdapter::GetDeviceType(chan); + SIDevices configured_device = SConfig::GetInstance().m_SIDevice[chan]; + + if (connected_device != SIDEVICE_NONE) + ChangeDevice(connected_device, chan); + else + ChangeDevice(configured_device, chan); + } + + SI_GCAdapter::Input(g_Channel); + } +#endif UpdateInterrupts(); } diff --git a/Source/Core/Core/HW/SI.h b/Source/Core/Core/HW/SI.h index 2e06b269a1..2f22996e6e 100644 --- a/Source/Core/Core/HW/SI.h +++ b/Source/Core/Core/HW/SI.h @@ -20,6 +20,56 @@ enum namespace SerialInterface { +// SI Channel Output +union USIChannelOut +{ + u32 Hex; + struct + { + u32 OUTPUT1 : 8; + u32 OUTPUT0 : 8; + u32 CMD : 8; + u32 : 8; + }; +}; + +// SI Channel Input High u32 +union USIChannelIn_Hi +{ + u32 Hex; + struct + { + u32 INPUT3 : 8; + u32 INPUT2 : 8; + u32 INPUT1 : 8; + u32 INPUT0 : 6; + u32 ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR. + u32 ERRSTAT : 1; // 0: no error 1: error on last transfer + }; +}; + +// SI Channel Input Low u32 +union USIChannelIn_Lo +{ + u32 Hex; + struct + { + u32 INPUT7 : 8; + u32 INPUT6 : 8; + u32 INPUT5 : 8; + u32 INPUT4 : 8; + }; +}; + +// SI Channel +struct SSIChannel +{ + USIChannelOut m_Out; + USIChannelIn_Hi m_InHi; + USIChannelIn_Lo m_InLo; + ISIDevice* m_pDevice; +}; + void Init(); void Shutdown(); void DoState(PointerWrap &p); diff --git a/Source/Core/Core/HW/SI_GCAdapter.cpp b/Source/Core/Core/HW/SI_GCAdapter.cpp new file mode 100644 index 0000000000..dad95b3eb2 --- /dev/null +++ b/Source/Core/Core/HW/SI_GCAdapter.cpp @@ -0,0 +1,284 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "Core/ConfigManager.h" +#include "Core/HW/SI_GCAdapter.h" +#include "InputCommon/GCPadStatus.h" + +static const u8 ENDPOINT_IN = 0x81; +static const u8 ENDPOINT_OUT = 0x02; + +namespace SI_GCAdapter +{ + +static libusb_device_handle* handle = nullptr; +static bool controller_connected[MAX_SI_CHANNELS] = { false, false, false, false }; +static u8 controller_payload[37]; +static u8 controller_last_rumble[4]; +static int controller_payload_size = 0; + +static std::thread adapter_thread; +static bool adapter_thread_running; + +static bool libusb_driver_not_supported = false; + +void Read() +{ + while (adapter_thread_running) + { + libusb_interrupt_transfer(handle, ENDPOINT_IN, controller_payload, sizeof(controller_payload), &controller_payload_size, 0); + Common::YieldCPU(); + } +} + +void Init() +{ + if (handle != nullptr) + return; + + libusb_driver_not_supported = false; + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + { + controller_connected[i] = false; + controller_last_rumble[i] = 0; + } + + int ret = libusb_init(nullptr); + + if (ret) + { + ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret); + Shutdown(); + } + else + { + libusb_device** list; + ssize_t cnt = libusb_get_device_list(nullptr, &list); + for (int d = 0; d < cnt; d++) + { + libusb_device* device = list[d]; + libusb_device_descriptor desc; + int dRet = libusb_get_device_descriptor(device, &desc); + if (dRet) + { + // could not aquire the descriptor, no point in trying to use it. + ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet); + continue; + } + + if (desc.idVendor == 0x057e && desc.idProduct == 0x0337) + { + NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", desc.idVendor, desc.idProduct, 1); + + u8 bus = libusb_get_bus_number(device); + u8 port = libusb_get_device_address(device); + ret = libusb_open(device, &handle); + if (ret) + { + if (ret == LIBUSB_ERROR_ACCESS) + { + if (dRet) + { + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID ????:???? (couldn't get id).", + bus, + port + ); + } + else + { + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", + bus, + port, + desc.idVendor, + desc.idProduct + ); + } + } + else + { + ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) + libusb_driver_not_supported = true; + } + Shutdown(); + } + else if ((ret = libusb_kernel_driver_active(handle, 0)) == 1) + { + if ((ret = libusb_detach_kernel_driver(handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) + { + ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret); + Shutdown(); + } + } + else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) + { + ERROR_LOG(SERIALINTERFACE, "libusb_kernel_driver_active error ret = %d", ret); + Shutdown(); + } + else if ((ret = libusb_claim_interface(handle, 0))) + { + ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret); + Shutdown(); + } + else + { + int tmp = 0; + unsigned char payload = 0x13; + libusb_interrupt_transfer(handle, ENDPOINT_OUT, &payload, sizeof(payload), &tmp, 0); + + RefreshConnectedDevices(); + + adapter_thread_running = true; + adapter_thread = std::thread(Read); + } + } + } + + libusb_free_device_list(list, 1); + } +} + +void Shutdown() +{ + if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter) + return; + + if (adapter_thread_running) + { + adapter_thread_running = false; + adapter_thread.join(); + } + + libusb_close(handle); + libusb_driver_not_supported = false; + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + controller_connected[i] = false; + + handle = nullptr; +} + +void Input(SerialInterface::SSIChannel* g_Channel) +{ + if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter) + return; + + if (controller_payload_size != 0x25 || controller_payload[0] != 0x21) + { + ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d)", controller_payload_size); + Shutdown(); + } + else + { + for (int chan = 0; chan < MAX_SI_CHANNELS; chan++) + { + bool connected = (controller_payload[1 + (9 * chan)] > 0); + if (connected && !controller_connected[chan]) + NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload[1 + (9 * chan)]); + + controller_connected[chan] = connected; + + if (controller_connected[chan]) + { + g_Channel[chan].m_InHi.Hex = 0; + g_Channel[chan].m_InLo.Hex = 0; + for (int j = 0; j < 4; j++) + { + g_Channel[chan].m_InHi.Hex |= (controller_payload[2 + chan + j + (8 * chan) + 0] << (8 * (3 - j))); + g_Channel[chan].m_InLo.Hex |= (controller_payload[2 + chan + j + (8 * chan) + 4] << (8 * (3 - j))); + } + + u8 buttons_0 = ((g_Channel[chan].m_InHi.Hex >> 24) & 0xf0) >> 4; + u8 buttons_1 = (g_Channel[chan].m_InHi.Hex >> 24) & 0x0f; + u8 buttons_2 = ((g_Channel[chan].m_InHi.Hex >> 16) & 0xf0) >> 4; + u8 buttons_3 = (g_Channel[chan].m_InHi.Hex >> 16) & 0x0f; + g_Channel[chan].m_InHi.Hex = buttons_3 << 28 | buttons_1 << 24 | (buttons_2) << 20 | buttons_0 << 16 | (g_Channel[chan].m_InHi.Hex & 0x0000ffff); + + if (controller_payload[1 + (9 * chan)] == 0x10) + g_Channel[chan].m_InHi.Hex |= (PAD_USE_ORIGIN << 16); + } + } + } +} + +void Output(SerialInterface::SSIChannel* g_Channel) +{ + if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter) + return; + + bool rumble_update = false; + for (int chan = 0; chan < MAX_SI_CHANNELS; chan++) + { + u8 current_rumble = g_Channel[chan].m_Out.Hex & 0xff; + if (current_rumble != controller_last_rumble[chan]) + rumble_update = true; + + controller_last_rumble[chan] = current_rumble; + } + + if (rumble_update) + { + unsigned char rumble[5] = { 0x11, static_cast(g_Channel[0].m_Out.Hex & 0xff), static_cast(g_Channel[1].m_Out.Hex & 0xff), static_cast(g_Channel[2].m_Out.Hex & 0xff), static_cast(g_Channel[3].m_Out.Hex & 0xff) }; + int size = 0; + libusb_interrupt_transfer(handle, ENDPOINT_OUT, rumble, sizeof(rumble), &size, 0); + + if (size != 0x05) + { + WARN_LOG(SERIALINTERFACE, "error reading rumble (size: %d)", size); + Shutdown(); + } + } +} + +SIDevices GetDeviceType(int channel) +{ + if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter) + return SIDEVICE_NONE; + + if (controller_connected[channel]) + return SIDEVICE_GC_CONTROLLER; + else + return SIDEVICE_NONE; +} + +void RefreshConnectedDevices() +{ + if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter) + return; + + int size = 0; + libusb_interrupt_transfer(handle, ENDPOINT_IN, controller_payload, sizeof(controller_payload), &size, 0); + + if (size != 0x25 || controller_payload[0] != 0x21) + { + WARN_LOG(SERIALINTERFACE, "error reading payload (size: %d)", size); + Shutdown(); + } + else + { + for (int chan = 0; chan < MAX_SI_CHANNELS; chan++) + { + bool connected = (controller_payload[1 + (9 * chan)] > 0); + if (connected && !controller_connected[chan]) + NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload[1 + (9 * chan)]); + + controller_connected[chan] = connected; + } + } +} + +bool IsDetected() +{ + return handle != nullptr; +} + +bool IsDriverDetected() +{ + return !libusb_driver_not_supported; +} + +} // end of namespace SI_GCAdapter diff --git a/Source/Core/Core/HW/SI_GCAdapter.h b/Source/Core/Core/HW/SI_GCAdapter.h new file mode 100644 index 0000000000..5095d3fbe8 --- /dev/null +++ b/Source/Core/Core/HW/SI_GCAdapter.h @@ -0,0 +1,29 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Thread.h" +#include "Core/HW/SI.h" + +struct libusb_device_handle; +struct libusb_device_descriptor; +struct libusb_config_descriptor; +struct libusb_interface_descriptor; +struct libusb_endpoint_descriptor; +struct libusb_transfer; + +namespace SI_GCAdapter +{ + +void Init(); +void Shutdown(); +void Input(SerialInterface::SSIChannel* g_Channel); +void Output(SerialInterface::SSIChannel* g_Channel); +SIDevices GetDeviceType(int channel); +void RefreshConnectedDevices(); +bool IsDetected(); +bool IsDriverDetected(); + +} // end of namespace SI_GCAdapter diff --git a/Source/Core/DolphinWX/ControllerConfigDiag.cpp b/Source/Core/DolphinWX/ControllerConfigDiag.cpp index da7cc6145a..9e3607b438 100644 --- a/Source/Core/DolphinWX/ControllerConfigDiag.cpp +++ b/Source/Core/DolphinWX/ControllerConfigDiag.cpp @@ -27,6 +27,9 @@ #include "Core/NetPlayProto.h" #include "Core/HW/GCPad.h" #include "Core/HW/SI.h" +#if defined(__LIBUSB__) || defined (_WIN32) +#include "Core/HW/SI_GCAdapter.h" +#endif #include "Core/HW/Wiimote.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "DolphinWX/ControllerConfigDiag.h" @@ -68,7 +71,7 @@ ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent) wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() { - wxStaticBoxSizer* const gamecube_static_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("GameCube Controllers")); + wxStaticBoxSizer* const gamecube_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("GameCube Controllers")); wxFlexGridSizer* const gamecube_flex_sizer = new wxFlexGridSizer(3, 5, 5); wxStaticText* pad_labels[4]; @@ -101,8 +104,22 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() if (NetPlay::IsNetPlayRunning() || Movie::IsMovieActive()) pad_type_choices[i]->Disable(); + SIDevices selected_device = SConfig::GetInstance().m_SIDevice[i]; + +#if defined(__LIBUSB__) || defined (_WIN32) + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + SI_GCAdapter::RefreshConnectedDevices(); + if (SI_GCAdapter::GetDeviceType(i) != SIDEVICE_NONE) + { + pad_type_choices[i]->Disable(); + selected_device = SI_GCAdapter::GetDeviceType(i); + } + } +#endif + // Set the saved pad type as the default choice. - switch (SConfig::GetInstance().m_SIDevice[i]) + switch (selected_device) { case SIDEVICE_GC_CONTROLLER: pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[1]); @@ -134,6 +151,36 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer() } gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND, 5); + gamecube_static_sizer->AddSpacer(5); + + wxStaticBoxSizer* const gamecube_adapter_group = new wxStaticBoxSizer(wxHORIZONTAL, this, _("GameCube Adapter")); + wxBoxSizer* const gamecube_adapter_sizer = new wxBoxSizer(wxHORIZONTAL); + + wxCheckBox* const gamecube_adapter = new wxCheckBox(this, wxID_ANY, _("Direct Connect")); + gamecube_adapter->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnGameCubeAdapter, this); + + gamecube_adapter_sizer->Add(gamecube_adapter, 0, wxEXPAND); + gamecube_adapter_group->Add(gamecube_adapter_sizer, 0, wxEXPAND); + gamecube_static_sizer->Add(gamecube_adapter_group, 0, wxEXPAND); + +#if defined(__LIBUSB__) || defined (_WIN32) + if (!SI_GCAdapter::IsDetected()) + { + if (!SI_GCAdapter::IsDriverDetected()) + gamecube_adapter->SetLabelText(_("Driver Not Detected")); + else + gamecube_adapter->SetLabelText(_("Adapter Not Detected")); + gamecube_adapter->SetValue(false); + gamecube_adapter->Disable(); + } + else + { + gamecube_adapter->SetValue(SConfig::GetInstance().m_GameCubeAdapter); + if (Core::GetState() != Core::CORE_UNINITIALIZED) + gamecube_adapter->Disable(); + } +#endif + return gamecube_static_sizer; } diff --git a/Source/Core/DolphinWX/ControllerConfigDiag.h b/Source/Core/DolphinWX/ControllerConfigDiag.h index 3fbc56fb76..1c5df13f4f 100644 --- a/Source/Core/DolphinWX/ControllerConfigDiag.h +++ b/Source/Core/DolphinWX/ControllerConfigDiag.h @@ -59,6 +59,11 @@ public: SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked(); event.Skip(); } + void OnGameCubeAdapter(wxCommandEvent& event) + { + SConfig::GetInstance().m_GameCubeAdapter = event.IsChecked(); + event.Skip(); + } private: wxStaticBoxSizer* CreateGamecubeSizer(); diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index f54b1a9c76..8e260f582d 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -7,6 +7,9 @@ #include "Common/Logging/LogManager.h" #include "Core/ConfigManager.h" +#if defined(__LIBUSB__) || defined (_WIN32) +#include "Core/HW/SI_GCAdapter.h" +#endif #include "Core/HW/Wiimote.h" #include "UICommon/UICommon.h" @@ -22,7 +25,9 @@ void Init() SConfig::Init(); VideoBackend::PopulateList(); WiimoteReal::LoadSettings(); - +#if defined(__LIBUSB__) || defined (_WIN32) + SI_GCAdapter::Init(); +#endif VideoBackend::ActivateBackend(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend); SetEnableAlert(SConfig::GetInstance().m_LocalCoreStartupParameter.bUsePanicHandlers); @@ -30,6 +35,9 @@ void Init() void Shutdown() { +#if defined(__LIBUSB__) || defined (_WIN32) + SI_GCAdapter::Shutdown(); +#endif WiimoteReal::Shutdown(); VideoBackend::ClearList(); SConfig::Shutdown();