Files
AnyPS5/core/libs/tests/GuestPad.cpp
T
JJenkxandClaude Opus 5.5 36bffbab1e fix(libScePad): check only the selected triggers' modes in scePadSetTriggerEffect
The trigger mask selects which of the two commands apply, but every mode
was checked against the range, so an unselected command with any value
made the call fail. Quake (PPSA01880) and Quake II (PPSA09477) set only
the right trigger (mask 0x2) and leave the left command uninitialised
(mode 0xb65951b8 in one call), and got SCE_PAD_ERROR_INVALID_ARG; the
right trigger was never switched. Only selected commands are checked now.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 08:19:26 -05:00

1261 lines
65 KiB
C++

#include "prx/libScePad/include/Pad.hpp"
#include "prx/libScePad/include/PadInputTypes.hpp"
#include "prx/libScePad/include/PadState.hpp"
#include "prx/libScePad/include/InputCommandFile.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <algorithm>
#include <vector>
#include <set>
#include <atomic>
#include <array>
#include <chrono>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <string>
#include <thread>
#include <utility>
static void Require(bool value) { if (!value) std::abort(); }
static void CheckInputCommands() {
const auto path = std::filesystem::temp_directory_path() / ("anyps5-pad-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
struct Cleanup {
std::filesystem::path path;
~Cleanup() { std::error_code error; std::filesystem::remove(path, error); }
} cleanup{path};
const auto write = [&](const char* command) { std::ofstream(path) << command << '\n'; };
write("1 16384 200");
Pad::InputCommandFile commands(path);
Require(commands.Buttons(1000) == 0);
write("2 16384 200");
Require(commands.Buttons(101000) == 0x4000);
Require(commands.Buttons(300999) == 0x4000);
Require(commands.Buttons(301000) == 0);
Require(commands.Buttons(401000) == 0);
write("3 8192 5001");
Require(commands.Buttons(501000) == 0);
write("-3 8192 300");
Require(commands.Buttons(601000) == 0);
write("3 1 300");
Require(commands.Buttons(701000) == 0);
write("3 8192 300 trailing");
Require(commands.Buttons(801000) == 0);
write("3 8192 300");
Require(commands.Buttons(901000) == 0x2000);
write("4 0 1");
Require(commands.Buttons(1001000) == 0);
write("2 16384 200");
Require(commands.Buttons(1101000) == 0);
write("5 0 300 0 128 255 64");
const auto tilted = commands.Poll(1201000);
Require(tilted.buttons == 0 && tilted.hasSticks);
Require(tilted.sticks[0] == 0 && tilted.sticks[1] == 128 && tilted.sticks[2] == 255 && tilted.sticks[3] == 64);
Require(!commands.Poll(1501000).hasSticks);
write("6 16384 300");
const auto pressed = commands.Poll(1601000);
Require(pressed.buttons == 0x4000 && !pressed.hasSticks);
const Pad::InputCommandFile::Input none;
write("7 16384 300 128 128");
Require(commands.Poll(1901000) == none);
write("7 16384 300 128 128 128 256");
Require(commands.Poll(2001000) == none);
write("7 16384 300 128 128 128 128 128");
Require(commands.Poll(2101000) == none);
write("7 16384 300 128 128 128 up");
Require(commands.Poll(2201000) == none);
write("7 16384 300 +");
Require(commands.Poll(2301000) == none);
write("7 16384 300 4294967296");
Require(commands.Poll(2401000) == none);
write("7 16384 300 128 128 128 128x");
Require(commands.Poll(2501000) == none);
write("7 8192 300 1 2 3 4");
const auto accepted = commands.Poll(2601000);
Require(accepted.buttons == 0x2000 && accepted.hasSticks && accepted.sticks[3] == 4);
write("8 2097152 300");
const auto left = commands.Poll(3001000);
Require(left.buttons == 0 && left.touchLeft && !left.touchRight && !left.hasSticks);
write("9 4210688 300");
const auto right = commands.Poll(3401000);
Require(right.buttons == 0x4000 && !right.touchLeft && right.touchRight);
write("10 8388608 300");
Require(commands.Poll(3801000) == none);
write("10 0 5000 touch(1919,942,127,1)");
const auto contact = commands.Poll(3901000);
Require(contact.hasTouch && contact.touchActive && contact.touchX == 1919 && contact.touchY == 942 && contact.touchId == 127);
Require(contact.buttons == 0 && !contact.touchLeft && !contact.touchRight);
Require(commands.Poll(4001000) == contact);
write("11 0 5000 1 2 3 4 touch(0,0,127,1)");
const auto moved = commands.Poll(4101000);
Require(moved.hasTouch && moved.touchActive && moved.touchX == 0 && moved.touchY == 0 && moved.touchId == contact.touchId);
Require(moved.hasSticks && moved.sticks[3] == 4);
write("12 0 5000 touch(0,0,127,0)");
const auto lifted = commands.Poll(4201000);
Require(lifted.hasTouch && !lifted.touchActive && lifted.touchId == 127 && lifted.buttons == 0);
std::uint64_t poll = 4301000;
for (const char* invalid : {"touch(1920,0,0,1)", "touch(0,943,0,1)", "touch(0,0,128,1)",
"touch(0,0,0,2)", "touch(-1,0,0,1)", "touch(0,0,0)", "touch(0,0,0,1,1)",
"touch(0,,0,1)", "touch(0,0,0,1)x", "touch(0,0,0,1) trailing"}) {
std::ofstream(path) << "13 0 5000 " << invalid << '\n';
Require(commands.Poll(poll) == lifted);
poll += 100000;
}
Require(commands.Poll(9201000) == none);
std::filesystem::remove(path);
Require(commands.Buttons(9301000) == 0);
Pad::InputCommandFile disabled(std::filesystem::path{});
Require(disabled.Buttons(0) == 0);
}
static float SettleOrientationW(int port = 0) {
float w = 1.0f;
for (int i = 0; i < 10; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
w = Pad::ReadState(port).orientation_w;
}
return w;
}
static void CheckTiltCorrection(int handle) {
PadInputState tilted;
tilted.hasMotion = true;
tilted.accel = {9.80665f, 0.0f, 0.0f};
PadPublishInput_nid_postfix(tilted);
Require(scePadSetTiltCorrectionState(handle + 1, false) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetTiltCorrectionState(handle, false) == PAD_OK);
Require(scePadResetOrientation(handle) == PAD_OK);
Require(SettleOrientationW() == 1.0f);
Require(scePadSetTiltCorrectionState(handle, true) == PAD_OK);
Require(SettleOrientationW() < 0.999f);
PadPublishInput_nid_postfix(PadInputState{});
}
static void CheckAngularVelocityDeadband(int handle) {
PadInputState moving;
moving.hasMotion = true;
moving.accel = {0.0f, 9.80665f, 0.0f};
moving.gyro = {0.01f, 0.0175f, -0.017f};
PadPublishInput_nid_postfix(moving);
PadData data{};
Require(scePadReadState(handle, &data) == PAD_OK);
Require(data.angular_velocity_x == 0.01f && data.angular_velocity_y == 0.0175f && data.angular_velocity_z == -0.017f);
Require(scePadSetAngularVelocityDeadbandState(handle, true) == PAD_OK);
Require(scePadReadState(handle, &data) == PAD_OK);
Require(data.angular_velocity_x == 0.0f && data.angular_velocity_y == 0.0175f && data.angular_velocity_z == 0.0f);
Require(scePadSetAngularVelocityDeadbandState(handle, false) == PAD_OK);
Require(scePadReadState(handle, &data) == PAD_OK);
Require(data.angular_velocity_x == 0.01f && data.angular_velocity_z == -0.017f);
PadPublishInput_nid_postfix(PadInputState{});
}
static void CheckStickDeadZones(int handle) {
PadControllerInformation info{};
Require(scePadGetControllerInformation(handle, &info) == PAD_OK);
Require(info.stickInfo.deadZoneLeft == 13 && info.stickInfo.deadZoneRight == 13);
}
static void CheckTouchContact(int handle) {
Require(scePadReadState(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadRead_nid_postfix(handle, nullptr, 1) == PAD_ERROR_INVALID_ARG);
for (const int invalid : {-1, 0, handle + 1, 9999}) {
Require(scePadReadState(invalid, nullptr) == PAD_ERROR_INVALID_HANDLE);
Require(scePadRead_nid_postfix(invalid, nullptr, 1) == PAD_ERROR_INVALID_HANDLE);
}
PadInputState touch;
touch.buttons = static_cast<std::uint32_t>(Pad::PadButton::TouchPad);
touch.touch[0] = {true, 960, 471, 0};
PadPublishInput_nid_postfix(touch);
PadData stateData{};
Require(scePadReadState(handle, &stateData) == PAD_OK);
Require((stateData.buttons & static_cast<std::uint32_t>(Pad::PadButton::TouchPad)) != 0);
Require(stateData.touch_data_touch_num == 1);
Require(stateData.touch_data_touch0_x == 960);
Require(stateData.touch_data_touch0_y == 471);
const auto data = Pad::ReadState();
Require((data.buttons & static_cast<std::uint32_t>(Pad::PadButton::TouchPad)) != 0);
Require(data.touch_data_touch_num == 1);
Require(data.touch_data_touch0_x == 960);
Require(data.touch_data_touch0_y == 471);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
}
static void CheckTouchPadInformation(int handle) {
PadControllerInformation info;
std::memset(&info, 0x5a, sizeof(info));
Require(scePadGetControllerInformation(handle, &info) == PAD_OK);
Require(info.touchPadInfo.resolution.x == kTouchPadWidth);
Require(info.touchPadInfo.resolution.y == kTouchPadHeight);
Require(info.touchPadInfo.pixelDensity == 44.86f);
Require(scePadGetControllerInformation(handle + 1, &info) == PAD_ERROR_INVALID_HANDLE);
}
static void CheckExtControllerInformation(int handle) {
constexpr int notPermitted = static_cast<int>(0x80920101);
std::array<std::uint8_t, 256> extended{};
Require(scePadGetExtControllerInformation(handle, extended.data()) == notPermitted);
Require(scePadGetExtControllerInformation(handle + 1, extended.data()) == PAD_ERROR_INVALID_HANDLE);
}
static bool LightBarValid() {
std::uint32_t seen = 0;
PadOutputState state;
Require(PadFetchOutput_nid_postfix(&seen, &state));
return state.lightBarValid;
}
static void CheckHandleQueries(int user, int handle) {
constexpr int notConnected = static_cast<int>(0x80920007);
const int closed = handle + 1;
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special > 0);
Require(scePadIsValidHandle(handle, PAD_PORT_TYPE_STANDARD));
Require(!scePadIsValidHandle(handle, PAD_PORT_TYPE_SPECIAL));
Require(scePadIsValidHandle(special, PAD_PORT_TYPE_SPECIAL));
Require(!scePadIsValidHandle(special, PAD_PORT_TYPE_STANDARD));
Require(!scePadIsValidHandle(closed, PAD_PORT_TYPE_STANDARD));
Require(!scePadIsValidHandle(0, PAD_PORT_TYPE_STANDARD));
float value = 1.0f;
Require(scePadGetMotionTimerUnit(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetMotionTimerUnit(closed, &value) == PAD_ERROR_INVALID_HANDLE && value == 0.0f);
value = 1.0f;
Require(scePadGetMotionTimerUnit(handle, &value) == notConnected && value == 0.0f);
value = 1.0f;
Require(scePadGetMotionTimerUnit(special, &value) == notConnected && value == 0.0f);
PadMotionSensorPosition position{1.0f, 2.0f, 3.0f};
Require(scePadGetMotionSensorPosition(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetMotionSensorPosition(closed, &position) == PAD_ERROR_INVALID_HANDLE);
for (const int invalid : {-1, 0, 9999}) {
Require(scePadGetMotionSensorPosition(invalid, &position) == PAD_ERROR_INVALID_HANDLE);
Require(scePadGetMotionSensorPosition(invalid, nullptr) == PAD_ERROR_INVALID_ARG);
}
Require(scePadGetMotionSensorPosition(handle, &position) == notConnected);
Require(position.x == 1.0f && position.y == 2.0f && position.z == 3.0f);
float radius = 1.0f;
Require(scePadGetSphereRadius(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetSphereRadius(handle, &radius) == PAD_ERROR_INVALID_HANDLE);
Require(scePadGetSphereRadius(special, &radius) == notConnected && radius == 1.0f);
std::uint64_t address = 7;
Require(scePadGetBluetoothAddress(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetBluetoothAddress(closed, &address) == PAD_ERROR_INVALID_HANDLE);
Require(scePadGetBluetoothAddress(handle, &address) == notConnected && address == 7);
std::array<std::uint8_t, 0x80> leonardo{};
std::array<std::uint8_t, 0x20> unique;
unique.fill(0xaa);
leonardo[0x5c] = 0x13;
Require(scePadParseUniqueDataLeonardo(handle, nullptr, reinterpret_cast<PadLeonardoUniqueData*>(unique.data())) == PAD_ERROR_INVALID_ARG);
Require(scePadParseUniqueDataLeonardo(handle, reinterpret_cast<const PadLeonardoData*>(leonardo.data()), nullptr) == PAD_ERROR_INVALID_ARG);
Require(unique[0] == 0xaa);
Require(scePadParseUniqueDataLeonardo(handle, reinterpret_cast<const PadLeonardoData*>(leonardo.data()), reinterpret_cast<PadLeonardoUniqueData*>(unique.data())) == PAD_ERROR_INVALID_HANDLE);
for (std::size_t i = 0; i < unique.size(); ++i) Require(unique[i] == (i < 0x18 ? 0 : 0xaa));
const PadLightBarParam color{10, 20, 30};
Require(scePadSetLightBar(handle, &color) == PAD_OK && LightBarValid());
Require(scePadResetLightBarAllByPortType(3) == PAD_ERROR_INVALID_ARG);
Require(scePadResetLightBarAllByPortType(PAD_PORT_TYPE_SPECIAL) == PAD_OK && LightBarValid());
Require(scePadResetLightBarAllByPortType(PAD_PORT_TYPE_STANDARD) == PAD_OK && !LightBarValid());
Require(scePadSetLightBar(handle, &color) == PAD_OK && LightBarValid());
Require(scePadResetLightBarAll() == PAD_OK && !LightBarValid());
Require(scePadClose_nid_postfix(special) == PAD_OK);
Require(!scePadIsValidHandle(special, PAD_PORT_TYPE_SPECIAL));
}
static void CheckVirtualTouchContact(int handle) {
PadInputState left;
left.touchLeft = true;
PadPublishInput_nid_postfix(left);
PadData leftState{};
Require(scePadReadState(handle, &leftState) == PAD_OK);
Require(leftState.touch_data_touch_num == 1);
Require(leftState.touch_data_touch0_x == kTouchPadWidth / 4);
Require(leftState.touch_data_touch0_y == kTouchPadHeight / 2);
const auto leftData = Pad::ReadState();
Require(leftData.touch_data_touch_num == 1);
Require(leftData.touch_data_touch0_x == kTouchPadWidth / 4);
Require(leftData.touch_data_touch0_y == kTouchPadHeight / 2);
PadInputState right;
right.touchRight = true;
PadPublishInput_nid_postfix(right);
PadData rightState{};
Require(scePadReadState(handle, &rightState) == PAD_OK);
Require(rightState.touch_data_touch_num == 1);
Require(rightState.touch_data_touch0_x == kTouchPadWidth * 3 / 4);
Require(rightState.touch_data_touch0_y == kTouchPadHeight / 2);
const auto rightData = Pad::ReadState();
Require(rightData.touch_data_touch_num == 1);
Require(rightData.touch_data_touch0_x == kTouchPadWidth * 3 / 4);
Require(rightData.touch_data_touch0_y == kTouchPadHeight / 2);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
}
static void CheckPartialTrigger() {
PadInputState partial;
partial.buttons = static_cast<std::uint32_t>(Pad::PadButton::L2) | static_cast<std::uint32_t>(Pad::PadButton::R2);
partial.analogButtonsL2 = 1;
partial.analogButtonsR2 = 73;
PadPublishInput_nid_postfix(partial);
const auto data = Pad::ReadState();
Require(data.buttons == partial.buttons);
Require(data.analog_buttons_l2 == 1 && data.analog_buttons_r2 == 73);
PadInputState digital;
digital.buttons = static_cast<std::uint32_t>(Pad::PadButton::L2);
PadPublishInput_nid_postfix(digital);
Require(Pad::ReadState().analog_buttons_l2 == 255);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
}
static void CheckReadQueue(int handle) {
PadData drained[64]{};
scePadRead_nid_postfix(handle, drained, 64);
PadInputState pressed;
pressed.buttons = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
PadData data[4]{};
Require(scePadRead_nid_postfix(handle, data, 4) == 2);
Require((data[0].buttons & pressed.buttons) != 0);
Require((data[1].buttons & pressed.buttons) == 0);
Require(data[0].timestamp <= data[1].timestamp);
Require(scePadRead_nid_postfix(handle, data, 4) == 1);
Require(data[0].buttons == 0);
}
static void CheckTapBetweenReads() {
const auto cross = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
PadInputState pressed;
pressed.buttons = cross;
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
Require((Pad::ReadState().buttons & cross) != 0);
Require((Pad::ReadState().buttons & cross) == 0);
PadPublishInput_nid_postfix(pressed);
Require((Pad::ReadState().buttons & cross) != 0);
Require((Pad::ReadState().buttons & cross) != 0);
PadPublishInput_nid_postfix(PadInputState{});
Require((Pad::ReadState().buttons & cross) == 0);
}
static void CheckCaptureHidesEarlierInput() {
constexpr std::uint32_t intercepted = 0x80000000u;
PadInputState pressed;
pressed.buttons = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadInputState captured;
captured.intercepted = true;
PadData drained[64]{};
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
Pad::Read(drained, 64, 0);
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(captured);
Require(Pad::ReadState().buttons == intercepted);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
Pad::Read(drained, 64, 0);
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(captured);
PadData data[64]{};
const int count = Pad::Read(data, 64, 0);
Require(count >= 1);
for (int index = 0; index < count; ++index) Require(data[index].buttons == intercepted);
Require(Pad::ReadState().buttons == intercepted);
PadPublishInput_nid_postfix(PadInputState{});
Pad::ReadState();
Pad::Read(drained, 64, 0);
}
static void CheckLatchedPressLifetime(int handle) {
const auto cross = static_cast<std::uint32_t>(Pad::PadButton::Cross);
const auto circle = static_cast<std::uint32_t>(Pad::PadButton::Circle);
PadInputState pressed;
PadData data{};
PadData drained[64]{};
scePadRead_nid_postfix(handle, drained, 64);
pressed.buttons = cross;
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
Require(scePadRead_nid_postfix(handle, drained, 64) >= 1);
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & cross) == 0);
pressed.buttons = cross;
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
std::this_thread::sleep_for(std::chrono::milliseconds(150));
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & cross) == 0);
Require(scePadReadState(handle, &data) == PAD_OK);
pressed.buttons = cross;
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
std::this_thread::sleep_for(std::chrono::milliseconds(150));
pressed.buttons = circle;
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & cross) == 0);
Require((data.buttons & circle) != 0);
}
static void CheckLatchedPressAtSlowReads(int handle) {
const auto cross = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadInputState pressed;
pressed.buttons = cross;
PadData data{};
Require(scePadReadState(handle, &data) == PAD_OK);
std::this_thread::sleep_for(std::chrono::milliseconds(170));
Require(scePadReadState(handle, &data) == PAD_OK);
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
std::this_thread::sleep_for(std::chrono::milliseconds(150));
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & cross) != 0);
PadPublishInput_nid_postfix(pressed);
PadPublishInput_nid_postfix(PadInputState{});
std::this_thread::sleep_for(std::chrono::milliseconds(400));
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & cross) == 0);
}
static void CheckLatchedPressPerPort() {
const auto cross = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadInputState pressed;
pressed.buttons = cross;
Pad::ReadState(0);
Pad::ReadState(1);
std::this_thread::sleep_for(std::chrono::milliseconds(170));
Pad::ReadState(0);
Pad::ReadState(1);
PadPublishInputForPort_nid_postfix(0, pressed);
PadPublishInputForPort_nid_postfix(0, PadInputState{});
std::this_thread::sleep_for(std::chrono::milliseconds(150));
Require((Pad::ReadState(0).buttons & cross) != 0);
}
static void CheckReadStateHandle(int handle) {
PadData data{};
Require(scePadReadState(0, &data) == PAD_ERROR_INVALID_HANDLE);
Require(scePadReadState(handle + 1, &data) == PAD_ERROR_INVALID_HANDLE);
Require(scePadRead_nid_postfix(0, &data, 1) == PAD_ERROR_INVALID_HANDLE);
Require(scePadReadState(handle, &data) == PAD_OK);
Require(scePadRead_nid_postfix(handle, nullptr, 1) == PAD_ERROR_INVALID_ARG);
Require(scePadRead_nid_postfix(handle, &data, 0) == PAD_ERROR_INVALID_ARG);
Require(scePadRead_nid_postfix(handle, &data, -1) == PAD_ERROR_INVALID_ARG);
Require(scePadReadState(handle, nullptr) == PAD_ERROR_INVALID_ARG);
}
static void CheckSpecialPort(int user, int handle) {
constexpr int noHandle = static_cast<int>(0x80920008);
constexpr int alreadyOpened = static_cast<int>(0x80920004);
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special > 0 && special != handle);
Require(scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == alreadyOpened);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == special);
Require(scePadGetHandle(user, 0, 0) == handle);
PadData data[2];
std::memset(data, 0xff, sizeof(data));
Require(scePadRead_nid_postfix(special, nullptr, 1) == PAD_ERROR_INVALID_ARG);
Require(scePadRead_nid_postfix(special, data, 0) == PAD_ERROR_INVALID_ARG);
Require(scePadRead_nid_postfix(special, data, 2) == 1);
Require(!data[0].connected && data[0].connected_count == 0 && data[0].buttons == 0 && data[0].timestamp == 0);
Require(data[0].left_stick_x == 0x80 && data[0].left_stick_y == 0x80 && data[0].right_stick_x == 0x80 && data[0].right_stick_y == 0x80);
Require(data[0].analog_buttons_l2 == 0 && data[0].orientation_x == 0.0f && data[0].orientation_w == 1.0f);
Require(data[0].acceleration_x == 0.0f && data[0].acceleration_y == 1.0f && data[0].acceleration_z == 0.0f && data[0].touch_data_touch_num == 0);
Require(data[1].buttons == 0xffffffff);
std::memset(data, 0xff, sizeof(data[0]));
Require(scePadReadState(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadReadState(special, data) == PAD_OK);
Require(!data[0].connected && data[0].connected_count == 0 && data[0].buttons == 0);
Require(data[0].left_stick_x == 0x80 && data[0].orientation_w == 1.0f && data[0].acceleration_y == 1.0f);
PadControllerInformation info;
std::memset(&info, 0xff, sizeof(info));
Require(scePadGetControllerInformation(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetControllerInformation(special, &info) == PAD_OK);
Require(!info.connected && info.connectedCount == 0 && info.deviceClass == 0);
PadDeviceClassData classData;
std::memset(&classData, 0xff, sizeof(classData));
Require(scePadDeviceClassParseData(special, data, &classData) == PAD_OK);
Require(!classData.dataValid && classData.deviceClass == 0);
PadDeviceClassExtendedInformation extended;
std::memset(&extended, 0xff, sizeof(extended));
Require(scePadDeviceClassGetExtendedInformation(special, &extended) == PAD_OK);
Require(extended.deviceClass == -1 && extended.reserved[0] == 0);
Require(scePadDeviceClassGetExtendedInformation(handle, &extended) == PAD_OK);
Require(extended.deviceClass == 0);
std::int32_t specialType = -1;
Require(scePadGetControllerType(special, &specialType) == PAD_OK);
Require(specialType == PAD_CONTROLLER_TYPE_UNKNOWN);
const PadVibrationParam vibration{0, 0};
const PadLightBarParam lightBar{0, 0, 255};
Require(scePadSetVibration(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadSetVibration(special, &vibration) == PAD_OK);
Require(scePadSetLightBar(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadSetLightBar(special, &lightBar) == PAD_OK);
Require(scePadResetLightBar(special) == PAD_OK);
Require(scePadSetMotionSensorState(special, true) == PAD_OK);
Require(scePadSetAngularVelocityDeadbandState(special, true) == PAD_OK);
Require(scePadSetTiltCorrectionState(special, true) == PAD_OK);
Require(scePadResetOrientation(special) == PAD_OK);
Require(scePadReadState(handle, data) == PAD_OK);
Require(data[0].connected);
Require(scePadGetControllerInformation(handle, &info) == PAD_OK);
Require(info.connected && info.connectedCount == 1);
Require(scePadClose_nid_postfix(special) == PAD_OK);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
Require(scePadClose_nid_postfix(special) == PAD_ERROR_INVALID_HANDLE);
Require(scePadRead_nid_postfix(special, data, 1) == PAD_ERROR_INVALID_HANDLE);
Require(scePadReadState(special, data) == PAD_ERROR_INVALID_HANDLE);
Require(scePadGetControllerInformation(special, &info) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(special, &vibration) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(special, nullptr) == PAD_ERROR_INVALID_HANDLE);
for (const int invalidHandle : {-1, -2, -100}) {
Require(scePadSetVibration(invalidHandle, &vibration) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(invalidHandle, nullptr) == PAD_ERROR_INVALID_HANDLE);
}
Require(scePadGetHandle(user, 0, 0) == handle);
Require(scePadReadState(handle, data) == PAD_OK);
const int other = user + 1;
const int first = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
const int second = scePadOpen_nid_postfix(other, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(first > 0 && second > 0 && first != second && second != handle);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == first);
Require(scePadGetHandle(other, PAD_PORT_TYPE_SPECIAL, 0) == second);
Require(scePadOpen_nid_postfix(other, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == alreadyOpened);
Require(scePadClose_nid_postfix(first) == PAD_OK);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
Require(scePadGetHandle(other, PAD_PORT_TYPE_SPECIAL, 0) == second);
Require(scePadRead_nid_postfix(first, data, 1) == PAD_ERROR_INVALID_HANDLE);
Require(scePadRead_nid_postfix(second, data, 1) == 1);
Require(!data[0].connected);
const int again = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(again > 0 && again != second);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == again);
Require(scePadClose_nid_postfix(again) == PAD_OK);
Require(scePadClose_nid_postfix(second) == PAD_OK);
Require(scePadGetHandle(other, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
std::array<int, 16> handles{};
for (int i = 0; i < 16; ++i) {
handles[i] = scePadOpen_nid_postfix(user + i, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(handles[i] > 0 && handles[i] != handle && (i == 0 || handles[i] != handles[i - 1]));
}
Require(scePadOpen_nid_postfix(user + 16, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == static_cast<int>(0x809b0081));
Require(scePadGetHandle(user + 16, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
Require(scePadGetHandle(user + 15, PAD_PORT_TYPE_SPECIAL, 0) == handles[15]);
for (int i = 0; i < 16; ++i) Require(scePadClose_nid_postfix(handles[i]) == PAD_OK);
Require(scePadGetHandle(user + 15, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
Require(scePadGetHandle(user, 0, 0) == handle);
}
static void CheckRemoteController(int handle) {
bool remote = true;
Require(scePadIsRemoteController(handle + 1, &remote) == PAD_ERROR_INVALID_HANDLE);
Require(scePadIsRemoteController(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(remote);
Require(scePadIsRemoteController(handle, &remote) == PAD_OK);
Require(!remote);
}
static void CheckTrackerDevices(int handle) {
for (const int candidate : {handle, handle + 1}) {
Require(!scePadIsDS4Connected(candidate));
Require(!scePadIsBlasterConnected(candidate));
Require(!scePadIsMoveConnected(candidate));
}
}
static void CheckSystemRemote(int handle) {
constexpr int noHandle = static_cast<int>(0x80920008);
Require(scePadGetHandle(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 0) == noHandle);
const int remote = scePadOpen_nid_postfix(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 0, nullptr);
Require(remote > 0 && remote != handle);
Require(scePadGetHandle(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 0) == remote);
Require(scePadGetHandle(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 1) == noHandle);
PadData drained[64]{};
scePadRead_nid_postfix(handle, drained, 64);
PadInputState pressed;
pressed.buttons = static_cast<std::uint32_t>(Pad::PadButton::Cross);
PadPublishInput_nid_postfix(pressed);
PadData data{};
data.buttons = ~0u;
data.connected = true;
Require(scePadReadState(remote, &data) == PAD_OK);
Require(data.buttons == 0 && !data.connected);
Require(scePadReadState(remote, nullptr) == PAD_ERROR_INVALID_ARG);
PadData queued[4]{};
queued[0].connected = true;
Require(scePadRead_nid_postfix(remote, queued, 4) == 1);
Require(queued[0].buttons == 0 && !queued[0].connected);
PadLightBarParam lightBar{1, 2, 3};
Require(scePadSetLightBar(remote, &lightBar) == PAD_ERROR_INVALID_HANDLE);
Require(scePadReadState(handle, &data) == PAD_OK);
Require((data.buttons & pressed.buttons) != 0 && data.connected);
PadPublishInput_nid_postfix(PadInputState{});
scePadRead_nid_postfix(handle, drained, 64);
Require(scePadClose_nid_postfix(remote) == PAD_OK);
Require(scePadGetHandle(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 0) == noHandle);
Require(scePadReadState(remote, &data) == PAD_ERROR_INVALID_HANDLE);
Require(scePadReadState(handle, &data) == PAD_OK);
}
static void CheckIsValidHandle(int user, int handle) {
Require(scePadIsValidHandle(handle, PAD_PORT_TYPE_STANDARD));
for (const int type : {PAD_PORT_TYPE_SPECIAL, PAD_PORT_TYPE_REMOTE, 1, 3, 4, 5, -1}) Require(!scePadIsValidHandle(handle, type));
for (const int other : {handle + 1, PAD_HANDLE_BASE + PAD_MAX_CONTROLLERS, 0, -1, PAD_SPECIAL_HANDLE}) Require(!scePadIsValidHandle(other, PAD_PORT_TYPE_STANDARD));
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special >= PAD_SPECIAL_HANDLE);
Require(scePadIsValidHandle(special, PAD_PORT_TYPE_SPECIAL) && !scePadIsValidHandle(special, PAD_PORT_TYPE_STANDARD));
Require(!scePadIsValidHandle(special + 1, PAD_PORT_TYPE_SPECIAL));
Require(scePadClose_nid_postfix(special) == PAD_OK);
Require(!scePadIsValidHandle(special, PAD_PORT_TYPE_SPECIAL));
Require(!scePadIsValidHandle(PAD_REMOTE_HANDLE, PAD_PORT_TYPE_REMOTE));
const int remote = scePadOpen_nid_postfix(PAD_USER_ID_SYSTEM, PAD_PORT_TYPE_REMOTE, 0, nullptr);
Require(remote == PAD_REMOTE_HANDLE);
Require(scePadIsValidHandle(remote, PAD_PORT_TYPE_REMOTE) && !scePadIsValidHandle(remote, PAD_PORT_TYPE_STANDARD));
Require(scePadClose_nid_postfix(remote) == PAD_OK);
Require(!scePadIsValidHandle(remote, PAD_PORT_TYPE_REMOTE));
}
static PadOutputState OutputOf(int port) {
std::uint32_t seen = 0xffffffffu;
PadOutputState output;
Require(PadFetchOutputForPort_nid_postfix(port, &seen, &output));
return output;
}
static bool LightBarIs(int port, bool valid, std::uint8_t r, std::uint8_t g, std::uint8_t b) {
const PadOutputState output = OutputOf(port);
return output.lightBarValid == valid && output.lightBar[0] == r && output.lightBar[1] == g && output.lightBar[2] == b;
}
static void CheckLightBarResets(int user, int handle) {
const PadLightBarParam color{10, 20, 30};
const auto setBoth = [&] {
Require(scePadSetLightBar(handle, &color) == PAD_OK);
Pad::SetLightBar(2, true, 40, 50, 60);
Require(LightBarIs(0, true, 10, 20, 30) && LightBarIs(2, true, 40, 50, 60));
};
const auto openPortReset = [] { return LightBarIs(0, false, 0, 0, 0) && LightBarIs(2, true, 40, 50, 60); };
setBoth();
Require(scePadResetLightBarAll() == PAD_OK && openPortReset());
Require(scePadResetLightBarAll() == PAD_OK && openPortReset());
setBoth();
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special >= PAD_SPECIAL_HANDLE);
for (const std::uint32_t type : {1u, 2u}) Require(scePadResetLightBarAllByPortType(type) == PAD_OK);
Require(scePadClose_nid_postfix(special) == PAD_OK);
for (const std::uint32_t type : {3u, 4u, 5u, 6u, 16u, 0xffu, 0xffffffffu}) Require(scePadResetLightBarAllByPortType(type) == PAD_ERROR_INVALID_ARG);
Require(LightBarIs(0, true, 10, 20, 30) && LightBarIs(2, true, 40, 50, 60));
Require(scePadResetLightBarAllByPortType(PAD_PORT_TYPE_STANDARD) == PAD_OK && openPortReset());
Pad::SetLightBar(2, false, 0, 0, 0);
}
static bool VibrationIs(int port, std::uint8_t large, std::uint8_t small) {
const PadOutputState output = OutputOf(port);
return output.vibrationLarge == large && output.vibrationSmall == small;
}
static void CheckVibrationForce(int user, int handle) {
const PadVibrationParam strong{70, 80};
const PadVibrationParam weak{5, 6};
Require(scePadSetVibrationForce(handle, &strong) == PAD_OK && VibrationIs(0, 70, 80));
Require(scePadSetVibration(handle, &weak) == PAD_OK && VibrationIs(0, 5, 6));
Require(scePadSetVibrationForce(handle, &weak) == scePadSetVibration(handle, &weak) && VibrationIs(0, 5, 6));
Require(scePadSetVibrationForce(handle, nullptr) == PAD_ERROR_INVALID_ARG && VibrationIs(0, 5, 6));
for (const int other : {handle + 1, 0x999, -1}) Require(scePadSetVibrationForce(other, nullptr) == PAD_ERROR_INVALID_ARG);
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special >= PAD_SPECIAL_HANDLE);
Require(scePadSetVibrationForce(special, &strong) == PAD_OK && VibrationIs(0, 5, 6));
Require(scePadSetVibrationForce(special, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadClose_nid_postfix(special) == PAD_OK);
Require(scePadSetVibration(special, &strong) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(special, nullptr) == PAD_ERROR_INVALID_HANDLE);
const PadVibrationParam off{0, 0};
Require(scePadSetVibrationForce(handle, &off) == PAD_OK && VibrationIs(0, 0, 0));
}
static bool TriggerIs(int trigger, bool valid, std::uint32_t mode, const std::array<std::uint8_t, 11>& effect, std::uint8_t fallback) {
const PadOutputState output = OutputOf(0);
const PadTriggerRequest& request = output.trigger[trigger];
return request.valid == valid && request.mode == mode && std::memcmp(request.effect, effect.data(), effect.size()) == 0 && request.fallback == fallback;
}
static void CheckTriggerEffect(int user, int handle) {
std::array<std::uint8_t, 120> param{};
const auto command = [&](int trigger, std::uint32_t mode, const std::array<std::uint8_t, 3>& data) {
std::memcpy(param.data() + 8 + 56 * trigger, &mode, sizeof(mode));
std::copy(data.begin(), data.end(), param.begin() + 16 + 56 * trigger);
};
const std::array<std::uint8_t, 11> weapon{0x25, 0x24, 0x00, 0x07};
const std::array<std::uint8_t, 11> feedback{0x21, 0x80, 0x03, 0x00, 0x00, 0x40, 0x12};
const std::array<std::uint8_t, 11> off{0x05};
Require(scePadSetTriggerEffect(handle, nullptr) == PAD_ERROR_INVALID_ARG);
for (const int other : {handle + 1, 0x999, -1}) Require(scePadSetTriggerEffect(other, param.data()) == PAD_ERROR_INVALID_HANDLE);
for (const int other : {handle + 1, 0x999, -1}) Require(scePadSetTriggerEffect(other, nullptr) == PAD_ERROR_INVALID_HANDLE);
param[0] = 0x4;
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_ERROR_INVALID_ARG);
param[0] = 0x1;
command(0, 7, {0, 0, 0});
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_ERROR_INVALID_ARG);
command(0, 0, {0, 0, 0});
param[0] = 0x2;
command(1, 7, {0, 0, 0});
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_ERROR_INVALID_ARG);
command(1, 0, {0, 0, 0});
param[0] = 0;
command(0, 0xb65951b8u, {0, 0, 0});
command(1, 0xffffffffu, {0, 0, 0});
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_OK && TriggerIs(0, false, 0, {}, 0) && TriggerIs(1, false, 0, {}, 0));
command(1, 0, {0, 0, 0});
param[0] = 0x2;
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_OK && TriggerIs(0, false, 0, {}, 0) && TriggerIs(1, false, 0, off, 0));
command(0, 0, {0, 0, 0});
command(0, 2, {2, 5, 8});
command(1, 1, {7, 3, 0});
param[0] = 0x1;
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_OK && TriggerIs(0, true, 2, weapon, 255) && TriggerIs(1, false, 0, off, 0));
command(0, 0, {0, 0, 0});
param[0] = 0x2;
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_OK && TriggerIs(0, true, 2, weapon, 255) && TriggerIs(1, true, 1, feedback, 96));
command(1, 0, {0, 0, 0});
param[0] = 0x3;
Require(scePadSetTriggerEffect(handle, param.data()) == PAD_OK && TriggerIs(0, false, 0, off, 0) && TriggerIs(1, false, 0, off, 0));
const int special = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(special >= PAD_SPECIAL_HANDLE);
command(0, 2, {2, 5, 8});
Require(scePadSetTriggerEffect(special, param.data()) == PAD_OK && TriggerIs(0, false, 0, off, 0));
Require(scePadClose_nid_postfix(special) == PAD_OK);
}
static void CheckAccessibilityControllerState(int handle) {
std::uint32_t state = 7;
Require(scePadGetAccessibilityControllerState(handle + 1, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetAccessibilityControllerState(handle + 1, &state) == PAD_ERROR_INVALID_HANDLE);
Require(state == 7);
Require(scePadGetAccessibilityControllerState(handle, &state) == PAD_OK);
Require(state == 0);
}
static void CheckControllerType(int handle) {
std::int32_t type = -1;
Require(scePadGetControllerType(handle + 1, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetControllerType(handle + 1, &type) == PAD_ERROR_INVALID_HANDLE);
Require(type == -1);
Require(scePadGetControllerType(handle, &type) == PAD_OK);
Require(type == PAD_CONTROLLER_TYPE_DUAL_SENSE);
}
static void CheckInjectedState(const std::filesystem::path& path) {
PadInputState physical;
physical.buttons = 0x2000;
physical.analogButtonsR2 = 73;
physical.sticks[0] = 42;
PadPublishInput_nid_postfix(physical);
const auto before = Pad::ReadState();
std::ofstream(path) << "1 16640 300\n";
PadData pressed{};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
pressed = Pad::ReadState();
if ((pressed.buttons & 0x4000) != 0) break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (std::chrono::steady_clock::now() < deadline);
Require(pressed.buttons == (0x2000 | 0x4000 | 0x100));
Require(pressed.analog_buttons_l2 == 255 && pressed.analog_buttons_r2 == 73);
Require(pressed.left_stick_x == 42 && pressed.timestamp > before.timestamp);
std::this_thread::sleep_for(std::chrono::milliseconds(310));
const auto released = Pad::ReadState();
Require(released.buttons == 0x2000 && released.analog_buttons_l2 == 0);
Require(released.timestamp > pressed.timestamp);
physical.sticks[3] = 17;
PadPublishInput_nid_postfix(physical);
const auto still = Pad::ReadState();
std::ofstream(path) << "2 0 300 200 0 128 128\n";
PadData moved{};
const auto stickDeadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
moved = Pad::ReadState();
if (moved.left_stick_x == 200) break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (std::chrono::steady_clock::now() < stickDeadline);
Require(moved.left_stick_x == 200 && moved.left_stick_y == 0);
Require(moved.right_stick_x == 128 && moved.right_stick_y == 128);
Require(moved.buttons == 0x2000 && moved.timestamp > still.timestamp);
std::this_thread::sleep_for(std::chrono::milliseconds(310));
const auto centred = Pad::ReadState();
Require(centred.left_stick_x == 42 && centred.right_stick_y == 17);
Require(centred.timestamp > moved.timestamp);
for (const auto& [command, x] : {std::pair{"3 2097152 300\n", kTouchPadWidth / 4}, std::pair{"4 4194304 300\n", kTouchPadWidth * 3 / 4}}) {
std::ofstream(path) << command;
PadData touched{};
const auto touchDeadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
touched = Pad::ReadState();
if (touched.touch_data_touch_num != 0) break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (std::chrono::steady_clock::now() < touchDeadline);
Require(touched.touch_data_touch_num == 1 && touched.touch_data_touch0_x == x && touched.touch_data_touch0_y == kTouchPadHeight / 2);
Require(touched.buttons == (0x2000 | 0x100000));
std::this_thread::sleep_for(std::chrono::milliseconds(310));
const auto lifted = Pad::ReadState();
Require(lifted.touch_data_touch_num == 0 && lifted.buttons == 0x2000);
}
bool contactActive = false;
const auto touchCommand = [&](const char* command, int x, int y, int id, bool active) {
std::ofstream(path) << command << '\n';
PadData touched{};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
touched = Pad::ReadState();
if (contactActive && active) Require(touched.touch_data_touch_num == 1 && touched.touch_data_touch0_id == id);
if (touched.touch_data_touch_num == (active ? 1 : 0) &&
(!active || (touched.touch_data_touch0_x == x && touched.touch_data_touch0_y == y && touched.touch_data_touch0_id == id))) break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (std::chrono::steady_clock::now() < deadline);
Require(touched.touch_data_touch_num == (active ? 1 : 0));
if (active) Require(touched.touch_data_touch0_x == x && touched.touch_data_touch0_y == y && touched.touch_data_touch0_id == id);
Require(touched.buttons == 0x2000);
contactActive = active;
return touched;
};
const auto start = touchCommand("5 0 5000 touch(1919,942,127,1)", 1919, 942, 127, true);
const auto turn = touchCommand("6 0 5000 touch(0,942,127,1)", 0, 942, 127, true);
Require(turn.timestamp > start.timestamp);
const auto end = touchCommand("7 0 5000 touch(0,0,127,1)", 0, 0, 127, true);
Require(end.timestamp > turn.timestamp);
const auto lifted = touchCommand("8 0 300 touch(0,0,127,0)", 0, 0, 127, false);
Require(lifted.timestamp > end.timestamp);
touchCommand("9 0 150 touch(960,471,0,1)", 960, 471, 0, true);
std::this_thread::sleep_for(std::chrono::milliseconds(160));
Require(Pad::ReadState().touch_data_touch_num == 0);
PadPublishInput_nid_postfix(PadInputState{});
}
static void CheckInjectedQueueTimes(const std::filesystem::path& path) {
PadData drained[64]{};
Pad::Read(drained, 64);
PadInputState physical;
physical.buttons = 0x2000;
PadPublishInput_nid_postfix(physical);
const auto before = Pad::ReadState();
std::ofstream(path) << "10 16384 300\n";
PadData pressed{};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
pressed = Pad::ReadState();
if ((pressed.buttons & 0x4000) != 0) break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (std::chrono::steady_clock::now() < deadline);
Require(pressed.buttons == 0x6000 && pressed.timestamp > before.timestamp);
PadData queued{};
Require(Pad::Read(&queued, 1) == 1);
Require(queued.buttons == pressed.buttons);
if (queued.timestamp < pressed.timestamp) {
std::fputs("queued input predates its injected button press\n", stderr);
std::abort();
}
physical.sticks[0] = 130;
PadPublishInput_nid_postfix(physical);
std::this_thread::sleep_for(std::chrono::milliseconds(310));
const auto released = Pad::ReadState();
Require(released.buttons == 0x2000 && released.timestamp > pressed.timestamp);
Require(Pad::Read(&queued, 1) == 1);
Require(queued.buttons == released.buttons && queued.left_stick_x == 130);
if (queued.timestamp < released.timestamp) {
std::fputs("queued input predates its injected button release\n", stderr);
std::abort();
}
PadPublishInput_nid_postfix(PadInputState{});
}
void CheckConcurrentSpecialOpens() {
constexpr int threads = 8;
for (int round = 0; round < 200; ++round) {
std::array<int, threads> handles{};
std::atomic<int> ready{0};
std::vector<std::thread> workers;
for (int index = 0; index < threads; ++index) {
workers.emplace_back([&, index] {
ready.fetch_add(1);
while (ready.load() < threads) std::this_thread::yield();
handles[index] = scePadOpen_nid_postfix(0x20000000 + index, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
});
}
for (auto& worker : workers) worker.join();
const std::set<int> distinct(handles.begin(), handles.end());
Require(distinct.size() == threads);
for (const auto handle : handles) {
Require(handle >= 0);
Require(scePadClose_nid_postfix(handle) == PAD_OK);
}
}
}
static void CheckPortZeroWithdrawn() {
constexpr int user = 0x10000000;
constexpr int noHandle = static_cast<int>(0x80920008);
const bool wasOpen = scePadGetHandle(user, 0, 0) != noHandle;
const int handle = wasOpen ? scePadGetHandle(user, 0, 0) : scePadOpen_nid_postfix(user, PAD_PORT_TYPE_STANDARD, 0, nullptr);
Require(handle > 0);
PadInputState pressed;
pressed.buttons = 0x4000;
PadSetPortConnected_nid_postfix(0, true);
PadPublishInput_nid_postfix(pressed);
PadData data[2];
std::memset(data, 0xff, sizeof(data));
Require(scePadReadState(handle, data) == PAD_OK);
Require(data[0].connected && (data[0].buttons & 0x4000) != 0);
PadSetPortConnected_nid_postfix(0, false);
std::memset(data, 0xff, sizeof(data));
Require(scePadReadState(handle, data) == PAD_OK);
Require(!data[0].connected && data[0].buttons == 0 && data[0].analog_buttons_l2 == 0 && data[0].analog_buttons_r2 == 0);
Require(data[0].left_stick_x == 0 && data[0].left_stick_y == 0 && data[0].right_stick_x == 0 && data[0].right_stick_y == 0);
Require(data[0].orientation_w == 1.0f && data[0].touch_data_touch_num == 0);
PadControllerInformation controllerInfo;
std::memset(&controllerInfo, 0xff, sizeof(controllerInfo));
Require(scePadGetControllerInformation(handle, &controllerInfo) == PAD_OK);
Require(!controllerInfo.connected && controllerInfo.connectedCount == 0);
PadInfo info;
std::memset(&info, 0xff, sizeof(info));
Require(scePadGetInfo_nid_postfix(&info) == PAD_OK);
std::uint8_t expected = 0;
for (int port = 0; port < PAD_MAX_CONTROLLERS; ++port) {
if (Pad::IsConnected(port)) ++expected;
}
Require(info.connectedCount[0] == expected);
if (expected == 0) Require(info.padTypeInfo[0].status == 0);
PadPublishInput_nid_postfix(pressed);
std::memset(data, 0xff, sizeof(data));
Require(scePadRead_nid_postfix(handle, data, 2) == 1);
Require(!data[0].connected && data[0].buttons == 0);
PadSetPortConnected_nid_postfix(0, true);
std::memset(data, 0xff, sizeof(data));
Require(scePadReadState(handle, data) == PAD_OK);
Require(data[0].connected && (data[0].buttons & 0x4000) != 0);
if (!wasOpen) Require(scePadClose_nid_postfix(handle) == PAD_OK);
}
int main() {
CheckConcurrentSpecialOpens();
CheckInputCommands();
std::uint32_t accessibilityState = 7;
Require(scePadGetAccessibilityControllerState(PAD_HANDLE, nullptr) == PAD_ERROR_NOT_INITIALIZED);
Require(scePadGetAccessibilityControllerState(PAD_HANDLE, &accessibilityState) == PAD_ERROR_NOT_INITIALIZED);
Require(accessibilityState == 7);
std::int32_t controllerType = -1;
Require(scePadGetControllerType(PAD_HANDLE, nullptr) == PAD_ERROR_NOT_INITIALIZED);
Require(scePadGetControllerType(PAD_HANDLE, &controllerType) == PAD_ERROR_NOT_INITIALIZED);
Require(controllerType == -1);
float earlyUnit = 1.0f;
PadMotionSensorPosition earlyPosition{1.0f, 2.0f, 3.0f};
float earlyRadius = 1.0f;
std::uint64_t earlyAddress = 7;
std::array<std::uint8_t, 0x80> earlyLeonardo{};
std::array<std::uint8_t, 0x20> earlyUnique;
earlyUnique.fill(0xaa);
Require(scePadGetMotionTimerUnit(PAD_HANDLE, nullptr) == PAD_ERROR_NOT_INITIALIZED);
Require(scePadGetMotionTimerUnit(PAD_HANDLE, &earlyUnit) == PAD_ERROR_NOT_INITIALIZED && earlyUnit == 1.0f);
Require(scePadGetMotionSensorPosition(PAD_HANDLE, &earlyPosition) == PAD_ERROR_NOT_INITIALIZED && earlyPosition.x == 1.0f && earlyPosition.y == 2.0f && earlyPosition.z == 3.0f);
Require(scePadGetSphereRadius(PAD_HANDLE, &earlyRadius) == PAD_ERROR_NOT_INITIALIZED && earlyRadius == 1.0f);
Require(scePadGetBluetoothAddress(PAD_HANDLE, &earlyAddress) == PAD_ERROR_NOT_INITIALIZED && earlyAddress == 7);
Require(scePadParseUniqueDataLeonardo(PAD_HANDLE, reinterpret_cast<const PadLeonardoData*>(earlyLeonardo.data()), reinterpret_cast<PadLeonardoUniqueData*>(earlyUnique.data())) == PAD_ERROR_NOT_INITIALIZED && earlyUnique[0] == 0xaa);
Require(scePadUnknown_n3kSX62fgNo() == static_cast<int>(0x80920005));
Require(!scePadIsValidHandle(PAD_HANDLE, PAD_PORT_TYPE_STANDARD));
const PadVibrationParam earlyVibration{1, 2};
for (const PadVibrationParam* param : {static_cast<const PadVibrationParam*>(nullptr), &earlyVibration}) Require(scePadSetVibrationForce(PAD_HANDLE, param) == PAD_ERROR_NOT_INITIALIZED);
Pad::SetLightBar(0, true, 1, 2, 3);
Require(scePadResetLightBarAll() == PAD_OK && LightBarIs(0, true, 1, 2, 3));
Require(scePadResetLightBarAllByPortType(3) == PAD_ERROR_INVALID_ARG && scePadResetLightBarAllByPortType(PAD_PORT_TYPE_STANDARD) == PAD_OK && LightBarIs(0, true, 1, 2, 3));
Pad::SetLightBar(0, false, 0, 0, 0);
const auto path = std::filesystem::temp_directory_path() / ("anyps5-pad-state-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
struct Cleanup {
std::filesystem::path path;
~Cleanup() { std::error_code error; std::filesystem::remove(path, error); }
};
auto commandPath = path;
commandPath += std::filesystem::path(u8"-žluťoučký-漢字");
Cleanup cleanup{commandPath};
#ifdef _WIN32
Require(_wputenv_s(L"APS5_PAD_INPUT_FILE", commandPath.c_str()) == 0);
#else
Require(setenv("APS5_PAD_INPUT_FILE", commandPath.string().c_str(), 1) == 0);
#endif
{
std::uint32_t sequence = 0;
PadOutputState snapshot;
Pad::SetVibration(12, 34);
PadSubmitAudioHaptics_nid_postfix(100, 150);
Require(PadFetchOutput_nid_postfix(&sequence, &snapshot));
Require(snapshot.audioVibrationLarge == 100 && snapshot.audioVibrationSmall == 150);
Require(snapshot.vibrationLarge == 12 && snapshot.vibrationSmall == 34);
PadQueueLegacyAudioHaptics_nid_postfix(1, 0, ~std::uint64_t{0}, 200, 20);
Require(PadFetchOutput_nid_postfix(&sequence, &snapshot));
Require(snapshot.audioVibrationLarge == 200 && snapshot.audioVibrationSmall == 150);
PadClearLegacyAudioHaptics_nid_postfix(1);
Require(PadFetchOutput_nid_postfix(&sequence, &snapshot));
Require(snapshot.audioVibrationLarge == 100 && snapshot.audioVibrationSmall == 150);
Require(snapshot.vibrationLarge == 12 && snapshot.vibrationSmall == 34);
PadSubmitAudioHaptics_nid_postfix(0, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Require(PadFetchOutput_nid_postfix(&sequence, &snapshot));
Require(snapshot.audioVibrationLarge == 0 && snapshot.audioVibrationSmall == 0);
Require(snapshot.vibrationLarge == 12 && snapshot.vibrationSmall == 34);
Pad::SetVibration(0, 0);
}
constexpr int noHandle = static_cast<int>(0x80920008);
constexpr int user = 0x10000000;
Require(scePadGetHandle(user, 0, 0) == noHandle);
for (const int invalidUser : {-1, 0, 0x10000005}) Require(scePadGetHandle(invalidUser, 0, 0) == noHandle);
Require(scePadOpen_nid_postfix(user, 1, 0, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadOpen_nid_postfix(user, 0, 1, nullptr) == static_cast<int>(0x809b0003));
for (const int type : {3, 4}) Require(scePadOpen_nid_postfix(user, type, 0, nullptr) == PAD_ERROR_INVALID_ARG);
for (const int type : {0, 2, 3, 4}) Require(scePadOpen_nid_postfix(-1, type, 0, nullptr) == static_cast<int>(0x809b0001));
Require(scePadOpen_nid_postfix(-1, 0, 1, nullptr) == static_cast<int>(0x809b0001));
Require(scePadOpen_nid_postfix(-1, 1, 0, nullptr) == PAD_ERROR_INVALID_ARG);
for (const int type : {5, 6, 16, 0xff, -1}) Require(scePadOpen_nid_postfix(user, type, 0, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetHandle(user, 0, 0) == noHandle);
const unsigned char wheel[16]{0xb7, 0x0e, 0x04, 0x0e, 0x08, 0x6e, 0x01};
Require(scePadOpenExt(user, PAD_PORT_TYPE_SPECIAL, 0, wheel) == static_cast<int>(0x80920007));
Require(scePadOpenExt(user, PAD_PORT_TYPE_SPECIAL, 1, wheel) == PAD_ERROR_INVALID_ARG);
Require(scePadOpenExt(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadGetHandle(user, 0, 0) == noHandle);
const int earlySpecial = scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr);
Require(earlySpecial > 0);
Require(!scePadIsValidHandle(earlySpecial, PAD_PORT_TYPE_SPECIAL));
Require(scePadGetHandle(user, 0, 0) == noHandle);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == earlySpecial);
Require(scePadClose_nid_postfix(earlySpecial) == PAD_OK);
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
PadSetPortConnected_nid_postfix(1, true);
Require(scePadInit_nid_postfix() == 0);
PadInfo initInfo{};
Require(scePadGetInfo_nid_postfix(&initInfo) == PAD_OK);
Require(initInfo.connectedCount[0] == 2);
Require(initInfo.padTypeInfo[0].connectPort == 0);
Require(initInfo.padTypeInfo[1].connectPort == 1);
PadSetPortConnected_nid_postfix(1, false);
Require(scePadUnknown_n3kSX62fgNo() == PAD_OK);
const int handle = scePadOpen_nid_postfix(user, 0, 0, nullptr);
Require(handle > 0);
Require(scePadUnknown_n3kSX62fgNo() == PAD_OK);
Require(scePadGetHandle(user, 0, 0) == handle);
for (const int invalidUser : {-1, 0, 0x10000005}) Require(scePadGetHandle(invalidUser, 0, 0) == noHandle);
CheckSpecialPort(user, handle);
CheckTiltCorrection(handle);
CheckAngularVelocityDeadband(handle);
CheckTouchContact(handle);
CheckHandleQueries(user, handle);
CheckVirtualTouchContact(handle);
CheckTouchPadInformation(handle);
CheckStickDeadZones(handle);
CheckExtControllerInformation(handle);
CheckPartialTrigger();
CheckReadQueue(handle);
CheckTapBetweenReads();
CheckCaptureHidesEarlierInput();
CheckLatchedPressLifetime(handle);
CheckLatchedPressAtSlowReads(handle);
CheckLatchedPressPerPort();
CheckReadStateHandle(handle);
CheckRemoteController(handle);
CheckTrackerDevices(handle);
CheckAccessibilityControllerState(handle);
CheckIsValidHandle(user, handle);
CheckLightBarResets(user, handle);
CheckVibrationForce(user, handle);
CheckTriggerEffect(user, handle);
CheckControllerType(handle);
CheckInjectedState(commandPath);
CheckInjectedQueueTimes(commandPath);
PadLightBarParam lightBar{255, 128, 64};
Require(scePadSetLightBar(handle, &lightBar) == PAD_OK);
Require(scePadSetLightBar(handle, nullptr) == PAD_ERROR_INVALID_ARG);
Require(scePadSetLightBar(handle + 1, &lightBar) == PAD_ERROR_INVALID_HANDLE);
Require(scePadResetLightBar(handle) == PAD_OK);
Require(scePadResetLightBar(handle + 1) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetMotionSensorState(handle, true) == PAD_OK);
Require(scePadSetMotionSensorState(handle, false) == PAD_OK);
for (const int invalid : {-1, 0, handle + 1, 9999}) {
Require(scePadSetMotionSensorState(invalid, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetMotionSensorState(invalid, false) == PAD_ERROR_INVALID_HANDLE);
}
Require(scePadSetVibrationMode(handle, 1) == 0);
Require(scePadSetVibrationMode(handle, 2) == 0);
Require(scePadSetVibrationMode(handle, 3) == 0);
Require(scePadSetVibrationMode(handle, 4) == PAD_ERROR_INVALID_ARG);
Require(scePadSetVibrationMode(handle, 5) == 0);
Require(scePadSetVibrationMode(handle, 6) == PAD_ERROR_INVALID_ARG);
Require(scePadSetVibrationMode(handle, -1) == PAD_ERROR_INVALID_ARG);
CheckSystemRemote(handle);
Require(scePadGetHandle(user, 16, 0) == noHandle);
Require(scePadGetHandle(user, 0, 1) == noHandle);
Require(scePadClose_nid_postfix(handle) == 0);
Require(scePadGetHandle(user, 0, 0) == noHandle);
Require(!scePadIsValidHandle(handle, PAD_PORT_TYPE_STANDARD));
Require(scePadSetLightBar(handle, &lightBar) == PAD_OK && LightBarIs(0, true, 255, 128, 64));
Require(scePadResetLightBarAll() == PAD_OK && LightBarIs(0, true, 255, 128, 64));
Require(scePadResetLightBar(handle) == PAD_OK && LightBarIs(0, false, 0, 0, 0));
Require(scePadUnknown_n3kSX62fgNo() == PAD_OK);
Require(scePadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(true) == 0);
Require(scePadSetAngularVelocityDeadbandState(handle, false) == 0);
Require(scePadSetAngularVelocityDeadbandState(handle + 1, false) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetAngularVelocityDeadbandState(handle + 1, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetAngularVelocityBiasCorrectionState(handle, false) == PAD_OK);
Require(scePadSetAngularVelocityBiasCorrectionState(handle + 1, false) == PAD_ERROR_INVALID_HANDLE);
PadInfo info{};
Require(scePadGetInfo_nid_postfix(&info) == PAD_OK);
Require(info.maxConnectCount == PAD_MAX_CONTROLLERS);
Require(info.connectedCount[0] == 1);
Require(info.padTypeInfo[0].connectPort == 0);
const int user2 = 0x10000001;
Require(scePadGetHandle(user2, 0, 0) == noHandle);
const int handle2 = scePadOpen_nid_postfix(user2, 0, 0, nullptr);
Require(handle2 == PAD_HANDLE_BASE + 1);
Require(scePadGetHandle(user2, 0, 0) == handle2);
Require(scePadIsValidHandle(handle2, PAD_PORT_TYPE_STANDARD) && scePadIsValidHandle(handle, PAD_PORT_TYPE_STANDARD) == false);
PadInputState p2Input{};
p2Input.buttons = static_cast<std::uint32_t>(Pad::PadButton::Triangle);
PadPublishInputForPort_nid_postfix(1, p2Input);
PadData p2Data{};
Require(scePadReadState(handle2, &p2Data) == 0);
Require((p2Data.buttons & static_cast<std::uint32_t>(Pad::PadButton::Triangle)) != 0);
Require(scePadSetVibrationMode(handle2, 1) == 0);
std::uint32_t p2AccessState = 7;
Require(scePadGetAccessibilityControllerState(handle2, &p2AccessState) == PAD_OK);
Require(p2AccessState == 0);
std::int32_t p2Type = -1;
Require(scePadGetControllerType(handle2, &p2Type) == PAD_OK);
Require(p2Type == PAD_CONTROLLER_TYPE_DUAL_SENSE);
Require(scePadDisableVibration(handle2, true) == PAD_OK);
Require(scePadDisableVibration(handle2 + 99, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadDisableTriggerEffect(handle2, true) == PAD_OK);
Require(scePadDisableTriggerEffect(handle2 + 99, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadIsLightBarBaseBrightnessControllable(handle2) == 1);
Require(scePadIsLightBarBaseBrightnessControllable(handle2 + 99) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetLightBarBaseBrightness(handle2, 1) == PAD_OK);
Require(scePadSetLightBarBaseBrightness(handle2 + 99, 1) == PAD_ERROR_INVALID_HANDLE);
PadSetPortConnected_nid_postfix(1, true);
Require(scePadGetInfo_nid_postfix(&info) == PAD_OK);
Require(info.connectedCount[0] == 2);
Require(info.padTypeInfo[0].connectPort == 0);
Require(info.padTypeInfo[1].connectPort == 1);
PadSetPortConnected_nid_postfix(1, false);
Require(scePadGetInfo_nid_postfix(&info) == PAD_OK);
Require(info.connectedCount[0] == 1);
Require(info.padTypeInfo[0].connectPort == 0);
const PadVibrationParam vibZero{0, 0};
const PadVibrationParam vibFull{255, 255};
const PadVibrationParam vibMixed{128, 64};
Require(scePadSetVibration(handle2, &vibZero) == PAD_OK);
Require(scePadSetVibration(handle2, &vibFull) == PAD_OK);
Require(scePadSetVibration(handle2, &vibMixed) == PAD_OK);
Require(scePadSetVibration(handle2 + 99, &vibMixed) == PAD_ERROR_INVALID_HANDLE);
for (const int invalidHandle : {-1, -2, -100, -9999}) {
Require(scePadSetVibration(invalidHandle, &vibMixed) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(invalidHandle, nullptr) == PAD_ERROR_INVALID_HANDLE);
}
Require(scePadSetVibration(handle2 + 99, nullptr) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetVibration(handle2, nullptr) == PAD_ERROR_INVALID_ARG);
const PadLightBarParam lightOff{0, 0, 0};
const PadLightBarParam lightFull{255, 255, 255};
const PadLightBarParam lightColor{100, 150, 200};
Require(scePadSetLightBar(handle2, &lightOff) == PAD_OK);
Require(scePadSetLightBar(handle2, &lightFull) == PAD_OK);
Require(scePadSetLightBar(handle2, &lightColor) == PAD_OK);
Require(scePadSetLightBar(handle2 + 99, &lightColor) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetLightBar(-1, &lightColor) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetLightBar(handle2, nullptr) == PAD_ERROR_INVALID_ARG);
const std::array<std::uint8_t, 120> triggerZero{};
Require(scePadSetTriggerEffect(handle2, triggerZero.data()) == PAD_OK);
Require(scePadSetTriggerEffect(handle2 + 99, triggerZero.data()) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetTriggerEffect(-1, triggerZero.data()) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetTriggerEffect(handle2, nullptr) == PAD_ERROR_INVALID_ARG);
PadInputState p0MotionSample{};
p0MotionSample.hasMotion = true;
p0MotionSample.accel = {0.0f, 9.80665f, 0.0f};
p0MotionSample.gyro = {0.5f, 0.5f, 0.5f};
PadPublishInputForPort_nid_postfix(0, p0MotionSample);
Require(scePadSetMotionSensorState(handle, true) == PAD_OK);
PadInputState p2MotionSample{};
p2MotionSample.hasMotion = true;
p2MotionSample.accel = {4.903325f, 9.80665f, 0.0f};
p2MotionSample.gyro = {0.1f, 0.2f, -0.3f};
PadPublishInputForPort_nid_postfix(1, p2MotionSample);
Require(scePadSetMotionSensorState(handle2, true) == PAD_OK);
PadData p2DataMotion{};
Require(scePadReadState(handle2, &p2DataMotion) == PAD_OK);
Require(p2DataMotion.angular_velocity_x == 0.1f && p2DataMotion.angular_velocity_y == 0.2f && p2DataMotion.angular_velocity_z == -0.3f);
Require(p2DataMotion.acceleration_x > 0.49f && p2DataMotion.acceleration_x < 0.51f);
Require(scePadSetMotionSensorState(handle2, false) == PAD_OK);
Require(scePadReadState(handle2, &p2DataMotion) == PAD_OK);
Require(p2DataMotion.angular_velocity_x == 0.0f && p2DataMotion.angular_velocity_y == 0.0f && p2DataMotion.angular_velocity_z == 0.0f);
Require(p2DataMotion.acceleration_x == 0.0f);
PadData p0DataMotion{};
Require(scePadReadState(handle, &p0DataMotion) == PAD_OK);
Require(p0DataMotion.angular_velocity_x == 0.5f && p0DataMotion.angular_velocity_y == 0.5f && p0DataMotion.angular_velocity_z == 0.5f);
Require(scePadSetMotionSensorState(handle2, true) == PAD_OK);
Require(scePadReadState(handle2, &p2DataMotion) == PAD_OK);
Require(p2DataMotion.angular_velocity_x == 0.1f && p2DataMotion.angular_velocity_y == 0.2f && p2DataMotion.angular_velocity_z == -0.3f);
Require(scePadSetMotionSensorState(handle2 + 99, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetMotionSensorState(-1, true) == PAD_ERROR_INVALID_HANDLE);
PadPublishInputForPort_nid_postfix(0, PadInputState{});
PadPublishInputForPort_nid_postfix(1, PadInputState{});
PadInputState tilted2;
tilted2.hasMotion = true;
tilted2.accel = {9.80665f, 0.0f, 0.0f};
PadPublishInputForPort_nid_postfix(1, tilted2);
Require(scePadSetTiltCorrectionState(handle2, false) == PAD_OK);
Require(scePadResetOrientation(handle2) == PAD_OK);
Require(SettleOrientationW(1) == 1.0f);
Require(scePadSetTiltCorrectionState(handle2, true) == PAD_OK);
Require(SettleOrientationW(1) < 0.999f);
PadPublishInputForPort_nid_postfix(1, PadInputState{});
Require(SettleOrientationW(0) == 1.0f);
Require(scePadSetTiltCorrectionState(handle2 + 99, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadSetTiltCorrectionState(-1, true) == PAD_ERROR_INVALID_HANDLE);
Require(scePadResetOrientation(handle2) == PAD_OK);
Require(scePadResetOrientation(handle2 + 99) == PAD_ERROR_INVALID_HANDLE);
Require(scePadResetOrientation(-1) == PAD_ERROR_INVALID_HANDLE);
Require(scePadClose_nid_postfix(handle2) == 0);
Require(scePadGetHandle(user2, 0, 0) == noHandle);
Require(!scePadIsValidHandle(handle2, PAD_PORT_TYPE_STANDARD));
Require(scePadSetAngularVelocityDeadbandState(handle2, false) == PAD_OK);
Require(scePadSetAngularVelocityDeadbandState(handle2 + 1, false) == PAD_ERROR_INVALID_HANDLE);
CheckPortZeroWithdrawn();
}