mirror of
https://github.com/MoHadiShibli/remotePad-ng.git
synced 2026-10-06 12:00:31 +02:00
init
This commit is contained in:
commit
a2fe88cdcb
22 files changed
+2382
No files matched your search
@@ -0,0 +1,8 @@
|
||||
|
||||
# CMake build directory
|
||||
cmake-build*/
|
||||
build*/
|
||||
|
||||
.idea/
|
||||
|
||||
*.gz
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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/GoldHEN_Plugins_SDK added at bcea3c7ef0.
Submodule
+1
Submodule lib/mongoose added at 0a86bc0af2.
+286
@@ -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 = §ion->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 §ion->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 = §ion->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 = §ion->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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
//
|
||||
// Created by fang on 2025/3/6.
|
||||
//
|
||||
+237
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user