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.
This commit is contained in:
skidau 2014-12-10 20:45:45 +11:00
parent 1ad8d4629c
commit 8d4a47d40c
11 changed files with 484 additions and 63 deletions

View File

@ -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})

View File

@ -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)

View File

@ -107,6 +107,7 @@ struct SConfig : NonCopyable
// Input settings
bool m_BackgroundInput;
bool m_GameCubeAdapter;
SysConf* m_SYSCONF;

View File

@ -147,6 +147,13 @@
<ClCompile Include="HW\SI_DeviceGBA.cpp" />
<ClCompile Include="HW\SI_DeviceGCController.cpp" />
<ClCompile Include="HW\SI_DeviceGCSteeringWheel.cpp" />
<ClCompile Include="HW\SI_GCAdapter.cpp">
<!--
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
which is hit in libusb.h.
-->
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="HW\Sram.cpp" />
<ClCompile Include="HW\StreamADPCM.cpp" />
<ClCompile Include="HW\SystemTimers.cpp" />
@ -176,7 +183,7 @@
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_hid.cpp">
<!--
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
which is hit in libusb.h (and this is the only file which uses that header).
which is hit in libusb.h.
-->
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
@ -345,6 +352,7 @@
<ClInclude Include="HW\SI_DeviceGBA.h" />
<ClInclude Include="HW\SI_DeviceGCController.h" />
<ClInclude Include="HW\SI_DeviceGCSteeringWheel.h" />
<ClInclude Include="HW\SI_GCAdapter.h" />
<ClInclude Include="HW\Sram.h" />
<ClInclude Include="HW\StreamADPCM.h" />
<ClInclude Include="HW\SystemTimers.h" />

View File

@ -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;
// 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);
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
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();
}

View File

@ -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);

View File

@ -0,0 +1,284 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <libusb.h>
#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<u8>(g_Channel[0].m_Out.Hex & 0xff), static_cast<u8>(g_Channel[1].m_Out.Hex & 0xff), static_cast<u8>(g_Channel[2].m_Out.Hex & 0xff), static_cast<u8>(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

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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();