fix: remove usb keyboard support

This commit is contained in:
MyLuxy committed 2026-10-15 01:31:14 +02:00
1 parent b58946c506
commit 13fb1dbdb1
12 files changed
+8 -383

No files matched your search

+1 -8
View File
@@ -257,7 +257,7 @@ that creates its tag, and installed apps offer exactly the tested files on their
</details>
<details>
<summary><b>Changes to upstream ps5-react</b></summary>
<summary><b>Fixes over upstream ps5-react</b></summary>
<br>
Two fixes in the vendored framework, both in the build tooling and both reported by a Linux build:
@@ -269,13 +269,6 @@ Two fixes in the vendored framework, both in the build tooling and both reported
which reads zstd only from Python 3.14 while the documented minimum is 3.12. It now falls back
to the `zstd` command.
One addition, for Overdrive's text fields:
- **USB keyboard.** `ps5-react/native/ps5/usb_keyboard.cpp` reads a keyboard plugged into the console
through libSceKeyboard and hands typed characters (US layout) and Backspace, Enter, Escape and Tab to
the new `useKeyboardInput` hook (input protocol v6, see `ps5-react/docs/NAVIGATION.md`). Without a
keyboard nothing changes. It has no hardware evidence yet.
Nothing else in the framework is changed.
</details>
-7
View File
@@ -273,13 +273,6 @@ Linux:
`tarfile`, а тот читает zstd только начиная с Python 3.14, тогда как документированный минимум —
3.12. Теперь есть откат на команду `zstd`.
Одно дополнение для текстовых полей Overdrive:
- **USB-клавиатура.** `ps5-react/native/ps5/usb_keyboard.cpp` читает клавиатуру, подключённую к консоли,
через libSceKeyboard и передаёт набранные символы (раскладка US), а также Backspace, Enter, Escape и Tab
новому хуку `useKeyboardInput` (протокол ввода v6, см. `ps5-react/docs/NAVIGATION.md`). Без клавиатуры
ничего не меняется. На консоли пока не проверено.
Больше во фреймворке ничего не изменено.
</details>
+1 -9
View File
@@ -1,6 +1,5 @@
// ../pub-game-store/native/search/Keyboard.jsx
// reconstructed from the esbuild bundle in eboot.bin
// MODIFIED: a USB keyboard types into the field too.
import '../assets/Inter-Regular.ttf';
import { Fragment, jsx, jsxs } from 'react/jsx-runtime';
@@ -11,7 +10,7 @@ import { ButtonGlyph } from '../ui/ButtonHints.jsx';
import { Icon } from '../ui/Icon.jsx';
import { HINT, useHints } from '../ui/Legend.jsx';
import { colors } from '../ui/colors.js';
import { FocusScope, Text, View, motion, useIsFocused, useKeyboardInput } from '@ps5-react/core';
import { FocusScope, Text, View, motion, useIsFocused } from '@ps5-react/core';
var COLUMNS2 = 6;
var LTR2 = { direction: "ltr" };
@@ -57,13 +56,6 @@ import { FocusScope, Text, View, motion, useIsFocused, useKeyboardInput } from '
(value) => value === "case" ? setUpper((value2) => !value2) : value === "done" ? onSubmit() : onChange((query) => edit(query, value, url ? 8192 : MAX_QUERY)),
[onChange, onSubmit, url]
);
// A USB keyboard types straight into the field: characters as if their keys were pressed here,
// Backspace deletes and Enter submits. The on-screen keys and the controller work as before.
useKeyboardInput((input) => {
if (input.type === "text") for (const character of input.value) onKey(character);
else if (input.value === "Backspace") onKey("delete");
else if (input.value === "Enter") onKey("done");
});
const onAction = (action) => {
if (action === "square") onKey("delete");
else if (action === "triangle" && !url) onKey(" ");
-8
View File
@@ -1,13 +1,5 @@
# Changelog
## Unreleased — USB keyboard
- The PS5 host reads a USB keyboard through libSceKeyboard (input protocol v6): typed characters, on a
US layout, and the editing keys Backspace, Enter, Escape and Tab, with hold-to-repeat, reach the new
`useKeyboardInput(handler)` as `{type: 'text' | 'key', value}`. They never move focus. Without a
keyboard, or if the console refuses one, nothing is sent and the title runs as before; the log says
which. The desktop host keeps its keys for the controller bindings. Not hardware-tested yet.
## Unreleased — comparing upscalers
- `<Video compare={{upscale, split}}>` (native ABI v15, `video.setCompare`): right of `split` (0 to 1
+1 -12
View File
@@ -60,7 +60,7 @@ AppRegistry.registerComponent('menu', () => Menu);
`npm run create` scaffolds this menu; `apps/starter`, `apps/tailwind-gallery`,
`apps/system-explorer`, and `apps/motion-lab` use the patterns below.
## Host input (input protocol v6)
## Host input (input protocol v5)
The input protocol is versioned on its own; it is not the native API's ABI
version ([NATIVE-API.md](NATIVE-API.md#contract)).
@@ -81,17 +81,6 @@ with its position 0..1 across and down the pad each frame it touches, and `__ps5
once when it lifts (the PS5 reads the DualSense; the desktop, a controller's touchpad through SDL).
See [Touchpad](#touchpad).
Since input protocol v6, the PS5 host also reads a USB keyboard: `__ps5ReactKeyboard('text', characters)`
for what was typed and `__ps5ReactKeyboard('key', name)` for `Backspace`, `Enter`, `Escape` and `Tab`, with
the platform's hold-to-repeat. Characters follow the US layout, since the console reports key positions,
not the layout's characters. These reach only `useKeyboardInput` handlers, never the focus manager, so
typing fills a field without moving focus. The desktop host keeps its keys for the controller bindings
and reports no keyboard input. The title does not load libSceKeyboard, and a fake-signed title may not
load it by path, so `main` has the system load it by id (`sceSysmoduleLoadModule(0x0106)`, before filesystem
elevation) and its calls come from `sceKernelDlsym`; where any step fails the keyboard stays off and the
log says which (`keyboard: module 0x0106 load=… handle=…`). A keyboard that isn't there at launch is tried
again every three seconds. The keyboard has no hardware evidence yet ([HARDWARE.md](HARDWARE.md)).
The focus manager does not move on these buttons: they go to the enclosing
`FocusScope.onAction` handlers (tabs on L1/R1, a filter on Triangle, settings on Options)
and to `useController`.
-13
View File
@@ -33,7 +33,6 @@
#include "video_api.hpp"
#include "layouts.h"
#include "video_output.hpp"
#include "usb_keyboard.hpp"
#include "video_frame.hpp"
#include "filesystem_access.hpp"
#include <unistd.h>
@@ -336,13 +335,6 @@ bool touch(bool down, float x, float y) {
return call_input("__ps5ReactTouch", 2, args);
}
// A USB keyboard's typed characters ("text") or editing key ("key"), input protocol v6.
bool keyboard(const char* type, const char* value) {
JSContext* ctx = er_runtime_context();
JSValue args[2] = {JS_NewString(ctx, type), JS_NewString(ctx, value)};
return call_input("__ps5ReactKeyboard", 2, args);
}
// Display.setRefreshRate: the rate can change only while EGL is terminated (ps5_opengl_display_modes.h),
// so the presenter's GL objects go with the context and are made again. The engine framebuffer is CPU
// memory and survives; the presenter's first draw uploads all of it.
@@ -363,7 +355,6 @@ bool switch_refresh_rate(hui::ps5::Display& display, GlPresenter& presenter, con
bool run_proof() {
hui::ps5::Display display;
hui::ps5::Pad pad;
usb_keyboard::Session usb_keys;
GlPresenter presenter;
hui::InputTracker tracker;
hui::PadSample samples[64];
@@ -427,7 +418,6 @@ bool run_proof() {
if (ok) {
async_log::write("[PS5-REACT] pad=%d at %lldms", pad.open(), since_launch_ms());
host_platform_set_pad(&pad);
async_log::write("[PS5-REACT] usb keyboard=%d", usb_keys.open());
}
if (ok) {
images::set_cache_bytes(image_cache_bytes());
@@ -558,7 +548,6 @@ bool run_proof() {
const bool touch_down = host_platform_read_touch(&touch_x, &touch_y);
if (ok && (touch_down || touched)) ok = touch(touch_down, touch_x, touch_y);
touched = touch_down;
if (ok) ok = usb_keys.poll(now, keyboard);
commands.poll(now, script);
if (const char* action = ok ? script.next(static_cast<std::uint64_t>(now / 1000)) : nullptr) {
async_log::write("[PS5-REACT] script: %s", action);
@@ -733,8 +722,6 @@ int main() {
#if PS5_REACT_VIDEO
video::preload_platform_decoder();
#endif
// Like the decoder, before elevation: the system refuses module loads afterwards.
usb_keyboard::preload();
initialize_filesystem_access();
pthread_attr_t attributes;
int result = pthread_attr_init(&attributes);
-233
View File
@@ -1,233 +0,0 @@
// Copyright (C) 2026 half144 and PS5 React contributors
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution term: see LICENSE-ATTRIBUTION.
#include "usb_keyboard.hpp"
#include "async_log.hpp"
#include <algorithm>
#include <cstring>
#include <iterator>
namespace {
// The state sceKeyboardReadState fills, laid out as the ps5-payload-dev SDL port reads it
// (src/video/ps5/SDL_ps5keyboard.c): an availability flag, the modifier bits, and up to sixteen held
// keys as USB HID usage IDs. The SDK ships the library's stubs without headers. The spare tail only
// absorbs a firmware that writes more than that layout, so it never lands on the stack around it.
struct KeyboardState {
std::uint64_t reserved0[2];
std::uint8_t available;
std::uint32_t reserved1[2];
std::uint32_t modifiers;
std::uint16_t keys[16];
std::uint64_t reserved2[4];
std::uint8_t spare[256];
};
constexpr std::uint32_t kShift = 0x02 | 0x20; // left and right Shift
constexpr std::uint32_t kCtrlAltGui = 0x01 | 0x04 | 0x08 | 0x10 | 0x40 | 0x80;
constexpr std::int64_t kRepeatDelayUs = 500000, kRepeatIntervalUs = 40000;
constexpr int kInvalidUserId = -1;
// USB HID usages 0x04..0x38 (letters, digits, Enter..Space and punctuation) on a US layout, plain and
// shifted. A zero is a key handled elsewhere or not a character.
constexpr char kPlain[] =
"abcdefghijklmnopqrstuvwxyz1234567890\0\0\0\0 -=[]\\\0;'`,./";
constexpr char kShifted[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()\0\0\0\0 _+{}|\0:\"~<>?";
static_assert(sizeof kPlain == 0x38 - 0x04 + 2 && sizeof kShifted == sizeof kPlain);
constexpr std::uint16_t kEnter = 0x28, kEscape = 0x29, kBackspace = 0x2A, kTab = 0x2B, kCapsLock = 0x39,
kDelete = 0x4C, kKeypadEnter = 0x58;
// The editing key a usage names, or nullptr.
const char* key_name(std::uint16_t usage) {
switch (usage) {
case kEnter: case kKeypadEnter: return "Enter";
case kEscape: return "Escape";
case kBackspace: case kDelete: return "Backspace";
case kTab: return "Tab";
default: return nullptr;
}
}
// The character a usage types, or 0. Caps Lock shifts letters only, as on a computer.
char character(std::uint16_t usage, bool shift, bool caps_lock) {
if (usage >= 0x04 && usage <= 0x38) {
const bool letter = usage <= 0x1D;
const char c = (shift != (letter && caps_lock) ? kShifted : kPlain)[usage - 0x04];
return c;
}
// Keypad: / * - + Enter(handled) 1..9 0 .
static constexpr char kKeypad[] = "/*-+\0" "1234567890.";
if (usage >= 0x54 && usage <= 0x63) return kKeypad[usage - 0x54];
return 0;
}
bool held(const std::uint16_t (&keys)[16], std::uint16_t usage) {
return usage && std::find(std::begin(keys), std::end(keys), usage) != std::end(keys);
}
} // namespace
extern "C" {
int sceUserServiceGetForegroundUser(int* user);
int sceSysmoduleLoadModule(std::uint16_t id);
int sceSysmoduleGetModuleHandleInternal(std::uint32_t id, std::int32_t* handle);
int sceKernelDlsym(std::int32_t handle, const char* symbol, void** address);
}
namespace {
// libSceKeyboard is not loaded for the title. Calling it directly jumps to nothing (CE-108255-1 at
// launch, rip 0x780); the SDK's dlopen works only in payloads; and a fake-signed title may not load
// the module by path (0x80020063). The system loads it as one of its modules by id instead, as
// prospero-win (native/pw_hid_ps5.c) measured on the console: sceSysmoduleLoadModule before
// filesystem elevation, the module's handle from sceSysmoduleGetModuleHandleInternal (by its id or
// its internal id, 0x80000000 | id), and each call from sceKernelDlsym. A step that fails leaves the
// keyboard off and the title running.
constexpr std::uint16_t kKeyboardModule = 0x0106;
constexpr std::int64_t kOpenRetryUs = 3000000;
struct KeyboardCalls {
int (*init)(void) = nullptr;
int (*open)(int user, int type, int index, void* param) = nullptr;
int (*read_state)(int handle, KeyboardState* state) = nullptr;
int (*close)(int handle) = nullptr;
};
KeyboardCalls calls;
// What preload found, for Session::open to log.
int load_status = 0;
std::int32_t module_handle = -1;
const char* missing_call = nullptr;
bool preloaded = false;
std::int32_t system_module_handle(std::uint16_t id) {
std::int32_t handle = -1;
if (sceSysmoduleGetModuleHandleInternal(id, &handle) == 0 && handle >= 0) return handle;
handle = -1;
if (sceSysmoduleGetModuleHandleInternal(0x80000000u | id, &handle) == 0 && handle >= 0) return handle;
return -1;
}
template <typename Function>
bool find_call(Function& function, const char* name) {
void* address = nullptr;
if (sceKernelDlsym(module_handle, name, &address) != 0 || !address) {
if (!missing_call) missing_call = name;
return false;
}
function = reinterpret_cast<Function>(address);
return true;
}
} // namespace
namespace usb_keyboard {
void preload() {
preloaded = true;
load_status = sceSysmoduleLoadModule(kKeyboardModule);
module_handle = system_module_handle(kKeyboardModule);
if (module_handle < 0) return;
// Every lookup runs, so the log names the first one missing.
const bool has_init = find_call(calls.init, "sceKeyboardInit");
const bool has_open = find_call(calls.open, "sceKeyboardOpen");
const bool has_read = find_call(calls.read_state, "sceKeyboardReadState");
const bool has_close = find_call(calls.close, "sceKeyboardClose");
if (!(has_init && has_open && has_read && has_close)) calls = {};
}
Session::~Session() {
if (handle_ >= 0 && calls.close) calls.close(handle_);
}
bool Session::open() {
async_log::write("[PS5-REACT] keyboard: module 0x%04x load=0x%08x handle=%d%s%s", static_cast<unsigned>(kKeyboardModule),
static_cast<unsigned>(load_status), static_cast<int>(module_handle),
missing_call ? " missing " : "", missing_call ? missing_call : "");
if (!preloaded || !calls.init) return false;
if (int error = sceUserServiceGetForegroundUser(&user_); error != 0 || user_ == kInvalidUserId) {
async_log::write("[PS5-REACT] keyboard: no foreground user 0x%08x", static_cast<unsigned>(error));
return false;
}
if (int error = calls.init(); error != 0) {
async_log::write("[PS5-REACT] keyboard: sceKeyboardInit=0x%08x", static_cast<unsigned>(error));
return false;
}
ready_ = true;
try_open(0);
return handle_ >= 0;
}
void Session::try_open(std::int64_t now_us) {
std::uint8_t param[8] = {};
const int handle = calls.open(user_, 0, 0, param);
retry_at_us_ = now_us + kOpenRetryUs;
if (handle >= 0) {
handle_ = handle;
async_log::write("[PS5-REACT] keyboard: open handle=%d", handle);
return;
}
// Without a keyboard plugged in this fails every few seconds: logged the first time only.
if (!open_failed_) async_log::write("[PS5-REACT] keyboard: sceKeyboardOpen=0x%08x, retrying", static_cast<unsigned>(handle));
open_failed_ = true;
}
bool Session::poll(std::int64_t now_us, Emit emit) {
if (handle_ < 0) {
if (!ready_ || now_us < retry_at_us_) return true;
try_open(now_us);
if (handle_ < 0) return true;
}
KeyboardState state;
std::memset(&state, 0, sizeof state);
if (int error = calls.read_state(handle_, &state); error != 0) {
// Logged once per run of failures: an unplugged keyboard must not fill the log every frame.
if (!read_failed_) async_log::write("[PS5-REACT] keyboard: sceKeyboardReadState=0x%08x", static_cast<unsigned>(error));
read_failed_ = true;
return true;
}
read_failed_ = false;
std::uint16_t keys[16] = {};
if (state.available) std::copy(std::begin(state.keys), std::end(state.keys), std::begin(keys));
// Ctrl, Alt and the system key make shortcuts, not text.
const bool shortcut = state.available && (state.modifiers & kCtrlAltGui);
const bool shift = state.available && (state.modifiers & kShift);
// Sends one press of `usage`; false when the app's input call failed.
auto press = [&](std::uint16_t usage) {
if (const char* name = key_name(usage)) return emit("key", name);
if (shortcut) return true;
const char c = character(usage, shift, caps_lock_);
if (!c) return true;
const char text[2] = {c, 0};
return emit("text", text);
};
bool ok = true;
for (std::uint16_t usage : keys) {
if (!usage || held(held_, usage)) continue; // only newly pressed keys
if (usage == kCapsLock) {
caps_lock_ = !caps_lock_;
continue;
}
if (ok) ok = press(usage);
repeat_key_ = usage;
repeat_shift_ = shift;
repeat_at_us_ = now_us + kRepeatDelayUs;
}
// The last key pressed repeats while it stays down; a released key or a Shift change stops it.
if (repeat_key_ && (!held(keys, repeat_key_) || repeat_shift_ != shift)) repeat_key_ = 0;
if (ok && repeat_key_ && repeat_key_ != kCapsLock && now_us >= repeat_at_us_) {
ok = press(repeat_key_);
// Keeps the cadence across frames that don't fall on it; after a stall it resumes, never bursts.
repeat_at_us_ += kRepeatIntervalUs;
if (repeat_at_us_ <= now_us) repeat_at_us_ = now_us + kRepeatIntervalUs;
}
std::copy(std::begin(keys), std::end(keys), std::begin(held_));
return ok;
}
} // namespace usb_keyboard
-50
View File
@@ -1,50 +0,0 @@
// Copyright (C) 2026 half144 and PS5 React contributors
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution term: see LICENSE-ATTRIBUTION.
#pragma once
#include <cstdint>
// A USB keyboard read through libSceKeyboard, turned into input protocol v6 keyboard input: typed
// characters (US layout) and the editing keys Backspace, Enter, Escape and Tab, with hold-to-repeat.
// Without a keyboard, or if the console refuses one, the title runs as before and nothing is sent.
namespace usb_keyboard {
// Has the system load libSceKeyboard and finds its calls. Call it from main(), before filesystem
// elevation: afterwards the system refuses module loads (0x80020063, as video_decoder.cpp found).
// It logs nothing itself; Session::open reports what it found once the log is running.
void preload();
// Receives ("text", characters) or ("key", name); false stops the frame, like the other input calls.
using Emit = bool (*)(const char* type, const char* value);
class Session {
public:
Session() = default;
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
~Session();
// After the pad opens: it initializes the user service the keyboard is opened for. True when a
// keyboard is open now; without one, poll keeps trying, so a keyboard plugged in later works.
bool open();
// Once per frame on the render thread.
bool poll(std::int64_t now_us, Emit emit);
private:
void try_open(std::int64_t now_us);
int user_ = -1;
int handle_ = -1;
bool ready_ = false;
bool open_failed_ = false;
std::int64_t retry_at_us_ = 0;
bool caps_lock_ = false;
bool read_failed_ = false;
std::uint16_t held_[16] = {};
std::uint16_t repeat_key_ = 0;
bool repeat_shift_ = false;
std::int64_t repeat_at_us_ = 0;
};
} // namespace usb_keyboard
+1 -1
View File
@@ -5,7 +5,7 @@
#include <cstddef>
// Input actions (input protocol v6, see runtime/js/input.js): what hosts pass to
// Input actions (input protocol v5, see runtime/js/input.js): what hosts pass to
// globalThis.__ps5ReactDispatch and input scripts accept. runtime/js/input.js names the same actions
// for JavaScript. Options reaches the app as `options`; the PS button leaves.
enum class HostAction : unsigned char { up, down, left, right, confirm, back, l1, r1, l2, r2, triangle, square, options };
+1 -14
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution term: see LICENSE-ATTRIBUTION.
import {useLayoutEffect, useRef} from 'react';
import {subscribeInput, subscribeKeyboard} from './input.js';
import {subscribeInput} from './input.js';
import {Text as EngineText, unstable_batchedUpdates} from 'embedded-react';
import {setFrameBatcher} from './frame.js';
import {inspectableText} from './focus/inspect-text.js';
@@ -35,19 +35,6 @@ export function useController(handler) {
}), []);
}
/**
* Subscribe to a USB keyboard while mounted (PS5 host only; see docs/NAVIGATION.md). The handler
* gets `{type: 'text', value}` for typed characters and `{type: 'key', value}` for `Backspace`,
* `Enter`, `Escape` and `Tab`. Pass a falsy handler to stop listening.
* @param {((input: import('./input.js').KeyboardInput) => void) | null | undefined} handler
*/
export function useKeyboardInput(handler) {
const current = useRef(handler);
useLayoutEffect(() => { current.current = handler; });
const listening = Boolean(handler);
useLayoutEffect(() => (listening ? subscribeKeyboard(input => current.current?.(input)) : undefined), [listening]);
}
/**
* Tailwind-style classes → a style object, compiled by the build (see docs/TAILWIND.md).
* @param {TemplateStringsArray} classes
+2 -27
View File
@@ -3,17 +3,14 @@
// Additional attribution term: see LICENSE-ATTRIBUTION.
const listeners = new Set();
const touchListeners = new Set();
const keyboardListeners = new Set();
// Actions as strings, touch samples as [x, y] (x null when the finger lifts), keyboard input as
// {type, value}, in arrival order.
// Actions as strings, touch samples as [x, y] (x null when the finger lifts), in arrival order.
const pending = [];
function deliver() {
for (const item of pending.splice(0)) {
if (typeof item === 'string') for (const listener of listeners) listener(item);
else if (Array.isArray(item)) for (const listener of touchListeners) listener(item[0], item[1]);
else for (const listener of keyboardListeners) listener(item);
else for (const listener of touchListeners) listener(item[0], item[1]);
}
}
@@ -33,14 +30,6 @@ globalThis.__ps5ReactTouch = (x, y) => {
if (pending.push([x ?? null, y ?? null]) === 1) Promise.resolve().then(deliver);
};
// Input protocol v6: a USB keyboard on the PS5 host, as ('text', characters) for what was typed and
// ('key', name) for Backspace, Enter, Escape and Tab. The keys reach only keyboard listeners, never
// the focus runtime, so a keyboard types into fields without moving focus.
globalThis.__ps5ReactKeyboard = (type, value) => {
if (type !== 'text' && type !== 'key') return;
if (pending.push({type, value: String(value)}) === 1) Promise.resolve().then(deliver);
};
/**
* The host actions, as native/shared/actions.hpp names them.
* @typedef {'l1' | 'r1' | 'l2' | 'r2' | 'triangle' | 'square' | 'options'} ButtonAction shoulders, triggers, Triangle, Square, Options
@@ -64,17 +53,3 @@ export function subscribeTouch(listener) {
touchListeners.add(listener);
return () => { touchListeners.delete(listener); };
}
/**
* Keyboard input: `text` carries typed characters, `key` names an editing key.
* @typedef {{type: 'text', value: string} | {type: 'key', value: 'Backspace' | 'Enter' | 'Escape' | 'Tab'}} KeyboardInput
*/
/**
* Internal: receive keyboard input until the returned function is called.
* @param {(input: KeyboardInput) => void} listener
*/
export function subscribeKeyboard(listener) {
keyboardListeners.add(listener);
return () => { keyboardListeners.delete(listener); };
}
+1 -1
View File
@@ -153,7 +153,7 @@ def main():
definitions = next(line.split("=", 1)[1] for line in
(cross / "bridge/engine/CMakeFiles/embedded-react.dir/flags.make").read_text().splitlines()
if line.startswith("C_DEFINES ="))
sources = [ROOT / "native/ps5/native_host.cpp", ROOT / "native/ps5/usb_keyboard.cpp", ROOT / "native/ps5/async_log.cpp", ROOT / "native/ps5/host_platform.cpp", ROOT / "native/ps5/video_output.cpp", ROOT / "native/ps5/time_compat.c", ROOT / "native/ps5/archive_compat.c",
sources = [ROOT / "native/ps5/native_host.cpp", ROOT / "native/ps5/async_log.cpp", ROOT / "native/ps5/host_platform.cpp", ROOT / "native/ps5/video_output.cpp", ROOT / "native/ps5/time_compat.c", ROOT / "native/ps5/archive_compat.c",
ROOT / "native/ps5/filesystem_access.cpp",
ROOT / "native/ps5/elevation_transport.cpp",
ROOT / "native/shared/host_api.cpp", ROOT / "native/shared/removals.cpp", ROOT / "native/shared/network.cpp",