This commit is contained in:
xfangfang committed 2025-03-15 14:32:26 +08:00
commit a2fe88cdcb
22 files changed
+2382

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# CMake build directory
cmake-build*/
build*/
.idea/
*.gz
+6
View File
@@ -0,0 +1,6 @@
[submodule "lib/mongoose"]
path = lib/mongoose
url = https://github.com/cesanta/mongoose.git
[submodule "lib/GoldHEN_Plugins_SDK"]
path = lib/GoldHEN_Plugins_SDK
url = https://github.com/GoldHEN/GoldHEN_Plugins_SDK.git
+33
View File
@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.10)
option(DEVICE_IP "Set your PS4 IP address to send the PRX directly to the GoldHEN plugin folder")
option(DEBUG_SDK "Enable debug output for GoldHEN SDK" OFF)
project(remote_pad C)
add_subdirectory(lib)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/static.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND gzip -k -c client/index.html > client/index.html.gz
COMMAND cc -o ${CMAKE_BINARY_DIR}/pack lib/mongoose/test/pack.c
COMMAND ${CMAKE_BINARY_DIR}/pack client/index.html.gz > ${CMAKE_BINARY_DIR}/static.c
DEPENDS client/index.html
)
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> -o <TARGET> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>")
file(GLOB SOURCE_FILES src/*.c src/pad/*.c)
add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${CMAKE_BINARY_DIR}/static.c)
target_include_directories(${PROJECT_NAME} PRIVATE include lib/GoldHEN_Plugins_SDK/include)
target_link_libraries(${PROJECT_NAME} SceSysmodule SceUserService ScePad SceNet mongoose ${GoldHEN_SDK_TARGET})
target_link_options(${PROJECT_NAME} PRIVATE --gc-sections)
add_dependencies(${PROJECT_NAME} goldhen_sdk)
add_prx(${PROJECT_NAME})
if (DEVICE_IP)
add_custom_target(${PROJECT_NAME}_send_prx
COMMAND curl -T ${PROJECT_NAME}.prx ftp://${DEVICE_IP}:2121/data/GoldHEN/plugins/
DEPENDS ${PROJECT_NAME}_prx
)
endif ()
+25
View File
@@ -0,0 +1,25 @@
FROM alpine:3
ENV OO_PS4_TOOLCHAIN="/opt/OpenOrbis/PS4Toolchain"
RUN apk update && \
apk add llvm18 clang18 lld18 cmake make git curl && \
apk cache clean -f && \
ln -s /usr/bin/clang-18 /usr/bin/clang && \
ln -s /usr/bin/clang++-18 /usr/bin/clang++ && \
ln -s /usr/bin/llvm18-ar /usr/bin/llvm-ar && \
ln -s /usr/bin/llvm18-ranlib /usr/bin/llvm-ranlib && \
ln -s /usr/bin/llvm18-strip /usr/bin/llvm-strip && \
wget https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/releases/download/v0.5.3/toolchain-llvm-18.2.zip && \
unzip toolchain-llvm-18.2.zip && \
rm toolchain-llvm-18.2.zip && \
tar -xzvf toolchain-llvm-18.tar.gz -C /opt && \
rm toolchain-llvm-18.tar.gz && \
wget https://codeload.github.com/GoldHEN/GoldHEN_Plugins_SDK/zip/refs/heads/main -O GoldHEN_Plugins_SDK.zip && \
unzip GoldHEN_Plugins_SDK.zip && \
rm GoldHEN_Plugins_SDK.zip && \
cd GoldHEN_Plugins_SDK-main && \
make install && \
cp build/crtprx.o ${OO_PS4_TOOLCHAIN}/lib/ && \
cd .. && rm -rf GoldHEN_Plugins_SDK-main
+16
View File
@@ -0,0 +1,16 @@
# RemotePad
Control your ps4 through Network / USB (WIP).
# How to use
See the Wiki for more information about how to use this plugin and game compatibility.
# Development
[LLVM 10+](https://llvm.org/)
[OpenOrbis SDK](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain)
```shell
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
```
+522
View File
@@ -0,0 +1,522 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;" charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>RemotePad</title>
<style>
body {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
padding: 0;
/*overflow: hidden;*/
touch-action: none;
}
.info {
margin: 1vw;
display: flex;
justify-content: center;
}
.pad {
width: 30vw;
height: 30vw;
display: flex;
flex-direction: column;
}
.pad-top {
display: flex;
justify-content: center;
}
.pad-center {
display: flex;
justify-content: space-between;
}
.btn {
width: 10vw;
height: 10vw;
font-size: 2em;
font-weight: bold;
background: #ccc;
border: 1px solid #aaa;
border-radius: 0.5em;
text-align: center;
}
.gamepad-top {
width: 100%;
display: flex;
justify-content: space-between;
}
.gamepad-center {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
}
.stick-area {
width: 50vw;
height: 20vw;
position: absolute;
bottom: -10vw;
left: 25vw;
display: flex;
justify-content: space-between;
}
.stick {
width: 20vw;
height: 20vw;
background-color: gray;
border-radius: 10vw;
position: relative;
}
.touch {
width: 30vw;
height: 20vw;
position: relative;
top: -10vw;
}
#pad {
margin-right: 10px;
}
#url {
margin-right: 10px;
}
</style>
</head>
<body>
<div class="info">
<select id="pad">
<option value="0">Pad0</option>
<option value="1">Pad1</option>
<option value="2">Pad2</option>
<option value="3">Pad3</option>
</select>
<label for="url">ws://</label>
<input id="url" type="text" placeholder="Type RemotePad server address" value="" style="width:20em;" />
<button id="connect">connect</button>
</div>
<div class="gamepad-top">
<div>
<button class="btn" id="btnL1">L1</button>
<button class="btn" id="btnL2">L2</button>
<button class="btn" id="btnShare">S</button>
</div>
<div>
<button class="btn" id="btnOptions">O</button>
<button class="btn" id="btnR2">R2</button>
<button class="btn" id="btnR1">R1</button>
</div>
</div>
<br>
<div class="gamepad-center">
<div class="pad">
<div class="pad-top">
<button class="btn" id="btnUp">↑</button>
</div>
<div class="pad-center">
<button class="btn" id="btnLeft">←</button>
<button class="btn" id="btnL3">L3</button>
<button class="btn" id="btnRight">→</button>
</div>
<div class="pad-top">
<button class="btn" id="btnDown">↓</button>
</div>
</div>
<button class="btn touch" id="btnTouch">TouchPad</button>
<div class="pad">
<div class="pad-top">
<button class="btn" id="btnTriangle">△</button>
</div>
<div class="pad-center">
<button class="btn" id="btnSquare">□</button>
<button class="btn" id="btnR3">R3</button>
<button class="btn" id="btnCircle">○</button>
</div>
<div class="pad-top">
<button class="btn" id="btnCross">X</button>
</div>
</div>
<div class="stick-area">
<div class="stick" id="cl">
</div>
<div class="stick" id="cr">
</div>
</div>
</div>
</body>
<script>
const OrbisPadButton =
{
ORBIS_PAD_BUTTON_L3: 0x0002,
ORBIS_PAD_BUTTON_R3: 0x0004,
ORBIS_PAD_BUTTON_OPTIONS: 0x0008,
ORBIS_PAD_BUTTON_UP: 0x0010,
ORBIS_PAD_BUTTON_RIGHT: 0x0020,
ORBIS_PAD_BUTTON_DOWN: 0x0040,
ORBIS_PAD_BUTTON_LEFT: 0x0080,
ORBIS_PAD_BUTTON_L2: 0x0100,
ORBIS_PAD_BUTTON_R2: 0x0200,
ORBIS_PAD_BUTTON_L1: 0x0400,
ORBIS_PAD_BUTTON_R1: 0x0800,
ORBIS_PAD_BUTTON_TRIANGLE: 0x1000,
ORBIS_PAD_BUTTON_CIRCLE: 0x2000,
ORBIS_PAD_BUTTON_CROSS: 0x4000,
ORBIS_PAD_BUTTON_SQUARE: 0x8000,
ORBIS_PAD_BUTTON_TOUCH_PAD: 0x100000
};
let rpc;
let rpc_running = false;
const JSONRPC_TIMEOUT_MS = 1000;
function jsonrpc(url, onopen, onclose, onnotification) {
var rpcid = 0, pending = {}, ws = new WebSocket(url);
if (!ws) return null;
ws.onclose = onclose;
ws.onmessage = function(ev) {
const frame = JSON.parse(ev.data);
console.log('rcvd', frame, 'pending:', pending);
if (frame.id !== undefined) {
if (pending[frame.id] !== undefined) pending[frame.id](frame); // Resolve
delete (pending[frame.id]);
} else {
if (onnotification) onnotification(frame);
}
};
if (onopen) onopen();
return {
close: () => ws.close(),
call: function(method, params) {
const id = rpcid++, request = {id, method, params};
ws.send(JSON.stringify(request));
console.log('sent', request);
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (pending[id] === undefined) return;
log('Timing out frame ', JSON.stringify(request));
delete (pending[id]);
reject();
}, JSONRPC_TIMEOUT_MS);
pending[id] = x => resolve(x);
});
},
callVoid: function(method, params) {
if (ws.readyState !== WebSocket.OPEN) return;
const id = rpcid++, request = {id, method, params};
ws.send(JSON.stringify(request));
},
};
}
function E(id) {
return document.getElementById(id);
}
const pad = E('pad');
const url = E('url');
const connect = E('connect');
const cl = E('cl');
const cr = E('cr');
const btn0 = E('btnCross');
const btn1 = E('btnCircle');
const btn2 = E('btnSquare');
const btn3 = E('btnTriangle');
const btn4 = E('btnL1');
const btn5 = E('btnR1');
const btn6 = E('btnL2');
const btn7 = E('btnR2');
const btn8 = E('btnShare');
const btn9 = E('btnOptions');
const btn10 = E('btnL3');
const btn11 = E('btnR3');
const btn12 = E('btnUp');
const btn13 = E('btnDown');
const btn14 = E('btnLeft');
const btn15 = E('btnRight');
const btn16 = E('btnTouch');
let lastUrl = localStorage.getItem('url');
if (lastUrl) {
url.value = lastUrl;
} else {
url.value = window.location.host;
}
const btnMap = [
{
btn: btn0,
value: OrbisPadButton.ORBIS_PAD_BUTTON_CROSS
},
{
btn: btn1,
value: OrbisPadButton.ORBIS_PAD_BUTTON_CIRCLE
},
{
btn: btn2,
value: OrbisPadButton.ORBIS_PAD_BUTTON_SQUARE
},
{
btn: btn3,
value: OrbisPadButton.ORBIS_PAD_BUTTON_TRIANGLE
},
{
btn: btn4,
value: OrbisPadButton.ORBIS_PAD_BUTTON_L1
},
{
btn: btn5,
value: OrbisPadButton.ORBIS_PAD_BUTTON_R1
},
{
btn: btn6,
value: OrbisPadButton.ORBIS_PAD_BUTTON_L2
},
{
btn: btn7,
value: OrbisPadButton.ORBIS_PAD_BUTTON_R2
},
{
// share => touchpad
btn: btn8,
value: OrbisPadButton.ORBIS_PAD_BUTTON_TOUCH_PAD
},
{
btn: btn9,
value: OrbisPadButton.ORBIS_PAD_BUTTON_OPTIONS
},
{
btn: btn10,
value: OrbisPadButton.ORBIS_PAD_BUTTON_L3
},
{
btn: btn11,
value: OrbisPadButton.ORBIS_PAD_BUTTON_R3
},
{
btn: btn12,
value: OrbisPadButton.ORBIS_PAD_BUTTON_UP
},
{
btn: btn13,
value: OrbisPadButton.ORBIS_PAD_BUTTON_DOWN
},
{
btn: btn14,
value: OrbisPadButton.ORBIS_PAD_BUTTON_LEFT
},
{
btn: btn15,
value: OrbisPadButton.ORBIS_PAD_BUTTON_RIGHT
},
{
btn: btn16,
value: OrbisPadButton.ORBIS_PAD_BUTTON_TOUCH_PAD
}
]
// 按键
let GamepadButton = 0;
// 摇杆
let leftStickX = 128, leftStickY = 128, rightStickX = 128, rightStickY = 128;
// 扳机
let left2 = 0, right2 = 0;
function setGamePadStatus(enabled) {
for (let i = 0; i < btnMap.length; i++) {
btnMap[i].btn.disabled = !enabled;
}
url.disabled = enabled;
connect.innerHTML = enabled ? 'disconnect' : 'connect';
if (!enabled) {
rpc = null;
}
}
setGamePadStatus(false);
function toggleConnection() {
localStorage.setItem('url', url.value);
if (rpc) {
rpc.close();
rpc = null;
return;
}
rpc = jsonrpc("ws://" + url.value,
() => setGamePadStatus(true),
() => setGamePadStatus(false),
msg => console.log('NOTIFICATION: ' + JSON.stringify(msg)));
}
connect.onclick = toggleConnection;
toggleConnection();
function sendData() {
if (!rpc)
return;
rpc.callVoid('u', [Number(pad.value), GamepadButton, leftStickX, leftStickY, rightStickX, rightStickY, left2, right2]);
}
function sendButtonDown(btn) {
GamepadButton |= btn;
sendData();
}
function sendButtonUp(btn) {
GamepadButton &= ~btn;
sendData();
}
// 摇杆
cl.onmousemove = function (ev) {
if (ev.buttons === 1) {
leftStickX = Math.floor((ev.offsetX / cl.offsetWidth) * 256);
leftStickY = Math.floor((ev.offsetY / cl.offsetHeight) * 256);
sendData();
}
};
cl.onmouseup = function (ev) {
leftStickX = 128;
leftStickY = 128;
sendData();
};
cr.onmousemove = function (ev) {
if (ev.buttons === 1) {
rightStickX = Math.floor((ev.offsetX / cr.offsetWidth) * 256);
rightStickY = Math.floor((ev.offsetY / cr.offsetHeight) * 256);
sendData();
}
};
cr.onmouseup = function (ev) {
rightStickX = 128;
rightStickY = 128;
sendData();
};
// 按键
for (let i = 0; i < btnMap.length; i++) {
btnMap[i].btn.ontouchstart = btnMap[i].btn.onmousedown = ev => {
// 模拟扳机
if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_L2) {
left2 = 255;
} else if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_R2) {
right2 = 255;
}
sendButtonDown(btnMap[i].value);
}
btnMap[i].btn.ontouchend = btnMap[i].btn.onmouseup = ev => {
// 模拟扳机
if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_L2) {
left2 = 0;
} else if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_R2) {
right2 = 0;
}
sendButtonUp(btnMap[i].value);
}
}
let haveEvents = 'GamepadEvent' in window;
let haveWebkitEvents = 'WebKitGamepadEvent' in window;
let controllers = {};
let rAF = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.requestAnimationFrame;
function addGamepad(gamepad) {
console.log("gamepad connected", gamepad);
btn16.innerHTML = "GamePad\nConnected";
controllers[gamepad.index] = gamepad;
rAF(updateStatus);
}
function removeGamepad(gamepad) {
delete controllers[gamepad.index];
}
function connectHandler(e) {
addGamepad(e.gamepad);
}
function disconnectHandler(e) {
removeGamepad(e.gamepad);
}
function scanGamepads() {
let gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
for (let i = 0; i < gamepads.length; i++) {
if (gamepads[i] && (gamepads[i].index in controllers)) {
controllers[gamepads[i].index] = gamepads[i];
}
}
}
function updateStatus() {
scanGamepads();
for (let j in controllers) {
let controller = controllers[j];
let buttons = 0;
for (let i=0; i<controller.buttons.length; i++) {
let val = controller.buttons[i];
let pressed = val == 1.0;
let touched = false;
if (typeof(val) == "object") {
pressed = val.pressed;
if ('touched' in val) {
touched = val.touched;
}
val = val.value;
}
if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_L2) {
left2 = Math.round(val * 255);
if (left2 > 128) {
buttons |= OrbisPadButton.ORBIS_PAD_BUTTON_L2;
}
} else if (btnMap[i].value === OrbisPadButton.ORBIS_PAD_BUTTON_R2) {
right2 = Math.round(val * 255);
if (right2 > 128) {
buttons |= OrbisPadButton.ORBIS_PAD_BUTTON_R2;
}
} else if (pressed || touched){
buttons |= btnMap[i].value;
}
}
GamepadButton = buttons;
leftStickX = Math.round((controller.axes[0] + 1) / 2 * 255);
leftStickY = Math.round((controller.axes[1] + 1) / 2 * 255);
rightStickX = Math.round((controller.axes[2] + 1) / 2 * 255);
rightStickY = Math.round((controller.axes[3] + 1) / 2 * 255);
sendData();
break;
}
rAF(updateStatus);
}
if (haveEvents) {
window.addEventListener("gamepadconnected", connectHandler);
window.addEventListener("gamepaddisconnected", disconnectHandler);
} else if (haveWebkitEvents) {
window.addEventListener("webkitgamepadconnected", connectHandler);
window.addEventListener("webkitgamepaddisconnected", disconnectHandler);
} else {
setInterval(scanGamepads, 500);
}
</script>
</html>
+116
View File
@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.10)
###################################################################
set(OO_PS4_TOOLCHAIN $ENV{OO_PS4_TOOLCHAIN})
set(PS4 TRUE)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(TARGET x86_64-pc-freebsd-elf)
set(CMAKE_SYSTEM_VERSION 12)
set(CMAKE_CROSSCOMPILING 1)
find_program(CLANG clang)
if (NOT CLANG)
message(FATAL_ERROR "clang not Found")
endif ()
find_program(CLANG_PP clang++)
if (NOT CLANG_PP)
message(FATAL_ERROR "clang++ not Found")
endif ()
find_program(LLD_LINKER ld.lld)
if (NOT LLD_LINKER)
message(FATAL_ERROR "ld.lld not Found")
endif ()
find_program(LLVM_AR llvm-ar)
if (NOT LLVM_AR)
message(FATAL_ERROR "llvm-ar not Found")
endif ()
find_program(LLD_RANLIB llvm-ranlib)
if (NOT LLD_RANLIB)
message(FATAL_ERROR "llvm-ranlib not Found")
endif ()
find_program(LLD_STRIP llvm-strip)
if (NOT LLD_STRIP)
message(FATAL_ERROR "llvm-strip not Found")
endif ()
set(CMAKE_ASM_COMPILER ${CLANG} CACHE PATH "")
set(CMAKE_C_COMPILER ${CLANG} CACHE PATH "")
set(CMAKE_CXX_COMPILER ${CLANG_PP} CACHE PATH "")
set(CMAKE_LINKER ${LLD_LINKER} CACHE PATH "")
set(CMAKE_AR ${LLVM_AR} CACHE PATH "")
set(CMAKE_RANLIB ${LLD_RANLIB} CACHE PATH "")
set(CMAKE_STRIP ${LLD_STRIP} CACHE PATH "")
# We use the linker directly instead of using the llvm wrapper.
# CMake uses `-Xlinker` for passing llvm linker flags
# added via `add_link_options(... "LINKER:...")`.
# Force the correct linker flag generation:
macro(reset_linker_wrapper_flag)
set(CMAKE_ASM_LINKER_WRAPPER_FLAG "")
set(CMAKE_C_LINKER_WRAPPER_FLAG "")
set(CMAKE_CXX_LINKER_WRAPPER_FLAG "")
endmacro()
variable_watch(CMAKE_ASM_LINKER_FLAG reset_linker_wrapper_flag)
variable_watch(CMAKE_C_LINKER_WRAPPER_FLAG reset_linker_wrapper_flag)
variable_watch(CMAKE_CXX_LINKER_WRAPPER_FLAG reset_linker_wrapper_flag)
set(CMAKE_LIBRARY_ARCHITECTURE x86_64 CACHE INTERNAL "abi")
set(CMAKE_FIND_ROOT_PATH ${OO_PS4_TOOLCHAIN})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available")
###################################################################
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_ASM_FLAGS_INIT
"-target x86_64-pc-freebsd12-elf \
-D__PS4__ -fPIC -funwind-tables \
-isysroot ${OO_PS4_TOOLCHAIN} -isystem ${OO_PS4_TOOLCHAIN}/include")
set(CMAKE_C_FLAGS_INIT "${CMAKE_ASM_FLAGS_INIT}")
set(CMAKE_C_STANDARD_LIBRARIES "-lkernel -lSceLibcInternal")
set(CMAKE_EXE_LINKER_FLAGS_INIT
"-m elf_x86_64 -pie --eh-frame-hdr -e _init \
--script ${OO_PS4_TOOLCHAIN}/link.x \
-L${OO_PS4_TOOLCHAIN}/lib")
set(CMAKE_ASM_LINK_EXECUTABLE
"<CMAKE_LINKER> -o <TARGET> <CMAKE_ASM_LINK_FLAGS> <LINK_FLAGS> --start-group \
<OBJECTS> <LINK_LIBRARIES> --end-group")
set(CMAKE_C_LINK_EXECUTABLE
"<CMAKE_LINKER> -o <TARGET> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> \
--start-group \
${OO_PS4_TOOLCHAIN}/lib/crt1.o ${OO_PS4_TOOLCHAIN}/lib/crti.o \
<OBJECTS> <LINK_LIBRARIES> -lc \
${OO_PS4_TOOLCHAIN}/lib/crtn.o \
--end-group")
function(add_prx project)
add_custom_command(
OUTPUT "${project}.prx"
COMMAND ${CMAKE_COMMAND} -E env "OO_PS4_TOOLCHAIN=${OO_PS4_TOOLCHAIN}" "${OO_PS4_TOOLCHAIN}/bin/linux/create-fself" "-in=${project}" "-out=${project}.oelf" "--lib=${project}.prx" "--paid" "0x3800000000000011"
VERBATIM
DEPENDS "${project}"
)
add_custom_target(
"${project}_prx" ALL
DEPENDS "${project}.prx"
)
endfunction()
+114
View File
@@ -0,0 +1,114 @@
#include <stdint.h>
#include <stdbool.h>
#ifndef REMOTE_PAD_CONFIG_H
#define REMOTE_PAD_CONFIG_H
typedef struct ini_entry_s {
char* key;
char* value;
} ini_entry_s;
typedef struct ini_section_s {
char* name;
ini_entry_s* entry;
int size;
} ini_section_s;
typedef struct ini_table_s {
ini_section_s* section;
int size;
} ini_table_s;
/**
* @brief Creates an empty ini_table_s struct for writing new entries to.
* @return ini_table_s*
*/
ini_table_s* ini_table_create();
/**
* @brief Free up all the allocated resources in the ini_table_s struct.
* @param table
*/
void ini_table_destroy(ini_table_s* table);
/**
* @brief Creates an ini_table_s struct filled with data from the specified
* `file'. Returns NULL if the file can not be read.
* @param table
* @param file
* @return ini_table_s*
*/
bool ini_table_read_from_file(ini_table_s* table, const char* file);
/**
* @brief Writes the specified ini_table_s struct to the specified `file'.
* Returns false if the file could not be opened for writing, otherwise
* true.
* @param table
* @param file
* @return bool
*/
bool ini_table_write_to_file(ini_table_s* table, const char* file);
/**
* @brief Creates a new entry in the `table' containing the `key' and `value'
* provided if it does not exist. Otherwise, modifies an exsiting `key'
* with the new `value'
* @param table
* @param section_name
* @param key
* @param value
*/
void ini_table_create_entry(ini_table_s* table, const char* section_name, const char* key,
const char* value);
/**
* @brief Checks for the existance of an entry in the specified `table'. Returns
* false if the entry does not exist, otherwise true.
* @param table
* @param section_name
* @param key
* @return bool
*/
bool ini_table_check_entry(ini_table_s* table, const char* section_name, const char* key);
/**
* @brief Retrieves the unmodified value of the specified `key' in `section_name'.
* Returns NULL if the entry does not exist, otherwise a pointer to the
* entry value data.
* @param table
* @param section_name
* @param key
* @return const char*
*/
const char* ini_table_get_entry(ini_table_s* table, const char* section_name, const char* key);
/**
* @brief Retrieves the value of the specified `key' in `section_name', converted
* to int. Returns false on failure, otherwise true.
* @param table
* @param section_name
* @param key
* @param [out]value
* @return int
*/
bool ini_table_get_entry_as_int(ini_table_s* table, const char* section_name, const char* key,
int* value);
/**
* @brief Retrieves the value of the specified `key' in `section_name', converted
* to bool. Returns false on failure, true otherwise.
* @param table
* @param section_name
* @param key
* @param [out]value
* @return bool
*/
bool ini_table_get_entry_as_bool(ini_table_s* table, const char* section_name, const char* key,
bool* value);
// Ctn: make this non-static
ini_section_s* _ini_section_find(ini_table_s* table, const char* name);
#endif //REMOTE_PAD_CONFIG_H
+98
View File
@@ -0,0 +1,98 @@
#include <stdint.h>
#include <stdbool.h>
#include <orbis/_types/pad.h>
#include <orbis/_types/user.h>
#include <orbis/_types/pthread.h>
#ifndef REMOTE_PAD_PAD_H
#define REMOTE_PAD_PAD_H
#define ORBIS_HID_ERROR_ALREADY_LOGGED_OUT 0x803B0101
#define REMOTE_PAD_MAX_PADS 4
#define REMOTE_PAD_MAX_HISTORY 64
typedef struct circularBuf {
OrbisPadData data[REMOTE_PAD_MAX_HISTORY];
uint32_t head;
uint32_t tail;
} circularBuf;
void emptyPadInfo(OrbisPadInformation *info);
void emptyPadData(OrbisPadData *data);
void circularInit(circularBuf *buf);
void circularPush(circularBuf *buf, OrbisPadData *data);
void circularGetLatest(circularBuf *buf, OrbisPadData *data);
int32_t circularGet(circularBuf *buf, OrbisPadData *data, int32_t count);
typedef struct RemotePad RemotePad;
typedef const struct RemotePadDriver *const RemotePadDriverPtr;
typedef struct RemotePadDriver {
int32_t (*init)(RemotePadDriverPtr driver);
int32_t (*term)(RemotePadDriverPtr driver);
int32_t (*setLightBar)(RemotePad *pad, OrbisPadColor *inputColor);
int32_t (*resetLightBar)(RemotePad *pad);
int32_t (*setVibration)(RemotePad *pad, const OrbisPadVibeParam *param);
int32_t (*getControllerInformation)(RemotePad *pad, OrbisPadInformation *info);
int32_t (*read)(RemotePad *pad, OrbisPadData *data, int32_t count);
int32_t (*readState)(RemotePad *pad, OrbisPadData *data);
int32_t (*close)(RemotePad *pad);
// Driver specific global data
void *data;
} RemotePadDriver;
typedef struct RemotePad {
int32_t index;
int32_t handle;
int32_t userId;
const RemotePadDriver *driver;
} RemotePad;
typedef struct RemotePadService {
int32_t (*init)();
int32_t (*term)();
int32_t (*getPad)(int32_t handle, RemotePad **padPtr);
int32_t (*setLightBar)(int32_t handle, OrbisPadColor *inputColor);
int32_t (*resetLightBar)(int32_t handle);
int32_t (*setVibration)(int32_t handle, const OrbisPadVibeParam *param);
int32_t (*getControllerInformation)(int32_t handle, OrbisPadInformation *info);
int32_t (*read)(int32_t handle, OrbisPadData *data, int32_t count);
int32_t (*readState)(int32_t handle, OrbisPadData *data);
int32_t (*getHandle)(int32_t userId, uint32_t controller_type, uint32_t controller_index);
int32_t (*open)(int32_t userId, int32_t type, int32_t index, void *param);
int32_t (*close)(int32_t handle);
RemotePad pads[REMOTE_PAD_MAX_PADS];
OrbisPthreadMutex padMutex;
} RemotePadService;
RemotePadService *initRemotePadService();
void termRemotePadService(RemotePadService *);
#endif //REMOTE_PAD_PAD_H
+42
View File
@@ -0,0 +1,42 @@
#include <stdint.h>
#include <stdbool.h>
#include <orbis/_types/user.h>
#ifndef REMOTE_PAD_USER_H
#define REMOTE_PAD_USER_H
#define REMOTE_PAD_MAX_USERS 4
typedef struct RemoteUser {
int32_t index;
int32_t userId;
char userName[ORBIS_USER_SERVICE_MAX_USER_NAME_LENGTH + 1];
bool enabled;
bool isSystemUserId;
/// private
// whether user login event has been returned
bool isLoggedIn;
} RemoteUser;
typedef struct RemoteUserService {
int32_t (*init)();
int32_t (*term)();
RemoteUser *(*getUser)(int32_t userId);
RemoteUser *(*getUserByIndex)(int32_t index);
int32_t (*getUserName)(int32_t userId, char *username, size_t size);
int32_t (*setUserName)(int32_t userId, const char *username);
RemoteUser users[REMOTE_PAD_MAX_USERS];
} RemoteUserService;
RemoteUserService *initRemoteUserService();
void termRemoteUserService(RemoteUserService *);
#endif //REMOTE_PAD_USER_H
+52
View File
@@ -0,0 +1,52 @@
#include <Common.h>
#include <stdbool.h>
#define GOLDHEN_PATH "/data/GoldHEN"
#define TEX_ICON_SYSTEM "cxml://psnotification/tex_icon_system"
#define MAX_PATH_ 260
#define attr_module_hidden __attribute__((weak)) __attribute__((visibility("hidden")))
#define attr_public __attribute__((visibility("default")))
#if (__FINAL__) == 1
#define BUILD_TYPE "(Release)"
#define debug_printf(a, args...)
#else
#define BUILD_TYPE "(Debug)"
#define debug_printf(a, args...) klog("[%s] (%s:%d) " a, __func__,__FILE__, __LINE__, ##args)
#endif
#define HOOK_DEFINE(func, ...) \
HOOK_INIT(func); \
typedef int32_t (*func##_def)(__VA_ARGS__); \
int32_t func(__VA_ARGS__); \
int32_t func##_hook(__VA_ARGS__)
#define HOOK_PASS(func, ...) \
HOOK_CONTINUE(func, func##_def, __VA_ARGS__)
#ifndef SCE_OK
#define SCE_OK 0
#endif
#define final_printf(a, args...) klog("\033[32m(%s:%d)\033[0m " a, __FILE__, __LINE__, ##args)
#define print_proc_info() {\
final_printf("process info\n");\
final_printf("pid: %d\n", procInfo.pid);\
final_printf("name: %s\n", procInfo.name);\
final_printf("path: %s\n", procInfo.path);\
final_printf("titleid: %s\n", procInfo.titleid);\
final_printf("contentid: %s\n", procInfo.contentid);\
final_printf("version: %s\n", procInfo.version);\
final_printf("base_address: 0x%lx\n", procInfo.base_address);\
}
void Notify(const char* IconUri, const char *FMT, ...);
int32_t load_prx(const char *name, bool syscall);
// Takes hardcoded input string 2 to strlen against during compile time.
// startsWith(input_1, "input 2");
#define startsWith(str1, str2) (strncmp(str1, str2, __builtin_strlen(str2)) == 0)
+26
View File
@@ -0,0 +1,26 @@
# Mongoose
set(MONGOOSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/mongoose)
add_library(mongoose STATIC ${MONGOOSE_DIR}/mongoose.c)
target_include_directories(mongoose PUBLIC ${MONGOOSE_DIR})
target_compile_options(mongoose PRIVATE -ffunction-sections -fdata-sections -DMG_ENABLE_PACKED_FS=1)
# GoldHEN SDK
set(GoldHEN_SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/GoldHEN_Plugins_SDK)
set(GoldHEN_SDK_TARGET ${GoldHEN_SDK_DIR}/libGoldHEN_Hook.a PARENT_SCOPE)
file(GLOB GoldHEN_SOURCE_FILES ${GoldHEN_SDK_DIR}/source/*.c ${GoldHEN_SDK_DIR}/include/*.c ${GoldHEN_SDK_DIR}/crt/*.c)
set(GoldHEN_SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/GoldHEN_Plugins_SDK)
set(GoldHEN_SDK_BUIILD_FLAG "-j1")
if (DEBUG_SDK)
list(APPEND GoldHEN_SDK_BUIILD_FLAG DEBUGFLAGS=1)
endif ()
add_custom_target(goldhen_sdk
COMMAND ${CMAKE_MAKE_PROGRAM} libGoldHEN_Hook.a ${GoldHEN_SDK_BUIILD_FLAG}
WORKING_DIRECTORY ${GoldHEN_SDK_DIR}
DEPENDS ${GoldHEN_SOURCE_FILES}
)
# A fake target to make IDE happy
file(GLOB GoldHEN_SOURCE_FAKE ${GoldHEN_SDK_DIR}/source/*.c)
add_library(GoldHEN_SDK_FAKE STATIC ${GoldHEN_SOURCE_FILES})
target_include_directories(GoldHEN_SDK_FAKE PUBLIC ${GoldHEN_SDK_DIR}/include)
Submodule
+1
Submodule lib/mongoose added at 0a86bc0af2.
+286
View File
@@ -0,0 +1,286 @@
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "utils.h"
#define _atoi atoi
/// https://github.com/Teklad/tconfig > https://github.com/gimli2/tconfig
#include "config.h"
static ini_entry_s* _ini_entry_create(ini_section_s* section, const char* key, const char* value) {
if ((section->size % 10) == 0) {
section->entry =
(ini_entry_s*)realloc(section->entry, (10 + section->size) * sizeof(ini_entry_s));
}
ini_entry_s* entry = &section->entry[section->size++];
entry->key = (char*)malloc((strlen(key) + 1) * sizeof(char));
entry->value = (char*)malloc((strlen(value) + 1) * sizeof(char));
debug_printf("key: %s = value: %s\n", key, value);
strcpy(entry->key, key);
strcpy(entry->value, value);
return entry;
}
static ini_section_s* _ini_section_create(ini_table_s* table, const char* section_name) {
if ((table->size % 10) == 0) {
table->section =
(ini_section_s*)realloc(table->section, (10 + table->size) * sizeof(ini_section_s));
}
ini_section_s* section = &table->section[table->size++];
section->size = 0;
section->name = (char*)malloc((strlen(section_name) + 1) * sizeof(char));
strcpy(section->name, section_name);
section->entry = (ini_entry_s*)malloc(10 * sizeof(ini_entry_s));
return section;
}
// Ctn: make this non-static
ini_section_s* _ini_section_find(ini_table_s* table, const char* name) {
for (int i = 0; i < table->size; i++) {
if (strcmp(table->section[i].name, name) == 0) {
return &table->section[i];
}
}
return NULL;
}
static ini_entry_s* _ini_entry_find(ini_section_s* section, const char* key) {
for (int i = 0; i < section->size; i++) {
if (strcmp(section->entry[i].key, key) == 0) {
return &section->entry[i];
}
}
return NULL;
}
static ini_entry_s* _ini_entry_get(ini_table_s* table, const char* section_name, const char* key) {
ini_section_s* section = _ini_section_find(table, section_name);
if (section == NULL) {
return NULL;
}
ini_entry_s* entry = _ini_entry_find(section, key);
if (entry == NULL) {
return NULL;
}
return entry;
}
ini_table_s* ini_table_create() {
ini_table_s* table = (ini_table_s*)malloc(sizeof(ini_table_s));
table->size = 0;
table->section = (ini_section_s*)malloc(10 * sizeof(ini_section_s));
return table;
}
void ini_table_destroy(ini_table_s* table) {
for (int i = 0; i < table->size; i++) {
ini_section_s* section = &table->section[i];
for (int q = 0; q < section->size; q++) {
ini_entry_s* entry = &section->entry[q];
free(entry->key);
free(entry->value);
}
free(section->entry);
free(section->name);
}
free(table->section);
free(table);
}
int eof_hack(int c) {
static bool first_time = true;
if (first_time && c == EOF) {
first_time = false;
return INT_MAX;
}
return EOF;
}
bool ini_table_read_from_file(ini_table_s* table, const char* file) {
FILE* f = fopen(file, "r");
if (f == NULL) return false;
enum { Section, Key, Value, Comment } state = Section;
int c;
int position = 0;
int spaces = 0;
int line = 0;
int buffer_size = 128 * sizeof(char);
char* buf = (char*)malloc(buffer_size);
char* value = NULL;
ini_section_s* current_section = NULL;
memset(buf, '\0', buffer_size);
bool first_eol = false;
while (1) {
c = fgetc(f);
if (c == eof_hack(c)) break;
if (c == '\r') continue;
if (position > buffer_size - 2) {
buffer_size += 128 * sizeof(char);
size_t value_offset = value == NULL ? 0 : value - buf;
buf = (char*)realloc(buf, buffer_size);
memset(buf + position, '\0', buffer_size - position);
if (value != NULL) value = buf + value_offset;
}
switch (c) {
case ' ':
switch (state) {
case Value:
if (value[0] != '\0') spaces++;
break;
default:
if (buf[0] != '\0') spaces++;
break;
}
break;
case ';':
while (c != eof_hack(c) && c != '\n') {
c = fgetc(f);
}
// fallthrough
case '\n':
// fallthrough
case EOF:
if (first_eol) {
continue;
first_eol = true;
}
line++;
if (state == Value) {
if (current_section == NULL) {
current_section = _ini_section_create(table, "");
}
_ini_entry_create(current_section, buf, value);
value = NULL;
} else if (strlen(buf) > 1 && position && state == Key) {
if (current_section == NULL) {
current_section = _ini_section_create(table, "");
}
_ini_entry_create(current_section, buf, "");
} else if (state == Comment) {
if (current_section == NULL) {
current_section = _ini_section_create(table, "");
}
_ini_entry_create(current_section, buf, "");
} else if (state == Section) {
debug_printf("Section `%s' missing `]' operator.", buf);
} else if (state == Key && position) {
debug_printf("Key `%s' missing `=' operator.", buf);
}
memset(buf, '\0', buffer_size);
state = Key;
position = 0;
spaces = 0;
break;
case '[':
state = Section;
break;
case ']':
current_section = _ini_section_create(table, buf);
memset(buf, '\0', buffer_size);
position = 0;
spaces = 0;
state = Key;
break;
case '=':
if (state == Key) {
state = Value;
buf[position++] = '\0';
value = buf + position;
spaces = 0;
continue;
}
default:
for (; spaces > 0; spaces--) buf[position++] = ' ';
buf[position++] = c;
break;
}
}
free(buf);
if (fflush(f) == 0) fsync(fileno(f));
fclose(f);
return true;
}
bool ini_table_write_to_file(ini_table_s* table, const char* file) {
FILE* f = fopen(file, "w+");
if (f == NULL) return false;
for (int i = 0; i < table->size; i++) {
ini_section_s* section = &table->section[i];
fprintf(f, i > 0 ? "\n[%s]\n" : "[%s]\n", section->name);
for (int q = 0; q < section->size; q++) {
ini_entry_s* entry = &section->entry[q];
if (entry->key[0] == ';') {
fprintf(f, "%s\n", entry->key);
} else {
fprintf(f, "%s = %s\n", entry->key, entry->value);
}
}
}
if (fflush(f) == 0) fsync(fileno(f));
fclose(f);
return true;
}
void ini_table_create_entry(ini_table_s* table, const char* section_name, const char* key,
const char* value) {
ini_section_s* section = _ini_section_find(table, section_name);
if (section == NULL) {
section = _ini_section_create(table, section_name);
}
ini_entry_s* entry = _ini_entry_find(section, key);
if (entry == NULL) {
entry = _ini_entry_create(section, key, value);
} else {
free(entry->value);
entry->value = (char*)malloc((strlen(value) + 1) * sizeof(char));
strcpy(entry->value, value);
}
}
bool ini_table_check_entry(ini_table_s* table, const char* section_name, const char* key) {
return (_ini_entry_get(table, section_name, key) != NULL);
}
const char* ini_table_get_entry(ini_table_s* table, const char* section_name, const char* key) {
ini_entry_s* entry = _ini_entry_get(table, section_name, key);
if (entry == NULL) {
return NULL;
}
return entry->value;
}
bool ini_table_get_entry_as_int(ini_table_s* table, const char* section_name, const char* key,
int* value) {
const char* val = ini_table_get_entry(table, section_name, key);
if (val == NULL) {
return false;
}
*value = _atoi(val);
return true;
}
bool ini_table_get_entry_as_bool(ini_table_s* table, const char* section_name, const char* key,
bool* value) {
const char* val = ini_table_get_entry(table, section_name, key);
if (val == NULL) {
return false;
}
if (strcasecmp(val, "on") == 0 || strcasecmp(val, "true") == 0 || strcasecmp(val, "1") == 0) {
*value = true;
} else {
*value = false;
}
return true;
}
+334
View File
@@ -0,0 +1,334 @@
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <Patcher.h>
#include "utils.h"
#include "pad.h"
#include "user.h"
#include "config.h"
attr_public const char *g_pluginName = "remote_gamepad";
attr_public const char *g_pluginDesc = "Control your ps4 through network";
attr_public const char *g_pluginAuth = "xfangfang";
attr_public uint32_t g_pluginVersion = 0x00000100; // 1.00
#define PLUGIN_CONFIG_PATH GOLDHEN_PATH "/remote_pad.ini"
#define PLUGIN_DEFAULT_SECTION "default"
static Patcher *scePadReadExtPatcher;
static Patcher *scePadReadStateExtPatcher;
static Patcher *sceUserServiceGetPsnPasswordForDebugPatcher;
static RemoteUserService *remoteUserService;
static RemotePadService *remotePad;
static bool prxLoaded = false;
int32_t scePadReadExt(int32_t, OrbisPadData *, int32_t);
int32_t scePadReadStateExt(int32_t, OrbisPadData *);
int32_t sceUserServiceGetPsnPasswordForDebug(int32_t, char *, size_t);
HOOK_DEFINE(scePadOpen, int32_t userId, int32_t type, int32_t index, void *param) {
RemoteUser *user = remoteUserService->getUser(userId);
if (user != NULL)
return remotePad->open(userId, type, user->index, param);
return HOOK_PASS(scePadOpen, userId, type, index, param);
}
HOOK_DEFINE(scePadClose, int32_t handle) {
if (remotePad->close(handle) == SCE_OK)
return SCE_OK;
return HOOK_PASS(scePadClose, handle);
}
HOOK_DEFINE(scePadGetHandle, int32_t userId, uint32_t controller_type, uint32_t controller_index) {
RemoteUser *user = remoteUserService->getUser(userId);
if (user != NULL)
return remotePad->getHandle(userId, controller_type, controller_index);
return HOOK_PASS(scePadGetHandle, userId, controller_type, controller_index);
}
HOOK_DEFINE(scePadReadState, int32_t handle, OrbisPadData *data) {
if (remotePad->readState(handle, data) == SCE_OK)
return SCE_OK;
return scePadReadStateExt(handle, data);
}
HOOK_DEFINE(scePadRead, int32_t handle, OrbisPadData *data, int32_t count) {
int32_t ret = remotePad->read(handle, data, count);
if (ret >= 0)
return ret;
return scePadReadExt(handle, data, count);
}
HOOK_DEFINE(scePadGetControllerInformation, int32_t handle, OrbisPadInformation *info) {
if (remotePad->getControllerInformation(handle, info) == SCE_OK)
return SCE_OK;
return HOOK_PASS(scePadGetControllerInformation, handle, info);
}
HOOK_DEFINE(scePadSetLightBar, int32_t handle, OrbisPadColor *inputColor) {
if (remotePad->setLightBar(handle, inputColor) == SCE_OK)
return SCE_OK;
return HOOK_PASS(scePadSetLightBar, handle, inputColor);
}
HOOK_DEFINE(scePadResetLightBar, int32_t handle) {
if (remotePad->resetLightBar(handle) == SCE_OK)
return SCE_OK;
return HOOK_PASS(scePadResetLightBar, handle);
}
HOOK_DEFINE(scePadSetVibration, int32_t handle, const OrbisPadVibeParam *param) {
if (remotePad->setVibration(handle, param) == SCE_OK)
return SCE_OK;
return HOOK_PASS(scePadSetVibration, handle, param);
}
HOOK_DEFINE(sceUserServiceGetUserName, int32_t userId, char *username, size_t size) {
if (remoteUserService->getUserName(userId, username, size) == SCE_OK)
return SCE_OK;
return sceUserServiceGetPsnPasswordForDebug(userId, username, size);
}
inline static RemoteUser *findFreeUser(int32_t *index) {
while(*index < REMOTE_PAD_MAX_USERS) {
RemoteUser *user = &remoteUserService->users[*index];
if (user->enabled && !user->isSystemUserId)
return user;
(*index)++;
}
return NULL;
}
HOOK_DEFINE(sceUserServiceGetLoginUserIdList, OrbisUserServiceLoginUserIdList *list) {
int32_t ret = HOOK_PASS(sceUserServiceGetLoginUserIdList, list);
if (ret < 0)
return ret;
for (int i = 0; i < ORBIS_USER_SERVICE_MAX_LOGIN_USERS; i++) {
if (list->userId[i] == ORBIS_USER_SERVICE_USER_ID_INVALID)
continue;
for (int j = 0; j < REMOTE_PAD_MAX_USERS; j++) {
RemoteUser *user = remoteUserService->getUserByIndex(j);
if (user != NULL && user->userId == list->userId[i])
user->isSystemUserId = true;
}
}
int remoteUserIndex = 0;
for (int i = 0; i < ORBIS_USER_SERVICE_MAX_LOGIN_USERS; i++) {
if (list->userId[i] != ORBIS_USER_SERVICE_USER_ID_INVALID)
continue;
// Fill empty position with remote users
RemoteUser *user = findFreeUser(&remoteUserIndex);
if (user == NULL) continue;
list->userId[i] = user->userId;
remoteUserIndex++;
}
// Disable extra remote users
for (int i = remoteUserIndex; i < REMOTE_PAD_MAX_USERS; i++) {
RemoteUser *user = remoteUserService->getUserByIndex(remoteUserIndex++);
if (user != NULL && !user->isSystemUserId)
user->enabled = false;
}
return ret;
}
HOOK_DEFINE(sceUserServiceGetEvent, OrbisUserServiceEvent *event) {
static int32_t loggedUserCount = 0;
int ret = HOOK_PASS(sceUserServiceGetEvent, event);
if (ret == SCE_OK) {
loggedUserCount += event->event == SCE_USER_SERVICE_EVENT_TYPE_LOGIN ? 1 : -1;
return ret;
}
static bool no_event = false;
if (!no_event && ret == ORBIS_USER_SERVICE_ERROR_NO_EVENT && loggedUserCount < ORBIS_USER_SERVICE_MAX_LOGIN_USERS) {
for (int i = 0; i < REMOTE_PAD_MAX_USERS; i++) {
RemoteUser *user = remoteUserService->getUserByIndex(i);
if (user == NULL || user->isLoggedIn || !user->enabled || user->isSystemUserId)
continue;
user->isLoggedIn = true;
event->userId = user->userId;
event->event = SCE_USER_SERVICE_EVENT_TYPE_LOGIN;
loggedUserCount++;
return SCE_OK;
}
no_event = true;
}
return ret;
}
inline static bool file_exists(const char* filename) {
struct stat buff;
return stat(filename, &buff) == 0 ? true : false;
}
int32_t load_config(ini_table_s* table, const char* section_name) {
char key[16];
for(int i = 0; i < REMOTE_PAD_MAX_USERS; i++) {
snprintf(key, sizeof(key), "user%d_enabled", i);
ini_table_get_entry_as_bool(table, section_name, key, &remoteUserService->users[i].enabled);
snprintf(key, sizeof(key), "user%d_id", i);
ini_table_get_entry_as_int(table, section_name, key, &remoteUserService->users[i].userId);
snprintf(key, sizeof(key), "user%d_name", i);
const char* name = ini_table_get_entry(table, section_name, key);
remoteUserService->setUserName(remoteUserService->users[i].userId, name);
}
return 0;
}
int32_t attr_public plugin_load(int32_t argc, const char *argv[]) {
final_printf("[GoldHEN] %s Plugin Started.\n", g_pluginName);
final_printf("[GoldHEN] <%s\\Ver.0x%08x> %s\n", g_pluginName, g_pluginVersion, __func__);
final_printf("[GoldHEN] Plugin Author(s): %s\n", g_pluginAuth);
if (load_prx("libScePad.sprx", true))
return -1;
if (load_prx("libSceUserService.sprx", false))
return -1;
prxLoaded = true;
remoteUserService = initRemoteUserService();
if (!remoteUserService) {
final_printf("Failed to init remote user service\n");
return -1;
}
remotePad = initRemotePadService();
if (!remotePad) {
final_printf("Failed to init remote pad service\n");
return -1;
}
// scePadReadExt => scePadRead
scePadReadExtPatcher = (Patcher *) malloc(sizeof(Patcher));
Patcher_Construct(scePadReadExtPatcher);
uint8_t xor_ecx_ecx[5] = {0x31, 0xC9, 0x90, 0x90, 0x90};
Patcher_Install_Patch(scePadReadExtPatcher, (uint64_t) scePadReadExt, xor_ecx_ecx, sizeof(xor_ecx_ecx));
// scePadReadStateExt => scePadReadState
scePadReadStateExtPatcher = (Patcher *) malloc(sizeof(Patcher));
Patcher_Construct(scePadReadStateExtPatcher);
uint8_t xor_edx_edx[5] = {0x31, 0xD2, 0x90, 0x90, 0x90};
Patcher_Install_Patch(scePadReadStateExtPatcher, (uint64_t) scePadReadStateExt, xor_edx_edx, sizeof(xor_edx_edx));
// sceUserServiceGetPsnPasswordForDebug => sceUserServiceGetUserName
sceUserServiceGetPsnPasswordForDebugPatcher = (Patcher *) malloc(sizeof(Patcher));
Patcher_Construct(sceUserServiceGetPsnPasswordForDebugPatcher);
uint8_t mov_esi_01[5] = {0xBE, 0x01, 0x00, 0x00, 0x00};
Patcher_Install_Patch(sceUserServiceGetPsnPasswordForDebugPatcher,
(uint64_t) sceUserServiceGetPsnPasswordForDebug + 17, mov_esi_01, sizeof(mov_esi_01));
HOOK32(scePadOpen);
HOOK32(scePadGetHandle);
HOOK32(scePadRead);
HOOK32(scePadReadState);
HOOK32(scePadGetControllerInformation);
HOOK32(scePadSetLightBar);
HOOK32(scePadResetLightBar);
HOOK32(scePadSetVibration);
HOOK32(scePadClose);
HOOK32(sceUserServiceGetLoginUserIdList);
HOOK32(sceUserServiceGetUserName);
HOOK32(sceUserServiceGetEvent);
// load config
struct proc_info procInfo;
if (sys_sdk_proc_info(&procInfo) == 0) {
print_proc_info();
} else {
final_printf("failed to initialise\n");
return -1;
}
if (!file_exists(PLUGIN_CONFIG_PATH)) {
final_printf("Not found config file at: \"%s\"\n", PLUGIN_CONFIG_PATH);
return 0;
}
ini_table_s* config = ini_table_create();
if (config == NULL) {
final_printf("Config parser failed to initialise\n");
return -1;
}
if (!ini_table_read_from_file(config, PLUGIN_CONFIG_PATH)) {
final_printf("Config parser failed to parse config: %s\n", PLUGIN_CONFIG_PATH);
return -1;
}
final_printf("Section is TitleID [%s]\n", procInfo.titleid);
for (int i = 0; i < config->size; i++) {
ini_section_s* section = &config->section[i];
if (section == NULL) continue;
if (strcmp(section->name, PLUGIN_DEFAULT_SECTION) == 0) {
final_printf("Section [%s] is default\n", section->name);
load_config(config, section->name);
} else if (strcmp(section->name, procInfo.titleid) == 0) {
final_printf("Section is TitleID [%s]\n", procInfo.titleid);
load_config(config, section->name);
}
}
ini_table_destroy(config);
return 0;
}
int32_t attr_public plugin_unload(int32_t argc, const char *argv[]) {
final_printf("[GoldHEN] <%s\\Ver.0x%08x> %s\n", g_pluginName, g_pluginVersion, __func__);
final_printf("[GoldHEN] %s Plugin Unloaded.\n", g_pluginName);
// If the prx is not loaded correctly, then we exit directly
if (!prxLoaded)
return 0;
termRemoteUserService(remoteUserService);
termRemotePadService(remotePad);
UNHOOK(scePadOpen);
UNHOOK(scePadGetHandle);
UNHOOK(scePadRead);
UNHOOK(scePadReadState);
UNHOOK(scePadGetControllerInformation);
UNHOOK(scePadSetLightBar);
UNHOOK(scePadResetLightBar);
UNHOOK(scePadSetVibration);
UNHOOK(scePadClose);
UNHOOK(sceUserServiceGetLoginUserIdList);
UNHOOK(sceUserServiceGetUserName);
UNHOOK(sceUserServiceGetEvent);
Patcher_Destroy(scePadReadExtPatcher);
Patcher_Destroy(scePadReadStateExtPatcher);
Patcher_Destroy(sceUserServiceGetPsnPasswordForDebugPatcher);
free(scePadReadExtPatcher);
free(scePadReadStateExtPatcher);
free(sceUserServiceGetPsnPasswordForDebugPatcher);
return 0;
}
int32_t attr_module_hidden module_start(int64_t argc, const void *args) {
return 0;
}
int32_t attr_module_hidden module_stop(int64_t argc, const void *args) {
return 0;
}
+257
View File
@@ -0,0 +1,257 @@
#include <stdio.h>
#include <orbis/_types/errors.h>
#include <orbis/libkernel.h>
#include "pad.h"
#include "utils.h"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
extern const RemotePadDriver dummyDriver;
extern const RemotePadDriver wsDriver;
extern const RemotePadDriver usbDriver;
static const RemotePadDriver *const padDrivers[] = {&dummyDriver, &wsDriver};
static int32_t init();
static int32_t term();
static int32_t getPad(int32_t handle, RemotePad **padPtr);
static int32_t padSetLightBar(int32_t handle, OrbisPadColor *inputColor);
static int32_t padResetLightBar(int32_t handle);
static int32_t padSetVibration(int32_t handle, const OrbisPadVibeParam *param);
static int32_t padGetControllerInformation(int32_t handle, OrbisPadInformation *info);
static int32_t padRead(int32_t handle, OrbisPadData *data, int32_t count);
static int32_t padReadState(int32_t handle, OrbisPadData *data);
static int32_t padGetHandle(int32_t userId, uint32_t controller_type, uint32_t controller_index);
static int32_t padOpen(int32_t userId, int32_t type, int32_t index, void *param);
static int32_t padClose(int32_t handle);
#define GET_PAD(handle) NULL; \
int ret = rps.getPad(handle, &pad); \
if (ret != 0) \
return ret;
RemotePadService rps = {
.init = init,
.term = term,
.getPad = getPad,
.setLightBar = padSetLightBar,
.resetLightBar = padResetLightBar,
.setVibration = padSetVibration,
.getControllerInformation = padGetControllerInformation,
.read = padRead,
.readState = padReadState,
.getHandle = padGetHandle,
.open = padOpen,
.close = padClose,
};
static int32_t init() {
int ret;
for (int i = 0; i < ARRAY_SIZE(padDrivers); i++) {
if (padDrivers[i]->init(padDrivers[i]) != 0) {
return -1;
}
}
ret = scePthreadMutexInit(&rps.padMutex, 0, "padMtx");
if (ret < 0) {
final_printf("[RemotePad]: failed to init the pad mutex, 0x%X\n", ret);
return -1;
}
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
rps.pads[i].index = i;
rps.pads[i].handle = 1000 + i;
rps.pads[i].userId = 0;
}
return 0;
}
static int32_t term() {
scePthreadMutexLock(&rps.padMutex);
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
if (rps.pads[i].userId != 0) {
rps.pads[i].driver->close(&rps.pads[i]);
}
}
scePthreadMutexUnlock(&rps.padMutex);
return 0;
}
static int32_t getPad(int32_t handle, RemotePad **padPtr) {
RemotePad *pad = NULL;
if (padPtr == NULL)
return ORBIS_PAD_ERROR_INVALID_ARG;
scePthreadMutexLock(&rps.padMutex);
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
if (rps.pads[i].handle == handle) {
pad = &rps.pads[i];
break;
}
}
scePthreadMutexUnlock(&rps.padMutex);
if (pad == NULL)
return ORBIS_PAD_ERROR_INVALID_HANDLE;
else if (pad->userId == 0)
return ORBIS_HID_ERROR_ALREADY_LOGGED_OUT;
*padPtr = pad;
return 0;
}
static int32_t padSetLightBar(int32_t handle, OrbisPadColor *inputColor) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->setLightBar(pad, inputColor);
}
static int32_t padResetLightBar(int32_t handle) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->resetLightBar(pad);
}
static int32_t padSetVibration(int32_t handle, const OrbisPadVibeParam *param) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->setVibration(pad, param);
}
static int32_t padGetControllerInformation(int32_t handle, OrbisPadInformation *info) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->getControllerInformation(pad, info);
}
static int32_t padRead(int32_t handle, OrbisPadData *data, int32_t count) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->read(pad, data, count);
}
static int32_t padReadState(int32_t handle, OrbisPadData *data) {
RemotePad *pad = GET_PAD(handle);
return pad->driver->readState(pad, data);
}
static int32_t padGetHandle(int32_t userId, uint32_t controller_type, uint32_t controller_index) {
int32_t handle = 0;
scePthreadMutexLock(&rps.padMutex);
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
if (rps.pads[i].userId == userId) {
handle = rps.pads[i].handle;
break;
}
}
scePthreadMutexUnlock(&rps.padMutex);
if (handle != 0)
return handle;
return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE;
}
static int32_t padOpen(int32_t userId, int32_t type, int32_t index, void *param) {
int32_t handle = 0;
if (index < 0 || index >= REMOTE_PAD_MAX_PADS)
return ORBIS_PAD_ERROR_INVALID_ARG;
scePthreadMutexLock(&rps.padMutex);
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
if (rps.pads[i].userId == userId) {
handle = ORBIS_PAD_ERROR_ALREADY_OPENED;
break;
}
}
if (handle != 0) {
scePthreadMutexUnlock(&rps.padMutex);
return handle;
}
// TODO: 根据配置文件选择驱动
rps.pads[index].userId = userId;
rps.pads[index].driver = &wsDriver;
handle = rps.pads[index].handle;
scePthreadMutexUnlock(&rps.padMutex);
if (handle != 0)
return handle;
return ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN;
}
static int32_t padClose(int32_t handle) {
RemotePad *pad = GET_PAD(handle);
pad->userId = 0;
return pad->driver->close(pad);
}
RemotePadService *initRemotePadService() {
rps.init();
return &rps;
}
void termRemotePadService(RemotePadService *remotePadService) {
if (remotePadService)
remotePadService->term();
}
void emptyPadData(OrbisPadData *data) {
memset(data, 0, sizeof(OrbisPadData));
data->connected = 1;
data->count = 1;
data->leftStick.x = 128;
data->leftStick.y = 128;
data->rightStick.x = 128;
data->rightStick.y = 128;
data->timestamp = sceKernelGetProcessTime();
}
void emptyPadInfo(OrbisPadInformation *info) {
memset(info, 0, sizeof(OrbisPadInformation));
info->connected = 1;
info->count = 1;
info->stickDeadzoneL = 0x0d;
info->stickDeadzoneR = 0x0d;
info->touchResolutionX = 1920;
info->touchResolutionY = 942;
info->touchpadDensity = 44.86f;
}
void circularInit(circularBuf *buf) {
memset(buf, 0, sizeof(circularBuf));
// If there is no data at start, this empty data will be returned
emptyPadData(&buf->data[REMOTE_PAD_MAX_HISTORY - 1]);
}
void circularPush(circularBuf *buf, OrbisPadData *data) {
memcpy(&buf->data[buf->head], data, sizeof(OrbisPadData));
buf->head = (buf->head + 1) % REMOTE_PAD_MAX_HISTORY;
if (buf->head == buf->tail)
buf->tail = (buf->tail + 1) % REMOTE_PAD_MAX_HISTORY;
}
void circularGetLatest(circularBuf *buf, OrbisPadData *data) {
uint32_t lastDataIndex = (buf->head + REMOTE_PAD_MAX_HISTORY - 1) % REMOTE_PAD_MAX_HISTORY;
memcpy(data, &buf->data[lastDataIndex], sizeof(OrbisPadData));
}
int32_t circularGet(circularBuf *buf, OrbisPadData *data, int32_t count) {
int32_t ret = count;
if (ret > REMOTE_PAD_MAX_HISTORY)
ret = REMOTE_PAD_MAX_HISTORY;
if (buf->tail == buf->head) {
emptyPadData(data);
return 0;
}
while (count > 0 && buf->tail != buf->head) {
memcpy(data, &buf->data[buf->tail], sizeof(OrbisPadData));
buf->tail = (buf->tail + 1) % REMOTE_PAD_MAX_HISTORY;
data++;
count--;
}
return ret;
}
+56
View File
@@ -0,0 +1,56 @@
#include <orbis/libkernel.h>
#include <string.h>
#include "pad.h"
static int32_t dummySetLightBar(RemotePad *pad, OrbisPadColor *inputColor) {
return 0;
}
static int32_t dummyResetLightBar(RemotePad *pad) {
return 0;
}
static int32_t dummySetVibration(RemotePad *pad, const OrbisPadVibeParam *param) {
return 0;
}
static int32_t dummyGetControllerInformation(RemotePad *pad, OrbisPadInformation *info) {
emptyPadInfo(info);
return 0;
}
static int32_t dummyRead(RemotePad *pad, OrbisPadData *data, int32_t count) {
emptyPadData(data);
return 1;
}
static int32_t dummyReadState(RemotePad *pad, OrbisPadData *data) {
emptyPadData(data);
return 0;
}
static int32_t dummyClose(RemotePad *pad) {
return 0;
}
int32_t dummyInit(RemotePadDriverPtr driver) {
return 0;
}
int32_t dummyTerm(RemotePadDriverPtr driver) {
return 0;
}
const struct RemotePadDriver dummyDriver = {
.init = dummyInit,
.term = dummyTerm,
.setLightBar = dummySetLightBar,
.resetLightBar = dummyResetLightBar,
.setVibration = dummySetVibration,
.getControllerInformation = dummyGetControllerInformation,
.read = dummyRead,
.readState = dummyReadState,
.close = dummyClose,
.data = NULL
};
+3
View File
@@ -0,0 +1,3 @@
//
// Created by fang on 2025/3/6.
//
+237
View File
@@ -0,0 +1,237 @@
#include <string.h>
#include <mongoose.h>
#include <orbis/libkernel.h>
#include <ifaddrs.h>
#include "pad.h"
#include "utils.h"
typedef struct wsDriverData {
struct mg_mgr mgr;
struct mg_rpc *rpc_head;
circularBuf padData[REMOTE_PAD_MAX_PADS];
OrbisPthreadMutex padMutex;
bool running;
} wsDriverData;
// Needed by mongoose errno() function
int *attr_public __errno_location(void) {
int *__error(void);
return __error();
}
#define REMOTE_PAD_WS_PORT "4263"
static const char *s_listen_on = "ws://0.0.0.0:" REMOTE_PAD_WS_PORT;
static OrbisPthread serverThread;
static wsDriverData globalDriverData = {
.rpc_head = NULL,
.running = false
};
static void notifyServerAddress() {
struct ifaddrs *ifaddr;
char ip[INET_ADDRSTRLEN];
if(getifaddrs(&ifaddr) == -1) {
final_printf("Failed to get network interfaces\n");
return;
}
// Enumerate all AF_INET IPs
for(struct ifaddrs *ifa=ifaddr; ifa!=NULL; ifa=ifa->ifa_next) {
if(ifa->ifa_addr == NULL) {
continue;
}
if(ifa->ifa_addr->sa_family != AF_INET) {
continue;
}
struct sockaddr_in *in = (struct sockaddr_in*)ifa->ifa_addr;
inet_ntop(AF_INET, &(in->sin_addr), ip, sizeof(ip));
if (strncmp(ip, "127.0.0.1", sizeof(ip)) == 0) {
continue;
}
Notify(TEX_ICON_SYSTEM, "RemotePad Server:\n %s:" REMOTE_PAD_WS_PORT, ip);
}
freeifaddrs(ifaddr);
}
static inline void climp(uint32_t *val, uint32_t min, uint32_t max) {
if (*val < min) *val = min;
if (*val > max) *val = max;
}
static void rpc_update(struct mg_rpc_req *r) {
wsDriverData *ctx = r->rpc->fn_data;
uint32_t index = mg_json_get_long(r->frame, "$.params[0]", 0);
uint32_t button = mg_json_get_long(r->frame, "$.params[1]", 0);
uint32_t leftStickX = mg_json_get_long(r->frame, "$.params[2]", 128);
uint32_t leftStickY = mg_json_get_long(r->frame, "$.params[3]", 128);
uint32_t rightStickX = mg_json_get_long(r->frame, "$.params[4]", 128);
uint32_t rightStickY = mg_json_get_long(r->frame, "$.params[5]", 128);
uint32_t left2 = mg_json_get_long(r->frame, "$.params[6]", 0);
uint32_t right2 = mg_json_get_long(r->frame, "$.params[7]", 0);
climp(&leftStickX, 0, 255);
climp(&leftStickY, 0, 255);
climp(&rightStickX, 0, 255);
climp(&rightStickY, 0, 255);
climp(&left2, 0, 255);
climp(&right2, 0, 255);
if (index >= REMOTE_PAD_MAX_PADS)
return;
OrbisPadData padData;
memset(&padData, 0, sizeof(OrbisPadData));
padData.buttons = button;
padData.leftStick.x = leftStickX;
padData.leftStick.y = leftStickY;
padData.rightStick.x = rightStickX;
padData.rightStick.y = rightStickY;
padData.analogButtons.l2 = left2;
padData.analogButtons.r2 = right2;
padData.timestamp = sceKernelGetProcessTime();
padData.count = 1;
padData.connected = 1;
scePthreadMutexLock(&ctx->padMutex);
circularPush(&ctx->padData[index], &padData);
scePthreadMutexUnlock(&ctx->padMutex);
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_WS_OPEN) {
c->data[0] = 'W'; // Mark this connection as an established WS client
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_str *connection = mg_http_get_header(hm, "Connection");
if (mg_strcasecmp(*connection, mg_str("Upgrade")) == 0) {
mg_ws_upgrade(c, hm, NULL);
return;
}
// Serve static files
struct mg_http_serve_opts opts = {.root_dir = "/client", .fs = &mg_fs_packed};
mg_http_serve_dir(c, ev_data, &opts);
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data
wsDriverData *data = (wsDriverData *) c->fn_data;
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
struct mg_iobuf io = {0, 0, 0, 512};
struct mg_rpc_req r = {&data->rpc_head, 0, mg_pfn_iobuf, &io, 0, wm->data};
mg_rpc_process(&r);
if (io.buf) mg_ws_send(c, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
mg_iobuf_free(&io);
}
}
void *websocketThread(void *thread_arg) {
wsDriverData *ctx = (wsDriverData *) thread_arg;
mg_mgr_init(&ctx->mgr);
mg_log_set(MG_LL_NONE);
mg_rpc_add(&ctx->rpc_head, mg_str("u"), rpc_update, ctx);
mg_rpc_add(&ctx->rpc_head, mg_str("rpc.list"), mg_rpc_list, &ctx->rpc_head);
final_printf("Starting WS listener on %s\n", s_listen_on);
mg_http_listen(&ctx->mgr, s_listen_on, fn, ctx);
notifyServerAddress();
while (ctx->running) {
mg_mgr_poll(&ctx->mgr, 100);
}
mg_mgr_free(&ctx->mgr);
mg_rpc_del(&ctx->rpc_head, NULL);
ctx->running = false;
return (void *) 0;
}
static int32_t wsSetLightBar(RemotePad *pad, OrbisPadColor *inputColor) {
return 0;
}
static int32_t wsResetLightBar(RemotePad *pad) {
return 0;
}
static int32_t wsSetVibration(RemotePad *pad, const OrbisPadVibeParam *param) {
return 0;
}
static int32_t wsGetControllerInformation(RemotePad *pad, OrbisPadInformation *info) {
emptyPadInfo(info);
return 0;
}
static int32_t wsRead(RemotePad *pad, OrbisPadData *data, int32_t count) {
wsDriverData *ctx = pad->driver->data;
int32_t ret;
scePthreadMutexLock(&ctx->padMutex);
ret = circularGet(&ctx->padData[pad->index], data, count);
scePthreadMutexUnlock(&ctx->padMutex);
return ret;
}
static int32_t wsReadState(RemotePad *pad, OrbisPadData *data) {
wsDriverData *ctx = pad->driver->data;
scePthreadMutexLock(&ctx->padMutex);
circularGetLatest(&ctx->padData[pad->index], data);
scePthreadMutexUnlock(&ctx->padMutex);
return 0;
}
static int32_t wsClose(RemotePad *pad) {
return 0;
}
static int32_t wsInit(RemotePadDriverPtr driver) {
wsDriverData *ctx = driver->data;
int ret = 0;
if (ctx->running) {
final_printf("[RemotePad]: ws driver is already initialized\n");
return 0;
}
ret = scePthreadMutexInit(&ctx->padMutex, 0, "wsSrvMtx");
if (ret < 0) {
final_printf("[RemotePad]: failed to init the server mutex, 0x%X\n", ret);
return -1;
}
for (int i = 0; i < REMOTE_PAD_MAX_PADS; i++) {
circularInit(&ctx->padData[i]);
}
ctx->running = true;
ret = scePthreadCreate(&serverThread, 0, (void *) &websocketThread, ctx, "WsSrvThr");
if (ret < 0) {
final_printf("[RemotePad]: Failed to spawn a ws server thread, 0x%X\n", ret);
return ret;
}
return 0;
}
static int32_t wsTerm(RemotePadDriverPtr driver) {
wsDriverData *ctx = driver->data;
if (ctx)
ctx->running = false;
return 0;
}
const struct RemotePadDriver wsDriver = {
.init = wsInit,
.term = wsTerm,
.setLightBar = wsSetLightBar,
.resetLightBar = wsResetLightBar,
.setVibration = wsSetVibration,
.getControllerInformation = wsGetControllerInformation,
.read = wsRead,
.readState = wsReadState,
.close = wsClose,
.data = &globalDriverData
};
+101
View File
@@ -0,0 +1,101 @@
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <orbis/_types/errors.h>
#include "user.h"
#include "config.h"
static int32_t init();
static int32_t term();
static RemoteUser *getUser(int32_t);
static RemoteUser *getUserByIndex(int32_t);
static int32_t getUserName(int32_t userId, char *username, size_t size);
static int32_t setUserName(int32_t userId, const char *username);
static RemoteUserService rus = {
.init = init,
.term = term,
.getUser = getUser,
.getUserByIndex = getUserByIndex,
.getUserName = getUserName,
.setUserName = setUserName,
};
static int32_t init() {
for (int i = 0; i < REMOTE_PAD_MAX_USERS; i++) {
rus.users[i].index = i;
rus.users[i].userId = 0x20000000 + i; // Guest user id
rus.users[i].isLoggedIn = false;
rus.users[i].isSystemUserId = false;
rus.users[i].enabled = true;
snprintf(rus.users[i].userName, ORBIS_USER_SERVICE_MAX_USER_NAME_LENGTH, "Remote%d", i);
}
return 0;
}
static int32_t term() {
return 0;
}
static RemoteUser *getUser(int32_t userId) {
for (int i = 0; i < REMOTE_PAD_MAX_USERS; i++) {
if (rus.users[i].userId == userId) {
return &rus.users[i];
}
}
return NULL;
}
static RemoteUser *getUserByIndex(int32_t index) {
if (index >= REMOTE_PAD_MAX_USERS || index < 0)
return NULL;
return &rus.users[index];
}
static int32_t getUserName(int32_t userId, char *username, size_t size) {
RemoteUser *user = rus.getUser(userId);
if (user == NULL)
return ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN;
if (size < strlen(user->userName) + 1)
return ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT;
strcpy(username, user->userName);
return 0;
}
static int32_t setUserName(int32_t userId, const char *username) {
RemoteUser *user = rus.getUser(userId);
if (user == NULL)
return ORBIS_USER_SERVICE_ERROR_NOT_LOGGED_IN;
if (username == NULL)
return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT;
size_t len = strlen(username);
if (len < 3 || len > 16) {
return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT;
}
strcpy(user->userName, username);
return 0;
}
RemoteUserService *initRemoteUserService() {
rus.init();
return &rus;
}
void termRemoteUserService(RemoteUserService *remoteUserService) {
if (remoteUserService)
remoteUserService->term();
}
+48
View File
@@ -0,0 +1,48 @@
#include "utils.h"
#include <orbis/libkernel.h>
int32_t load_prx(const char *name, bool syscall) {
int32_t ret;
char module[256];
snprintf(module, 256, "/%s/common/lib/%s", sceKernelGetFsSandboxRandomWord(), name);
//TODO: what's the difference ?
if (syscall) {
sys_dynlib_load_prx(module, &ret);
} else {
ret = sceKernelLoadStartModule(module, 0, 0, 0, 0, 0);
}
final_printf("%s prx id = %d\n", name, ret);
if (ret < 0) {
Notify(TEX_ICON_SYSTEM, "[RemotePad]\n %s load failed 0x%X", name, ret);
return 1;
}
return 0;
}
// Taken from https://github.com/Al-Azif/ps4-notifi
void Notify(const char *p_Uri, const char *p_Format, ...) {
OrbisNotificationRequest s_Request;
memset(&s_Request, '\0', sizeof(s_Request));
s_Request.reqId = NotificationRequest;
s_Request.unk3 = 0;
s_Request.useIconImageUri = 1;
s_Request.targetId = -1;
// Maximum size to move is destination size - 1 to allow for null terminator
if (p_Uri != NULL && strnlen(p_Uri, sizeof(s_Request.iconUri)) + 1 < sizeof(s_Request.iconUri)) {
strncpy(s_Request.iconUri, p_Uri, strnlen(p_Uri, sizeof(s_Request.iconUri) - 1));
} else {
s_Request.useIconImageUri = 0;
}
va_list p_Args;
va_start(p_Args, p_Format);
// p_Format is controlled externally, some compiler/linter options will mark this as a security issue
vsnprintf(s_Request.message, sizeof(s_Request.message), p_Format, p_Args);
va_end(p_Args);
sceKernelSendNotificationRequest(NotificationRequest, &s_Request, sizeof(s_Request), 0);
}