mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-20 05:33:23 -04:00
210 lines
7.3 KiB
C++
210 lines
7.3 KiB
C++
#include "hle_stubs.h"
|
|
#include "memory.h"
|
|
#include "hle/controller_status_contract.h"
|
|
#include "wii_remote_input.h"
|
|
|
|
#include <cstdint>
|
|
|
|
#include <dolphin/pad.h>
|
|
|
|
extern "C" bool PAD_HLE_RumbleEnabled();
|
|
void NandQueueIosCallback(uint32_t callbackPtr, int32_t result, uint32_t callbackArg);
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kDefaultWorkMemSize = 0x20000;
|
|
constexpr uint8_t kDefaultDpdSensitivity = 3;
|
|
constexpr int32_t kStatusOk = 0;
|
|
|
|
struct WpadStubState {
|
|
bool initSubRan = false;
|
|
bool simpleSyncActive = false;
|
|
uint32_t syncDeviceCallback = 0;
|
|
uint32_t workMemSize = kDefaultWorkMemSize;
|
|
uint8_t dpdSensitivity = kDefaultDpdSensitivity;
|
|
WpadContract::State contract{};
|
|
};
|
|
|
|
WpadStubState g_state{};
|
|
|
|
void InvokeWpadCallback(uint32_t callback, uint32_t chan, int32_t result)
|
|
{
|
|
if (callback == 0) {
|
|
return;
|
|
}
|
|
if (!TranslatedFunctionRegistry::FindByAddressPtr(callback)) {
|
|
return;
|
|
}
|
|
auto& cpu = GetPersistentCpuContext();
|
|
cpu.gpr[3] = chan;
|
|
cpu.gpr[4] = static_cast<uint32_t>(result);
|
|
InvokeIndirectCpu(callback, &cpu);
|
|
}
|
|
|
|
int32_t InitializeWpadLibrary()
|
|
{
|
|
g_state.contract.Initialize();
|
|
return kStatusOk;
|
|
}
|
|
|
|
// The async WPAD entry points all report their outcome to the guest callback and
|
|
// then return the same value.
|
|
int32_t CompleteWpadRequest(uint32_t chan, uint32_t callback, int32_t result)
|
|
{
|
|
InvokeWpadCallback(callback, chan, result);
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" int32_t WPADGetStatus_HLE()
|
|
{
|
|
// RVL::WPADGetStatus takes no channel arg; it reports global WUD library state.
|
|
// Reading r3 here would leak a caller's stale register value into the result.
|
|
return g_state.contract.GetLibraryStatus();
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF64C, WPADGetStatus_HLE, int32_t, (), ());
|
|
|
|
extern "C" uint32_t WPADGetDpdSensitivity_HLE()
|
|
{
|
|
return static_cast<uint32_t>(g_state.dpdSensitivity);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C329C, WPADGetDpdSensitivity_HLE, uint32_t, (), ());
|
|
|
|
extern "C" int32_t WPADInitSub_HLE()
|
|
{
|
|
if (!g_state.initSubRan) {
|
|
g_state.initSubRan = true;
|
|
return InitializeWpadLibrary();
|
|
}
|
|
return kStatusOk;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF3B4, WPADInitSub_HLE, int32_t, (), ());
|
|
|
|
extern "C" int32_t WPADInit_HLE()
|
|
{
|
|
return InitializeWpadLibrary();
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF5C4, WPADInit_HLE, int32_t, (), ());
|
|
|
|
extern "C" int32_t WUDGetStatus_HLE()
|
|
{
|
|
return WpadContract::kStatusReady;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801CDB84, WUDGetStatus_HLE, int32_t, (), ());
|
|
|
|
// WPADGetDataFormat reads the per-channel format set by WPADSetDataFormat. Can't reuse the
|
|
// translated SDK implementation: it dereferences Bluetooth control blocks that HLE'd WPADInit
|
|
// never constructs.
|
|
extern "C" int32_t WPADGetDataFormat_HLE(uint32_t chan)
|
|
{
|
|
return g_state.contract.GetDataFormat(chan);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0B54, WPADGetDataFormat_HLE, int32_t, (uint32_t chan), (chan));
|
|
|
|
// WPADSetDataFormat: records the per-channel data format the game asked for.
|
|
extern "C" int32_t WPADSetDataFormat_HLE(uint32_t chan, int32_t format)
|
|
{
|
|
return g_state.contract.SetDataFormat(chan, format);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0B9C, WPADSetDataFormat_HLE, int32_t, (uint32_t chan, int32_t format), (chan, format));
|
|
|
|
// WPADProbe: reports the extension type of a Bluetooth remote on `chan`, or no controller.
|
|
extern "C" int32_t WPADProbe_HLE(uint32_t chan, uint32_t typePtr)
|
|
{
|
|
if (chan >= WpadContract::kChannelCount) {
|
|
return WpadContract::kErrorBadChannel;
|
|
}
|
|
|
|
// Drive the rescan state machine here too: a reconnect probe can arrive
|
|
// before the next PADRead, and only Poll() brings a dropped remote back.
|
|
WiiRemoteInput::Poll();
|
|
|
|
// A real Bluetooth remote: WPAD_DEV_CORE (0) for a bare remote,
|
|
// WPAD_DEV_FREESTYLE (1) with a Nunchuk, WPAD_DEV_CLASSIC (2) with a Classic
|
|
// Controller. The game reads the type from here (not from
|
|
// KPADStatus.dev_type) to pick its control scheme, and re-reads it when it
|
|
// changes, which is what makes an extension swap mid-game work like on the
|
|
// console. EffectiveKind keeps the last type through SDL's re-creation of
|
|
// the joystick after a swap.
|
|
const WiiRemoteInput::Kind kind = WiiRemoteInput::EffectiveKind(chan);
|
|
if (WiiRemoteInput::IsRemoteChannel(chan)) {
|
|
if (typePtr != 0) {
|
|
uint32_t type = WpadContract::kExtensionCore;
|
|
if (kind == WiiRemoteInput::Kind::RemoteWithNunchuk) type = 1u;
|
|
if (kind == WiiRemoteInput::Kind::RemoteWithClassic) type = 2u;
|
|
Memory::Write32(typePtr, type);
|
|
}
|
|
return kStatusOk;
|
|
}
|
|
if (typePtr != 0) {
|
|
Memory::Write32(typePtr, WpadContract::kExtensionCore);
|
|
}
|
|
return WpadContract::kErrorNoController;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0990, WPADProbe_HLE, int32_t, (uint32_t chan, uint32_t typePtr), (chan, typePtr));
|
|
|
|
extern "C" void WPADControlMotor_HLE(uint32_t chan, uint32_t command)
|
|
{
|
|
const bool on = command != 0 && PAD_HLE_RumbleEnabled();
|
|
PADControlMotor(chan, on ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP_HARD);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0EC4, WPADControlMotor_HLE, void, (uint32_t chan, uint32_t command), (chan, command));
|
|
|
|
extern "C" int32_t WPADGetInfoAsync_HLE(uint32_t chan, uint32_t infoPtr, uint32_t callback)
|
|
{
|
|
if (chan >= WpadContract::kChannelCount) {
|
|
return CompleteWpadRequest(chan, callback, WpadContract::kErrorBadChannel);
|
|
}
|
|
|
|
(void)infoPtr;
|
|
return CompleteWpadRequest(chan, callback, WpadContract::kErrorNoController);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0CA4, WPADGetInfoAsync_HLE, int32_t,
|
|
(uint32_t chan, uint32_t infoPtr, uint32_t callback), (chan, infoPtr, callback));
|
|
|
|
extern "C" int32_t WPADControlLed_HLE(uint32_t chan, uint32_t ledMask, uint32_t callback)
|
|
{
|
|
if (chan >= WpadContract::kChannelCount) {
|
|
return CompleteWpadRequest(chan, callback, WpadContract::kErrorBadChannel);
|
|
}
|
|
(void)ledMask;
|
|
return CompleteWpadRequest(chan, callback, WpadContract::kErrorNoController);
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801C0FF8, WPADControlLed_HLE, int32_t,
|
|
(uint32_t chan, uint32_t ledMask, uint32_t callback), (chan, ledMask, callback));
|
|
|
|
extern "C" int32_t WPADStartSimpleSync_HLE()
|
|
{
|
|
if (g_state.simpleSyncActive) {
|
|
return 0;
|
|
}
|
|
g_state.simpleSyncActive = true;
|
|
return 1;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF634, WPADStartSimpleSync_HLE, int32_t, (), ()); // WUDStartSyncSimple
|
|
PPC_NATIVE_OVERRIDE(801BF638, WPADStartSimpleSync_HLE, int32_t, (), ()); // WPADStartSimpleSync (HBM)
|
|
|
|
// due to multiplayer controller screen hle this to avoid startsyncdevice to return fail every frame
|
|
// causing you to get softlocked in the game
|
|
extern "C" int32_t WPADStopSimpleSync_HLE()
|
|
{
|
|
if (g_state.simpleSyncActive) {
|
|
g_state.simpleSyncActive = false;
|
|
const uint32_t callback = g_state.syncDeviceCallback;
|
|
if (callback != 0 && TranslatedFunctionRegistry::FindByAddressPtr(callback)) {
|
|
NandQueueIosCallback(callback, 1, 0); // callback(WUD_SYNC_DONE, devicesSynced=0)
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF63C, WPADStopSimpleSync_HLE, int32_t, (), ());
|
|
|
|
extern "C" uint32_t WPADSetSyncDeviceCallback_HLE(uint32_t callback)
|
|
{
|
|
const uint32_t previous = g_state.syncDeviceCallback;
|
|
g_state.syncDeviceCallback = callback;
|
|
return previous;
|
|
}
|
|
PPC_NATIVE_OVERRIDE(801BF640, WPADSetSyncDeviceCallback_HLE, uint32_t, (uint32_t callback), (callback));
|