mirror of
https://github.com/ayozetr/bloodborne_linux.git
synced 2026-11-09 09:19:42 -10:00
Compare commits
34
Commits
main
...
0.5-linux.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18a7dd7e39 | ||
|
|
28a9233034 | ||
|
|
9c056c437f | ||
|
|
2683273a17 | ||
|
|
4d297b96bb | ||
|
|
917c4bdd20 | ||
|
|
8e24cfdcf5 | ||
|
|
cfede387ed | ||
|
|
f44390da19 | ||
|
|
c89f4d432c | ||
|
|
4fd1cd6ee6 | ||
|
|
cfe7af67a5 | ||
|
|
124384eac1 | ||
|
|
5227432b1b | ||
|
|
81db68f552 | ||
|
|
f723f6a972 | ||
|
|
82a96e0d25 | ||
|
|
71717863d5 | ||
|
|
c4281227da | ||
|
|
ee20c8b147 | ||
|
|
a39a0073de | ||
|
|
09a1e4981a | ||
|
|
992fc95ed1 | ||
|
|
851712537b | ||
|
|
e289b722b5 | ||
|
|
c7cae2a585 | ||
|
|
7d53c5e9d6 | ||
|
|
7dfbf49367 | ||
|
|
52a6daf288 | ||
|
|
912641c298 | ||
|
|
dca76e386f | ||
|
|
0b465f8ee1 | ||
|
|
82782d8155 | ||
|
|
89ea3deb70 |
No files matched your search
@@ -23,6 +23,8 @@ perf.data
|
||||
/bloodborne_upscalers_*.md
|
||||
mods.json
|
||||
patches.json
|
||||
# The mods folder of runs from source (the launcher's default, DATA_DIR/mods)
|
||||
/mods/
|
||||
# Local: game logs, messages and archives from users, internal plans
|
||||
/logs/
|
||||
/message*.txt
|
||||
@@ -30,3 +32,10 @@ patches.json
|
||||
/docs/ISSUE_ACTION_PLAN_*.md
|
||||
# AMD FidelityFX DLLs (FSR 4.1.1 is built from the user's own): never in the repository
|
||||
*.dll
|
||||
# Game files and dumps: never in the repository
|
||||
eboot.bin
|
||||
param.sfo
|
||||
*.pkg
|
||||
*.dcx
|
||||
*.7z
|
||||
/CUSA*/
|
||||
@@ -1,6 +1,8 @@
|
||||
[submodule "gpu/third_party/fsr-vulkan"]
|
||||
path = gpu/third_party/fsr-vulkan
|
||||
url = https://github.com/FireBurn/FSR-Vulkan.git
|
||||
# build.sh applies gpu/patches/fsr-vulkan to its working tree
|
||||
ignore = dirty
|
||||
[submodule "gpu/third_party/imgui"]
|
||||
path = gpu/third_party/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
THIS PROJECT IS NOT RELATED TO SHADPS4. ALL QUESTIONS RELATED TO THIS PROJECT SHOULD BE SENT TO THE DISCORD SERVER https://discord.gg/KYZRKk9CB, NOT TO THE SHADPS4 SERVER.
|
||||
|
||||
|
||||
# bbport — a native Linux port of Bloodborne
|
||||
|
||||
> This is the README of the original project (deadinside28/bloodborne_pc), kept for its
|
||||
> technical details. The fork's own README is [README.md](README.md).
|
||||
|
||||
**English** · [Русский](BBPORT.ru.md)
|
||||
|
||||
bbport is the counterpart of Wine + DXVK for a single game: *Bloodborne* for PlayStation 4
|
||||
(CUSA03173, game version 1.09) on an x86-64 Linux PC. The game's original executable runs
|
||||
directly on the PC:
|
||||
|
||||
- **as in Wine**, the game's x86-64 code runs on the CPU directly, and a runtime written for
|
||||
this one game replaces the PS4 system libraries;
|
||||
- **as in DXVK**, the game's graphics are translated to Vulkan — by a renderer derived from
|
||||
[shadPS4](https://github.com/shadps4-emu/shadPS4) and heavily extended for this game, including
|
||||
temporal upscaling with AMD FSR 3.1, FSR 4 and FSR 4.1.1;
|
||||
- memory and the GPU's work are moving to the PC model: the launcher has an experimental
|
||||
*New memory and translation model* mode; without it the game runs on the old
|
||||
memory model, as in 0.3.
|
||||
|
||||
One step remains to the full Wine + DXVK model: make the new mode the default and remove the old
|
||||
memory model (see below).
|
||||
|
||||
> **No game files are included.** You need your own dump of Bloodborne (CUSA03173, v1.09).
|
||||
> This project is not affiliated with Sony Interactive Entertainment, FromSoftware or AMD.
|
||||
|
||||
**Status: experimental, playable.** The game boots, loads saves and plays (the Hunter's Dream
|
||||
and several areas of Yharnam were played with it) with sound, gamepad and saving.
|
||||
A full play-through has not been verified, and only one machine (Linux, AMD Radeon RX 7800 XT,
|
||||
Mesa/RADV) has been tested thoroughly.
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Native execution.** The eboot is converted offline into a flat memory image; PS4 libc and
|
||||
libSceFios2 are linked into it as native code. No CPU emulation and no per-instruction
|
||||
translation: the game code runs at full speed.
|
||||
- **Two modes** (launcher → *Mode*):
|
||||
- **Switch off (the default) — as in 0.3.** The old memory model: VRAM copies of the game's
|
||||
memory, writes tracked through page protection. With every fix made since 0.3 (motion
|
||||
vectors and the upscaler, flicker with DoF on, a damaged shader cache).
|
||||
- **New memory and translation model — experimental.** The game's memory lives
|
||||
in system RAM and the GPU reads it where it is, as a PC game's buffers; data it reads often
|
||||
is kept in VRAM and given back when unused (textures after 20 s, buffers after 60 s). The
|
||||
layer's memory module tracks CPU writes to mirrored blocks to keep those copies coherent;
|
||||
it does not copy the whole GPU-visible memory. The PS4 command processor's work
|
||||
is translated into Vulkan commands rather than emulated on the CPU: memory writes
|
||||
(`WRITE_DATA`, DMA) are done by the GPU in command-stream order, and fences are written by the
|
||||
GPU itself at the end of the pipeline, at the same point of the stream as the PS4's command
|
||||
processor writes them. **It may crash**; hardware testing has been performed on an
|
||||
RX 7800 XT, and performance depends on the scene and settings. On AMD the game's memory is kept
|
||||
in a sparse buffer (the arena). **On NVIDIA, Intel and others** it goes through the layer's
|
||||
memory module: the game's memory is bound in place and VRAM copies are separate buffers, with
|
||||
no sparse rebinding. This avoids the sparse-binding path associated with reported freezes
|
||||
on a GTX 1660 Ti. On NVIDIA the module runs on a tester's GTX 1660 Ti, its speed is still
|
||||
being checked; not tested on Intel. Without the launcher: `BB_PC_MODEL=1`; to choose the memory
|
||||
path: `BB_GUEST_MEMORY=host|dmabuf`; the memory module: `BB_LAYER_MEMORY=1/0`.
|
||||
|
||||
[0.5-pre4 changes](docs/CHANGES_0.5-pre4.md): no stutter of the memory module (NVIDIA) while areas
|
||||
stream in, shader compilation in the background, VRAM on 4–6 GB cards, damaged game extractions
|
||||
reported, the settings menu as in 0.3. [0.5-pre3](docs/CHANGES_0.5-pre3.md): the memory module on
|
||||
NVIDIA — VRAM copies, one VRAM budget with the texture cache, watched blocks. [0.5-pre2](docs/CHANGES_0.5-pre2.md): the memory
|
||||
module, native GPU copy shader, portable GPU completion labels, and shader-cache migration.
|
||||
|
||||
Unused textures are freed in both modes, so VRAM no longer grows with every area visited.
|
||||
- **Unlocked frame rate.** Community patches (`patches/Bloodborne.xml`) make the simulation
|
||||
use the real frame time; ~90 FPS at 4K with FSR 4 Balanced on an RX 7800 XT, ~150 FPS at
|
||||
1440p with FSR 4 Quality. Also 30/60/90 FPS modes.
|
||||
- **Temporal upscaling built for this game.** Bloodborne has no velocity buffer, so bbport
|
||||
computes motion vectors itself: camera motion from depth and the scene matrices, and object
|
||||
motion (characters, cloth, weapons) from the vertex positions of the previous frame. The
|
||||
scene is jittered sub-pixel (Halton) and rendered at a reduced resolution; the upscaler fills
|
||||
the output (720p for the Steam Deck, 1080p, 1440p or 2160p) and the UI is drawn natively at the output resolution.
|
||||
- **FSR 3.1** (FireBurn/FSR-Vulkan).
|
||||
- **FSR 4 (INT8, model v07)** on GPUs exposing the required Vulkan shader features —
|
||||
RDNA2/3 included (see Requirements).
|
||||
- **FSR 4.1.1**: AMD's 4.1.1 DLL is recorded once under vkd3d-proton and its passes are
|
||||
replayed natively on Vulkan; the output is **bit-exact** with the DLL. Two variants, as in
|
||||
the DLL: INT8 on any GPU with the required shader features, and FP8 matrices on RDNA4 (RX
|
||||
9000; picked automatically). The assets are built on your machine from one DLL of your own
|
||||
(4.1.x): the launcher's *FSR 4.1.1 from your own AMD DLL → Choose DLL…* button (2–5 minutes,
|
||||
twice that on RDNA4; needs a recent Proton), or `tools/fsr4cap`.
|
||||
- Faster than AMD's own shaders on RDNA3: the final passes of FSR 4 and 4.1.1 were rewritten
|
||||
to store through workgroup memory (3.5× and 2.3× faster, bit-exact); FSR 4 costs ~4 ms at
|
||||
4K on an RX 7800 XT instead of ~6 ms.
|
||||
- **Multi-threaded GPU command processing.** The PS4 command stream is decoded on one thread
|
||||
and draws are bound and recorded on another (two-stage pipeline), with a Vulkan recording
|
||||
thread and helper threads for memory copies. Early on the single GPU thread capped the game
|
||||
at ~26 FPS; now it runs at 90–150 FPS depending on resolution and scene.
|
||||
- **Settings inside the game's own menu:** *System → Display / Game effects / Game patches*
|
||||
(after *Screen/Sound*), built and drawn by the game itself — output resolution, upscaler,
|
||||
preset, sharpness, FPS counter, model detail and the effect patches, in the game's language.
|
||||
- **Overlay menu** (Insert or L3+R3), styled like the game's dialogs: everything above plus the
|
||||
advanced settings, with gamepad, keyboard and mouse.
|
||||
- **GTK4 launcher** and an **AppImage** for the Steam Deck.
|
||||
|
||||
## How it differs from shadPS4
|
||||
|
||||
| | shadPS4 | bbport |
|
||||
|---|---|---|
|
||||
| Scope | General PS4 emulator, many games | One game: Bloodborne v1.09 |
|
||||
| Loading | Its own ELF loader and kernel emulation at run time | The eboot is converted offline (`scripts/`) into an image with PS4 libc/Fios2 linked in; a C loader maps it and jumps into the game (loader and runtime: ~5k lines) |
|
||||
| Memory | The GPU's view of PS4 memory is kept in VRAM copies, synchronized through page-protection write tracking | By default the same model (as in 0.3); in the *New memory and translation model* mode: the game's memory in system RAM, used by the GPU in place, frequently read data in VRAM, freed when unused |
|
||||
| Command processor | Emulated: memory writes, DMA and fences are done by the CPU while decoding | By default the same; in the new mode translated into Vulkan commands that the GPU runs in stream order, fences written after the work has really finished |
|
||||
| System libraries | Broad HLE of the PS4 OS | A small runtime (`src/runtime_*.c`) that implements exactly what Bloodborne calls: memory, threads, sync, files, audio (incl. ATRAC9), pad, saves, AppContent |
|
||||
| GPU | shadPS4 video core and shader recompiler | The same core (vendored, GPL) with ~200 marked changes (`bbport:`) plus new modules: two-stage draw pipeline, render-state and texture-set memoization, render-scale proxies, motion vectors, FSR 3.1/4/4.1.1, frame capture and GPU profiler |
|
||||
| GPU thread | One thread processes the whole command stream (the bottleneck in Bloodborne) | Decode and draw recording run on separate threads; the work scales with the hardware threads (Steam Deck included) |
|
||||
| Upscaling | — | Temporal (FSR 3.1, FSR 4, FSR 4.1.1) with the game's own motion vectors and jitter |
|
||||
| Game patches | Patch files applied by the emulator | The same community patches, compiled at start (`scripts/patches.py`); render resolution, effects and FPS from the launcher |
|
||||
|
||||
Without shadPS4 there would be no bbport: its renderer and shader recompiler are the base of
|
||||
the graphics side.
|
||||
|
||||
### Wine + DXVK for one game
|
||||
|
||||
- **CPU.** The PS4 CPU is x86-64, so the game's code runs directly on the PC's CPU, with no
|
||||
emulation and no instruction translation.
|
||||
- **System libraries.** As Wine replaces the Windows API, the bbport runtime (`src/runtime_*.c`)
|
||||
implements exactly the PS4 OS functions Bloodborne calls: memory, threads, files, audio, pad,
|
||||
saves.
|
||||
- **Memory.** In the new mode, as in a PC game: the game's data in system RAM, VRAM used the way
|
||||
a PC game uses it. The mode is still experimental.
|
||||
- **Graphics.** The PS4 GPU's command stream (PM4) is a recording of the game's graphics API
|
||||
calls: it is written by 99 functions of the statically linked libGnm, so decoding the stream
|
||||
and translating the calls themselves come to the same thing. GCN shaders are translated to
|
||||
SPIR-V. In the new mode the command processor's work (memory writes, DMA, fences) is
|
||||
translated into Vulkan commands as well; fences are written by the GPU itself. Resource
|
||||
descriptors (textures, buffers) are read by the translator on the CPU, as DXVK reads resource
|
||||
bindings: a PS4 texture has to be converted from the PS4 tiling into a Vulkan image before the
|
||||
draw runs, so the CPU must know its descriptor beforehand — translation, not emulation. The
|
||||
descriptor tables (the constant engine's dumps) are read only by the translator, so the CPU
|
||||
performs them. The game's occlusion queries (~60 a frame) are
|
||||
Vulkan occlusion queries; predication, `COPY_DATA` and `COND_EXEC` are translated too (check:
|
||||
`BB_PM4_SELFTEST=1`). Details:
|
||||
[docs/EMULATION_REMOVAL_PLAN.ru.md](docs/EMULATION_REMOVAL_PLAN.ru.md), "Шаг 4" (Russian).
|
||||
|
||||
"Port" here means a build for this one game, not a rewrite of its source code, which the project
|
||||
neither has nor includes.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux x86-64, a Vulkan 1.3 GPU. Tested: AMD RX 7800 XT with Mesa 26 (RADV).
|
||||
The *New memory and translation model* mode is not tested on NVIDIA yet.
|
||||
FSR 4 / 4.1.1 require shader Float16, Int8/Int16, integer dot products, linear compute
|
||||
derivatives and extended storage image formats; FSR 4.1.1 additionally requires
|
||||
`VK_VALVE_shader_mixed_float_dot_product`. Unsupported choices fall back to FSR 3.1
|
||||
before the first frame and are disabled in the in-game menu.
|
||||
- Your decrypted game dump: the `CUSA03173` folder (eboot.bin, sce_module, ...), version 1.09.
|
||||
The US release (`CUSA00900`) runs too: its 1.09 executable is the same as this European
|
||||
Game of the Year edition's.
|
||||
A dumped update is a separate folder: copy it over the base game, replacing files. The base
|
||||
game alone (1.00) crashes at start (guest offset 0x20348b8); the launcher and `run.sh` check
|
||||
the executable and say what is missing (`BB_SKIP_GAME_CHECK=1` skips the check). Some dumps
|
||||
ship the 1.09 eboot.bin with Lance McDonald's 60 fps patch baked in; that one crashes when the
|
||||
gestures menu opens, and the check asks for a clean eboot.bin (bbport has its own 60 fps).
|
||||
- To build: GCC, CMake, Ninja, Python 3, glslang, SDL3, Vulkan headers and the libraries in
|
||||
`shell.nix`. With [Nix](https://nixos.org) everything comes from `shell.nix` automatically.
|
||||
|
||||
## Build and run
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/deadinside28/bloodborne_pc.git bbport && cd bbport
|
||||
bash build.sh # builds out/bb-probe and out/gpu/libbbgpu.so
|
||||
BB_GAME_DIR=/path/to/CUSA03173 bash run.sh
|
||||
```
|
||||
|
||||
or the launcher (pick the game folder, settings, *Start*):
|
||||
|
||||
```bash
|
||||
bash launcher/bb-launcher.sh # launcher/install-desktop.sh adds it to the app menu
|
||||
```
|
||||
|
||||
By default the game folder is expected next to the repository (`../CUSA03173`). Saves and the
|
||||
shader cache go to `user/` (the launcher lets you choose another folder); settings to
|
||||
`bbport.ini`. A gamepad is used through SDL3 (the launcher's *Controls → Controller* picks one
|
||||
when several are connected; `BB_GAMEPAD=<GUID or part of the name>`). The keyboard works too,
|
||||
also next to a connected gamepad (the Steam Deck always has one); both are remapped in the
|
||||
launcher (*Controls*). The character name is typed on the keyboard in a box over the game.
|
||||
The touchpad: its left half (Tab, Back/Select) opens the gestures, the right half (Backspace)
|
||||
the key items. There is no PSN: the *Play online / offline* screen is skipped and the game opens
|
||||
its main menu offline (a game patch; the launcher's *Game effects → Skip the “play online / offline” choice*,
|
||||
`BB_SKIP_NETWORK_CHOICE=0` shows it). With several monitors, *Screen → Monitor* in the launcher
|
||||
(`BB_DISPLAY=<number or part of the name>`) picks the one for the game.
|
||||
|
||||
Add-ons: `BB_DLC_DIR=/path/to/dlc` (the launcher's *DLC folder*) holds the dumped add-ons of
|
||||
the game, one folder each named by its entitlement label (`SPEXPANSIONDLC03/sce_sys/...`).
|
||||
Their data ships with the 1.09 update, so a folder with only `sce_sys` is enough. `run.sh` links
|
||||
it as `user/addcont/<title id>`, where the runtime finds add-ons (see **DLC** below); a real
|
||||
folder already there is kept. The GOTY edition (CUSA03173) needs none.
|
||||
|
||||
**Resolution and preset changes:** for outputs other than 1080p (720p on the Steam Deck,
|
||||
1440p, 4K) the whole game renders at the preset's resolution, set by a patch at start — the
|
||||
fastest path. Changing the output or the preset in the in-game menu then needs *Apply and
|
||||
restart the game*. The *Live resolution changes* setting (launcher, in-game menu,
|
||||
`bbport.ini` `live_resolution=0|1|auto`; **off by default**) instead keeps the game at
|
||||
1080p internally and scales its render targets at run time, so 720p/1080p/1440p/4K and the
|
||||
presets switch without a restart. It costs more: the game then believes it renders 1080p
|
||||
and draws more (e.g. ~8× more small lights), and some targets are copied between sizes —
|
||||
use it on strong desktop GPUs only (`auto` turns it on for discrete GPUs with 8+ GB that are
|
||||
not pre-Turing NVIDIA). 1080p output and TAA always use the live path.
|
||||
|
||||
**TAA:** a separate native-resolution temporal AA mode in the launcher and overlay,
|
||||
switchable live without an FSR model. The saved FSR preset is restored when returning
|
||||
to FSR. FSR Native AA adds reconstruction on top of full-resolution rendering and can
|
||||
be slower than disabling AA. TAA also adds work compared with no temporal AA.
|
||||
The RCAS switch and the 0–2 sharpness control also work with TAA. Sharpening runs after
|
||||
temporal accumulation and leaves its history and HUD unchanged.
|
||||
|
||||
**Mods:** the launcher accepts separate loose-file mod folders (with `dvdroot_ps4/`, an extra
|
||||
wrapper folder, or the game folders such as `chr/` directly; file name case does not matter),
|
||||
with enable switches and load order. A sibling `CUSA03173-mods/` overlay also works.
|
||||
The original game is preserved; later mods override conflicting files.
|
||||
**Third-party patches:** shadPS4-format XML patch files in the data directory's `patches/`,
|
||||
switched on and off in the launcher. See [mods and patches](docs/MODS.md).
|
||||
**DLC:** put your add-on dumps in `user/addcont/<title id>/<entitlement label>/` (shadPS4's
|
||||
layout, e.g. `user/addcont/CUSA03173/SPEXPANSIONDLC03` for The Old Hunters): each folder there is
|
||||
reported to the game as an installed add-on.
|
||||
|
||||
**Launcher language:** Russian, English, Brazilian Portuguese, Spanish, Italian, French, German or Simplified Chinese (follows the system language by default).
|
||||
**Game language:** the PS4 system language, *System language* in the launcher; by default it
|
||||
follows the desktop's locale (Japanese, English, French, Spanish, German, Italian or Russian,
|
||||
English for the rest). `run.sh` takes it as `BB_LANGUAGE` (PS4 code: 0 Japanese, 1 English,
|
||||
2 French, 3 Spanish, 4 German, 5 Italian, 8 Russian; without it, the desktop's locale as above).
|
||||
**Game region:** *Game region* in the launcher, `BB_REGION=eu|na` for `run.sh`: the regional text
|
||||
of a language, such as Spanish from Spain (`eu`) or Latin American Spanish (`na`). It sets the
|
||||
game's `USER_DEFINED_PARAM_1` (13 Europe, 2 the Americas) for the launch; by default the dump's
|
||||
`param.sfo` decides.
|
||||
|
||||
**Free camera and game debug menu** (v1.09): enable the corresponding switches in the
|
||||
launcher or in-game menu and restart. Free camera uses Lance McDonald's
|
||||
[GoldHEN patch](https://github.com/GoldHEN/GoldHEN_Patch_Repository/blob/main/patches/xml/Bloodborne-Orbis.xml):
|
||||
hold Cross and press L3 to cycle modes (keyboard: hold Space and press Z). It needs no fonts
|
||||
and conflicts with *Enemy Control*.
|
||||
For the game debug menu, install `DbgFont14h.ccm` and `DbgFont14h.tpf` from
|
||||
[Debug Menu and XML Patch](https://www.nexusmods.com/bloodborne/mods/253) into the game's
|
||||
`dvdroot_ps4/font/` first. Startup rejects missing or empty font files instead of launching
|
||||
the unsafe patch. Open it with the left touchpad / Tab (with the debug menu on, the left half
|
||||
no longer opens the gestures); Backspace is the right touchpad.
|
||||
Touch coordinates are forwarded from SDL gamepads; Back/Select emulates a left click on
|
||||
pads without a touch surface. The port's settings menu remains Insert / L3+R3.
|
||||
|
||||
GPU occlusion queries still use synthetic pixel counters (`PixelPipeStatDump`), and
|
||||
`IT_SET_PREDICATION` is unimplemented. Free camera allows visual investigation; it does
|
||||
not implement GPU occlusion culling.
|
||||
|
||||
**Upscaler assets** (not included; FSR 3.1 needs none):
|
||||
|
||||
```bash
|
||||
bash tools/fetch_fsr4_assets.sh # FSR 4 v07 (MIT, built from AMD's source by Q2RTX)
|
||||
# FSR 4.1.1, from your own AMD DLLs (e.g. OptiScaler's FSR4_LATEST), needs GE-Proton 10 or newer:
|
||||
bash tools/fsr4cap/build_assets.sh <amd_fidelityfx_upscaler_dx12.dll>
|
||||
```
|
||||
|
||||
**FSR 4.1.1 from your own DLL.** The easiest way is the launcher: *Upscaler → FSR 4.1.1 from your
|
||||
own AMD DLL → Choose DLL…*, then pick `amd_fidelityfx_upscaler_dx12.dll` version 4.1.x: from
|
||||
OptiScaler's `FSR4_LATEST` folder or from a game with FSR 4.1. That one file is enough: AMD's DLL
|
||||
exports the FidelityFX API itself, so no loader is needed (only a DLL without these exports would
|
||||
be recorded through `amd_fidelityfx_loader_dx12.dll`, and the launcher would ask for it). The DLL is checked at once,
|
||||
without a long capture; these do not fit: AMD's official FSR 4.0.x (e.g. Pragmata's 4.0.3 —
|
||||
AMD enables it on RDNA4 only, and under Proton it does not start on other GPUs) and community
|
||||
builds (4.0.2b, 4.1.1b and similar OptiScaler INT8 builds: another model and pass count). Then
|
||||
the DLL runs under Proton and is recorded at 20 sizes (progress in the row and in the *Log* tab;
|
||||
on RDNA4 a second time, for the FP8 variant),
|
||||
the passes are translated to SPIR-V, and FSR 4.1.1 is selected. The Proton build is picked
|
||||
automatically — the first under which the DLL enables FSR 4.1: GE-Proton 10 or newer,
|
||||
Proton-CachyOS, Proton Experimental (tested: GE-Proton 11, Proton-CachyOS 11, Experimental of
|
||||
October 2026; Steam's Proton 11.0 and GE-Proton 9 do not). It runs in the Steam runtime it
|
||||
requires (Steam installs it the first time any game runs with that Proton), or through
|
||||
umu-launcher when installed. On NixOS umu-launcher comes from `nix-shell` (downloaded the first
|
||||
time, ~1.7 GB); the AppImage runs the recording on the system itself, through the user's systemd
|
||||
(`systemd-run --user`), since the system's `/nix` is out of its sight. Recording on the Steam Deck is not verified yet; if the DLL does not
|
||||
enable FSR 4.1 there, build on a PC and copy the `fsr4_411` folder. The same from the command
|
||||
line: the launcher's (and the AppImage's) `--build-fsr411 <DLL>`.
|
||||
|
||||
**DLSS (NVIDIA GeForce RTX).** NVIDIA's DLSS SDK is not included: check out
|
||||
[github.com/NVIDIA/DLSS](https://github.com/NVIDIA/DLSS) (`include/` and `lib/Linux_x86_64` are
|
||||
enough) and build with `DLSS_SDK_ROOT=/path/to/DLSS bash build.sh`. That also builds the bridge
|
||||
`out/libbbport_dlss.so` (`gpu/dlss_bridge`, the only code that uses the SDK) and copies NVIDIA's
|
||||
`libnvidia-ngx-dlss.so.<version>` next to `out/bb-probe`; the driver provides the rest. Then pick
|
||||
*DLSS* in the launcher or the in-game menu: the preset sets the render size, Native AA is DLAA.
|
||||
DLSS gets the inputs FSR 3.1 gets (scene color, depth, motion vectors, jitter). Without the
|
||||
libraries, on other GPUs or with `BB_DLSS=0` it is listed as unavailable, and a DLSS setting falls
|
||||
back to FSR 3.1.
|
||||
|
||||
From source the script takes its tools from the system (MinGW GCC, CMake, Ninja, Python 3,
|
||||
SPIRV-Tools, Git) or from Nix; the AppImage has them prebuilt.
|
||||
|
||||
**FP8 on RDNA4.** When vkd3d-proton offers FP8 cooperative matrices (RDNA4), the DLL runs
|
||||
another variant of its passes: other shaders and weights, other dispatch sizes. The build records
|
||||
both, INT8 into `fsr4_411/` and FP8 into `fsr4_411/fp8/`, and the game picks FP8 itself when the
|
||||
GPU has FP8 matrices (`VK_EXT_shader_float8`); the log says `Upscaler: FSR 4.1.1 replay, FP8 …`.
|
||||
`BB_FSR411_VARIANT=int8` forces INT8. Checked on an RX 7800 XT through vkd3d-proton's FP16
|
||||
emulation of FP8 (`BB_FSR4CAP_FP8=1` records it there too, `fp8emu/`): bit-exact with the DLL;
|
||||
**not yet run on RDNA4 hardware.**
|
||||
|
||||
**AppImage** (Steam Deck): `bash build.sh && bash packaging/appimage.sh` →
|
||||
`dist/Bloodborne-bbport-x86_64.AppImage`; data in `~/.local/share/bbport`, `--play` starts the
|
||||
game without the launcher window (Game Mode). FSR 4.1.1 models are not packaged: build them with
|
||||
the launcher's button (see above); they go to `~/.local/share/bbport/fsr4_411`
|
||||
(`BB_PACKAGE_FSR411=1` bundles a local `fsr4_411` into an AppImage for your own devices). On the Steam Deck pick the 1280×720 output (the
|
||||
game is 16:9; on the 1280×800 screen it gets thin bars).
|
||||
|
||||
**Adding the AppImage to Steam** (*Add a Non-Steam Game*) needs no options; the compatibility tool
|
||||
does not matter. (Steam preloads its overlay into every non-Steam game; the AppImage removes it
|
||||
before its own programs start, so the Steam overlay is not shown in the game.) Where Steam
|
||||
runs games without FUSE (NixOS: Steam's FHS sandbox; the AppImage then exits with *Cannot mount
|
||||
AppImage*), set the launch options to
|
||||
|
||||
```
|
||||
TMPDIR=$HOME/.cache APPIMAGE_EXTRACT_AND_RUN=1 NO_CLEANUP=1 %command%
|
||||
```
|
||||
|
||||
The AppImage then unpacks itself (~2 GB, `~/.cache/appimage_extracted_*`) on the first start
|
||||
(~10 s) and reuses that copy afterwards; a new AppImage version gets a new copy, the old one can
|
||||
be deleted. Without `TMPDIR` it would unpack into Steam's `/tmp`, which is in RAM there. Add
|
||||
` --play` after `%command%` to skip the launcher.
|
||||
|
||||
**NVIDIA in the AppImage:** startup discovers the host's installed 64-bit NVIDIA Vulkan ICD
|
||||
and exposes its vendor libraries alongside the bundled AMD/Intel drivers. This keeps the
|
||||
NVIDIA userspace driver matched to the host kernel module. Standard Linux distributions keep
|
||||
these libraries under `/usr/lib*`; on NixOS the AppImage's internal `/nix/store` may hide them.
|
||||
In that case copy the NVIDIA libraries into an accessible directory and set
|
||||
`BB_NVIDIA_LIB_DIR=/path/to/libraries` (the NVIDIA manifest must also be accessible).
|
||||
Explicit `VK_DRIVER_FILES`/`VK_ICD_FILENAMES` overrides are preserved. Diagnose drivers inside
|
||||
the package with:
|
||||
|
||||
```bash
|
||||
./Bloodborne-bbport-x86_64.AppImage --vulkan-info 2>&1 | tee bbport-vulkan.log
|
||||
```
|
||||
|
||||
A user reported successful startup with FSR 3 on a GTX 1060 6GB (Fedora 44, NVIDIA
|
||||
580.178.04); selecting FSR 4 caused a black window. Use FSR 3 on this configuration.
|
||||
|
||||
MangoHud is bundled in the AppImage; enable its checkbox in the launcher. If MangoHud is also
|
||||
installed system-wide, or Steam's performance overlay is on (Steam Deck game mode), only one
|
||||
overlay is drawn (two drew doubled, offset text).
|
||||
When running from source, install MangoHud separately. A diagnostic launch with
|
||||
`VK_LOADER_LAYERS_DISABLE=~implicit~` also disables MangoHud.
|
||||
|
||||
Useful variables: `BB_FRAME_STATS=1` (frame statistics, including a `Memory:` line: VRAM, GTT,
|
||||
RSS, images and guest blocks in VRAM), `BB_PC_MODEL=1` (the new memory and translation model,
|
||||
0, the old model as in 0.3, is the default), `BB_ANISO=N` (anisotropic filtering of scene textures; 16 by
|
||||
default, 0 = the game's own),
|
||||
`BB_GC_IDLE_SECONDS=N` / `BB_VRAM_IDLE_SECONDS=N` (how long unused textures / buffers stay in VRAM;
|
||||
20 / 60), `BB_BREADCRUMBS=0` (no GPU breadcrumbs; with them a GPU hang names the draw or dispatch
|
||||
it is stuck in), `BB_GPU_PROFILE=1` (GPU time per
|
||||
pass), `BB_FSR4_PROFILE=1` (GPU time per FSR 4 pass), `BB_UPSCALER=taa|fsr3|fsr4|fsr411|off|none`,
|
||||
`BB_FRAMES_AHEAD=N` (how many frames the GPU command thread may run ahead of the GPU; 1 by default,
|
||||
0 = unbounded), `BB_PRESENT_THREAD=0` (present on the vblank thread, as before),
|
||||
`BB_LIVE_RES=1` (live resolution changes instead of the startup patch for outputs other than 1080p),
|
||||
`BB_PAD_RECORD=file` / `BB_PAD_REPLAY=file` (record a route with F9, replay it in scripted tests),
|
||||
`BB_GC_BUDGET_MB=N` (texture cache budget, as on integrated GPUs), `BB_VRAM_LIMIT_MB=N` (the VRAM
|
||||
budget taken as at most N MiB: checks the behaviour of cards with little VRAM), `BB_ASYNC_PIPELINES=0`
|
||||
(new pipelines compile at once and the game waits; by default in the background), `BB_PRESENT_DUMP_TRIGGER=file`
|
||||
with `BB_PRESENT_DUMP_COUNT=N` (dump N consecutive presented frames),
|
||||
`BB_FSR411_VARIANT=int8|fp8|fp8emu` (FSR 4.1.1 variant; by default FP8 where the GPU has FP8
|
||||
matrices), `BB_READBACKS=0|1|2` (reads of GPU-written memory by the game: 1 by default, 2
|
||||
precise and slow, 0 off).
|
||||
More in [docs/](docs); recent changes: [docs/CHANGES_0.4.md](docs/CHANGES_0.4.md) (in Russian),
|
||||
[docs/CHANGES_2026-10-06.md](docs/CHANGES_2026-10-06.md).
|
||||
|
||||
## Repository layout
|
||||
|
||||
| Path | Contents |
|
||||
|---|---|
|
||||
| `src/` | Loader (`probe.c`) and the HLE runtime |
|
||||
| `scripts/` | Offline preparation of the game image, module linking, patch compiler |
|
||||
| `gpu/` | Renderer library: vendored shadPS4 video core with this port's changes (`gpu/VENDOR.txt`), shims, ImGui menu, FSR 4.1.1 runtime (`gpu/shadps4/video_core/renderer_vulkan/fsr411`) |
|
||||
| `launcher/`, `packaging/` | GTK4 launcher; Nix package and AppImage |
|
||||
| `patches/` | Community patches for Bloodborne |
|
||||
| `tools/` | Developer tools: scripted runs, A/B toggles, FSR benchmark helpers, FSR 4 shader rewrites, `fsr4cap` (FSR 4.1.1 recording/extraction) |
|
||||
| `tests/` | Loader, runtime, patch and renderer tests |
|
||||
| `docs/` | Design notes and measurements ([upscaler](docs/upscaler.md), [parallel GPU](docs/parallel_gpu.md), [motion vectors](docs/motion_vectors.md), [roadmap](docs/ROADMAP.md)) |
|
||||
|
||||
Tests: `bash build.sh --test`, `python3 -m unittest discover -s tests`, and
|
||||
`ninja -C out/gpu motion-history-test ui-composition-test scene-resolution-test motion-shader-test settings-test`.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- The new memory and translation model on by default once it is stable and tested on NVIDIA;
|
||||
the old model is removed after that.
|
||||
- Textures as objects created at load time (less guessing by address, uploads outside the
|
||||
frame).
|
||||
- Shaders translated ahead of time, at install, not during play.
|
||||
- More CPU parallelism in GPU command processing (split the draw-recording stage further),
|
||||
scaling to all hardware threads — most important for the Steam Deck.
|
||||
- Async compute for the upscaler (the frame is GPU-bound at 4K).
|
||||
- XeSS (super resolution) and XeFG frame generation through a Wine helper sharing Vulkan
|
||||
memory (a memory-bridge prototype is in `tools/bridge_helper`); inputs exposed so that OptiScaler-style mapping works.
|
||||
- Frame generation (FSR 3.1 FG first), reactive and transparency masks for particles and fog.
|
||||
- Fix the races in AMD's FSR 4.1.1 shaders at output widths that are not multiples of 64
|
||||
(e.g. 1600×900), as already done for the left-edge race in FSR 4 v07 at 1080p.
|
||||
- Steam Deck validation of the AppImage; HDR output.
|
||||
|
||||
## Credits and licenses
|
||||
|
||||
bbport is licensed under the **GNU GPL v2 or later** ([LICENSE](LICENSE)) — it contains code
|
||||
from shadPS4 (GPL-2.0-or-later). Third-party components keep their licenses:
|
||||
[shadPS4](https://github.com/shadps4-emu/shadPS4) video core and shader recompiler (GPL-2.0+),
|
||||
[sirit](https://github.com/shadps4-emu/sirit), [half](https://half.sourceforge.net/),
|
||||
[FSR-Vulkan](https://github.com/FireBurn/FSR-Vulkan) by FireBurn (MIT; FSR 3.1 on Vulkan and the
|
||||
FSR 4 v07 provider), AMD FidelityFX SDK (MIT), [LibAtrac9](https://github.com/Thealexbarney/LibAtrac9)
|
||||
(MIT), [Dear ImGui](https://github.com/ocornut/imgui) (MIT), DejaVu fonts,
|
||||
[dxil-spirv](https://github.com/HansKristian-Work/dxil-spirv) (MIT, used to build the
|
||||
FSR 4.1.1 assets). Game patches by Kyo, Lance McDonald, auser1337, illusion, emoose and other
|
||||
community members (`patches/Bloodborne.xml`). The DLSS bridge (`gpu/dlss_bridge`, MIT) and
|
||||
loader come from [Supermedo's Windows port](https://github.com/Supermedo/bloodborne_pc), adapted
|
||||
from [IFreemz/shadPS4-Bloodborne-DLSS-FSR](https://github.com/IFreemz/shadPS4-Bloodborne-DLSS-FSR).
|
||||
AMD's FSR 4 DLLs and model data and NVIDIA's DLSS SDK and libraries are not distributed here.
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
# bbport — нативный порт Bloodborne для Linux
|
||||
|
||||
[English](README.md) · **Русский**
|
||||
> README оригинального проекта (deadinside28/bloodborne_pc), сохранён ради технических
|
||||
> подробностей. README этого форка — [README.md](README.md) (на английском).
|
||||
|
||||
[English](BBPORT.md) · **Русский**
|
||||
|
||||
bbport — это аналог Wine + DXVK, только для одной игры: *Bloodborne* для PlayStation 4
|
||||
(CUSA03173, версия игры 1.09) на ПК с Linux x86-64. Оригинальный исполняемый файл игры
|
||||
@@ -1,388 +1,150 @@
|
||||
THIS PROJECT IS NOT RELATED TO SHADPS4. ALL QUESTIONS RELATED TO THIS PROJECT SHOULD BE SENT TO THE DISCORD SERVER https://discord.gg/KYZRKk9CB, NOT TO THE SHADPS4 SERVER.
|
||||
# Bloodborne for Linux
|
||||
|
||||
The PlayStation 4 version of *Bloodborne* running natively on Linux. The game's own x86-64 code
|
||||
runs directly on the CPU and its graphics are translated to Vulkan. It is not an emulator.
|
||||
|
||||
# bbport — a native Linux port of Bloodborne
|
||||
This fork builds on [bbport](https://github.com/deadinside28/bloodborne_pc), the native Linux port
|
||||
by deadinside28. It adds the features of [Supermedo's Windows port](https://github.com/Supermedo/bloodborne_pc)
|
||||
(online play, DLSS, cheats) brought to Linux, plus a few of its own.
|
||||
|
||||
**English** · [Русский](README.ru.md)
|
||||
> **No game files are included.** You need your own decrypted dump of Bloodborne 1.09: the
|
||||
> European Game of the Year edition (`CUSA03173`) or the US release (`CUSA00900`). This project is
|
||||
> not affiliated with Sony Interactive Entertainment or FromSoftware.
|
||||
|
||||
bbport is the counterpart of Wine + DXVK for a single game: *Bloodborne* for PlayStation 4
|
||||
(CUSA03173, game version 1.09) on an x86-64 Linux PC. The game's original executable runs
|
||||
directly on the PC:
|
||||
## Features
|
||||
|
||||
- **as in Wine**, the game's x86-64 code runs on the CPU directly, and a runtime written for
|
||||
this one game replaces the PS4 system libraries;
|
||||
- **as in DXVK**, the game's graphics are translated to Vulkan — by a renderer derived from
|
||||
[shadPS4](https://github.com/shadps4-emu/shadPS4) and heavily extended for this game, including
|
||||
temporal upscaling with AMD FSR 3.1, FSR 4 and FSR 4.1.1;
|
||||
- memory and the GPU's work follow the PC model on AMD GPUs by default (the new memory and
|
||||
translation model); NVIDIA and other GPUs keep the old memory model of 0.3 for now. The
|
||||
launcher's *Memory model* switches either way.
|
||||
|
||||
One step remains to the full Wine + DXVK model: the new model on NVIDIA as well, then removing the
|
||||
old memory model (see below).
|
||||
|
||||
> **No game files are included.** You need your own dump of Bloodborne (CUSA03173, v1.09).
|
||||
> This project is not affiliated with Sony Interactive Entertainment, FromSoftware or AMD.
|
||||
|
||||
**Status: experimental, playable.** The game boots, loads saves and plays (the Hunter's Dream
|
||||
and several areas of Yharnam were played with it) with sound, gamepad and saving.
|
||||
A full play-through has not been verified, and only one machine (Linux, AMD Radeon RX 7800 XT,
|
||||
Mesa/RADV) has been tested thoroughly.
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Native execution.** The eboot is converted offline into a flat memory image; PS4 libc and
|
||||
libSceFios2 are linked into it as native code. No CPU emulation and no per-instruction
|
||||
translation: the game code runs at full speed.
|
||||
- **Two memory models** (launcher → *Mode → Memory and translation model*; *Auto*, the default,
|
||||
picks by GPU):
|
||||
- **The old memory model, as in 0.3 — the default on NVIDIA, Intel and others.** VRAM copies
|
||||
of the game's memory, writes tracked through page protection. With every fix made since 0.3
|
||||
(motion vectors and the upscaler, flicker with DoF on, a damaged shader cache).
|
||||
- **The new memory and translation model — the default on AMD.** The game's memory lives
|
||||
in system RAM and the GPU reads it where it is, as a PC game's buffers; data it reads often
|
||||
is kept in VRAM and given back when unused (textures after 20 s, buffers after 60 s). The
|
||||
layer's memory module tracks CPU writes to mirrored blocks to keep those copies coherent;
|
||||
it does not copy the whole GPU-visible memory. The PS4 command processor's work
|
||||
is translated into Vulkan commands rather than emulated on the CPU: memory writes
|
||||
(`WRITE_DATA`, DMA) are done by the GPU in command-stream order, and fences are written by the
|
||||
GPU itself at the end of the pipeline, at the same point of the stream as the PS4's command
|
||||
processor writes them. On AMD the game's memory is kept in a sparse buffer (the arena); on
|
||||
NVIDIA, Intel and others it goes through the layer's memory module: bound in place, VRAM
|
||||
copies as separate buffers, no sparse rebinding (NVIDIA's sparse binding froze the GPU for
|
||||
seconds); `BB_LAYER_MEMORY=1/0` chooses by hand. Tested on an RX 7800 XT. On NVIDIA it can be chosen by hand (*New*); testers' results differ by scene,
|
||||
some scenes are slower than with the old model, so it is not the default there yet. Not
|
||||
tested on Intel. Without the launcher: `BB_PC_MODEL=1/0` (unset: by GPU, as *Auto*).
|
||||
|
||||
[Changes in 0.5](docs/CHANGES_0.5.md); the prereleases: [pre4](docs/CHANGES_0.5-pre4.md),
|
||||
[pre3](docs/CHANGES_0.5-pre3.md), [pre2](docs/CHANGES_0.5-pre2.md).
|
||||
|
||||
Unused textures are freed in both modes, so VRAM no longer grows with every area visited.
|
||||
- **Unlocked frame rate.** Community patches (`patches/Bloodborne.xml`) make the simulation
|
||||
use the real frame time; ~90 FPS at 4K with FSR 4 Balanced on an RX 7800 XT, ~150 FPS at
|
||||
1440p with FSR 4 Quality. Also 30/60/90 FPS modes.
|
||||
- **Temporal upscaling built for this game.** Bloodborne has no velocity buffer, so bbport
|
||||
computes motion vectors itself: camera motion from depth and the scene matrices, and object
|
||||
motion (characters, cloth, weapons) from the vertex positions of the previous frame. The
|
||||
scene is jittered sub-pixel (Halton) and rendered at a reduced resolution; the upscaler fills
|
||||
the output (720p for the Steam Deck, 1080p, 1440p or 2160p) and the UI is drawn natively at the output resolution.
|
||||
- **FSR 3.1** (FireBurn/FSR-Vulkan).
|
||||
- **FSR 4 (INT8, model v07)** on GPUs exposing the required Vulkan shader features —
|
||||
RDNA2/3 included (see Requirements).
|
||||
- **FSR 4.1.1**: AMD's 4.1.1 DLL is recorded once under vkd3d-proton and its passes are
|
||||
replayed natively on Vulkan; the output is **bit-exact** with the DLL. Two variants, as in
|
||||
the DLL: INT8 on any GPU with the required shader features, and FP8 matrices on RDNA4 (RX
|
||||
9000; picked automatically). The assets are built on your machine from one DLL of your own
|
||||
(4.1.x): the launcher's *FSR 4.1.1 from your own AMD DLL → Choose DLL…* button (2–5 minutes,
|
||||
twice that on RDNA4; needs a recent Proton), or `tools/fsr4cap`.
|
||||
- Faster than AMD's own shaders on RDNA3: the final passes of FSR 4 and 4.1.1 were rewritten
|
||||
to store through workgroup memory (3.5× and 2.3× faster, bit-exact); FSR 4 costs ~4 ms at
|
||||
4K on an RX 7800 XT instead of ~6 ms.
|
||||
- **Multi-threaded GPU command processing.** The PS4 command stream is decoded on one thread
|
||||
and draws are bound and recorded on another (two-stage pipeline), with a Vulkan recording
|
||||
thread and helper threads for memory copies. Early on the single GPU thread capped the game
|
||||
at ~26 FPS; now it runs at 90–150 FPS depending on resolution and scene.
|
||||
- **Settings inside the game's own menu:** *System → Display / Effects* (after *Screen/Sound*),
|
||||
built and drawn by the game itself — output resolution, upscaler, preset, sharpness, FPS
|
||||
counter, model detail and the game's effects, in the game's language.
|
||||
- **Overlay menu** (Insert or L3+R3), styled like the game's dialogs: everything above plus the
|
||||
advanced settings, with gamepad, keyboard and mouse.
|
||||
- **GTK4 launcher** and an **AppImage** for the Steam Deck.
|
||||
|
||||
## How it differs from shadPS4
|
||||
|
||||
| | shadPS4 | bbport |
|
||||
|---|---|---|
|
||||
| Scope | General PS4 emulator, many games | One game: Bloodborne v1.09 |
|
||||
| Loading | Its own ELF loader and kernel emulation at run time | The eboot is converted offline (`scripts/`) into an image with PS4 libc/Fios2 linked in; a C loader maps it and jumps into the game (loader and runtime: ~5k lines) |
|
||||
| Memory | The GPU's view of PS4 memory is kept in VRAM copies, synchronized through page-protection write tracking | By default the same model (as in 0.3); in the *New memory and translation model* mode: the game's memory in system RAM, used by the GPU in place, frequently read data in VRAM, freed when unused |
|
||||
| Command processor | Emulated: memory writes, DMA and fences are done by the CPU while decoding | By default the same; in the new mode translated into Vulkan commands that the GPU runs in stream order, fences written after the work has really finished |
|
||||
| System libraries | Broad HLE of the PS4 OS | A small runtime (`src/runtime_*.c`) that implements exactly what Bloodborne calls: memory, threads, sync, files, audio (incl. ATRAC9), pad, saves, AppContent |
|
||||
| GPU | shadPS4 video core and shader recompiler | The same core (vendored, GPL) with ~200 marked changes (`bbport:`) plus new modules: two-stage draw pipeline, render-state and texture-set memoization, render-scale proxies, motion vectors, FSR 3.1/4/4.1.1, frame capture and GPU profiler |
|
||||
| GPU thread | One thread processes the whole command stream (the bottleneck in Bloodborne) | Decode and draw recording run on separate threads; the work scales with the hardware threads (Steam Deck included) |
|
||||
| Upscaling | — | Temporal (FSR 3.1, FSR 4, FSR 4.1.1) with the game's own motion vectors and jitter |
|
||||
| Game patches | Patch files applied by the emulator | The same community patches, compiled at start (`scripts/patches.py`); render resolution, effects and FPS from the launcher |
|
||||
|
||||
Without shadPS4 there would be no bbport: its renderer and shader recompiler are the base of
|
||||
the graphics side.
|
||||
|
||||
### Wine + DXVK for one game
|
||||
|
||||
- **CPU.** The PS4 CPU is x86-64, so the game's code runs directly on the PC's CPU, with no
|
||||
emulation and no instruction translation.
|
||||
- **System libraries.** As Wine replaces the Windows API, the bbport runtime (`src/runtime_*.c`)
|
||||
implements exactly the PS4 OS functions Bloodborne calls: memory, threads, files, audio, pad,
|
||||
saves.
|
||||
- **Memory.** In the new mode, as in a PC game: the game's data in system RAM, VRAM used the way
|
||||
a PC game uses it. The mode is still experimental.
|
||||
- **Graphics.** The PS4 GPU's command stream (PM4) is a recording of the game's graphics API
|
||||
calls: it is written by 99 functions of the statically linked libGnm, so decoding the stream
|
||||
and translating the calls themselves come to the same thing. GCN shaders are translated to
|
||||
SPIR-V. In the new mode the command processor's work (memory writes, DMA, fences) is
|
||||
translated into Vulkan commands as well; fences are written by the GPU itself. Resource
|
||||
descriptors (textures, buffers) are read by the translator on the CPU, as DXVK reads resource
|
||||
bindings: a PS4 texture has to be converted from the PS4 tiling into a Vulkan image before the
|
||||
draw runs, so the CPU must know its descriptor beforehand — translation, not emulation. The
|
||||
descriptor tables (the constant engine's dumps) are read only by the translator, so the CPU
|
||||
performs them. The game's occlusion queries (~60 a frame) are
|
||||
Vulkan occlusion queries; predication, `COPY_DATA` and `COND_EXEC` are translated too (check:
|
||||
`BB_PM4_SELFTEST=1`). Details:
|
||||
[docs/EMULATION_REMOVAL_PLAN.ru.md](docs/EMULATION_REMOVAL_PLAN.ru.md), "Шаг 4" (Russian).
|
||||
|
||||
"Port" here means a build for this one game, not a rewrite of its source code, which the project
|
||||
neither has nor includes.
|
||||
- **Online play (beta)** through [The Hunter's Dream](https://thehuntersdream.com) and shadNet:
|
||||
messages, bloodstains, ghosts, bells and summons. Linux and Windows players can play together.
|
||||
- **Upscaling:** AMD FSR 3.1, FSR 4 and FSR 4.1.1, **NVIDIA DLSS** on GeForce RTX, and
|
||||
native-resolution TAA.
|
||||
- **Unlocked frame rate**, capped at your display's refresh rate up to 120 FPS (above that the
|
||||
game's own movement timing breaks), or 30/60/90 FPS. Output from 720p to 4K.
|
||||
- **Launcher** with every setting in one window, in English, Spanish, Italian, French, German,
|
||||
Portuguese and Russian, like the in-game menu (Insert or L3+R3).
|
||||
- **Settings in the game's own System menu** as well (Display and Effects pages), in the game's
|
||||
language.
|
||||
- **Two memory models:** the new one, where the GPU works with the game's memory as in a PC game
|
||||
(the default on AMD), and the one of 0.3 (the default elsewhere); the launcher switches them.
|
||||
- **Xbox, Switch and keyboard button prompts**, picked by the controller in use, and a gestures
|
||||
menu without the motion-control icons.
|
||||
- **Cheats and gameplay tweaks:** never die, stealth, silent steps, enemy control, camera
|
||||
distance, ragdoll physics and more.
|
||||
- **Game effects** on and off, **mods** and community **patches**, loaded without changing the
|
||||
game files.
|
||||
- **Add to Steam** in one click (native or Flatpak Steam), and **Discord Rich Presence**.
|
||||
- **AppImage:** one file with everything inside, for any distribution and the Steam Deck. It
|
||||
updates itself: the launcher's **About** page checks this repository's releases and installs
|
||||
a new AppImage over the old one.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux x86-64, a Vulkan 1.3 GPU. Tested: AMD RX 7800 XT with Mesa 26 (RADV).
|
||||
The *New memory and translation model* mode is not tested on NVIDIA yet.
|
||||
FSR 4 / 4.1.1 require shader Float16, Int8/Int16, integer dot products, linear compute
|
||||
derivatives and extended storage image formats; FSR 4.1.1 additionally requires
|
||||
`VK_VALVE_shader_mixed_float_dot_product`. Unsupported choices fall back to FSR 3.1
|
||||
before the first frame and are disabled in the in-game menu.
|
||||
- Your decrypted game dump: the `CUSA03173` folder (eboot.bin, sce_module, ...), version 1.09.
|
||||
A dumped update is a separate folder: copy it over the base game, replacing files. The base
|
||||
game alone (1.00) crashes at start (guest offset 0x20348b8); the launcher and `run.sh` check
|
||||
the executable and say what is missing (`BB_SKIP_GAME_CHECK=1` skips the check).
|
||||
- To build: GCC, CMake, Ninja, Python 3, glslang, SDL3, Vulkan headers and the libraries in
|
||||
`shell.nix`. With [Nix](https://nixos.org) everything comes from `shell.nix` automatically.
|
||||
- Linux x86-64 and a GPU with Vulkan 1.3: AMD or Intel with Mesa, or NVIDIA with NVIDIA's driver
|
||||
(open kernel modules included). Mesa's NVK has not been tested.
|
||||
- DLSS needs a GeForce RTX (20 series or newer) and NVIDIA's driver; it does not work with NVK.
|
||||
- About 6 GB of free memory, 10 GB for 1440p or 4K output.
|
||||
|
||||
## Build and run
|
||||
## Getting started
|
||||
|
||||
### AppImage
|
||||
|
||||
Build it with `bash build.sh && bash packaging/appimage-sharun.sh`, then run
|
||||
`dist/Bloodborne-bbport-x86_64.AppImage`. Choose your game folder (the one with `eboot.bin`) and
|
||||
press **Play**. Saves and settings go to `~/.local/share/bbport`.
|
||||
|
||||
### From source
|
||||
|
||||
On Arch Linux (or CachyOS, Manjaro…):
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/deadinside28/bloodborne_pc.git bbport && cd bbport
|
||||
bash build.sh # builds out/bb-probe and out/gpu/libbbgpu.so
|
||||
BB_GAME_DIR=/path/to/CUSA03173 bash run.sh
|
||||
sudo pacman -S --needed base-devel cmake ninja python python-gobject gtk4 libadwaita sdl3 \
|
||||
vulkan-headers vulkan-icd-loader glslang spirv-headers spirv-cross ffmpeg boost fmt \
|
||||
robin-map xxhash zydis miniz protobuf nlohmann-json openssl miniupnpc python-pillow ttf-dejavu
|
||||
paru -S magic_enum vulkan-memory-allocator xbyak # from the AUR
|
||||
|
||||
git clone --recursive https://github.com/ayozetr/bloodborne_linux
|
||||
cd bloodborne_linux
|
||||
bash build.sh
|
||||
python3 launcher/bbport_launcher.py # or launcher/install-desktop.sh for a menu entry
|
||||
```
|
||||
|
||||
or the launcher (pick the game folder, settings, *Start*):
|
||||
With [Nix](https://nixos.org), `bash build.sh` gets every dependency from `shell.nix` by itself.
|
||||
|
||||
```bash
|
||||
bash launcher/bb-launcher.sh # launcher/install-desktop.sh adds it to the app menu
|
||||
```
|
||||
### Online play
|
||||
|
||||
By default the game folder is expected next to the repository (`../CUSA03173`). Saves and the
|
||||
shader cache go to `user/` (the launcher lets you choose another folder); settings to
|
||||
`bbport.ini`. A gamepad is used through SDL3 (the launcher's *Controls → Controller* picks one
|
||||
when several are connected; `BB_GAMEPAD=<GUID or part of the name>`). The keyboard works too,
|
||||
also next to a connected gamepad (the Steam Deck always has one); both are remapped in the
|
||||
launcher (*Controls*). The character name is typed on the keyboard in a box over the game.
|
||||
The touchpad: its left half (Tab, Back/Select) opens the gestures, the right half (Backspace)
|
||||
the key items. There is no PSN: the *Play online / offline* screen is skipped and the game opens
|
||||
its main menu offline (a game patch; the launcher's *Game effects → Skip the “play online / offline” choice*,
|
||||
`BB_SKIP_NETWORK_CHOICE=0` shows it). With several monitors, *Screen → Monitor* in the launcher
|
||||
(`BB_DISPLAY=<number or part of the name>`) picks the one for the game.
|
||||
1. Register a shadNet account at [shadnet.shadps4.net](https://shadnet.shadps4.net).
|
||||
2. In the launcher, open **Online**, turn on **Play online** and enter your account name (not
|
||||
your email) and password. **Test the connection**: all three servers should be reachable.
|
||||
3. Start the game and choose online at the title screen.
|
||||
|
||||
**Resolution and preset changes:** for outputs other than 1080p (720p on the Steam Deck,
|
||||
1440p, 4K) the whole game renders at the preset's resolution, set by a patch at start — the
|
||||
fastest path. Changing the output or the preset in the in-game menu then needs *Apply and
|
||||
restart the game*. The *Live resolution changes* setting (launcher, in-game menu,
|
||||
`bbport.ini` `live_resolution=0|1|auto`; **off by default**) instead keeps the game at
|
||||
1080p internally and scales its render targets at run time, so 720p/1080p/1440p/4K and the
|
||||
presets switch without a restart. It costs more: the game then believes it renders 1080p
|
||||
and draws more (e.g. ~8× more small lights), and some targets are copied between sizes —
|
||||
use it on strong desktop GPUs only (`auto` turns it on for discrete GPUs with 8+ GB that are
|
||||
not pre-Turing NVIDIA). 1080p output and TAA always use the live path.
|
||||
Everyone in a session needs game version 1.09 and the same servers. Turn cheats off when you
|
||||
play with others.
|
||||
|
||||
**TAA:** a separate native-resolution temporal AA mode in the launcher and overlay,
|
||||
switchable live without an FSR model. The saved FSR preset is restored when returning
|
||||
to FSR. FSR Native AA adds reconstruction on top of full-resolution rendering and can
|
||||
be slower than disabling AA. TAA also adds work compared with no temporal AA.
|
||||
The RCAS switch and the 0–2 sharpness control also work with TAA. Sharpening runs after
|
||||
temporal accumulation and leaves its history and HUD unchanged.
|
||||
Co-op connects the players directly over UDP. With ufw or firewalld on, the launcher's **Online**
|
||||
page says so and gives the commands: pin the port with `SHADPS4_P2P_PORT=3658` in the extra
|
||||
variables, open it (`sudo ufw allow 3658/udp`), and for UPnP let the router's answers in
|
||||
(`sudo ufw allow from <router> proto udp`).
|
||||
|
||||
**Mods:** the launcher accepts separate loose-file mod folders (with `dvdroot_ps4/`, an extra
|
||||
wrapper folder, or the game folders such as `chr/` directly; file name case does not matter),
|
||||
with enable switches and load order. A sibling `CUSA03173-mods/` overlay also works.
|
||||
The original game is preserved; later mods override conflicting files.
|
||||
**Third-party patches:** shadPS4-format XML patch files in the data directory's `patches/`,
|
||||
switched on and off in the launcher. See [mods and patches](docs/MODS.md).
|
||||
**DLC:** put your add-on dumps in `user/addcont/<title id>/<entitlement label>/` (shadPS4's
|
||||
layout, e.g. `user/addcont/CUSA03173/SPEXPANSIONDLC03` for The Old Hunters): each folder there is
|
||||
reported to the game as an installed add-on.
|
||||
### DLSS
|
||||
|
||||
**Launcher language:** Russian, English, Brazilian Portuguese or Simplified Chinese (follows the system language by default).
|
||||
On a PC with a GeForce RTX, run `bash tools/build_dlss.sh` once after `build.sh`. It downloads
|
||||
what it needs from the NVIDIA DLSS SDK and builds the bridge. Then choose **DLSS** as the
|
||||
upscaler.
|
||||
|
||||
**Free camera and game debug menu** (v1.09): enable the corresponding switches in the
|
||||
launcher or in-game menu and restart. Free camera uses Lance McDonald's
|
||||
[GoldHEN patch](https://github.com/GoldHEN/GoldHEN_Patch_Repository/blob/main/patches/xml/Bloodborne-Orbis.xml):
|
||||
hold Cross and press L3 to cycle modes (keyboard: hold Space and press Z). It needs no fonts
|
||||
and conflicts with *Enemy Control*.
|
||||
For the game debug menu, install `DbgFont14h.ccm` and `DbgFont14h.tpf` from
|
||||
[Debug Menu and XML Patch](https://www.nexusmods.com/bloodborne/mods/253) into the game's
|
||||
`dvdroot_ps4/font/` first. Startup rejects missing or empty font files instead of launching
|
||||
the unsafe patch. Open it with the left touchpad / Tab (with the debug menu on, the left half
|
||||
no longer opens the gestures); Backspace is the right touchpad.
|
||||
Touch coordinates are forwarded from SDL gamepads; Back/Select emulates a left click on
|
||||
pads without a touch surface. The port's settings menu remains Insert / L3+R3.
|
||||
### DLC
|
||||
|
||||
GPU occlusion queries still use synthetic pixel counters (`PixelPipeStatDump`), and
|
||||
`IT_SET_PREDICATION` is unimplemented. Free camera allows visual investigation; it does
|
||||
not implement GPU occlusion culling.
|
||||
The Game of the Year edition (`CUSA03173`) already includes The Old Hunters. With the base game
|
||||
(such as the US `CUSA00900`), dump your add-ons too: each one is a folder (`SPEXPANSIONDLC03`,
|
||||
`SPDLCMESSENGER00`…) with only `sce_sys` inside, because their data ships with the 1.09 update.
|
||||
Put them in one folder and pick it in the launcher (**Game → DLC folder**), or set
|
||||
`BB_DLC_DIR=/path/to/dlc` for `run.sh`; the launcher shows how many it found. Each folder must
|
||||
keep the add-on's name (its entitlement label, such as `SPEXPANSIONDLC03`). Copying them to
|
||||
`user/addcont/<title id>/`, shadPS4's layout, works too.
|
||||
|
||||
**Upscaler assets** (not included; FSR 3.1 needs none):
|
||||
### Add to Steam
|
||||
|
||||
```bash
|
||||
bash tools/fetch_fsr4_assets.sh # FSR 4 v07 (MIT, built from AMD's source by Q2RTX)
|
||||
# FSR 4.1.1, from your own AMD DLLs (e.g. OptiScaler's FSR4_LATEST), needs GE-Proton 10 or newer:
|
||||
bash tools/fsr4cap/build_assets.sh <amd_fidelityfx_upscaler_dx12.dll>
|
||||
```
|
||||
**Game → Add to Steam** puts Bloodborne in your Steam library; it starts with the launcher's
|
||||
settings. Steam has to be closed while the game is added, and the launcher offers to do that.
|
||||
Flatpak Steam runs games inside its sandbox, which has neither the game nor your drivers, so
|
||||
the shortcut starts the game outside it with `flatpak-spawn --host`. That needs the
|
||||
`org.freedesktop.Flatpak` permission for Steam, which the launcher asks for once: it lets Steam
|
||||
start programs on your system.
|
||||
|
||||
**FSR 4.1.1 from your own DLL.** The easiest way is the launcher: *Upscaler → FSR 4.1.1 from your
|
||||
own AMD DLL → Choose DLL…*, then pick `amd_fidelityfx_upscaler_dx12.dll` version 4.1.x: from
|
||||
OptiScaler's `FSR4_LATEST` folder or from a game with FSR 4.1. That one file is enough: AMD's DLL
|
||||
exports the FidelityFX API itself, so no loader is needed (only a DLL without these exports would
|
||||
be recorded through `amd_fidelityfx_loader_dx12.dll`, and the launcher would ask for it). The DLL is checked at once,
|
||||
without a long capture; these do not fit: AMD's official FSR 4.0.x (e.g. Pragmata's 4.0.3 —
|
||||
AMD enables it on RDNA4 only, and under Proton it does not start on other GPUs) and community
|
||||
builds (4.0.2b, 4.1.1b and similar OptiScaler INT8 builds: another model and pass count). Then
|
||||
the DLL runs under Proton and is recorded at 20 sizes (progress in the row and in the *Log* tab;
|
||||
on RDNA4 a second time, for the FP8 variant),
|
||||
the passes are translated to SPIR-V, and FSR 4.1.1 is selected. The Proton build is picked
|
||||
automatically — the first under which the DLL enables FSR 4.1: GE-Proton 10 or newer,
|
||||
Proton-CachyOS, Proton Experimental (tested: GE-Proton 11, Proton-CachyOS 11, Experimental of
|
||||
October 2026; Steam's Proton 11.0 and GE-Proton 9 do not). It runs in the Steam runtime it
|
||||
requires (Steam installs it the first time any game runs with that Proton), or through
|
||||
umu-launcher when installed. On NixOS umu-launcher comes from `nix-shell` (downloaded the first
|
||||
time, ~1.7 GB); the AppImage runs the recording on the system itself, through the user's systemd
|
||||
(`systemd-run --user`), since the system's `/nix` is out of its sight. Recording on the Steam Deck is not verified yet; if the DLL does not
|
||||
enable FSR 4.1 there, build on a PC and copy the `fsr4_411` folder. The same from the command
|
||||
line: the launcher's (and the AppImage's) `--build-fsr411 <DLL>`.
|
||||
### Button prompts
|
||||
|
||||
**DLSS (NVIDIA GeForce RTX).** NVIDIA's DLSS SDK is not included: check out
|
||||
[github.com/NVIDIA/DLSS](https://github.com/NVIDIA/DLSS) (`include/` and `lib/Linux_x86_64` are
|
||||
enough) and build with `DLSS_SDK_ROOT=/path/to/DLSS bash build.sh`. That also builds the bridge
|
||||
`out/libbbport_dlss.so` (`gpu/dlss_bridge`, the only code that uses the SDK) and copies NVIDIA's
|
||||
`libnvidia-ngx-dlss.so.<version>` next to `out/bb-probe`; the driver provides the rest. Then pick
|
||||
*DLSS* in the launcher or the in-game menu: the preset sets the render size, Native AA is DLAA.
|
||||
DLSS gets the inputs FSR 3.1 gets (scene color, depth, motion vectors, jitter). Without the
|
||||
libraries, on other GPUs or with `BB_DLSS=0` it is listed as unavailable, and a DLSS setting falls
|
||||
back to FSR 3.1.
|
||||
The game shows PlayStation buttons. In the launcher, **Controls → Button prompts** switches them to
|
||||
Xbox, Nintendo Switch or keyboard ones; *Automatic* follows the controller in use, and the
|
||||
keyboard's when none is connected. Keyboard prompts show the keys bound in **Controls →
|
||||
Keyboard**. **Gestures menu without motion-control icons** cleans up the gestures menu. The
|
||||
prompts are put into your own game's menu files at each start, and nothing in the game folder
|
||||
changes.
|
||||
|
||||
From source the script takes its tools from the system (MinGW GCC, CMake, Ninja, Python 3,
|
||||
SPIRV-Tools, Git) or from Nix; the AppImage has them prebuilt.
|
||||
## Credits
|
||||
|
||||
**FP8 on RDNA4.** When vkd3d-proton offers FP8 cooperative matrices (RDNA4), the DLL runs
|
||||
another variant of its passes: other shaders and weights, other dispatch sizes. The build records
|
||||
both, INT8 into `fsr4_411/` and FP8 into `fsr4_411/fp8/`, and the game picks FP8 itself when the
|
||||
GPU has FP8 matrices (`VK_EXT_shader_float8`); the log says `Upscaler: FSR 4.1.1 replay, FP8 …`.
|
||||
`BB_FSR411_VARIANT=int8` forces INT8. Checked on an RX 7800 XT through vkd3d-proton's FP16
|
||||
emulation of FP8 (`BB_FSR4CAP_FP8=1` records it there too, `fp8emu/`): bit-exact with the DLL;
|
||||
**not yet run on RDNA4 hardware.**
|
||||
- [deadinside28/bloodborne_pc](https://github.com/deadinside28/bloodborne_pc): **bbport**, the
|
||||
native Linux port this fork is built on, and almost all of the work behind it. Its README, with
|
||||
the technical details, is in [BBPORT.md](BBPORT.md).
|
||||
- [Supermedo/bloodborne_pc](https://github.com/Supermedo/bloodborne_pc): the Windows port, whose
|
||||
online play, DLSS support, cheats page, Add to Steam and icon were brought to Linux here.
|
||||
- [shadPS4](https://github.com/shadps4-emu/shadPS4): the renderer and shader recompiler.
|
||||
- [shadp2p](https://github.com/Wozzardman/shadp2p) by Wozzardman: the network and PSN libraries
|
||||
for online play. Servers by the [shadPS4](https://shadps4.net) team and
|
||||
[The Hunter's Dream](https://thehuntersdream.com) by droogie and its team.
|
||||
- [IFreemz/shadPS4-Bloodborne-DLSS-FSR](https://github.com/IFreemz/shadPS4-Bloodborne-DLSS-FSR):
|
||||
the DLSS bridge, and the [NVIDIA DLSS SDK](https://github.com/NVIDIA/DLSS).
|
||||
- [FSR-Vulkan](https://github.com/FireBurn/FSR-Vulkan) by FireBurn and the AMD FidelityFX SDK.
|
||||
- **Dommo**: the Xbox and Switch button prompts (*DS3 Style - Modern Xbox prompts*, *DS3 Style -
|
||||
Nintendo Switch prompts*) and the *Stripped down gesture menu*, from
|
||||
[Nexus Mods (Bloodborne, mod 30)](https://www.nexusmods.com/bloodborne/mods/30), uploaded by
|
||||
goomab. Only their drawn prompts are in this repository ([assets/menu_prompts](assets/menu_prompts)).
|
||||
- **Abken**: the [KBM Icons Generator](https://www.nexusmods.com/bloodborne/mods/537), which showed
|
||||
which textures keyboard prompts need (after the idea of
|
||||
[mod 460](https://www.nexusmods.com/bloodborne/mods/460)); bbport draws its own.
|
||||
- [quick-sharun](https://github.com/pkgforge-dev/Anylinux-AppImages) for the AppImage.
|
||||
- Game patches by Kyo, Lance McDonald, auser1337, illusion, emoose and the Bloodborne community.
|
||||
|
||||
**AppImage** (Steam Deck): `bash build.sh && bash packaging/appimage.sh` →
|
||||
`dist/Bloodborne-bbport-x86_64.AppImage`; data in `~/.local/share/bbport`, `--play` starts the
|
||||
game without the launcher window (Game Mode). FSR 4.1.1 models are not packaged: build them with
|
||||
the launcher's button (see above); they go to `~/.local/share/bbport/fsr4_411`
|
||||
(`BB_PACKAGE_FSR411=1` bundles a local `fsr4_411` into an AppImage for your own devices). On the Steam Deck pick the 1280×720 output (the
|
||||
game is 16:9; on the 1280×800 screen it gets thin bars).
|
||||
Full list of components and licenses: [BBPORT.md](BBPORT.md#credits-and-licenses).
|
||||
|
||||
**Adding the AppImage to Steam** (*Add a Non-Steam Game*) needs no options; the compatibility tool
|
||||
does not matter. (Steam preloads its overlay into every non-Steam game; the AppImage removes it
|
||||
before its own programs start, so the Steam overlay is not shown in the game.) Where Steam
|
||||
runs games without FUSE (NixOS: Steam's FHS sandbox; the AppImage then exits with *Cannot mount
|
||||
AppImage*), set the launch options to
|
||||
## License
|
||||
|
||||
```
|
||||
TMPDIR=$HOME/.cache APPIMAGE_EXTRACT_AND_RUN=1 NO_CLEANUP=1 %command%
|
||||
```
|
||||
|
||||
The AppImage then unpacks itself (~2 GB, `~/.cache/appimage_extracted_*`) on the first start
|
||||
(~10 s) and reuses that copy afterwards; a new AppImage version gets a new copy, the old one can
|
||||
be deleted. Without `TMPDIR` it would unpack into Steam's `/tmp`, which is in RAM there. Add
|
||||
` --play` after `%command%` to skip the launcher.
|
||||
|
||||
**NVIDIA in the AppImage:** startup discovers the host's installed 64-bit NVIDIA Vulkan ICD
|
||||
and exposes its vendor libraries alongside the bundled AMD/Intel drivers. This keeps the
|
||||
NVIDIA userspace driver matched to the host kernel module. Standard Linux distributions keep
|
||||
these libraries under `/usr/lib*`; on NixOS the AppImage's internal `/nix/store` may hide them.
|
||||
In that case copy the NVIDIA libraries into an accessible directory and set
|
||||
`BB_NVIDIA_LIB_DIR=/path/to/libraries` (the NVIDIA manifest must also be accessible).
|
||||
With the host's NVIDIA driver the AppImage also passes the host's EGL vendor files, NVIDIA's
|
||||
first (`__EGL_VENDOR_LIBRARY_FILENAMES`; without them NVIDIA's ICD gave no `vkCreateInstance` on
|
||||
some hosts, #107; `BB_NVIDIA_EGL=0`: off). Explicit `VK_DRIVER_FILES`/`VK_ICD_FILENAMES` overrides are preserved. Diagnose drivers inside
|
||||
the package with:
|
||||
|
||||
```bash
|
||||
./Bloodborne-bbport-x86_64.AppImage --vulkan-info 2>&1 | tee bbport-vulkan.log
|
||||
```
|
||||
|
||||
A user reported successful startup with FSR 3 on a GTX 1060 6GB (Fedora 44, NVIDIA
|
||||
580.178.04); selecting FSR 4 caused a black window. Use FSR 3 on this configuration.
|
||||
|
||||
MangoHud is bundled in the AppImage; enable its checkbox in the launcher. If MangoHud is also
|
||||
installed system-wide, or Steam's performance overlay is on (Steam Deck game mode), only one
|
||||
overlay is drawn (two drew doubled, offset text).
|
||||
When running from source, install MangoHud separately. A diagnostic launch with
|
||||
`VK_LOADER_LAYERS_DISABLE=~implicit~` also disables MangoHud.
|
||||
|
||||
Useful variables: `BB_FRAME_STATS=1` (frame statistics, including a `Memory:` line: VRAM, GTT,
|
||||
RSS, images and guest blocks in VRAM), `BB_PC_MODEL=1` (the new memory and translation model,
|
||||
0, the old model as in 0.3, is the default), `BB_ANISO=N` (anisotropic filtering of scene textures; 16 by
|
||||
default, 0 = the game's own),
|
||||
`BB_GC_IDLE_SECONDS=N` / `BB_VRAM_IDLE_SECONDS=N` (how long unused textures / buffers stay in VRAM;
|
||||
20 / 60), `BB_BREADCRUMBS=0` (no GPU breadcrumbs; with them a GPU hang names the draw or dispatch
|
||||
it is stuck in), `BB_GPU_PROFILE=1` (GPU time per
|
||||
pass), `BB_FSR4_PROFILE=1` (GPU time per FSR 4 pass), `BB_UPSCALER=taa|fsr3|fsr4|fsr411|off|none`,
|
||||
`BB_FRAMES_AHEAD=N` (how many frames the GPU command thread may run ahead of the GPU; 1 by default,
|
||||
0 = unbounded), `BB_PRESENT_THREAD=0` (present on the vblank thread, as before),
|
||||
`BB_LIVE_RES=1` (live resolution changes instead of the startup patch for outputs other than 1080p),
|
||||
`BB_PAD_RECORD=file` / `BB_PAD_REPLAY=file` (record a route with F9, replay it in scripted tests),
|
||||
`BB_GC_BUDGET_MB=N` (texture cache budget, as on integrated GPUs), `BB_VRAM_LIMIT_MB=N` (the VRAM
|
||||
budget taken as at most N MiB: checks the behaviour of cards with little VRAM), `BB_ASYNC_PIPELINES=0`
|
||||
(new pipelines compile at once and the game waits; by default in the background), `BB_PRESENT_DUMP_TRIGGER=file`
|
||||
with `BB_PRESENT_DUMP_COUNT=N` (dump N consecutive presented frames),
|
||||
`BB_FSR411_VARIANT=int8|fp8|fp8emu` (FSR 4.1.1 variant; by default FP8 where the GPU has FP8
|
||||
matrices), `BB_READBACKS=0|1|2` (reads of GPU-written memory by the game: 1 by default, 2
|
||||
precise and slow, 0 off).
|
||||
More in [docs/](docs); recent changes: [docs/CHANGES_0.4.md](docs/CHANGES_0.4.md) (in Russian),
|
||||
[docs/CHANGES_2026-10-06.md](docs/CHANGES_2026-10-06.md).
|
||||
|
||||
## Repository layout
|
||||
|
||||
| Path | Contents |
|
||||
|---|---|
|
||||
| `src/` | Loader (`probe.c`) and the HLE runtime |
|
||||
| `scripts/` | Offline preparation of the game image, module linking, patch compiler |
|
||||
| `gpu/` | Renderer library: vendored shadPS4 video core with this port's changes (`gpu/VENDOR.txt`), shims, ImGui menu, FSR 4.1.1 runtime (`gpu/shadps4/video_core/renderer_vulkan/fsr411`) |
|
||||
| `launcher/`, `packaging/` | GTK4 launcher; Nix package and AppImage |
|
||||
| `patches/` | Community patches for Bloodborne |
|
||||
| `tools/` | Developer tools: scripted runs, A/B toggles, FSR benchmark helpers, FSR 4 shader rewrites, `fsr4cap` (FSR 4.1.1 recording/extraction) |
|
||||
| `tests/` | Loader, runtime, patch and renderer tests |
|
||||
| `docs/` | Design notes and measurements ([upscaler](docs/upscaler.md), [parallel GPU](docs/parallel_gpu.md), [motion vectors](docs/motion_vectors.md), [roadmap](docs/ROADMAP.md)) |
|
||||
|
||||
Tests: `bash build.sh --test`, `python3 -m unittest discover -s tests`, and
|
||||
`ninja -C out/gpu motion-history-test ui-composition-test scene-resolution-test motion-shader-test settings-test`.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- The new memory and translation model on by default on NVIDIA too, once it is as fast there;
|
||||
the old model is removed after that.
|
||||
- Textures as objects created at load time (less guessing by address, uploads outside the
|
||||
frame).
|
||||
- Shaders translated ahead of time, at install, not during play.
|
||||
- More CPU parallelism in GPU command processing (split the draw-recording stage further),
|
||||
scaling to all hardware threads — most important for the Steam Deck.
|
||||
- Async compute for the upscaler (the frame is GPU-bound at 4K).
|
||||
- XeSS (super resolution) and XeFG frame generation through a Wine helper sharing Vulkan
|
||||
memory (a memory-bridge prototype is in `tools/bridge_helper`); inputs exposed so that OptiScaler-style mapping works.
|
||||
- Frame generation (FSR 3.1 FG first), reactive and transparency masks for particles and fog.
|
||||
- Fix the races in AMD's FSR 4.1.1 shaders at output widths that are not multiples of 64
|
||||
(e.g. 1600×900), as already done for the left-edge race in FSR 4 v07 at 1080p.
|
||||
- Steam Deck validation of the AppImage; HDR output.
|
||||
|
||||
## Credits and licenses
|
||||
|
||||
bbport is licensed under the **GNU GPL v2 or later** ([LICENSE](LICENSE)) — it contains code
|
||||
from shadPS4 (GPL-2.0-or-later). Third-party components keep their licenses:
|
||||
[shadPS4](https://github.com/shadps4-emu/shadPS4) video core and shader recompiler (GPL-2.0+),
|
||||
[sirit](https://github.com/shadps4-emu/sirit), [half](https://half.sourceforge.net/),
|
||||
[FSR-Vulkan](https://github.com/FireBurn/FSR-Vulkan) by FireBurn (MIT; FSR 3.1 on Vulkan and the
|
||||
FSR 4 v07 provider), AMD FidelityFX SDK (MIT), [LibAtrac9](https://github.com/Thealexbarney/LibAtrac9)
|
||||
(MIT), [Dear ImGui](https://github.com/ocornut/imgui) (MIT), DejaVu fonts,
|
||||
[dxil-spirv](https://github.com/HansKristian-Work/dxil-spirv) (MIT, used to build the
|
||||
FSR 4.1.1 assets). Game patches by Kyo, Lance McDonald, auser1337, illusion, emoose and other
|
||||
community members (`patches/Bloodborne.xml`). The DLSS bridge (`gpu/dlss_bridge`, MIT) and
|
||||
loader come from [Supermedo's Windows port](https://github.com/Supermedo/bloodborne_pc), adapted
|
||||
from [IFreemz/shadPS4-Bloodborne-DLSS-FSR](https://github.com/IFreemz/shadPS4-Bloodborne-DLSS-FSR).
|
||||
AMD's FSR 4 DLLs and model data and NVIDIA's DLSS SDK and libraries are not distributed here.
|
||||
GNU GPL v2 or later ([LICENSE](LICENSE)). Third-party components keep their own licenses. NVIDIA
|
||||
and DLSS are trademarks of NVIDIA Corporation.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Button prompts and gestures menu
|
||||
|
||||
By **Dommo**, from [Nexus Mods: Bloodborne, mod 30](https://www.nexusmods.com/bloodborne/mods/30)
|
||||
(uploaded by goomab): *DS3 Style - Modern Xbox prompts*, *DS3 Style - Nintendo Switch prompts* and
|
||||
*Stripped down gesture menu*. The mod page allows using its assets and uploading them elsewhere
|
||||
with credit to the author, but not in anything sold.
|
||||
|
||||
Only what the mods drew is here, never the game's own files:
|
||||
|
||||
- `xbox/`, `switch/`: the 20 button icons (`KG_*.dds`, BC7 exactly as in the mods) and the strip
|
||||
of big prompts from the shared UI atlas (`atlas.dds`, scaled down to the game's atlas size).
|
||||
- `gesturetop.json`: the edits that turn the game's `menu/gesturetop.gfx` into the mod's.
|
||||
|
||||
`scripts/menu_prompts.py` puts them into the player's own `menu/common.tpf.dcx` and
|
||||
`menu/gesturetop.gfx` at each start; `tools/extract_prompt_assets.py` rebuilds this folder from
|
||||
the mods' ZIPs. Keyboard prompts are drawn by `scripts/menu_prompts.py` itself.
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"original_sha256": "5d7bae1451d97296e88a6add62b1fd9d1425901acc5c907e90f52354bc0b624e",
|
||||
"result_sha256": "c77f7398c02613bd3d1a9613ce773fa8b6b74143053c6092b52a3c8d959dd868",
|
||||
"edits": [
|
||||
[
|
||||
4,
|
||||
1,
|
||||
"2f"
|
||||
],
|
||||
[
|
||||
2548,
|
||||
1,
|
||||
"18"
|
||||
],
|
||||
[
|
||||
2556,
|
||||
1,
|
||||
"8e"
|
||||
],
|
||||
[
|
||||
2563,
|
||||
0,
|
||||
"c9"
|
||||
],
|
||||
[
|
||||
2564,
|
||||
0,
|
||||
"0034bca0480238"
|
||||
],
|
||||
[
|
||||
3189,
|
||||
1,
|
||||
"92"
|
||||
],
|
||||
[
|
||||
3229,
|
||||
1,
|
||||
"92"
|
||||
],
|
||||
[
|
||||
3242,
|
||||
5,
|
||||
"207b2673a5e0"
|
||||
],
|
||||
[
|
||||
3268,
|
||||
1,
|
||||
"98"
|
||||
],
|
||||
[
|
||||
3281,
|
||||
5,
|
||||
"2077b30a7b80"
|
||||
],
|
||||
[
|
||||
3519,
|
||||
1,
|
||||
"41"
|
||||
],
|
||||
[
|
||||
3529,
|
||||
1,
|
||||
"33"
|
||||
],
|
||||
[
|
||||
3539,
|
||||
5,
|
||||
"c900004000207d11920000"
|
||||
],
|
||||
[
|
||||
5795,
|
||||
1,
|
||||
"ab"
|
||||
],
|
||||
[
|
||||
5817,
|
||||
1,
|
||||
"1a"
|
||||
],
|
||||
[
|
||||
5826,
|
||||
5,
|
||||
"c900004000207a015deba0"
|
||||
],
|
||||
[
|
||||
6076,
|
||||
12,
|
||||
""
|
||||
]
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -82,6 +82,8 @@ if [[ ${1:-} == --test ]]; then
|
||||
out/file-mods-test
|
||||
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -I. -Isrc tests/test_sema.c "${runtime[@]}" out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/sema-test
|
||||
out/sema-test
|
||||
"$CC" -std=c11 -O2 -g -Wall -Wextra -Werror -pthread -I. -Isrc tests/test_online.c "${runtime[@]}" out/libatrac9.a -lm "${gpu[@]}" "${libraries[@]}" -o out/online-test
|
||||
out/online-test
|
||||
"$CC" -std=c11 -D_GNU_SOURCE -O2 -g -Wall -Wextra -Werror -I. -Isrc tests/test_content.c src/runtime_content.c -o out/content-test
|
||||
out/content-test
|
||||
fi
|
||||
@@ -0,0 +1,46 @@
|
||||
# Release notes template
|
||||
|
||||
Every release uses this format: same sections, same order. A section with nothing to say keeps
|
||||
its heading and says "None.". Notes are in English. "New", "Improved" and "Fixed" cover the
|
||||
changes since the previous release. Third-party work is credited in the line that uses it.
|
||||
|
||||
- Tag: `<upstream bbport version>-linux.<n>` (e.g. `0.4-linux.1`).
|
||||
- Title: `bbport Linux <tag>`.
|
||||
- Asset: `dist/Bloodborne-bbport-x86_64.AppImage` (`bash build.sh && bash packaging/appimage-sharun.sh`).
|
||||
|
||||
```markdown
|
||||
<One or two sentences: what this release is and which upstream bbport it is based on.>
|
||||
|
||||
**Requirements:** your own decrypted dump of Bloodborne with update 1.09, any retail release (US,
|
||||
Europe, UK, Japan, Asia, and the editions with The Old Hunters). No game files are included.
|
||||
Linux x86-64 and a Vulkan 1.3 GPU.
|
||||
|
||||
## Download
|
||||
|
||||
| File | For |
|
||||
|---|---|
|
||||
| `Bloodborne-bbport-x86_64.AppImage` | Any x86-64 distribution and the Steam Deck |
|
||||
|
||||
Make it executable, run it, choose your game folder and press **Play**. Saves and settings go
|
||||
to `~/.local/share/bbport`.
|
||||
|
||||
SHA-256: `<sha256sum of the AppImage>`
|
||||
|
||||
## New
|
||||
- **<Feature>**: <what it does>.
|
||||
|
||||
## Improved
|
||||
- <Change to something that existed>.
|
||||
|
||||
## Fixed
|
||||
- <Bug fixed>.
|
||||
|
||||
## Upgrading
|
||||
- <What someone coming from the previous release has to do, or "None.">
|
||||
|
||||
## Known issues
|
||||
- <Limitations and known bugs.>
|
||||
|
||||
**Full changes:** https://github.com/ayozetr/bloodborne_linux/compare/<previous tag>...<tag>
|
||||
**Credits and licenses:** https://github.com/ayozetr/bloodborne_linux#credits
|
||||
```
|
||||
@@ -134,6 +134,42 @@ add_library(bbgpu SHARED ${VIDEO_CORE} ${RECOMPILER} ${COMMON} ${SHIM_SOURCES} $
|
||||
shadps4/core/libraries/kernel/equeue.cpp ${AVPLAYER}
|
||||
shadps4/core/libraries/videodec/video_utils.cpp)
|
||||
add_dependencies(bbgpu host_shaders)
|
||||
# bbport co-op: PS4 network and PSN libraries with the shadNet client, from shadp2p
|
||||
# (github.com/Wozzardman/shadp2p, GPL-2.0), for private Bloodborne co-op servers.
|
||||
find_package(Protobuf CONFIG REQUIRED)
|
||||
set(SHADNET_PROTO_OUT "${CMAKE_CURRENT_BINARY_DIR}/shadnet_proto_gen")
|
||||
file(MAKE_DIRECTORY "${SHADNET_PROTO_OUT}")
|
||||
add_custom_command(
|
||||
OUTPUT "${SHADNET_PROTO_OUT}/shadnet.pb.cc" "${SHADNET_PROTO_OUT}/shadnet.pb.h"
|
||||
COMMAND protobuf::protoc "--proto_path=${CMAKE_CURRENT_SOURCE_DIR}/shadps4/shadnet"
|
||||
"--cpp_out=${SHADNET_PROTO_OUT}" "${CMAKE_CURRENT_SOURCE_DIR}/shadps4/shadnet/shadnet.proto"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/shadps4/shadnet/shadnet.proto")
|
||||
file(GLOB_RECURSE BBNET_SOURCES CONFIGURE_DEPENDS shadps4/core/libraries/network/*.cpp
|
||||
shadps4/core/libraries/np/*.cpp shadps4/shadnet/*.cpp)
|
||||
# Trophies and the system dialogs stay in the C runtime; their shadPS4 versions need the
|
||||
# emulator's UI layers.
|
||||
list(FILTER BBNET_SOURCES EXCLUDE REGEX
|
||||
"np_trophy|trophy_ui|np_sns_facebook_dialog|np_profile_dialog/|np_commerce/")
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
add_library(bbnet STATIC ${BBNET_SOURCES} "${SHADNET_PROTO_OUT}/shadnet.pb.cc" bbnet/bbnet_glue.cpp)
|
||||
target_include_directories(bbnet PRIVATE shim shadps4 shadps4/shadnet "${SHADNET_PROTO_OUT}"
|
||||
third_party/imgui third_party/httplib)
|
||||
target_link_libraries(bbnet PRIVATE nlohmann_json::nlohmann_json)
|
||||
# HTTPS (the community game server only speaks TLS) and gzip replies in cpp-httplib.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_compile_definitions(bbnet PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT CPPHTTPLIB_ZLIB_SUPPORT)
|
||||
target_link_libraries(bbnet PRIVATE OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB)
|
||||
# UPnP: opens the co-op port on the router (net_upnp.cpp).
|
||||
find_path(MINIUPNPC_INCLUDE miniupnpc.h PATH_SUFFIXES miniupnpc REQUIRED)
|
||||
find_library(MINIUPNPC_LIBRARY NAMES miniupnpc REQUIRED)
|
||||
target_include_directories(bbnet PRIVATE "${MINIUPNPC_INCLUDE}")
|
||||
target_link_libraries(bbnet PRIVATE "${MINIUPNPC_LIBRARY}")
|
||||
target_compile_definitions(bbnet PRIVATE BOOST_ASIO_STANDALONE NDEBUG)
|
||||
target_compile_options(bbnet PRIVATE -Wno-deprecated-declarations -Wno-missing-field-initializers)
|
||||
target_link_libraries(bbnet PUBLIC Vulkan::Headers fmt::fmt Boost::headers magic_enum::magic_enum
|
||||
protobuf::libprotobuf)
|
||||
target_link_libraries(bbgpu PUBLIC bbnet)
|
||||
# In-game settings menu: Dear ImGui (third_party/imgui, MIT) with its Vulkan backend, and an
|
||||
# embedded Cyrillic font (third_party/fonts).
|
||||
target_sources(bbgpu PRIVATE third_party/imgui/imgui.cpp third_party/imgui/imgui_draw.cpp
|
||||
|
||||
@@ -16,6 +16,8 @@ typedef struct {
|
||||
} BbGpuConfig;
|
||||
/* Registers kernel event queues (needed with or without graphics). */
|
||||
void bbgpu_register_kernel(void);
|
||||
/* Co-op: the game folder and param.sfo identity for the network and PSN libraries. */
|
||||
void bbnet_configure(const char *app0, const char *serial, const char *title, const char *app_ver);
|
||||
/* Creates window, Vulkan device, presenter and GPU command processor. */
|
||||
int bbgpu_init(const BbGpuConfig *config);
|
||||
/* Function for an imported NID ("NID#lib#mod"), or 0 when the GPU library does not provide it. */
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// bbport co-op: what the network and NP libraries from shadp2p need from the rest of
|
||||
// shadPS4, implemented on top of the bbport runtime, and their registration.
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/types.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/bloodborne_re.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/user_manager.h"
|
||||
#include "imgui/invitation_prompt_layer.h"
|
||||
#include "imgui/shadnet_notifications_layer.h"
|
||||
|
||||
#include "core/libraries/network/http.h"
|
||||
#include "core/libraries/network/http2.h"
|
||||
#include "core/libraries/network/net.h"
|
||||
#include "core/libraries/network/netctl.h"
|
||||
#include "core/libraries/network/ssl.h"
|
||||
#include "core/libraries/network/ssl2.h"
|
||||
#include "core/libraries/np/np_auth.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2.h"
|
||||
#include "core/libraries/np/np_partner.h"
|
||||
#include "core/libraries/np/np_party.h"
|
||||
#include "core/libraries/np/np_score/np_score.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling.h"
|
||||
#include "core/libraries/np/np_tus.h"
|
||||
#include "core/libraries/np/np_web_api/np_web_api.h"
|
||||
#include "core/libraries/np/np_web_api2/np_web_api2.h"
|
||||
|
||||
extern "C" {
|
||||
int32_t runtime_thread_spawn(void** out, void* PS4_SYSV_ABI (*entry)(void*), void* argument,
|
||||
uint64_t stack, const char* name);
|
||||
int32_t runtime_thread_join_spawned(void* thread, void** result);
|
||||
}
|
||||
|
||||
// ---- Kernel ---------------------------------------------------------------------------------
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
struct BbNetMutex {
|
||||
std::recursive_mutex mutex;
|
||||
bool recursive{};
|
||||
int depth{};
|
||||
};
|
||||
struct BbNetMutexAttr {
|
||||
PthreadMutexType type{PthreadMutexType::ErrorCheck};
|
||||
};
|
||||
struct BbNetCond {
|
||||
std::condition_variable_any cond;
|
||||
};
|
||||
struct BbNetCondAttr {};
|
||||
struct BbNetThreadAttr {
|
||||
u64 stack{};
|
||||
};
|
||||
|
||||
s32 ErrnoToSceKernelError(s32 e) {
|
||||
return e > 0 && e <= 0xffff ? s32(0x80020000u | u32(e)) : e;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) {
|
||||
if (!ret) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
std::memset(ret->text_representation, 0, sizeof(ret->text_representation));
|
||||
std::snprintf(ret->text_representation, sizeof(ret->text_representation), "%s", "11.000.000");
|
||||
ret->hex_representation = 0x11000001;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp) {
|
||||
if (!tp) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
s64 ns;
|
||||
if (clock_id == ORBIS_CLOCK_REALTIME || clock_id == ORBIS_CLOCK_REALTIME_PRECISE ||
|
||||
clock_id == ORBIS_CLOCK_REALTIME_FAST || clock_id == ORBIS_CLOCK_SECOND) {
|
||||
ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
} else {
|
||||
ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
tp->tv_sec = ns / 1000000000;
|
||||
tp->tv_nsec = ns % 1000000000;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32, s32,
|
||||
const char*) {
|
||||
if (!addr_in_out || !len) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
void* memory = std::calloc(1, len);
|
||||
if (!memory) {
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
*addr_in_out = memory;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMunmap(void* addr, u64) {
|
||||
std::free(addr);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
*attr = new BbNetMutexAttr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) {
|
||||
if (!attr || !*attr) return 22;
|
||||
(*attr)->type = type;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
int scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* attr, const char*) {
|
||||
if (!mutex) return 22;
|
||||
*mutex = new BbNetMutex;
|
||||
(*mutex)->recursive = attr && *attr && (*attr)->type == PthreadMutexType::Recursive;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
||||
if (!mutex || !*mutex) return 22;
|
||||
delete *mutex;
|
||||
*mutex = nullptr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutex_lock(PthreadMutexT* mutex) {
|
||||
if (!mutex || !*mutex) return 22;
|
||||
(*mutex)->mutex.lock();
|
||||
if (++(*mutex)->depth > 1 && !(*mutex)->recursive) {
|
||||
--(*mutex)->depth;
|
||||
(*mutex)->mutex.unlock();
|
||||
return 11; // EDEADLK
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutex_trylock(PthreadMutexT* mutex) {
|
||||
if (!mutex || !*mutex) return 22;
|
||||
if (!(*mutex)->mutex.try_lock()) return 16; // EBUSY
|
||||
if (++(*mutex)->depth > 1 && !(*mutex)->recursive) {
|
||||
--(*mutex)->depth;
|
||||
(*mutex)->mutex.unlock();
|
||||
return 16;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
||||
if (!mutex || !*mutex) return 22;
|
||||
--(*mutex)->depth;
|
||||
(*mutex)->mutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int posix_pthread_condattr_init(PthreadCondAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
*attr = new BbNetCondAttr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_condattr_destroy(PthreadCondAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
int scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT*, const char*) {
|
||||
if (!cond) return 22;
|
||||
*cond = new BbNetCond;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_cond_destroy(PthreadCondT* cond) {
|
||||
if (!cond || !*cond) return 22;
|
||||
delete *cond;
|
||||
*cond = nullptr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_cond_signal(PthreadCondT* cond) {
|
||||
if (!cond || !*cond) return 22;
|
||||
(*cond)->cond.notify_one();
|
||||
return 0;
|
||||
}
|
||||
namespace {
|
||||
// The caller holds the mutex once; the wait releases and retakes it like pthread_cond_wait.
|
||||
struct HeldMutex {
|
||||
BbNetMutex* mutex;
|
||||
void lock() {
|
||||
mutex->mutex.lock();
|
||||
++mutex->depth;
|
||||
}
|
||||
void unlock() {
|
||||
--mutex->depth;
|
||||
mutex->mutex.unlock();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
int posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) {
|
||||
if (!cond || !*cond || !mutex || !*mutex) return 22;
|
||||
HeldMutex held{*mutex};
|
||||
(*cond)->cond.wait(held);
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex, u64 usec) {
|
||||
if (!cond || !*cond || !mutex || !*mutex) return 22;
|
||||
HeldMutex held{*mutex};
|
||||
const auto status = (*cond)->cond.wait_for(held, std::chrono::microseconds(usec));
|
||||
return status == std::cv_status::timeout ? 60 : 0; // ETIMEDOUT
|
||||
}
|
||||
|
||||
int posix_pthread_attr_init(PthreadAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
*attr = new BbNetThreadAttr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_attr_destroy(PthreadAttrT* attr) {
|
||||
if (!attr) return 22;
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stack_size) {
|
||||
if (!attr || !*attr) return 22;
|
||||
(*attr)->stack = stack_size;
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_attr_setinheritsched(PthreadAttrT*, int) {
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_attr_setschedpolicy(PthreadAttrT*, SchedPolicy) {
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_attr_setschedparam(PthreadAttrT*, const SchedParam*) {
|
||||
return 0;
|
||||
}
|
||||
int scePthreadAttrSetaffinity(PthreadAttrT*, u64) {
|
||||
return 0;
|
||||
}
|
||||
int posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr,
|
||||
PthreadEntryFunc start, void* arg, const char* name) {
|
||||
if (!thread || !start) return 22;
|
||||
const u64 stack = attr && *attr ? (*attr)->stack : 0;
|
||||
return runtime_thread_spawn(thread, start, arg, stack, name ? name : "np");
|
||||
}
|
||||
int posix_pthread_join(PthreadT thread, void** ret) {
|
||||
return runtime_thread_join_spawned(thread, ret);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
||||
// ---- User and system service, debugger, Bloodborne helpers -------------------------------------
|
||||
|
||||
namespace Libraries::UserService {
|
||||
s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) {
|
||||
if (!user_id) {
|
||||
return s32(0x80960009); // SCE_USER_SERVICE_ERROR_INVALID_ARGUMENT
|
||||
}
|
||||
*user_id = UserManager::RuntimeUserId;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
} // namespace Libraries::UserService
|
||||
|
||||
namespace Libraries::SystemService {
|
||||
void PushSystemServiceEvent(const OrbisSystemServiceEvent& event) {
|
||||
std::printf("Co-op: system service event %#x not delivered (invitations are not ported)\n",
|
||||
unsigned(event.event_type));
|
||||
}
|
||||
} // namespace Libraries::SystemService
|
||||
|
||||
namespace Core::Debugger {
|
||||
s32 GetCurrentPid() {
|
||||
#ifdef _WIN32
|
||||
return s32(GetCurrentProcessId());
|
||||
#else
|
||||
return s32(getpid());
|
||||
#endif
|
||||
}
|
||||
} // namespace Core::Debugger
|
||||
|
||||
namespace Core::Bloodborne {
|
||||
namespace {
|
||||
std::mutex placement_mutex;
|
||||
std::optional<std::string> placement;
|
||||
} // namespace
|
||||
std::optional<std::string> GetSeamlessHostPlacementHeader() {
|
||||
std::scoped_lock lock{placement_mutex};
|
||||
return placement;
|
||||
}
|
||||
bool SetSeamlessHostPlacementHeader(std::string_view value) {
|
||||
if (value.empty() || value.size() > 4096) {
|
||||
return false;
|
||||
}
|
||||
std::scoped_lock lock{placement_mutex};
|
||||
placement = std::string{value};
|
||||
return true;
|
||||
}
|
||||
void ClearSeamlessHostPlacementHeader() {
|
||||
std::scoped_lock lock{placement_mutex};
|
||||
placement.reset();
|
||||
}
|
||||
void TraceMatching2LeaveRoom(std::uintptr_t, std::uint64_t) {}
|
||||
} // namespace Core::Bloodborne
|
||||
|
||||
// ---- Notifications (log only for now) ------------------------------------------------------------
|
||||
|
||||
namespace ImGui::ShadNetNotify {
|
||||
namespace {
|
||||
std::mutex history_mutex;
|
||||
std::vector<HistoryEntry> history;
|
||||
std::size_t unread{};
|
||||
} // namespace
|
||||
KindDisplay DisplayOf(Kind kind) {
|
||||
switch (kind) {
|
||||
case Kind::FriendRequest:
|
||||
return {"Friend request", {0.9f, 0.8f, 0.3f, 1.0f}};
|
||||
case Kind::FriendNew:
|
||||
return {"Friend", {0.4f, 0.9f, 0.4f, 1.0f}};
|
||||
case Kind::FriendLost:
|
||||
return {"Friend", {0.9f, 0.4f, 0.4f, 1.0f}};
|
||||
case Kind::Online:
|
||||
return {"Online", {0.4f, 0.8f, 0.9f, 1.0f}};
|
||||
default:
|
||||
return {"Info", {0.8f, 0.8f, 0.8f, 1.0f}};
|
||||
}
|
||||
}
|
||||
void Push(Kind kind, std::string text) {
|
||||
std::printf("Co-op: %s: %s\n", DisplayOf(kind).tag, text.c_str());
|
||||
std::scoped_lock lock{history_mutex};
|
||||
history.push_back({kind, std::move(text), {}});
|
||||
++unread;
|
||||
}
|
||||
std::vector<HistoryEntry> GetHistory() {
|
||||
std::scoped_lock lock{history_mutex};
|
||||
return history;
|
||||
}
|
||||
void ClearHistory() {
|
||||
std::scoped_lock lock{history_mutex};
|
||||
history.clear();
|
||||
unread = 0;
|
||||
}
|
||||
std::size_t UnreadCount() {
|
||||
std::scoped_lock lock{history_mutex};
|
||||
return unread;
|
||||
}
|
||||
void MarkRead() {
|
||||
std::scoped_lock lock{history_mutex};
|
||||
unread = 0;
|
||||
}
|
||||
void Register() {}
|
||||
void Unregister() {}
|
||||
} // namespace ImGui::ShadNetNotify
|
||||
|
||||
namespace ImGui::InvitationPrompt {
|
||||
void Push(s32, std::string invitation_id, std::string, std::string from_npid) {
|
||||
std::printf("Co-op: invitation %s from %s (invitations are not supported yet)\n",
|
||||
invitation_id.c_str(), from_npid.c_str());
|
||||
}
|
||||
void Dismiss(const std::string&) {}
|
||||
void Register() {}
|
||||
void Unregister() {}
|
||||
} // namespace ImGui::InvitationPrompt
|
||||
|
||||
// ---- Game identity -------------------------------------------------------------------------------
|
||||
|
||||
// ElfInfo lets shadPS4's Core::Emulator fill it in; this port has no emulator class, so the
|
||||
// same name is used here for that one job.
|
||||
namespace Core {
|
||||
class Emulator {
|
||||
public:
|
||||
static void SetGame(const char* app0, const char* serial, const char* title,
|
||||
const char* app_ver) {
|
||||
auto& info = Common::ElfInfo::Instance();
|
||||
info.game_serial = serial ? serial : "";
|
||||
info.title = title ? title : "";
|
||||
info.app_ver = app_ver ? app_ver : "";
|
||||
info.game_folder = app0 ? app0 : "";
|
||||
info.initialized = true;
|
||||
info.npCommIds = ReadNpCommIds(info.game_folder / "sce_sys" / "npbind.dat");
|
||||
}
|
||||
|
||||
private:
|
||||
// npbind.dat: a 0x80-byte header, then tagged records (big-endian u16 tag and length);
|
||||
// tag 0x10 holds an NP communication id such as NPWR05818_00.
|
||||
static std::vector<std::string> ReadNpCommIds(const std::filesystem::path& path) {
|
||||
std::vector<std::string> ids;
|
||||
std::ifstream file{path, std::ios::binary};
|
||||
const std::vector<u8> data{std::istreambuf_iterator<char>(file), {}};
|
||||
for (size_t at = 0x80; at + 4 <= data.size();) {
|
||||
const u16 tag = u16(data[at] << 8 | data[at + 1]);
|
||||
const u16 size = u16(data[at + 2] << 8 | data[at + 3]);
|
||||
at += 4;
|
||||
if (at + size > data.size()) {
|
||||
break;
|
||||
}
|
||||
if (tag == 0x10 && size) {
|
||||
std::string id(reinterpret_cast<const char*>(&data[at]), size);
|
||||
id.resize(std::strlen(id.c_str()));
|
||||
ids.push_back(id);
|
||||
}
|
||||
at += size;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
};
|
||||
} // namespace Core
|
||||
|
||||
extern "C" void bbnet_configure(const char* app0, const char* serial, const char* title,
|
||||
const char* app_ver) {
|
||||
Core::Emulator::SetGame(app0, serial, title, app_ver);
|
||||
const auto& ids = Common::ElfInfo::Instance().GetNpCommIds();
|
||||
std::printf("Co-op: %s, NP communication id %s%s\n", serial ? serial : "?",
|
||||
ids.empty() ? "missing" : ids.front().c_str(),
|
||||
EmulatorSettings.IsShadNetEnabled() ? "; online play on" : "");
|
||||
}
|
||||
|
||||
// ---- Registration ------------------------------------------------------------------------------
|
||||
|
||||
extern "C" void bbnet_register(void) {
|
||||
Core::Loader::SymbolsResolver resolver;
|
||||
Core::Loader::SymbolsResolver* sym = &resolver;
|
||||
Libraries::Net::RegisterLib(sym);
|
||||
Libraries::NetCtl::RegisterLib(sym);
|
||||
Libraries::Http::RegisterLib(sym);
|
||||
Libraries::Http2::RegisterLib(sym);
|
||||
Libraries::Ssl::RegisterLib(sym);
|
||||
Libraries::Ssl2::RegisterLib(sym);
|
||||
Libraries::Np::NpAuth::RegisterLib(sym);
|
||||
Libraries::Np::NpCommon::RegisterLib(sym);
|
||||
Libraries::Np::NpManager::RegisterLib(sym);
|
||||
Libraries::Np::NpMatching2::RegisterLib(sym);
|
||||
Libraries::Np::NpPartner::RegisterLib(sym);
|
||||
Libraries::Np::NpParty::RegisterLib(sym);
|
||||
Libraries::Np::NpScore::RegisterLib(sym);
|
||||
Libraries::Np::NpSignaling::RegisterLib(sym);
|
||||
Libraries::Np::NpTus::RegisterLib(sym);
|
||||
Libraries::Np::NpWebApi::RegisterLib(sym);
|
||||
Libraries::Np::NpWebApi2::RegisterLib(sym);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,332 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/ssl.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Http {
|
||||
|
||||
enum OrbisHttpNBEvents : u32 {
|
||||
ORBIS_HTTP_NB_EVENT_IN = 0x00000001U, // Ready to receive (response data available).
|
||||
ORBIS_HTTP_NB_EVENT_OUT = 0x00000002U, // Ready to send.
|
||||
ORBIS_HTTP_NB_EVENT_SOCK_ERR = 0x00000008U, // Socket-level error during send/recv.
|
||||
ORBIS_HTTP_NB_EVENT_HUP = 0x00000010U, // Request interrupted by the application.
|
||||
ORBIS_HTTP_NB_EVENT_RESOLVED = 0x00010000U, // DNS resolution completed.
|
||||
ORBIS_HTTP_NB_EVENT_RESOLVER_ERR = 0x00020000U, // DNS resolution failed.
|
||||
};
|
||||
|
||||
enum OrbisHttpsFlags : u32 {
|
||||
ORBIS_HTTPS_FLAG_SERVER_VERIFY = 0x01,
|
||||
ORBIS_HTTPS_FLAG_CLIENT_VERIFY = 0x02,
|
||||
ORBIS_HTTPS_FLAG_CN_CHECK = 0x04,
|
||||
ORBIS_HTTPS_FLAG_NOT_AFTER_CHECK = 0x08,
|
||||
ORBIS_HTTPS_FLAG_NOT_BEFORE_CHECK = 0x10,
|
||||
ORBIS_HTTPS_FLAG_KNOWN_CA_CHECK = 0x20,
|
||||
ORBIS_HTTPS_FLAG_SESSION_REUSE = 0x40,
|
||||
ORBIS_HTTPS_FLAG_SNI = 0x80,
|
||||
};
|
||||
|
||||
// Used as the initial value of HttpSettings::ssl_flags.
|
||||
constexpr u32 ORBIS_HTTPS_FLAG_SDK_DEFAULT = ORBIS_HTTPS_FLAG_SERVER_VERIFY |
|
||||
ORBIS_HTTPS_FLAG_CN_CHECK |
|
||||
ORBIS_HTTPS_FLAG_KNOWN_CA_CHECK | ORBIS_HTTPS_FLAG_SNI;
|
||||
|
||||
// Validation masks consumed by sceHttpsEnableOption / sceHttpsDisableOption
|
||||
constexpr u32 ORBIS_HTTPS_FLAG_PUBLIC_VALID = 0x000020ff;
|
||||
constexpr u32 ORBIS_HTTPS_FLAG_PRIVATE_VALID = 0x00002dff;
|
||||
|
||||
enum OrbisHttpMethod : s32 {
|
||||
ORBIS_HTTP_METHOD_GET = 0,
|
||||
ORBIS_HTTP_METHOD_POST = 1,
|
||||
ORBIS_HTTP_METHOD_HEAD = 2,
|
||||
ORBIS_HTTP_METHOD_OPTIONS = 3,
|
||||
ORBIS_HTTP_METHOD_PUT = 4,
|
||||
ORBIS_HTTP_METHOD_DELETE = 5,
|
||||
ORBIS_HTTP_METHOD_TRACE = 6,
|
||||
ORBIS_HTTP_METHOD_CONNECT = 7,
|
||||
ORBIS_HTTP_METHOD_CUSTOM = 8,
|
||||
};
|
||||
|
||||
// mode argument for sceHttpAddRequestHeader.
|
||||
enum OrbisHttpHeaderModes : s32 {
|
||||
ORBIS_HTTP_HEADER_OVERWRITE = 0,
|
||||
ORBIS_HTTP_HEADER_ADD = 1,
|
||||
};
|
||||
|
||||
enum OrbisUriBuild : s32 {
|
||||
ORBIS_HTTP_URI_BUILD_WITH_SCHEME = 0x01,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_HOSTNAME = 0x02,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_PORT = 0x04,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_PATH = 0x08,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_USERNAME = 0x10,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_PASSWORD = 0x20,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_QUERY = 0x40,
|
||||
ORBIS_HTTP_URI_BUILD_WITH_FRAGMENT = 0x80
|
||||
};
|
||||
|
||||
struct OrbisHttpUriElement {
|
||||
bool opaque;
|
||||
char* scheme;
|
||||
char* username;
|
||||
char* password;
|
||||
char* hostname;
|
||||
char* path;
|
||||
char* query;
|
||||
char* fragment;
|
||||
u16 port;
|
||||
u8 reserved[10];
|
||||
};
|
||||
|
||||
struct HttpRequestInternal {
|
||||
int state; // +0x20
|
||||
int errorCode; // +0x28
|
||||
int httpStatusCode; // +0x20C
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
// Memory pool statistics
|
||||
struct OrbisHttpMemoryPoolStats {
|
||||
u64 poolSize;
|
||||
u64 maxInuseSize;
|
||||
u64 currentInuseSize;
|
||||
s32 reserved;
|
||||
};
|
||||
|
||||
// Cookie statistics
|
||||
struct OrbisHttpCookieStats {
|
||||
u64 currentInuseSize;
|
||||
u32 currentInuseNum;
|
||||
u64 maxInuseSize;
|
||||
u32 maxInuseNum;
|
||||
u32 removedNum;
|
||||
s32 reserved;
|
||||
};
|
||||
|
||||
using OrbisHttpEpollHandle = void*;
|
||||
|
||||
// Non-blocking event reported by sceHttpWaitRequest.
|
||||
struct OrbisHttpNBEvent {
|
||||
u32 events;
|
||||
u32 eventDetail;
|
||||
int id;
|
||||
void* userArg;
|
||||
};
|
||||
|
||||
// Callbacks
|
||||
using OrbisHttpAuthInfoCallback = int(PS4_SYSV_ABI*)(int request, int authType, const char* realm,
|
||||
char* username, char* password,
|
||||
int isNeedEntity, u8** entityBody,
|
||||
u64* entitySize, int* isSave, void* userArg);
|
||||
|
||||
using OrbisHttpRedirectCallback = int(PS4_SYSV_ABI*)(int request, s32 statusCode, s32* method,
|
||||
const char* location, void* userArg);
|
||||
|
||||
using OrbisHttpRequestStatusCallback = void(PS4_SYSV_ABI*)(int request, int requestStatus,
|
||||
void* userArg);
|
||||
|
||||
using OrbisHttpCookieRecvCallback = int(PS4_SYSV_ABI*)(int request, const char* url,
|
||||
const char* cookieHeader, u64 headerLen,
|
||||
void* userArg);
|
||||
|
||||
using OrbisHttpCookieSendCallback = int(PS4_SYSV_ABI*)(int request, const char* url,
|
||||
const char* cookieHeader, void* userArg);
|
||||
|
||||
using OrbisHttpsCallback = int(PS4_SYSV_ABI*)(int libsslCtxId, u32 verifyErr, void* const sslCert[],
|
||||
int certNum, void* userArg);
|
||||
|
||||
using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
|
||||
|
||||
// Functions
|
||||
int PS4_SYSV_ABI sceHttpAddCookie(int libhttpCtxId, const char* url, const char* cookie,
|
||||
u64 cookieLength);
|
||||
int PS4_SYSV_ABI sceHttpAddQuery();
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeaderRaw();
|
||||
int PS4_SYSV_ABI sceHttpAuthCacheExport();
|
||||
int PS4_SYSV_ABI sceHttpAuthCacheFlush(int libhttpCtxId);
|
||||
int PS4_SYSV_ABI sceHttpAuthCacheImport();
|
||||
int PS4_SYSV_ABI sceHttpCacheRedirectedConnectionEnabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpCookieExport(int libhttpCtxId, void* buffer, u64 bufferSize,
|
||||
u64* exportSize);
|
||||
int PS4_SYSV_ABI sceHttpCookieFlush(int libhttpCtxId);
|
||||
int PS4_SYSV_ABI sceHttpCookieImport(int libhttpCtxId, const void* buffer, u64 bufferSize);
|
||||
int PS4_SYSV_ABI sceHttpCreateConnection(int tmplId, const char* serverName, const char* scheme,
|
||||
u16 port, int isEnableKeepalive);
|
||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive);
|
||||
int PS4_SYSV_ABI sceHttpCreateRequest(int connId, int method, const char* path, u64 contentLength);
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url,
|
||||
u64 contentLength);
|
||||
int PS4_SYSV_ABI sceHttpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer,
|
||||
int isAutoProxyConf);
|
||||
int PS4_SYSV_ABI sceHttpDbgEnableProfile();
|
||||
int PS4_SYSV_ABI sceHttpDbgGetConnectionStat();
|
||||
int PS4_SYSV_ABI sceHttpDbgGetRequestStat();
|
||||
int PS4_SYSV_ABI sceHttpDbgSetPrintf();
|
||||
int PS4_SYSV_ABI sceHttpDbgShowConnectionStat();
|
||||
int PS4_SYSV_ABI sceHttpDbgShowMemoryPoolStat();
|
||||
int PS4_SYSV_ABI sceHttpDbgShowRequestStat();
|
||||
int PS4_SYSV_ABI sceHttpDbgShowStat();
|
||||
int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpGetConnectionStat();
|
||||
int PS4_SYSV_ABI sceHttpGetCookie(int libhttpCtxId, const char* url, char* cookie, u64* required,
|
||||
u64 prepared, int isSecure);
|
||||
int PS4_SYSV_ABI sceHttpGetCookieEnabled(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpGetCookieStats(int libhttpCtxId, OrbisHttpCookieStats* stats);
|
||||
int PS4_SYSV_ABI sceHttpGetEpollId();
|
||||
int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(int libhttpCtxId, OrbisHttpMemoryPoolStats* currentStat);
|
||||
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
|
||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize);
|
||||
int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId);
|
||||
int PS4_SYSV_ABI sceHttpRequestGetAllHeaders();
|
||||
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size);
|
||||
int PS4_SYSV_ABI sceHttpSetAuthEnabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetAuthInfoCallback(int id, OrbisHttpAuthInfoCallback cbfunc,
|
||||
void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetChunkedTransferEnabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieEnabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieMaxNum(int libhttpCtxId, u32 num);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieMaxNumPerDomain(int libhttpCtxId, u32 num);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieMaxSize(int libhttpCtxId, u32 size);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieRecvCallback(int id, OrbisHttpCookieRecvCallback cbfunc,
|
||||
void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieSendCallback(int id, OrbisHttpCookieSendCallback cbfunc,
|
||||
void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetCookieTotalMaxSize(int libhttpCtxId, u32 size);
|
||||
int PS4_SYSV_ABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int libhttpCtxId, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetDelayBuildRequestEnabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetEpoll(int id, OrbisHttpEpollHandle eh, void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetEpollId();
|
||||
int PS4_SYSV_ABI sceHttpSetHttp09Enabled(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetPolicyOption();
|
||||
int PS4_SYSV_ABI sceHttpSetPriorityOption();
|
||||
int PS4_SYSV_ABI sceHttpSetRedirectCallback(int id, OrbisHttpRedirectCallback cbfunc,
|
||||
void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetRequestStatusCallback(int id, OrbisHttpRequestStatusCallback cbfunc,
|
||||
void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpSetSocketCreationCallback();
|
||||
int PS4_SYSV_ABI sceHttpsFreeCaList(int libhttpCtxId, OrbisHttpsCaList* caList);
|
||||
int PS4_SYSV_ABI sceHttpsGetCaList(int httpCtxId, OrbisHttpsCaList* list);
|
||||
int PS4_SYSV_ABI sceHttpsGetSslError(int id, int* errNum, u32* detail);
|
||||
int PS4_SYSV_ABI sceHttpsLoadCert(int libhttpCtxId, int caCertNum, const void** caList,
|
||||
const void* cert, const void* privKey);
|
||||
int PS4_SYSV_ABI sceHttpsSetMinSslVersion(int id, int version);
|
||||
int PS4_SYSV_ABI sceHttpsSetSslCallback(int id, OrbisHttpsCallback cbfunc, void* userArg);
|
||||
int PS4_SYSV_ABI sceHttpsSetSslVersion(int id, int version);
|
||||
int PS4_SYSV_ABI sceHttpsUnloadCert(int libhttpCtxId);
|
||||
int PS4_SYSV_ABI sceHttpWaitRequest(OrbisHttpEpollHandle eh, OrbisHttpNBEvent* nbev, int maxevents,
|
||||
int timeout);
|
||||
int PS4_SYSV_ABI sceHttpUriCopy();
|
||||
//***********************************
|
||||
// Init/Terminate functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId);
|
||||
//***********************************
|
||||
// Misc functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpSetRecvBlockSize(int id, u32 blockSize);
|
||||
int PS4_SYSV_ABI sceHttpSetProxy(int id, int httpProxyConf, int wlanProxyConf, const char* host,
|
||||
u16 port);
|
||||
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int libhttpCtxId, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int isEnable);
|
||||
//***********************************
|
||||
// Non-blocking processing functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpCreateEpoll(int libhttpCtxId, OrbisHttpEpollHandle* eh);
|
||||
int PS4_SYSV_ABI sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh);
|
||||
int PS4_SYSV_ABI sceHttpGetEpoll(int id, OrbisHttpEpollHandle* eh, void** userArg);
|
||||
int PS4_SYSV_ABI sceHttpUnsetEpoll(int id);
|
||||
int PS4_SYSV_ABI sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh);
|
||||
int PS4_SYSV_ABI sceHttpGetNonblock(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetNonblock(int id, int isEnable);
|
||||
int PS4_SYSV_ABI sceHttpTryGetNonblock(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpTrySetNonblock(int id, int isEnable);
|
||||
//***********************************
|
||||
// Http Communication functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size);
|
||||
int PS4_SYSV_ABI sceHttpAbortRequest(int reqId);
|
||||
int PS4_SYSV_ABI sceHttpAbortRequestForce(int reqId);
|
||||
//***********************************
|
||||
// Https Option setting functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpsDisableOption(int id, u32 sslFlags);
|
||||
int PS4_SYSV_ABI sceHttpsDisableOptionPrivate(int id, u32 sslFlags);
|
||||
int PS4_SYSV_ABI sceHttpsEnableOption(int id, u32 sslFlags);
|
||||
int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(int id, u32 sslFlags);
|
||||
//***********************************
|
||||
// Response Information functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpSetResolveTimeOut(int id, u32 usec);
|
||||
int PS4_SYSV_ABI sceHttpSetResponseHeaderMaxSize(int id, u64 headerSize);
|
||||
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize);
|
||||
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength);
|
||||
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
|
||||
int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable);
|
||||
//***********************************
|
||||
// Http Header setting functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode);
|
||||
int PS4_SYSV_ABI sceHttpRemoveRequestHeader(int id, const char* name);
|
||||
int PS4_SYSV_ABI sceHttpSetRequestContentLength(int id, u64 contentLength);
|
||||
//***********************************
|
||||
// Redirection setting functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpGetAutoRedirect(int id, int* isEnable);
|
||||
int PS4_SYSV_ABI sceHttpSetAutoRedirect(int id, int isEnable);
|
||||
//***********************************
|
||||
// Timeout settting functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpSetResolveRetry(int id, int retry);
|
||||
int PS4_SYSV_ABI sceHttpSetConnectTimeOut(int id, u32 usec);
|
||||
int PS4_SYSV_ABI sceHttpSetSendTimeOut(int id, u32 usec);
|
||||
int PS4_SYSV_ABI sceHttpSetRecvTimeOut(int id, u32 usec);
|
||||
//***********************************
|
||||
// Connection functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpDeleteConnection(int connId);
|
||||
//***********************************
|
||||
// Template functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpDeleteTemplate(int tmplId);
|
||||
//***********************************
|
||||
// Request functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpDeleteRequest(int reqId);
|
||||
int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const char* path,
|
||||
u64 contentLength);
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL2(int connId, const char* method, const char* url,
|
||||
u64 contentLength);
|
||||
//***********************************
|
||||
// Error Obtainment functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpGetLastErrno(int reqId, int* errNum);
|
||||
//***********************************
|
||||
// HTTP Header Parsing functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer,
|
||||
int32_t* httpMinorVer, int32_t* responseCode,
|
||||
const char** reasonPhrase, u64* phraseLen);
|
||||
int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr,
|
||||
const char** fieldValue, u64* valueLen);
|
||||
//***********************************
|
||||
// URI functions
|
||||
//***********************************
|
||||
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
|
||||
const OrbisHttpUriElement* srcElement, u32 option);
|
||||
int PS4_SYSV_ABI sceHttpUriEscape(char* out, u64* require, u64 prepare, const char* in);
|
||||
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
||||
u64 prepare, u32 option);
|
||||
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
||||
u64* require, u64 prepare);
|
||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize);
|
||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in);
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Http
|
||||
@@ -0,0 +1,417 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/network/http.h"
|
||||
#include "core/libraries/network/http2.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace Libraries::Http2 {
|
||||
|
||||
std::map<s32, s32> requests_to_connections{};
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2AbortRequest(s32 req_id) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpAbortRequest(req_id);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to get abort HTTP request, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2AddCookie() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2AddRequestHeader(s32 template_or_req_id, const char* name,
|
||||
const char* value, u32 mode) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpAddRequestHeader(template_or_req_id, name, value, mode);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to add HTTP response header, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2AuthCacheFlush() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CookieExport() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CookieFlush() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CookieImport() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CreateCookieBox() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CreateRequestWithURL(s32 tmpl_id, const char* method, const char* url,
|
||||
u64 content_length) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
// Http1 has a connection thing to go through first
|
||||
s32 conn_id = Libraries::Http::sceHttpCreateConnectionWithURL(tmpl_id, url, true);
|
||||
if (conn_id < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to create HTTP connection, error = {:#x}", conn_id);
|
||||
return conn_id;
|
||||
}
|
||||
s32 req_id =
|
||||
Libraries::Http::sceHttpCreateRequestWithURL2(conn_id, method, url, content_length);
|
||||
if (req_id < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to create HTTP request, error = {:#x}", req_id);
|
||||
return req_id;
|
||||
}
|
||||
requests_to_connections[req_id] = conn_id;
|
||||
return req_id;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2CreateTemplate(s32 ctx_id, const char* user_agent, s32 http_ver,
|
||||
s32 auto_proxy_conf) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 tmpl_id =
|
||||
Libraries::Http::sceHttpCreateTemplate(ctx_id, user_agent, http_ver, auto_proxy_conf);
|
||||
if (tmpl_id < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to create HTTP template, error = {:#x}", tmpl_id);
|
||||
}
|
||||
return tmpl_id;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2DeleteCookieBox() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2DeleteRequest(s32 req_id) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpDeleteRequest(req_id);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to delete HTTP request, error = {:#x}", result);
|
||||
return result;
|
||||
}
|
||||
s32 conn_id = requests_to_connections[req_id];
|
||||
result = Libraries::Http::sceHttpDeleteConnection(conn_id);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to delete HTTP connection, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2DeleteTemplate(s32 tmpl_id) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpDeleteTemplate(tmpl_id);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to delete HTTP template, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(s32 req_id, char** header, u64* header_size) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpGetAllResponseHeaders(req_id, header, header_size);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to get HTTP response headers, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetAuthEnabled() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetAutoRedirect() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetCookie() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetCookieBox() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetCookieStats() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetMemoryPoolStats() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetResponseContentLength() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2GetStatusCode(s32 req_id, s32* status_code) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpGetStatusCode(req_id, status_code);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to get HTTP status code, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests) {
|
||||
LOG_ERROR(Lib_Http2, "(DUMMY) called");
|
||||
s32 ctx_id = Libraries::Http::sceHttpInit(net_id, ssl_id, pool_size);
|
||||
if (ctx_id < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to init HTTP context, error = {:#x}", ctx_id);
|
||||
}
|
||||
return ctx_id;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2ReadData(s32 req_id, void* data, u64 size) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpReadData(req_id, data, size);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to read HTTP data, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2ReadDataAsync() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2RedirectCacheFlush() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2RemoveRequestHeader() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SendRequest(s32 req_id, const void* data, u64 size) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpSendRequest(req_id, data, size);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to send HTTP request, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SendRequestAsync() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetAuthEnabled() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetAuthInfoCallback() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetAutoRedirect() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetConnectionWaitTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetConnectTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieBox() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNum() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieMaxNumPerDomain() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieMaxSize() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieRecvCallback() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetCookieSendCallback() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetInflateGZIPEnabled() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetMinSslVersion() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetPreSendCallback(s32 template_id, OrbisHttp2PreSendCallback cb_func,
|
||||
void* user_arg) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetRecvTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetRedirectCallback() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetRequestContentLength(s32 req_id, u64 content_length) {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
s32 result = Libraries::Http::sceHttpSetRequestContentLength(req_id, content_length);
|
||||
if (result < 0) {
|
||||
LOG_ERROR(Lib_Http2, "Failed to set HTTP request content length, error = {:#x}", result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetResolveRetry() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetResolveTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetSendTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetSslCallback() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SetTimeOut() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SslDisableOption() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2SslEnableOption() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2Term() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2WaitAsync() {
|
||||
LOG_ERROR(Lib_Http2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("IZ-qjhRqvjk", "libSceHttp2", 1, "libSceHttp2", sceHttp2AbortRequest);
|
||||
LIB_FUNCTION("flPxnowtvWY", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddCookie);
|
||||
LIB_FUNCTION("nrPfOE8TQu0", "libSceHttp2", 1, "libSceHttp2", sceHttp2AddRequestHeader);
|
||||
LIB_FUNCTION("WeuDjj5m4YU", "libSceHttp2", 1, "libSceHttp2", sceHttp2AuthCacheFlush);
|
||||
LIB_FUNCTION("JlFGR4v50Kw", "libSceHttp2", 1, "libSceHttp2", sceHttp2CookieExport);
|
||||
LIB_FUNCTION("5VlQSzXW-SQ", "libSceHttp2", 1, "libSceHttp2", sceHttp2CookieFlush);
|
||||
LIB_FUNCTION("B5ibZI5UlzU", "libSceHttp2", 1, "libSceHttp2", sceHttp2CookieImport);
|
||||
LIB_FUNCTION("N4UfjvWJsMw", "libSceHttp2", 1, "libSceHttp2", sceHttp2CreateCookieBox);
|
||||
LIB_FUNCTION("mmyOCxQMVYQ", "libSceHttp2", 1, "libSceHttp2", sceHttp2CreateRequestWithURL);
|
||||
LIB_FUNCTION("+wCt7fCijgk", "libSceHttp2", 1, "libSceHttp2", sceHttp2CreateTemplate);
|
||||
LIB_FUNCTION("O9ync3F-JVI", "libSceHttp2", 1, "libSceHttp2", sceHttp2DeleteCookieBox);
|
||||
LIB_FUNCTION("c8D9qIjo8EY", "libSceHttp2", 1, "libSceHttp2", sceHttp2DeleteRequest);
|
||||
LIB_FUNCTION("pDom5-078DA", "libSceHttp2", 1, "libSceHttp2", sceHttp2DeleteTemplate);
|
||||
LIB_FUNCTION("-rdXUi2XW90", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetAllResponseHeaders);
|
||||
LIB_FUNCTION("m-OL13q8AI8", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetAuthEnabled);
|
||||
LIB_FUNCTION("od5QCZhZSfw", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetAutoRedirect);
|
||||
LIB_FUNCTION("GQFGj0rYX+A", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetCookie);
|
||||
LIB_FUNCTION("IX23slKvtQI", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetCookieBox);
|
||||
LIB_FUNCTION("eij7UzkUqK8", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetCookieStats);
|
||||
LIB_FUNCTION("otUQuZa-mv0", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetMemoryPoolStats);
|
||||
LIB_FUNCTION("o0DBQpFE13o", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetResponseContentLength);
|
||||
LIB_FUNCTION("9XYJwCf3lEA", "libSceHttp2", 1, "libSceHttp2", sceHttp2GetStatusCode);
|
||||
LIB_FUNCTION("3JCe3lCbQ8A", "libSceHttp2", 1, "libSceHttp2", sceHttp2Init);
|
||||
LIB_FUNCTION("QygCNNmbGss", "libSceHttp2", 1, "libSceHttp2", sceHttp2ReadData);
|
||||
LIB_FUNCTION("bGN-6zbo7ms", "libSceHttp2", 1, "libSceHttp2", sceHttp2ReadDataAsync);
|
||||
LIB_FUNCTION("klwUy2Wg+q8", "libSceHttp2", 1, "libSceHttp2", sceHttp2RedirectCacheFlush);
|
||||
LIB_FUNCTION("jHdP0CS4ZlA", "libSceHttp2", 1, "libSceHttp2", sceHttp2RemoveRequestHeader);
|
||||
LIB_FUNCTION("rbqZig38AT8", "libSceHttp2", 1, "libSceHttp2", sceHttp2SendRequest);
|
||||
LIB_FUNCTION("A+NVAFu4eCg", "libSceHttp2", 1, "libSceHttp2", sceHttp2SendRequestAsync);
|
||||
LIB_FUNCTION("jjFahkBPCYs", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetAuthEnabled);
|
||||
LIB_FUNCTION("Wwj6HbB2mOo", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetAuthInfoCallback);
|
||||
LIB_FUNCTION("b9AvoIaOuHI", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetAutoRedirect);
|
||||
LIB_FUNCTION("n8hMLe31OPA", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetConnectionWaitTimeOut);
|
||||
LIB_FUNCTION("-HIO4VT87v8", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetConnectTimeOut);
|
||||
LIB_FUNCTION("jrVHsKCXA0g", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieBox);
|
||||
LIB_FUNCTION("mPKVhQqh2Es", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieMaxNum);
|
||||
LIB_FUNCTION("o7+WXe4WadE", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieMaxNumPerDomain);
|
||||
LIB_FUNCTION("6a0N6GPD7RM", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieMaxSize);
|
||||
LIB_FUNCTION("zdtXKn9X7no", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieRecvCallback);
|
||||
LIB_FUNCTION("McYmUpQ3-DY", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetCookieSendCallback);
|
||||
LIB_FUNCTION("uRosf8GQbHQ", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetInflateGZIPEnabled);
|
||||
LIB_FUNCTION("09tk+kIA1Ns", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetMinSslVersion);
|
||||
LIB_FUNCTION("UL4Fviw+IAM", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetPreSendCallback);
|
||||
LIB_FUNCTION("izvHhqgDt44", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetRecvTimeOut);
|
||||
LIB_FUNCTION("BJgi0CH7al4", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetRedirectCallback);
|
||||
LIB_FUNCTION("FSAFOzi0FpM", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetRequestContentLength);
|
||||
LIB_FUNCTION("Gcjh+CisAZM", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetResolveRetry);
|
||||
LIB_FUNCTION("ACjtE27aErY", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetResolveTimeOut);
|
||||
LIB_FUNCTION("XPtW45xiLHk", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetSendTimeOut);
|
||||
LIB_FUNCTION("YrWX+DhPHQY", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetSslCallback);
|
||||
LIB_FUNCTION("VYMxTcBqSE0", "libSceHttp2", 1, "libSceHttp2", sceHttp2SetTimeOut);
|
||||
LIB_FUNCTION("B37SruheQ5Y", "libSceHttp2", 1, "libSceHttp2", sceHttp2SslDisableOption);
|
||||
LIB_FUNCTION("EWcwMpbr5F8", "libSceHttp2", 1, "libSceHttp2", sceHttp2SslEnableOption);
|
||||
LIB_FUNCTION("YiBUtz-pGkc", "libSceHttp2", 1, "libSceHttp2", sceHttp2Term);
|
||||
LIB_FUNCTION("MOp-AUhdfi8", "libSceHttp2", 1, "libSceHttp2", sceHttp2WaitAsync);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Http2
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/ssl2.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Http2 {
|
||||
|
||||
enum OrbisHttp2HttpVersion : u32 {
|
||||
ORBIS_HTTP2_VERSION_1_0 = 1,
|
||||
ORBIS_HTTP2_VERSION_1_1 = 2,
|
||||
ORBIS_HTTP2_VERSION_2_0 = 3,
|
||||
};
|
||||
|
||||
// TODO: Figure out the whole struct. What's here is based on libSceNpWebApi2 use.
|
||||
struct OrbisHttp2PreSendCallbackData {
|
||||
void* unk;
|
||||
void* cert;
|
||||
};
|
||||
using OrbisHttp2PreSendCallback = PS4_SYSV_ABI s32 (*)(s32 request_id, s32 ssl_id,
|
||||
OrbisHttp2PreSendCallbackData* data,
|
||||
void* user_arg);
|
||||
|
||||
s32 PS4_SYSV_ABI sceHttp2AbortRequest(s32 req_id);
|
||||
s32 PS4_SYSV_ABI sceHttp2AddRequestHeader(s32 template_or_req_id, const char* name,
|
||||
const char* value, u32 mode);
|
||||
s32 PS4_SYSV_ABI sceHttp2CreateRequestWithURL(s32 tmpl_id, const char* method, const char* url,
|
||||
u64 content_length);
|
||||
s32 PS4_SYSV_ABI sceHttp2CreateTemplate(s32 ctx_id, const char* user_agent, s32 http_ver,
|
||||
s32 auto_proxy_conf);
|
||||
s32 PS4_SYSV_ABI sceHttp2DeleteRequest(s32 req_id);
|
||||
s32 PS4_SYSV_ABI sceHttp2DeleteTemplate(s32 tmpl_id);
|
||||
s32 PS4_SYSV_ABI sceHttp2GetAllResponseHeaders(s32 req_id, char** header, u64* header_size);
|
||||
s32 PS4_SYSV_ABI sceHttp2GetStatusCode(s32 request_id, s32* status_code);
|
||||
s32 PS4_SYSV_ABI sceHttp2Init(s32 net_id, s32 ssl_id, u64 pool_size, s32 max_requests);
|
||||
s32 PS4_SYSV_ABI sceHttp2ReadData(s32 req_id, void* data, u64 size);
|
||||
s32 PS4_SYSV_ABI sceHttp2SendRequest(s32 req_id, const void* data, u64 size);
|
||||
s32 PS4_SYSV_ABI sceHttp2SetPreSendCallback(s32 template_id, OrbisHttp2PreSendCallback cb_func,
|
||||
void* user_arg);
|
||||
s32 PS4_SYSV_ABI sceHttp2SetRequestContentLength(s32 req_id, u64 content_length);
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Http2
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
constexpr int ORBIS_HTTP_ERROR_BEFORE_INIT = 0x80431001;
|
||||
constexpr int ORBIS_HTTP_ERROR_ALREADY_INITED = 0x80431020;
|
||||
constexpr int ORBIS_HTTP_ERROR_BUSY = 0x80431021;
|
||||
constexpr int ORBIS_HTTP_ERROR_OUT_OF_MEMORY = 0x80431022;
|
||||
constexpr int ORBIS_HTTP_ERROR_NOT_FOUND = 0x80431025;
|
||||
constexpr int ORBIS_HTTP_ERROR_INVALID_VERSION = 0x8043106a;
|
||||
constexpr int ORBIS_HTTP_ERROR_INVALID_ID = 0x80431100;
|
||||
constexpr int ORBIS_HTTP_ERROR_OUT_OF_SIZE = 0x80431104;
|
||||
constexpr int ORBIS_HTTP_ERROR_INVALID_VALUE = 0x804311fe;
|
||||
|
||||
constexpr int ORBIS_HTTP_ERROR_INVALID_URL = 0x80433060;
|
||||
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_SCHEME = 0x80431061;
|
||||
constexpr int ORBIS_HTTP_ERROR_NETWORK = 0x80431063;
|
||||
constexpr int ORBIS_HTTP_ERROR_BAD_RESPONSE = 0x80431064;
|
||||
constexpr int ORBIS_HTTP_ERROR_BEFORE_SEND = 0x80431065;
|
||||
constexpr int ORBIS_HTTP_ERROR_AFTER_SEND = 0x80431066;
|
||||
constexpr int ORBIS_HTTP_ERROR_TIMEOUT = 0x80431068;
|
||||
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_AUTH_TYPE = 0x80431069;
|
||||
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_METHOD = 0x8043106b;
|
||||
constexpr int ORBIS_HTTP_ERROR_READ_BY_HEAD_METHOD = 0x8043106f;
|
||||
constexpr int ORBIS_HTTP_ERROR_NOT_IN_COM = 0x80431070;
|
||||
constexpr int ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH = 0x80431071;
|
||||
constexpr int ORBIS_HTTP_ERROR_CHUNK_ENC = 0x80431072;
|
||||
constexpr int ORBIS_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER = 0x80431073;
|
||||
constexpr int ORBIS_HTTP_ERROR_SSL = 0x80431075;
|
||||
constexpr int ORBIS_HTTP_ERROR_INSUFFICIENT_STACKSIZE = 0x80431076;
|
||||
constexpr int ORBIS_HTTP_ERROR_ABORTED = 0x80431080;
|
||||
constexpr int ORBIS_HTTP_ERROR_UNKNOWN = 0x80431081;
|
||||
constexpr int ORBIS_HTTP_ERROR_EAGAIN = 0x80431082;
|
||||
constexpr int ORBIS_HTTP_ERROR_PROXY = 0x80431084;
|
||||
constexpr int ORBIS_HTTP_ERROR_BROKEN = 0x80431085;
|
||||
|
||||
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_NOT_FOUND = 0x80432025;
|
||||
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE = 0x80432060;
|
||||
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE = 0x804321fe;
|
||||
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_EPACKET = 0x80436001;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENODNS = 0x80436002;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ETIMEDOUT = 0x80436003;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOSUPPORT = 0x80436004;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_EFORMAT = 0x80436005;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERFAILURE = 0x80436006;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOHOST = 0x80436007;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x80436008;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERREFUSED = 0x80436009;
|
||||
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENORECORD = 0x8043600a;
|
||||
|
||||
constexpr int ORBIS_HTTPS_ERROR_CERT = 0x80435060;
|
||||
constexpr int ORBIS_HTTPS_ERROR_HANDSHAKE = 0x80435061;
|
||||
constexpr int ORBIS_HTTPS_ERROR_IO = 0x80435062;
|
||||
constexpr int ORBIS_HTTPS_ERROR_INTERNAL = 0x80435063;
|
||||
constexpr int ORBIS_HTTPS_ERROR_PROXY = 0x80435064;
|
||||
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_INTERNAL = 0x01;
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_INVALID_CERT = 0x02;
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_CN_CHECK = 0x04;
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_AFTER_CHECK = 0x08;
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK = 0x10;
|
||||
constexpr int ORBIS_HTTPS_ERROR_SSL_UNKNOWN_CA = 0x20;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,495 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "netctl.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
// Define our own htonll and ntohll because its not available in some systems/platforms
|
||||
#ifndef HTONLL
|
||||
#define HTONLL(x) (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))
|
||||
#endif
|
||||
#ifndef NTOHLL
|
||||
#define NTOHLL(x) (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))
|
||||
#endif
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
static int ConvertFamilies(int family);
|
||||
|
||||
enum OrbisNetFamily : u32 {
|
||||
ORBIS_NET_AF_UNIX = 1,
|
||||
ORBIS_NET_AF_INET = 2,
|
||||
ORBIS_NET_AF_INET6 = 28,
|
||||
};
|
||||
enum OrbisNetSocketType : u32 {
|
||||
ORBIS_NET_SOCK_STREAM = 1,
|
||||
ORBIS_NET_SOCK_DGRAM = 2,
|
||||
ORBIS_NET_SOCK_RAW = 3,
|
||||
ORBIS_NET_SOCK_DGRAM_P2P = 6,
|
||||
ORBIS_NET_SOCK_STREAM_P2P = 10
|
||||
};
|
||||
|
||||
enum OrbisNetProtocol : u32 {
|
||||
ORBIS_NET_IPPROTO_IP = 0,
|
||||
ORBIS_NET_IPPROTO_ICMP = 1,
|
||||
ORBIS_NET_IPPROTO_IGMP = 2,
|
||||
ORBIS_NET_IPPROTO_TCP = 6,
|
||||
ORBIS_NET_IPPROTO_UDP = 17,
|
||||
ORBIS_NET_IPPROTO_IPV6 = 41,
|
||||
ORBIS_NET_SOL_SOCKET = 0xFFFF
|
||||
};
|
||||
|
||||
constexpr std::string_view NameOf(OrbisNetProtocol p) {
|
||||
switch (p) {
|
||||
case ORBIS_NET_IPPROTO_IP:
|
||||
return "ORBIS_NET_IPPROTO_IP";
|
||||
case ORBIS_NET_IPPROTO_ICMP:
|
||||
return "ORBIS_NET_IPPROTO_ICMP";
|
||||
case ORBIS_NET_IPPROTO_IGMP:
|
||||
return "ORBIS_NET_IPPROTO_IGMP";
|
||||
case ORBIS_NET_IPPROTO_TCP:
|
||||
return "ORBIS_NET_IPPROTO_TCP";
|
||||
case ORBIS_NET_IPPROTO_UDP:
|
||||
return "ORBIS_NET_IPPROTO_UDP";
|
||||
case ORBIS_NET_IPPROTO_IPV6:
|
||||
return "ORBIS_NET_IPPROTO_IPV6";
|
||||
case ORBIS_NET_SOL_SOCKET:
|
||||
return "ORBIS_NET_SOL_SOCKET";
|
||||
default:
|
||||
UNREACHABLE_MSG("{}", (u32)p);
|
||||
}
|
||||
}
|
||||
|
||||
enum OrbisNetSocketIpOption : u32 {
|
||||
ORBIS_NET_IP_HDRINCL = 2,
|
||||
ORBIS_NET_IP_TOS = 3,
|
||||
ORBIS_NET_IP_TTL = 4,
|
||||
ORBIS_NET_IP_MULTICAST_IF = 9,
|
||||
ORBIS_NET_IP_MULTICAST_TTL = 10,
|
||||
ORBIS_NET_IP_MULTICAST_LOOP = 11,
|
||||
ORBIS_NET_IP_ADD_MEMBERSHIP = 12,
|
||||
ORBIS_NET_IP_DROP_MEMBERSHIP = 13,
|
||||
ORBIS_NET_IP_TTLCHK = 23,
|
||||
ORBIS_NET_IP_MAXTTL = 24,
|
||||
};
|
||||
|
||||
enum OrbisNetSocketTcpOption : u32 {
|
||||
ORBIS_NET_TCP_NODELAY = 1,
|
||||
ORBIS_NET_TCP_MAXSEG = 2,
|
||||
ORBIS_NET_TCP_MSS_TO_ADVERTISE = 3,
|
||||
};
|
||||
|
||||
enum OrbisNetSocketSoOption : u32 {
|
||||
ORBIS_NET_SO_REUSEADDR = 0x00000004,
|
||||
ORBIS_NET_SO_KEEPALIVE = 0x00000008,
|
||||
ORBIS_NET_SO_BROADCAST = 0x00000020,
|
||||
ORBIS_NET_SO_LINGER = 0x00000080,
|
||||
ORBIS_NET_SO_REUSEPORT = 0x00000200,
|
||||
ORBIS_NET_SO_ONESBCAST = 0x00010000,
|
||||
ORBIS_NET_SO_USECRYPTO = 0x00020000,
|
||||
ORBIS_NET_SO_USESIGNATURE = 0x00040000,
|
||||
ORBIS_NET_SO_SNDBUF = 0x1001,
|
||||
ORBIS_NET_SO_RCVBUF = 0x1002,
|
||||
ORBIS_NET_SO_ERROR = 0x1007,
|
||||
ORBIS_NET_SO_TYPE = 0x1008,
|
||||
ORBIS_NET_SO_SNDTIMEO = 0x1105,
|
||||
ORBIS_NET_SO_RCVTIMEO = 0x1106,
|
||||
ORBIS_NET_SO_ERROR_EX = 0x1107,
|
||||
ORBIS_NET_SO_ACCEPTTIMEO = 0x1108,
|
||||
ORBIS_NET_SO_CONNECTTIMEO = 0x1109,
|
||||
ORBIS_NET_SO_NBIO = 0x1200,
|
||||
ORBIS_NET_SO_POLICY = 0x1201,
|
||||
ORBIS_NET_SO_NAME = 0x1202,
|
||||
ORBIS_NET_SO_PRIORITY = 0x1203
|
||||
};
|
||||
|
||||
enum OrbisNetFlags : u32 {
|
||||
ORBIS_NET_MSG_PEEK = 0x00000002,
|
||||
ORBIS_NET_MSG_WAITALL = 0x00000040,
|
||||
ORBIS_NET_MSG_DONTWAIT = 0x00000080,
|
||||
ORBIS_NET_MSG_USECRYPTO = 0x00100000,
|
||||
ORBIS_NET_MSG_USESIGNATURE = 0x00200000,
|
||||
ORBIS_NET_MSG_PEEKLEN = (0x00400000 | ORBIS_NET_MSG_PEEK)
|
||||
};
|
||||
|
||||
constexpr std::string_view NameOf(OrbisNetSocketSoOption o) {
|
||||
switch (o) {
|
||||
case ORBIS_NET_SO_REUSEADDR:
|
||||
return "ORBIS_NET_SO_REUSEADDR";
|
||||
case ORBIS_NET_SO_KEEPALIVE:
|
||||
return "ORBIS_NET_SO_KEEPALIVE";
|
||||
case ORBIS_NET_SO_BROADCAST:
|
||||
return "ORBIS_NET_SO_BROADCAST";
|
||||
case ORBIS_NET_SO_LINGER:
|
||||
return "ORBIS_NET_SO_LINGER";
|
||||
case ORBIS_NET_SO_REUSEPORT:
|
||||
return "ORBIS_NET_SO_REUSEPORT";
|
||||
case ORBIS_NET_SO_ONESBCAST:
|
||||
return "ORBIS_NET_SO_ONESBCAST";
|
||||
case ORBIS_NET_SO_USECRYPTO:
|
||||
return "ORBIS_NET_SO_USECRYPTO";
|
||||
case ORBIS_NET_SO_USESIGNATURE:
|
||||
return "ORBIS_NET_SO_USESIGNATURE";
|
||||
case ORBIS_NET_SO_SNDBUF:
|
||||
return "ORBIS_NET_SO_SNDBUF";
|
||||
case ORBIS_NET_SO_RCVBUF:
|
||||
return "ORBIS_NET_SO_RCVBUF";
|
||||
case ORBIS_NET_SO_ERROR:
|
||||
return "ORBIS_NET_SO_ERROR";
|
||||
case ORBIS_NET_SO_TYPE:
|
||||
return "ORBIS_NET_SO_TYPE";
|
||||
case ORBIS_NET_SO_SNDTIMEO:
|
||||
return "ORBIS_NET_SO_SNDTIMEO";
|
||||
case ORBIS_NET_SO_RCVTIMEO:
|
||||
return "ORBIS_NET_SO_RCVTIMEO";
|
||||
case ORBIS_NET_SO_ERROR_EX:
|
||||
return "ORBIS_NET_SO_ERROR_EX";
|
||||
case ORBIS_NET_SO_ACCEPTTIMEO:
|
||||
return "ORBIS_NET_SO_ACCEPTTIMEO";
|
||||
case ORBIS_NET_SO_CONNECTTIMEO:
|
||||
return "ORBIS_NET_SO_CONNECTTIMEO";
|
||||
case ORBIS_NET_SO_NBIO:
|
||||
return "ORBIS_NET_SO_NBIO";
|
||||
case ORBIS_NET_SO_POLICY:
|
||||
return "ORBIS_NET_SO_POLICY";
|
||||
case ORBIS_NET_SO_NAME:
|
||||
return "ORBIS_NET_SO_NAME";
|
||||
case ORBIS_NET_SO_PRIORITY:
|
||||
return "ORBIS_NET_SO_PRIORITY";
|
||||
default:
|
||||
UNREACHABLE_MSG("{}", (u32)o);
|
||||
}
|
||||
}
|
||||
|
||||
enum OrbisNetEpollFlag : u32 {
|
||||
ORBIS_NET_EPOLL_CTL_ADD = 1,
|
||||
ORBIS_NET_EPOLL_CTL_MOD = 2,
|
||||
ORBIS_NET_EPOLL_CTL_DEL = 3,
|
||||
};
|
||||
|
||||
enum OrbisNetEpollEvents : u32 {
|
||||
ORBIS_NET_EPOLLIN = 0x1,
|
||||
ORBIS_NET_EPOLLOUT = 0x2,
|
||||
ORBIS_NET_EPOLLERR = 0x8,
|
||||
ORBIS_NET_EPOLLHUP = 0x10,
|
||||
ORBIS_NET_EPOLLDESCID = 0x10000,
|
||||
};
|
||||
|
||||
enum OrbisNetResolverFlag : u32 {
|
||||
ORBIS_NET_RESOLVER_ASYNC = 0x1,
|
||||
ORBIS_NET_RESOLVER_START_NTOA_DISABLE_IPADDRESS = 0x10000,
|
||||
};
|
||||
|
||||
using OrbisNetId = s32;
|
||||
|
||||
struct OrbisNetSockaddr {
|
||||
u8 sa_len;
|
||||
u8 sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
struct OrbisNetSockaddrIn {
|
||||
u8 sin_len;
|
||||
u8 sin_family;
|
||||
u16 sin_port;
|
||||
u32 sin_addr;
|
||||
u16 sin_vport;
|
||||
char sin_zero[6];
|
||||
};
|
||||
|
||||
using OrbisNetInAddr_t = u32;
|
||||
|
||||
struct OrbisNetInAddr {
|
||||
OrbisNetInAddr_t inaddr_addr;
|
||||
};
|
||||
|
||||
struct OrbisNetSockaddrUn {
|
||||
u8 sun_len;
|
||||
u8 sun_family; // AF_UNIX
|
||||
char sun_path[104];
|
||||
};
|
||||
|
||||
struct OrbisNetIovec {
|
||||
void* iov_base;
|
||||
u64 iov_len;
|
||||
};
|
||||
|
||||
struct OrbisNetMsghdr {
|
||||
void* msg_name;
|
||||
u32 msg_namelen;
|
||||
OrbisNetIovec* msg_iov;
|
||||
int msg_iovlen;
|
||||
void* msg_control;
|
||||
u32 msg_controllen;
|
||||
int msg_flags;
|
||||
};
|
||||
|
||||
union OrbisNetEpollData {
|
||||
void* ptr;
|
||||
u32 data_u32;
|
||||
int fd;
|
||||
u64 data_u64;
|
||||
};
|
||||
|
||||
struct OrbisNetEpollEvent {
|
||||
u32 events;
|
||||
u32 pad;
|
||||
u64 ident;
|
||||
OrbisNetEpollData data;
|
||||
};
|
||||
|
||||
union OrbisNetAddrUnion {
|
||||
OrbisNetInAddr addr;
|
||||
u8 addr6[16];
|
||||
};
|
||||
|
||||
struct OrbisNetResolverAddr {
|
||||
OrbisNetAddrUnion u;
|
||||
u32 af;
|
||||
u32 pad[3];
|
||||
};
|
||||
|
||||
struct OrbisNetResolverInfo {
|
||||
OrbisNetResolverAddr addrs[10];
|
||||
u32 records;
|
||||
u32 recordsv4;
|
||||
u32 pad[14];
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI in6addr_any();
|
||||
int PS4_SYSV_ABI in6addr_loopback();
|
||||
int PS4_SYSV_ABI sce_net_dummy();
|
||||
int PS4_SYSV_ABI sce_net_in6addr_any();
|
||||
int PS4_SYSV_ABI sce_net_in6addr_linklocal_allnodes();
|
||||
int PS4_SYSV_ABI sce_net_in6addr_linklocal_allrouters();
|
||||
int PS4_SYSV_ABI sce_net_in6addr_loopback();
|
||||
int PS4_SYSV_ABI sce_net_in6addr_nodelocal_allnodes();
|
||||
OrbisNetId PS4_SYSV_ABI sceNetAccept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sceNetAddrConfig6GetInfo();
|
||||
int PS4_SYSV_ABI sceNetAddrConfig6Start();
|
||||
int PS4_SYSV_ABI sceNetAddrConfig6Stop();
|
||||
int PS4_SYSV_ABI sceNetAllocateAllRouteInfo();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlGetDataTraffic();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlGetDefaultParam();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlGetIfParam();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlGetPolicy();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlSetDefaultParam();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlSetIfParam();
|
||||
int PS4_SYSV_ABI sceNetBandwidthControlSetPolicy();
|
||||
int PS4_SYSV_ABI sceNetBind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sceNetClearDnsCache();
|
||||
int PS4_SYSV_ABI sceNetConfigAddArp();
|
||||
int PS4_SYSV_ABI sceNetConfigAddArpWithInterface();
|
||||
int PS4_SYSV_ABI sceNetConfigAddIfaddr();
|
||||
int PS4_SYSV_ABI sceNetConfigAddMRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigAddRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigAddRoute6();
|
||||
int PS4_SYSV_ABI sceNetConfigAddRouteWithInterface();
|
||||
int PS4_SYSV_ABI sceNetConfigCleanUpAllInterfaces();
|
||||
int PS4_SYSV_ABI sceNetConfigDelArp();
|
||||
int PS4_SYSV_ABI sceNetConfigDelArpWithInterface();
|
||||
int PS4_SYSV_ABI sceNetConfigDelDefaultRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigDelDefaultRoute6();
|
||||
int PS4_SYSV_ABI sceNetConfigDelIfaddr();
|
||||
int PS4_SYSV_ABI sceNetConfigDelIfaddr6();
|
||||
int PS4_SYSV_ABI sceNetConfigDelMRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigDelRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigDelRoute6();
|
||||
int PS4_SYSV_ABI sceNetConfigDownInterface();
|
||||
int PS4_SYSV_ABI sceNetConfigEtherGetLinkMode();
|
||||
int PS4_SYSV_ABI sceNetConfigEtherPostPlugInOutEvent();
|
||||
int PS4_SYSV_ABI sceNetConfigEtherSetLinkMode();
|
||||
int PS4_SYSV_ABI sceNetConfigFlushRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigGetDefaultRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigGetDefaultRoute6();
|
||||
int PS4_SYSV_ABI sceNetConfigGetIfaddr();
|
||||
int PS4_SYSV_ABI sceNetConfigGetIfaddr6();
|
||||
int PS4_SYSV_ABI sceNetConfigRoutingShowRoutingConfig();
|
||||
int PS4_SYSV_ABI sceNetConfigRoutingShowtCtlVar();
|
||||
int PS4_SYSV_ABI sceNetConfigRoutingStart();
|
||||
int PS4_SYSV_ABI sceNetConfigRoutingStop();
|
||||
int PS4_SYSV_ABI sceNetConfigSetDefaultRoute();
|
||||
int PS4_SYSV_ABI sceNetConfigSetDefaultRoute6();
|
||||
int PS4_SYSV_ABI sceNetConfigSetDefaultScope();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfaddr();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfaddr6();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfaddr6WithFlags();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfFlags();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfLinkLocalAddr6();
|
||||
int PS4_SYSV_ABI sceNetConfigSetIfmtu();
|
||||
int PS4_SYSV_ABI sceNetConfigUnsetIfFlags();
|
||||
int PS4_SYSV_ABI sceNetConfigUpInterface();
|
||||
int PS4_SYSV_ABI sceNetConfigUpInterfaceWithFlags();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocClearWakeOnWlan();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocCreate();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocGetWakeOnWlanInfo();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocJoin();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocLeave();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocPspEmuClearWakeOnWlan();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocPspEmuSetWakeOnWlan();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocScanJoin();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocSetExtInfoElement();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanAdhocSetWakeOnWlan();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanApStart();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanApStop();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanBackgroundScanQuery();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanBackgroundScanStart();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanBackgroundScanStop();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanDiagGetDeviceInfo();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanDiagSetAntenna();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanDiagSetTxFixedRate();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanGetDeviceConfig();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanInfraGetRssiInfo();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanInfraLeave();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanInfraScanJoin();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanScan();
|
||||
int PS4_SYSV_ABI sceNetConfigWlanSetDeviceConfig();
|
||||
int PS4_SYSV_ABI sceNetConnect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sceNetControl();
|
||||
int PS4_SYSV_ABI sceNetDhcpdStart();
|
||||
int PS4_SYSV_ABI sceNetDhcpdStop();
|
||||
int PS4_SYSV_ABI sceNetDhcpGetAutoipInfo();
|
||||
int PS4_SYSV_ABI sceNetDhcpGetInfo();
|
||||
int PS4_SYSV_ABI sceNetDhcpGetInfoEx();
|
||||
int PS4_SYSV_ABI sceNetDhcpStart();
|
||||
int PS4_SYSV_ABI sceNetDhcpStop();
|
||||
int PS4_SYSV_ABI sceNetDumpAbort();
|
||||
int PS4_SYSV_ABI sceNetDumpCreate();
|
||||
int PS4_SYSV_ABI sceNetDumpDestroy();
|
||||
int PS4_SYSV_ABI sceNetDumpRead();
|
||||
int PS4_SYSV_ABI sceNetDuplicateIpStart();
|
||||
int PS4_SYSV_ABI sceNetDuplicateIpStop();
|
||||
int PS4_SYSV_ABI sceNetEpollAbort();
|
||||
int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, OrbisNetId id,
|
||||
OrbisNetEpollEvent* event);
|
||||
int PS4_SYSV_ABI sceNetEpollCreate(const char* name, int flags);
|
||||
int PS4_SYSV_ABI sceNetEpollDestroy(OrbisNetId epollid);
|
||||
int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, int maxevents,
|
||||
int timeout);
|
||||
int* PS4_SYSV_ABI sceNetErrnoLoc();
|
||||
int PS4_SYSV_ABI sceNetEtherNtostr();
|
||||
int PS4_SYSV_ABI sceNetEtherStrton();
|
||||
int PS4_SYSV_ABI sceNetEventCallbackCreate();
|
||||
int PS4_SYSV_ABI sceNetEventCallbackDestroy();
|
||||
int PS4_SYSV_ABI sceNetEventCallbackGetError();
|
||||
int PS4_SYSV_ABI sceNetEventCallbackWaitCB();
|
||||
int PS4_SYSV_ABI sceNetFreeAllRouteInfo();
|
||||
int PS4_SYSV_ABI sceNetGetArpInfo();
|
||||
int PS4_SYSV_ABI sceNetGetDns6Info();
|
||||
int PS4_SYSV_ABI sceNetGetDnsInfo();
|
||||
int PS4_SYSV_ABI sceNetGetIfList();
|
||||
int PS4_SYSV_ABI sceNetGetIfListOnce();
|
||||
int PS4_SYSV_ABI sceNetGetIfName();
|
||||
int PS4_SYSV_ABI sceNetGetIfnameNumList();
|
||||
int PS4_SYSV_ABI sceNetGetMacAddress(Libraries::NetCtl::OrbisNetEtherAddr* addr, int flags);
|
||||
int PS4_SYSV_ABI sceNetGetMemoryPoolStats();
|
||||
int PS4_SYSV_ABI sceNetGetNameToIndex();
|
||||
int PS4_SYSV_ABI sceNetGetpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sceNetGetRandom();
|
||||
int PS4_SYSV_ABI sceNetGetRouteInfo();
|
||||
int PS4_SYSV_ABI sceNetGetSockInfo();
|
||||
int PS4_SYSV_ABI sceNetGetSockInfo6();
|
||||
int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen);
|
||||
int PS4_SYSV_ABI sceNetGetStatisticsInfo();
|
||||
int PS4_SYSV_ABI sceNetGetStatisticsInfoInternal();
|
||||
int PS4_SYSV_ABI sceNetGetSystemTime();
|
||||
u32 PS4_SYSV_ABI sceNetHtonl(u32 host32);
|
||||
u64 PS4_SYSV_ABI sceNetHtonll(u64 host64);
|
||||
u16 PS4_SYSV_ABI sceNetHtons(u16 host16);
|
||||
const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size);
|
||||
int PS4_SYSV_ABI sceNetInetNtopWithScopeId();
|
||||
int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst);
|
||||
int PS4_SYSV_ABI sceNetInetPtonEx(int af, const char* src, void* dst, int flags);
|
||||
int PS4_SYSV_ABI sceNetInetPtonWithScopeId();
|
||||
int PS4_SYSV_ABI sceNetInfoDumpStart();
|
||||
int PS4_SYSV_ABI sceNetInfoDumpStop();
|
||||
int PS4_SYSV_ABI sceNetInit();
|
||||
int PS4_SYSV_ABI sceNetInitParam();
|
||||
int PS4_SYSV_ABI sceNetIoctl();
|
||||
int PS4_SYSV_ABI sceNetListen(OrbisNetId s, int backlog);
|
||||
int PS4_SYSV_ABI sceNetMemoryAllocate();
|
||||
int PS4_SYSV_ABI sceNetMemoryFree();
|
||||
u32 PS4_SYSV_ABI sceNetNtohl(u32 net32);
|
||||
int PS4_SYSV_ABI sceNetNtohll();
|
||||
u16 PS4_SYSV_ABI sceNetNtohs(u16 net16);
|
||||
int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags);
|
||||
int PS4_SYSV_ABI sceNetPoolDestroy();
|
||||
int PS4_SYSV_ABI sceNetPppoeStart();
|
||||
int PS4_SYSV_ABI sceNetPppoeStop();
|
||||
int PS4_SYSV_ABI sceNetRecv(OrbisNetId s, void* buf, u64 len, int flags);
|
||||
int PS4_SYSV_ABI sceNetRecvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
|
||||
u32* paddrlen);
|
||||
int PS4_SYSV_ABI sceNetRecvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags);
|
||||
int PS4_SYSV_ABI sceNetResolverAbort();
|
||||
int PS4_SYSV_ABI sceNetResolverConnect();
|
||||
int PS4_SYSV_ABI sceNetResolverConnectAbort();
|
||||
int PS4_SYSV_ABI sceNetResolverConnectCreate();
|
||||
int PS4_SYSV_ABI sceNetResolverConnectDestroy();
|
||||
int PS4_SYSV_ABI sceNetResolverCreate(const char* name, int poolid, int flags);
|
||||
int PS4_SYSV_ABI sceNetResolverDestroy(OrbisNetId resolverid);
|
||||
int PS4_SYSV_ABI sceNetResolverGetError(OrbisNetId resolverid, s32* status);
|
||||
int PS4_SYSV_ABI sceNetResolverStartAton();
|
||||
int PS4_SYSV_ABI sceNetResolverStartAton6();
|
||||
int PS4_SYSV_ABI sceNetResolverStartNtoa(OrbisNetId resolverid, const char* hostname,
|
||||
OrbisNetInAddr* addr, int timeout, int retry, int flags);
|
||||
int PS4_SYSV_ABI sceNetResolverStartNtoa6();
|
||||
int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecords(OrbisNetId resolverid, const char* hostname,
|
||||
OrbisNetResolverInfo* info, int timeout,
|
||||
int retry, int flags);
|
||||
int PS4_SYSV_ABI sceNetResolverStartNtoaMultipleRecordsEx();
|
||||
int PS4_SYSV_ABI sceNetSend(OrbisNetId s, const void* buf, u64 len, int flags);
|
||||
int PS4_SYSV_ABI sceNetSendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags);
|
||||
int PS4_SYSV_ABI sceNetSendto(OrbisNetId s, const void* buf, u64 len, int flags,
|
||||
const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sceNetSetDns6Info();
|
||||
int PS4_SYSV_ABI sceNetSetDns6InfoToKernel();
|
||||
int PS4_SYSV_ABI sceNetSetDnsInfo();
|
||||
int PS4_SYSV_ABI sceNetSetDnsInfoToKernel();
|
||||
int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval,
|
||||
u32 optlen);
|
||||
int PS4_SYSV_ABI sceNetShowIfconfig();
|
||||
int PS4_SYSV_ABI sceNetShowIfconfigForBuffer();
|
||||
int PS4_SYSV_ABI sceNetShowIfconfigWithMemory();
|
||||
int PS4_SYSV_ABI sceNetShowNetstat();
|
||||
int PS4_SYSV_ABI sceNetShowNetstatEx();
|
||||
int PS4_SYSV_ABI sceNetShowNetstatExForBuffer();
|
||||
int PS4_SYSV_ABI sceNetShowNetstatForBuffer();
|
||||
int PS4_SYSV_ABI sceNetShowNetstatWithMemory();
|
||||
int PS4_SYSV_ABI sceNetShowPolicy();
|
||||
int PS4_SYSV_ABI sceNetShowPolicyWithMemory();
|
||||
int PS4_SYSV_ABI sceNetShowRoute();
|
||||
int PS4_SYSV_ABI sceNetShowRoute6();
|
||||
int PS4_SYSV_ABI sceNetShowRoute6ForBuffer();
|
||||
int PS4_SYSV_ABI sceNetShowRoute6WithMemory();
|
||||
int PS4_SYSV_ABI sceNetShowRouteForBuffer();
|
||||
int PS4_SYSV_ABI sceNetShowRouteWithMemory();
|
||||
int PS4_SYSV_ABI sceNetShutdown(OrbisNetId s, int how);
|
||||
OrbisNetId PS4_SYSV_ABI sceNetSocket(const char* name, int family, int type, int protocol);
|
||||
int PS4_SYSV_ABI sceNetSocketAbort(OrbisNetId s, int flags);
|
||||
int PS4_SYSV_ABI sceNetSocketClose(OrbisNetId s);
|
||||
int PS4_SYSV_ABI sceNetSyncCreate();
|
||||
int PS4_SYSV_ABI sceNetSyncDestroy();
|
||||
int PS4_SYSV_ABI sceNetSyncGet();
|
||||
int PS4_SYSV_ABI sceNetSyncSignal();
|
||||
int PS4_SYSV_ABI sceNetSyncWait();
|
||||
int PS4_SYSV_ABI sceNetSysctl();
|
||||
int PS4_SYSV_ABI sceNetTerm();
|
||||
int PS4_SYSV_ABI sceNetThreadCreate();
|
||||
int PS4_SYSV_ABI sceNetThreadExit();
|
||||
int PS4_SYSV_ABI sceNetThreadJoin();
|
||||
int PS4_SYSV_ABI sceNetUsleep();
|
||||
int PS4_SYSV_ABI Func_0E707A589F751C68();
|
||||
int PS4_SYSV_ABI sceNetEmulationGet();
|
||||
int PS4_SYSV_ABI sceNetEmulationSet();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// error codes
|
||||
constexpr int ORBIS_NET_CTL_ERROR_CALLBACK_MAX = 0x80412103;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_ID_NOT_FOUND = 0x80412104;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_INVALID_ID = 0x80412105;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_INVALID_ADDR = 0x80412107;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_INVALID_SIZE = 0x80412111;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_NOT_CONNECTED = 0x80412108;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_NOT_AVAIL = 0x80412109;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_NETWORK_DISABLED = 0x8041210D;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_DISCONNECT_REQ = 0x8041210E;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_ETHERNET_PLUGOUT = 0x80412115;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_WIFI_DEAUTHED = 0x80412116;
|
||||
constexpr int ORBIS_NET_CTL_ERROR_WIFI_BEACON_LOST = 0x80412117;
|
||||
|
||||
// state codes
|
||||
constexpr int ORBIS_NET_CTL_STATE_DISCONNECTED = 0;
|
||||
constexpr int ORBIS_NET_CTL_STATE_CONNECTING = 1;
|
||||
constexpr int ORBIS_NET_CTL_STATE_IPOBTAINING = 2;
|
||||
constexpr int ORBIS_NET_CTL_STATE_IPOBTAINED = 3;
|
||||
|
||||
// event type
|
||||
constexpr int ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED = 1;
|
||||
constexpr int ORBIS_SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED = 2;
|
||||
constexpr int ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED = 3;
|
||||
|
||||
// get info codes
|
||||
// device
|
||||
constexpr int ORBIS_NET_CTL_DEVICE_WIRED = 0;
|
||||
constexpr int ORBIS_NET_CTL_DEVICE_WIRELESS = 1;
|
||||
// link
|
||||
constexpr int ORBIS_NET_CTL_LINK_DISCONNECTED = 0;
|
||||
constexpr int ORBIS_NET_CTL_LINK_CONNECTED = 1;
|
||||
@@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/network/net_ctl_codes.h"
|
||||
#include "core/libraries/network/net_ctl_obj.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
namespace Libraries::NetCtl {
|
||||
|
||||
NetCtlInternal::NetCtlInternal() = default;
|
||||
|
||||
NetCtlInternal::~NetCtlInternal() = default;
|
||||
|
||||
s32 NetCtlInternal::RegisterCallback(OrbisNetCtlCallback func, void* arg) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
// Find the next available slot
|
||||
const auto it = std::ranges::find(callbacks, nullptr, &NetCtlCallback::func);
|
||||
if (it == callbacks.end()) {
|
||||
return ORBIS_NET_CTL_ERROR_CALLBACK_MAX;
|
||||
}
|
||||
|
||||
const int next_id = std::distance(callbacks.begin(), it);
|
||||
callbacks[next_id].func = func;
|
||||
callbacks[next_id].arg = arg;
|
||||
return next_id;
|
||||
}
|
||||
|
||||
s32 NetCtlInternal::RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
// Find the next available slot
|
||||
const auto it = std::ranges::find(nptool_callbacks, nullptr, &NetCtlCallbackForNpToolkit::func);
|
||||
if (it == nptool_callbacks.end()) {
|
||||
return ORBIS_NET_CTL_ERROR_CALLBACK_MAX;
|
||||
}
|
||||
|
||||
const int next_id = std::distance(nptool_callbacks.begin(), it);
|
||||
nptool_callbacks[next_id].func = func;
|
||||
nptool_callbacks[next_id].arg = arg;
|
||||
return next_id;
|
||||
}
|
||||
|
||||
void NetCtlInternal::CheckCallback() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto event = EmulatorSettings.IsConnectedToNetwork()
|
||||
? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : callbacks) {
|
||||
if (func != nullptr) {
|
||||
func(event, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetCtlInternal::CheckNpToolkitCallback() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto event = EmulatorSettings.IsConnectedToNetwork()
|
||||
? ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED
|
||||
: ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED;
|
||||
for (const auto [func, arg] : nptool_callbacks) {
|
||||
if (func != nullptr) {
|
||||
func(event, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libraries::NetCtl
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::NetCtl {
|
||||
|
||||
using OrbisNetCtlCallback = PS4_SYSV_ABI void (*)(int eventType, void* arg);
|
||||
using OrbisNetCtlCallbackForNpToolkit = PS4_SYSV_ABI void (*)(int eventType, void* arg);
|
||||
|
||||
struct NetCtlCallback {
|
||||
OrbisNetCtlCallback func;
|
||||
void* arg;
|
||||
};
|
||||
|
||||
struct NetCtlCallbackForNpToolkit {
|
||||
OrbisNetCtlCallbackForNpToolkit func;
|
||||
void* arg;
|
||||
};
|
||||
|
||||
class NetCtlInternal {
|
||||
public:
|
||||
explicit NetCtlInternal();
|
||||
~NetCtlInternal();
|
||||
|
||||
s32 RegisterCallback(OrbisNetCtlCallback func, void* arg);
|
||||
s32 RegisterNpToolkitCallback(OrbisNetCtlCallbackForNpToolkit func, void* arg);
|
||||
void CheckCallback();
|
||||
void CheckNpToolkitCallback();
|
||||
|
||||
public:
|
||||
std::array<NetCtlCallbackForNpToolkit, 8> nptool_callbacks{};
|
||||
std::array<NetCtlCallback, 8> callbacks{};
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
} // namespace Libraries::NetCtl
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "net_epoll.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
u32 ConvertEpollEventsIn(u32 orbis_events) {
|
||||
u32 ret = 0;
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
if ((orbis_events & ORBIS_NET_EPOLLIN) != 0) {
|
||||
ret |= EPOLLIN;
|
||||
}
|
||||
if ((orbis_events & ORBIS_NET_EPOLLOUT) != 0) {
|
||||
ret |= EPOLLOUT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 ConvertEpollEventsOut(u32 epoll_events) {
|
||||
u32 ret = 0;
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
if ((epoll_events & EPOLLIN) != 0) {
|
||||
ret |= ORBIS_NET_EPOLLIN;
|
||||
}
|
||||
if ((epoll_events & EPOLLOUT) != 0) {
|
||||
ret |= ORBIS_NET_EPOLLOUT;
|
||||
}
|
||||
if ((epoll_events & EPOLLERR) != 0) {
|
||||
ret |= ORBIS_NET_EPOLLERR;
|
||||
}
|
||||
if ((epoll_events & EPOLLHUP) != 0) {
|
||||
ret |= ORBIS_NET_EPOLLHUP;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int EpollTable::CreateHandle(const char* name) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (auto it = std::ranges::find_if(epolls, [](const Epoll& e) { return e.Destroyed(); });
|
||||
it != epolls.end()) {
|
||||
*it = Epoll(name);
|
||||
const auto ret = std::distance(epolls.begin(), it);
|
||||
LOG_DEBUG(Lib_Net, "epollid = {}", ret);
|
||||
return ret;
|
||||
} else {
|
||||
epolls.emplace_back(name);
|
||||
const auto ret = epolls.size() - 1;
|
||||
LOG_DEBUG(Lib_Net, "epollid = {}", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void EpollTable::DeleteHandle(int d) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Epoll* EpollTable::GetEpoll(int epollid) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if (epollid >= epolls.size() || epolls[epollid].Destroyed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &epolls[epollid];
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/net.h"
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wepoll.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||
// ADD libepoll-shim if using freebsd!
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
#ifdef _WIN32
|
||||
using epoll_handle = HANDLE;
|
||||
#else
|
||||
using epoll_handle = int;
|
||||
#endif
|
||||
|
||||
struct Epoll {
|
||||
std::vector<std::pair<u32 /*netId*/, OrbisNetEpollEvent>> events{};
|
||||
std::string name;
|
||||
epoll_handle epoll_fd;
|
||||
std::deque<u32> async_resolutions{};
|
||||
|
||||
explicit Epoll(const char* name_) : name(name_), epoll_fd(epoll_create1(0)) {
|
||||
#ifdef _WIN32
|
||||
ASSERT(epoll_fd != nullptr);
|
||||
#else
|
||||
ASSERT(epoll_fd != -1);
|
||||
#endif
|
||||
if (name_ == nullptr) {
|
||||
name = "anon";
|
||||
}
|
||||
}
|
||||
|
||||
bool Destroyed() const noexcept {
|
||||
return destroyed;
|
||||
}
|
||||
|
||||
void Destroy() noexcept {
|
||||
events.clear();
|
||||
#ifdef _WIN32
|
||||
epoll_close(epoll_fd);
|
||||
epoll_fd = nullptr;
|
||||
#else
|
||||
close(epoll_fd);
|
||||
epoll_fd = -1;
|
||||
#endif
|
||||
name = "";
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool destroyed{};
|
||||
};
|
||||
|
||||
u32 ConvertEpollEventsIn(u32 orbis_events);
|
||||
u32 ConvertEpollEventsOut(u32 epoll_events);
|
||||
|
||||
class EpollTable {
|
||||
public:
|
||||
EpollTable() = default;
|
||||
virtual ~EpollTable() = default;
|
||||
|
||||
int CreateHandle(const char* name);
|
||||
void DeleteHandle(int d);
|
||||
Epoll* GetEpoll(int d);
|
||||
|
||||
private:
|
||||
std::vector<Epoll> epolls;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// net errno codes
|
||||
constexpr int ORBIS_NET_EPERM = 1;
|
||||
constexpr int ORBIS_NET_ENOENT = 2;
|
||||
constexpr int ORBIS_NET_EINTR = 4;
|
||||
constexpr int ORBIS_NET_EBADF = 9;
|
||||
constexpr int ORBIS_NET_ENOMEM = 12;
|
||||
constexpr int ORBIS_NET_EACCES = 13;
|
||||
constexpr int ORBIS_NET_EFAULT = 14;
|
||||
constexpr int ORBIS_NET_ENOTBLK = 15;
|
||||
constexpr int ORBIS_NET_EBUSY = 16;
|
||||
constexpr int ORBIS_NET_EEXIST = 17;
|
||||
constexpr int ORBIS_NET_ENODEV = 19;
|
||||
constexpr int ORBIS_NET_EINVAL = 22;
|
||||
constexpr int ORBIS_NET_ENFILE = 23;
|
||||
constexpr int ORBIS_NET_EMFILE = 24;
|
||||
constexpr int ORBIS_NET_ENOSPC = 28;
|
||||
constexpr int ORBIS_NET_EPIPE = 32;
|
||||
constexpr int ORBIS_NET_EAGAIN = 35;
|
||||
constexpr int ORBIS_NET_EWOULDBLOCK = 35;
|
||||
constexpr int ORBIS_NET_EINPROGRESS = 36;
|
||||
constexpr int ORBIS_NET_EALREADY = 37;
|
||||
constexpr int ORBIS_NET_ENOTSOCK = 38;
|
||||
constexpr int ORBIS_NET_EDESTADDRREQ = 39;
|
||||
constexpr int ORBIS_NET_EMSGSIZE = 40;
|
||||
constexpr int ORBIS_NET_EPROTOTYPE = 41;
|
||||
constexpr int ORBIS_NET_ENOPROTOOPT = 42;
|
||||
constexpr int ORBIS_NET_EPROTONOSUPPORT = 43;
|
||||
constexpr int ORBIS_NET_EOPNOTSUPP = 45;
|
||||
constexpr int ORBIS_NET_EAFNOSUPPORT = 47;
|
||||
constexpr int ORBIS_NET_EADDRINUSE = 48;
|
||||
constexpr int ORBIS_NET_EADDRNOTAVAIL = 49;
|
||||
constexpr int ORBIS_NET_ENETDOWN = 50;
|
||||
constexpr int ORBIS_NET_ENETUNREACH = 51;
|
||||
constexpr int ORBIS_NET_ENETRESET = 52;
|
||||
constexpr int ORBIS_NET_ECONNABORTED = 53;
|
||||
constexpr int ORBIS_NET_ECONNRESET = 54;
|
||||
constexpr int ORBIS_NET_ENOBUFS = 55;
|
||||
constexpr int ORBIS_NET_EISCONN = 56;
|
||||
constexpr int ORBIS_NET_ENOTCONN = 57;
|
||||
constexpr int ORBIS_NET_ETOOMANYREFS = 59;
|
||||
constexpr int ORBIS_NET_ETIMEDOUT = 60;
|
||||
constexpr int ORBIS_NET_ECONNREFUSED = 61;
|
||||
constexpr int ORBIS_NET_ELOOP = 62;
|
||||
constexpr int ORBIS_NET_ENAMETOOLONG = 63;
|
||||
constexpr int ORBIS_NET_EHOSTDOWN = 64;
|
||||
constexpr int ORBIS_NET_EHOSTUNREACH = 65;
|
||||
constexpr int ORBIS_NET_ENOTEMPTY = 66;
|
||||
constexpr int ORBIS_NET_EPROCUNAVAIL = 76;
|
||||
constexpr int ORBIS_NET_ECANCELED = 87;
|
||||
constexpr int ORBIS_NET_EPROTO = 92;
|
||||
constexpr int ORBIS_NET_EADHOC = 160;
|
||||
constexpr int ORBIS_NET_EINACTIVEDISABLED = 163;
|
||||
constexpr int ORBIS_NET_ENODATA = 164;
|
||||
constexpr int ORBIS_NET_EDESC = 165;
|
||||
constexpr int ORBIS_NET_EDESCTIMEDOUT = 166;
|
||||
constexpr int ORBIS_NET_ENETINTR = 167;
|
||||
constexpr int ORBIS_NET_ENOTINIT = 200;
|
||||
constexpr int ORBIS_NET_ENOLIBMEM = 201;
|
||||
constexpr int ORBIS_NET_ECALLBACK = 203;
|
||||
constexpr int ORBIS_NET_EINTERNAL = 204;
|
||||
constexpr int ORBIS_NET_ERETURN = 205;
|
||||
constexpr int ORBIS_NET_ENOALLOCMEM = 206;
|
||||
|
||||
// errno for dns resolver
|
||||
constexpr int ORBIS_NET_RESOLVER_EINTERNAL = 220;
|
||||
constexpr int ORBIS_NET_RESOLVER_EBUSY = 221;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENOSPACE = 222;
|
||||
constexpr int ORBIS_NET_RESOLVER_EPACKET = 223;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENODNS = 225;
|
||||
constexpr int ORBIS_NET_RESOLVER_ETIMEDOUT = 226;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENOSUPPORT = 227;
|
||||
constexpr int ORBIS_NET_RESOLVER_EFORMAT = 228;
|
||||
constexpr int ORBIS_NET_RESOLVER_ESERVERFAILURE = 229;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENOHOST = 230;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENOTIMPLEMENTED = 231;
|
||||
constexpr int ORBIS_NET_RESOLVER_ESERVERREFUSED = 232;
|
||||
constexpr int ORBIS_NET_RESOLVER_ENORECORD = 233;
|
||||
constexpr int ORBIS_NET_RESOLVER_EALIGNMENT = 234;
|
||||
|
||||
// error codes
|
||||
constexpr int ORBIS_NET_ERROR_BASE = 0x80410100; // not existed used for calculation
|
||||
constexpr int ORBIS_NET_ERROR_EPERM = 0x80410101;
|
||||
constexpr int ORBIS_NET_ERROR_ENOENT = 0x80410102;
|
||||
constexpr int ORBIS_NET_ERROR_EINTR = 0x80410104;
|
||||
constexpr int ORBIS_NET_ERROR_EBADF = 0x80410109;
|
||||
constexpr int ORBIS_NET_ERROR_ENOMEM = 0x8041010c;
|
||||
constexpr int ORBIS_NET_ERROR_EACCES = 0x8041010d;
|
||||
constexpr int ORBIS_NET_ERROR_EFAULT = 0x8041010e;
|
||||
constexpr int ORBIS_NET_ERROR_ENOTBLK = 0x8041010f;
|
||||
constexpr int ORBIS_NET_ERROR_EEXIST = 0x80410111;
|
||||
constexpr int ORBIS_NET_ERROR_ENODEV = 0x80410113;
|
||||
constexpr int ORBIS_NET_ERROR_EINVAL = 0x80410116;
|
||||
constexpr int ORBIS_NET_ERROR_ENFILE = 0x80410117;
|
||||
constexpr int ORBIS_NET_ERROR_EMFILE = 0x80410118;
|
||||
constexpr int ORBIS_NET_ERROR_ENOSPC = 0x8041011c;
|
||||
constexpr int ORBIS_NET_ERROR_EPIPE = 0x80410120;
|
||||
constexpr int ORBIS_NET_ERROR_EAGAIN = 0x80410123;
|
||||
constexpr int ORBIS_NET_ERROR_EWOULDBLOCK = 0x80410123;
|
||||
constexpr int ORBIS_NET_ERROR_EINPROGRESS = 0x80410124;
|
||||
constexpr int ORBIS_NET_ERROR_EALREADY = 0x80410125;
|
||||
constexpr int ORBIS_NET_ERROR_ENOTSOCK = 0x80410126;
|
||||
constexpr int ORBIS_NET_ERROR_EDESTADDRREQ = 0x80410127;
|
||||
constexpr int ORBIS_NET_ERROR_EMSGSIZE = 0x80410128;
|
||||
constexpr int ORBIS_NET_ERROR_EPROTOTYPE = 0x80410129;
|
||||
constexpr int ORBIS_NET_ERROR_ENOPROTOOPT = 0x8041012a;
|
||||
constexpr int ORBIS_NET_ERROR_EPROTONOSUPPORT = 0x8041012b;
|
||||
constexpr int ORBIS_NET_ERROR_EOPNOTSUPP = 0x8041012d;
|
||||
constexpr int ORBIS_NET_ERROR_EPFNOSUPPORT = 0x8041012e;
|
||||
constexpr int ORBIS_NET_ERROR_EAFNOSUPPORT = 0x8041012f;
|
||||
constexpr int ORBIS_NET_ERROR_EADDRINUSE = 0x80410130;
|
||||
constexpr int ORBIS_NET_ERROR_EADDRNOTAVAIL = 0x80410131;
|
||||
constexpr int ORBIS_NET_ERROR_ENETDOWN = 0x80410132;
|
||||
constexpr int ORBIS_NET_ERROR_ENETUNREACH = 0x80410133;
|
||||
constexpr int ORBIS_NET_ERROR_ENETRESET = 0x80410134;
|
||||
constexpr int ORBIS_NET_ERROR_ECONNABORTED = 0x80410135;
|
||||
constexpr int ORBIS_NET_ERROR_ECONNRESET = 0x80410136;
|
||||
constexpr int ORBIS_NET_ERROR_ENOBUFS = 0x80410137;
|
||||
constexpr int ORBIS_NET_ERROR_EISCONN = 0x80410138;
|
||||
constexpr int ORBIS_NET_ERROR_ENOTCONN = 0x80410139;
|
||||
constexpr int ORBIS_NET_ERROR_ESHUTDOWN = 0x8041013a;
|
||||
constexpr int ORBIS_NET_ERROR_ETOOMANYREFS = 0x8041013b;
|
||||
constexpr int ORBIS_NET_ERROR_ETIMEDOUT = 0x8041013c;
|
||||
constexpr int ORBIS_NET_ERROR_ECONNREFUSED = 0x8041013d;
|
||||
constexpr int ORBIS_NET_ERROR_ELOOP = 0x8041013e;
|
||||
constexpr int ORBIS_NET_ERROR_ENAMETOOLONG = 0x8041013f;
|
||||
constexpr int ORBIS_NET_ERROR_EHOSTDOWN = 0x80410140;
|
||||
constexpr int ORBIS_NET_ERROR_EHOSTUNREACH = 0x80410141;
|
||||
constexpr int ORBIS_NET_ERROR_ENOTEMPTY = 0x80410142;
|
||||
constexpr int ORBIS_NET_ERROR_EPROCUNAVAIL = 0x8041014C;
|
||||
constexpr int ORBIS_NET_ERROR_ECANCELED = 0x80410157;
|
||||
constexpr int ORBIS_NET_ERROR_EPROTO = 0x8041015C;
|
||||
constexpr int ORBIS_NET_ERROR_EADHOC = 0x804101a0;
|
||||
constexpr int ORBIS_NET_ERROR_ERESERVED161 = 0x804101a1;
|
||||
constexpr int ORBIS_NET_ERROR_ERESERVED162 = 0x804101a2;
|
||||
constexpr int ORBIS_NET_ERROR_EINACTIVEDISABLED = 0x804101a3;
|
||||
constexpr int ORBIS_NET_ERROR_ENODATA = 0x804101a4;
|
||||
constexpr int ORBIS_NET_ERROR_EDESC = 0x804101a5;
|
||||
constexpr int ORBIS_NET_ERROR_EDESCTIMEDOUT = 0x804101a6;
|
||||
constexpr int ORBIS_NET_ERROR_ENOTINIT = 0x804101c8;
|
||||
constexpr int ORBIS_NET_ERROR_ENOLIBMEM = 0x804101c9;
|
||||
constexpr int ORBIS_NET_ERROR_ECALLBACK = 0x804101cb;
|
||||
constexpr int ORBIS_NET_ERROR_EINTERNAL = 0x804101cc;
|
||||
constexpr int ORBIS_NET_ERROR_ERETURN = 0x804101cd;
|
||||
constexpr int ORBIS_NET_ERROR_ENOALLOCMEM = 0x804101ce;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_EINTERNAL = 0x804101dc;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_EBUSY = 0x804101dd;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSPACE = 0x804101de;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_EPACKET = 0x804101df;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENODNS = 0x804101e1;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101e2;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOSUPPORT = 0x804101e3;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_EFORMAT = 0x804101e4;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERFAILURE = 0x804101e5;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOHOST = 0x804101e6;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x804101e7;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ESERVERREFUSED = 0x804101e8;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ENORECORD = 0x804101e9;
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_EALIGNMENT = 0x804101ea;
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "net_error.h"
|
||||
#include "net_resolver.h"
|
||||
#include "net_util.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
int Resolver::ResolveAsync(const char* hostname, OrbisNetInAddr* addr, int timeout, int retry,
|
||||
int flags) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (async_resolution) {
|
||||
*sceNetErrnoLoc() = ORBIS_NET_RESOLVER_EBUSY;
|
||||
return ORBIS_NET_ERROR_RESOLVER_EBUSY;
|
||||
}
|
||||
|
||||
async_resolution = AsyncResolution{hostname, addr, timeout, retry, flags};
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void Resolver::Resolve() {
|
||||
if (!EmulatorSettings.IsConnectedToNetwork()) {
|
||||
resolution_error = ORBIS_NET_ERROR_RESOLVER_ENODNS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (async_resolution) {
|
||||
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
|
||||
auto ret = netinfo->ResolveHostname(async_resolution->hostname, async_resolution->addr);
|
||||
resolution_error = ret;
|
||||
if (ret != ORBIS_OK) {
|
||||
// Resolver errors are stored as ORBIS_NET_ERROR values.
|
||||
resolution_error = -ret | ORBIS_NET_ERROR_BASE;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Lib_Net, "async resolution has not been set-up");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/net.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
struct Resolver {
|
||||
public:
|
||||
Resolver(const char* name, int poolid, int flags) : name(name), poolid(poolid), flags(flags) {}
|
||||
|
||||
int ResolveAsync(const char* hostname, OrbisNetInAddr* addr, int timeout, int retry, int flags);
|
||||
void Resolve();
|
||||
|
||||
int resolution_error = ORBIS_OK;
|
||||
|
||||
private:
|
||||
struct AsyncResolution {
|
||||
const char* hostname;
|
||||
OrbisNetInAddr* addr;
|
||||
int timeout;
|
||||
int retry;
|
||||
int flags;
|
||||
};
|
||||
|
||||
std::string name;
|
||||
int poolid;
|
||||
int flags;
|
||||
std::optional<AsyncResolution> async_resolution{};
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,150 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <upnperrors.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "net_upnp.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
UPnPClient& UPnPClient::Instance() {
|
||||
static UPnPClient instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
UPnPClient::~UPnPClient() {
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
if (m_available.load())
|
||||
FreeUPNPUrls(&m_urls);
|
||||
}
|
||||
|
||||
void UPnPClient::Start() {
|
||||
if (!m_p2p_features_enabled.load()) {
|
||||
LOG_INFO(Lib_Net, "UPNP: skipped because Matching2 is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
bool expected = false;
|
||||
if (!m_started.compare_exchange_strong(expected, true))
|
||||
return;
|
||||
LOG_INFO(Lib_Net, "UPNP: starting discovery thread");
|
||||
m_thread = std::thread(&UPnPClient::DiscoverThread, this);
|
||||
}
|
||||
|
||||
void UPnPClient::SetP2PFeaturesEnabled(bool enabled) {
|
||||
m_p2p_features_enabled.store(enabled);
|
||||
}
|
||||
|
||||
bool UPnPClient::WaitReady(int timeout_ms) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] { return m_done.load(); });
|
||||
return m_available.load();
|
||||
}
|
||||
|
||||
bool UPnPClient::IsAvailable() const {
|
||||
return m_available.load();
|
||||
}
|
||||
|
||||
u32 UPnPClient::GetExternalIp() const {
|
||||
return m_external_ip_nbo.load();
|
||||
}
|
||||
|
||||
std::string UPnPClient::GetExternalIpString() const {
|
||||
return m_available.load() ? std::string(m_external_ip_str) : std::string{};
|
||||
}
|
||||
|
||||
u16 UPnPClient::GetExternalPort() const {
|
||||
return m_external_port.load();
|
||||
}
|
||||
|
||||
void UPnPClient::DiscoverThread() {
|
||||
int error = 0;
|
||||
UPNPDev* devlist = upnpDiscover(2000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &error);
|
||||
|
||||
if (!devlist) {
|
||||
LOG_INFO(Lib_Net, "UPNP: no IGD found (error={})", error);
|
||||
m_done = true;
|
||||
m_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
char wan_addr[40]{};
|
||||
const int ret = UPNP_GetValidIGD(devlist, &m_urls, &m_data, m_lan_addr, sizeof(m_lan_addr),
|
||||
wan_addr, sizeof(wan_addr));
|
||||
freeUPNPDevlist(devlist);
|
||||
|
||||
if (ret != 1) {
|
||||
LOG_INFO(Lib_Net, "UPNP: no valid IGD (ret={})", ret);
|
||||
m_done = true;
|
||||
m_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
char external_ip[40]{};
|
||||
if (UPNP_GetExternalIPAddress(m_urls.controlURL, m_data.first.servicetype, external_ip) !=
|
||||
UPNPCOMMAND_SUCCESS) {
|
||||
LOG_WARNING(Lib_Net, "UPNP: IGD found but failed to get external IP");
|
||||
FreeUPNPUrls(&m_urls);
|
||||
m_done = true;
|
||||
m_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
u32 ip_nbo = 0;
|
||||
inet_pton(AF_INET, external_ip, &ip_nbo);
|
||||
m_external_ip_nbo.store(ip_nbo);
|
||||
std::strncpy(m_external_ip_str, external_ip, sizeof(m_external_ip_str) - 1);
|
||||
m_available = true;
|
||||
|
||||
LOG_INFO(Lib_Net, "UPNP: IGD found - external IP={} lan={} (port mapping available)",
|
||||
external_ip, m_lan_addr);
|
||||
|
||||
m_done = true;
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void UPnPClient::AddMapping(u16 port) {
|
||||
if (!m_p2p_features_enabled.load())
|
||||
return;
|
||||
|
||||
if (!m_available.load())
|
||||
return;
|
||||
|
||||
const std::string port_str = std::to_string(port);
|
||||
const int ret =
|
||||
UPNP_AddPortMapping(m_urls.controlURL, m_data.first.servicetype, port_str.c_str(),
|
||||
port_str.c_str(), m_lan_addr, "shadPS4", "UDP", nullptr, "0");
|
||||
|
||||
if (ret == UPNPCOMMAND_SUCCESS) {
|
||||
m_external_port.store(port);
|
||||
LOG_INFO(Lib_Net, "UPNP: mapped UDP port {}", port);
|
||||
} else {
|
||||
// Store the requested port as best-guess even on failure so callers still get a value.
|
||||
m_external_port.store(port);
|
||||
LOG_WARNING(Lib_Net, "UPNP: failed to map port {} ({})", port, strupnperror(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void UPnPClient::RemoveMapping(u16 port) {
|
||||
if (!m_available.load())
|
||||
return;
|
||||
|
||||
const std::string port_str = std::to_string(port);
|
||||
UPNP_DeletePortMapping(m_urls.controlURL, m_data.first.servicetype, port_str.c_str(), "UDP",
|
||||
nullptr);
|
||||
|
||||
LOG_INFO(Lib_Net, "UPNP: removed UDP port mapping {}", port);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "common/types.h"
|
||||
|
||||
#include <miniupnpc.h>
|
||||
#include <upnpcommands.h>
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
class UPnPClient {
|
||||
public:
|
||||
static UPnPClient& Instance();
|
||||
|
||||
// Kick off async IGD discovery. Safe to call multiple times — only the first call has effect.
|
||||
void Start();
|
||||
void SetP2PFeaturesEnabled(bool enabled);
|
||||
|
||||
// Block until discovery completes or timeout_ms elapses. Returns true if IGD was found.
|
||||
bool WaitReady(int timeout_ms);
|
||||
|
||||
bool IsAvailable() const;
|
||||
|
||||
// External IP in network byte order as reported by the IGD. 0 if not yet known.
|
||||
u32 GetExternalIp() const;
|
||||
|
||||
// External IP as a dotted-decimal string. Empty if not yet known.
|
||||
std::string GetExternalIpString() const;
|
||||
|
||||
// External port the IGD mapped for us. Matches the internal port unless the router remapped it.
|
||||
u16 GetExternalPort() const;
|
||||
|
||||
void AddMapping(u16 port);
|
||||
void RemoveMapping(u16 port);
|
||||
|
||||
private:
|
||||
UPnPClient() = default;
|
||||
~UPnPClient();
|
||||
UPnPClient(const UPnPClient&) = delete;
|
||||
UPnPClient& operator=(const UPnPClient&) = delete;
|
||||
|
||||
void DiscoverThread();
|
||||
|
||||
std::atomic<bool> m_started{false};
|
||||
std::atomic<bool> m_p2p_features_enabled{true};
|
||||
std::atomic<bool> m_done{false};
|
||||
std::atomic<bool> m_available{false};
|
||||
std::atomic<u32> m_external_ip_nbo{0};
|
||||
std::atomic<u16> m_external_port{0};
|
||||
char m_external_ip_str[16]{};
|
||||
|
||||
char m_lan_addr[64]{};
|
||||
UPNPUrls m_urls{};
|
||||
IGDdatas m_data{};
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::thread m_thread;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,416 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#include <Ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <winsock2.h>
|
||||
typedef SOCKET net_socket;
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
typedef int net_socket;
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#include <net/if_dl.h>
|
||||
#include <net/route.h>
|
||||
#endif
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "net.h"
|
||||
#include "net_error.h"
|
||||
#include "net_util.h"
|
||||
|
||||
namespace NetUtil {
|
||||
|
||||
const std::array<u8, 6>& NetUtilInternal::GetEthernetAddr() const {
|
||||
return ether_address;
|
||||
}
|
||||
|
||||
bool NetUtilInternal::RetrieveEthernetAddr() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
#ifdef _WIN32
|
||||
std::vector<u8> adapter_infos(sizeof(IP_ADAPTER_INFO));
|
||||
ULONG size_infos = sizeof(IP_ADAPTER_INFO);
|
||||
|
||||
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) ==
|
||||
ERROR_BUFFER_OVERFLOW)
|
||||
adapter_infos.resize(size_infos);
|
||||
|
||||
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) ==
|
||||
NO_ERROR &&
|
||||
size_infos) {
|
||||
PIP_ADAPTER_INFO info = reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data());
|
||||
memcpy(ether_address.data(), info[0].Address, 6);
|
||||
return true;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
ifaddrs* ifap;
|
||||
|
||||
if (getifaddrs(&ifap) == 0) {
|
||||
ifaddrs* p;
|
||||
for (p = ifap; p; p = p->ifa_next) {
|
||||
if (p->ifa_addr->sa_family == AF_LINK) {
|
||||
sockaddr_dl* sdp = reinterpret_cast<sockaddr_dl*>(p->ifa_addr);
|
||||
memcpy(ether_address.data(), sdp->sdl_data + sdp->sdl_nlen, 6);
|
||||
freeifaddrs(ifap);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
// todo
|
||||
#else
|
||||
ifreq ifr;
|
||||
ifconf ifc;
|
||||
char buf[1024];
|
||||
int success = 0;
|
||||
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sock == -1)
|
||||
return false;
|
||||
|
||||
ifc.ifc_len = sizeof(buf);
|
||||
ifc.ifc_buf = buf;
|
||||
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1)
|
||||
return false;
|
||||
|
||||
ifreq* it = ifc.ifc_req;
|
||||
const ifreq* const end = it + (ifc.ifc_len / sizeof(ifreq));
|
||||
|
||||
for (; it != end; ++it) {
|
||||
strcpy(ifr.ifr_name, it->ifr_name);
|
||||
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
|
||||
if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
|
||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
|
||||
success = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
if (success) {
|
||||
memcpy(ether_address.data(), ifr.ifr_hwaddr.sa_data, 6);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& NetUtilInternal::GetDefaultGateway() const {
|
||||
return default_gateway;
|
||||
}
|
||||
|
||||
bool NetUtilInternal::RetrieveDefaultGateway() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
#ifdef _WIN32
|
||||
ULONG flags = GAA_FLAG_INCLUDE_GATEWAYS;
|
||||
ULONG family = AF_INET; // Only IPv4
|
||||
ULONG buffer_size = 15000;
|
||||
|
||||
std::vector<BYTE> buffer(buffer_size);
|
||||
PIP_ADAPTER_ADDRESSES adapter_addresses =
|
||||
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());
|
||||
|
||||
DWORD result = GetAdaptersAddresses(family, flags, nullptr, adapter_addresses, &buffer_size);
|
||||
if (result != NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses; adapter != nullptr;
|
||||
adapter = adapter->Next) {
|
||||
if (adapter->OperStatus != IfOperStatusUp)
|
||||
continue;
|
||||
|
||||
IP_ADAPTER_GATEWAY_ADDRESS_LH* gateway = adapter->FirstGatewayAddress;
|
||||
while (gateway) {
|
||||
sockaddr* sa = gateway->Address.lpSockaddr;
|
||||
if (sa->sa_family == AF_INET) {
|
||||
char str[INET_ADDRSTRLEN];
|
||||
sockaddr_in* sa_in = reinterpret_cast<sockaddr_in*>(sa);
|
||||
if (inet_ntop(AF_INET, &sa_in->sin_addr, str, sizeof(str))) {
|
||||
this->default_gateway = str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
gateway = gateway->Next;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#elif defined(__APPLE__)
|
||||
// adapted from
|
||||
// https://github.com/seladb/PcapPlusPlus/blob/a49a79e0b67b402ad75ffa96c1795def36df75c8/Pcap%2B%2B/src/PcapLiveDevice.cpp#L1236
|
||||
// route message struct for communication in APPLE device
|
||||
struct BSDRoutingMessage {
|
||||
struct rt_msghdr header;
|
||||
char messageSpace[512];
|
||||
};
|
||||
|
||||
struct BSDRoutingMessage routingMessage;
|
||||
// It creates a raw socket that can be used for routing-related operations
|
||||
int sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
|
||||
if (sockfd < 0) {
|
||||
return false;
|
||||
}
|
||||
memset(reinterpret_cast<char*>(&routingMessage), 0, sizeof(routingMessage));
|
||||
routingMessage.header.rtm_msglen = sizeof(struct rt_msghdr);
|
||||
routingMessage.header.rtm_version = RTM_VERSION;
|
||||
routingMessage.header.rtm_type = RTM_GET;
|
||||
routingMessage.header.rtm_addrs = RTA_DST | RTA_NETMASK;
|
||||
routingMessage.header.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
|
||||
routingMessage.header.rtm_msglen += 2 * sizeof(sockaddr_in);
|
||||
|
||||
if (write(sockfd, reinterpret_cast<char*>(&routingMessage), routingMessage.header.rtm_msglen) <
|
||||
0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the response from the route socket
|
||||
if (read(sockfd, reinterpret_cast<char*>(&routingMessage), sizeof(routingMessage)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct in_addr* gateAddr = nullptr;
|
||||
struct sockaddr* sa = nullptr;
|
||||
char* spacePtr = (reinterpret_cast<char*>(&routingMessage.header + 1));
|
||||
auto rtmAddrs = routingMessage.header.rtm_addrs;
|
||||
int index = 1;
|
||||
auto roundUpClosestMultiple = [](int multiple, int num) {
|
||||
return ((num + multiple - 1) / multiple) * multiple;
|
||||
};
|
||||
while (rtmAddrs) {
|
||||
if (rtmAddrs & 1) {
|
||||
sa = reinterpret_cast<sockaddr*>(spacePtr);
|
||||
if (index == RTA_GATEWAY) {
|
||||
gateAddr = &((sockaddr_in*)sa)->sin_addr;
|
||||
break;
|
||||
}
|
||||
spacePtr += sa->sa_len > 0 ? roundUpClosestMultiple(sizeof(uint32_t), sa->sa_len)
|
||||
: sizeof(uint32_t);
|
||||
}
|
||||
index++;
|
||||
rtmAddrs >>= 1;
|
||||
}
|
||||
|
||||
if (gateAddr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, gateAddr, str, sizeof(str));
|
||||
this->default_gateway = str;
|
||||
return true;
|
||||
#elif defined(__FreeBSD__)
|
||||
return true;
|
||||
#else
|
||||
std::ifstream route{"/proc/net/route"};
|
||||
std::string line;
|
||||
|
||||
std::getline(route, line);
|
||||
while (std::getline(route, line)) {
|
||||
std::istringstream iss{line};
|
||||
std::string iface, destination, gateway;
|
||||
int flags;
|
||||
|
||||
iss >> iface >> destination >> gateway >> std::hex >> flags;
|
||||
|
||||
if (destination == "00000000") {
|
||||
u64 default_gateway{};
|
||||
std::stringstream ss;
|
||||
ss << std::hex << gateway;
|
||||
ss >> default_gateway;
|
||||
|
||||
char str[INET_ADDRSTRLEN];
|
||||
in_addr addr;
|
||||
addr.s_addr = default_gateway;
|
||||
inet_ntop(AF_INET, &addr, str, sizeof(str));
|
||||
this->default_gateway = str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& NetUtilInternal::GetNetmask() const {
|
||||
return netmask;
|
||||
}
|
||||
|
||||
bool NetUtilInternal::RetrieveNetmask() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
char netmaskStr[INET_ADDRSTRLEN];
|
||||
auto success = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
||||
ULONG family = AF_INET; // Only IPv4
|
||||
ULONG buffer_size = 15000;
|
||||
|
||||
std::vector<BYTE> buffer(buffer_size);
|
||||
auto adapter_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());
|
||||
|
||||
DWORD result = GetAdaptersAddresses(family, flags, nullptr, adapter_addresses, &buffer_size);
|
||||
if (result == ERROR_BUFFER_OVERFLOW) {
|
||||
buffer.resize(buffer_size);
|
||||
adapter_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());
|
||||
result = GetAdaptersAddresses(family, flags, nullptr, adapter_addresses, &buffer_size);
|
||||
}
|
||||
|
||||
if (result != NO_ERROR)
|
||||
return false;
|
||||
|
||||
for (auto adapter = adapter_addresses; adapter != nullptr; adapter = adapter->Next) {
|
||||
// Skip loopback and down interfaces
|
||||
if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK || adapter->OperStatus != IfOperStatusUp)
|
||||
continue;
|
||||
|
||||
for (auto unicast = adapter->FirstUnicastAddress; unicast != nullptr;
|
||||
unicast = unicast->Next) {
|
||||
if (unicast->Address.lpSockaddr->sa_family == AF_INET) {
|
||||
ULONG prefix_length = unicast->OnLinkPrefixLength;
|
||||
ULONG mask = prefix_length == 0 ? 0 : 0xFFFFFFFF << (32 - prefix_length);
|
||||
|
||||
in_addr mask_addr{};
|
||||
mask_addr.S_un.S_addr = htonl(mask);
|
||||
|
||||
if (inet_ntop(AF_INET, &mask_addr, netmaskStr, INET_ADDRSTRLEN)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
ifaddrs* ifap;
|
||||
|
||||
if (getifaddrs(&ifap) == 0) {
|
||||
ifaddrs* p;
|
||||
for (p = ifap; p; p = p->ifa_next) {
|
||||
if (p->ifa_addr && p->ifa_addr->sa_family == AF_INET) {
|
||||
auto sa = reinterpret_cast<sockaddr_in*>(p->ifa_netmask);
|
||||
inet_ntop(AF_INET, &sa->sin_addr, netmaskStr, INET_ADDRSTRLEN);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifap);
|
||||
#endif
|
||||
|
||||
if (success) {
|
||||
netmask = netmaskStr;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
const std::string& NetUtilInternal::GetIp() const {
|
||||
return ip;
|
||||
}
|
||||
|
||||
u32 NetUtilInternal::GetExternalIp() const {
|
||||
return external_ip;
|
||||
}
|
||||
|
||||
void NetUtilInternal::SetExternalIp(u32 addr) {
|
||||
external_ip = addr;
|
||||
}
|
||||
|
||||
u32 NetUtilInternal::GetNatType() const {
|
||||
return nat_type;
|
||||
}
|
||||
|
||||
bool NetUtilInternal::RetrieveIp() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_in sa{};
|
||||
socklen_t sa_len{sizeof(sa)};
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = inet_addr("1.1.1.1");
|
||||
sa.sin_port = htons(80);
|
||||
|
||||
#ifdef _WIN32
|
||||
#define close closesocket
|
||||
#endif
|
||||
|
||||
if (connect(sockfd, (sockaddr*)&sa, sa_len) == -1) {
|
||||
close(sockfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getsockname(sockfd, (struct sockaddr*)&sa, &sa_len) == -1) {
|
||||
close(sockfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
char netmaskStr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &sa.sin_addr, netmaskStr, INET_ADDRSTRLEN);
|
||||
ip = netmaskStr;
|
||||
|
||||
close(sockfd);
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef close
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NetUtilInternal::ResolveHostname(const char* hostname, Libraries::Net::OrbisNetInAddr* addr) {
|
||||
const addrinfo hints = {
|
||||
.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG,
|
||||
.ai_family = AF_INET,
|
||||
};
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
auto gai_result = getaddrinfo(hostname, nullptr, &hints, &info);
|
||||
|
||||
auto ret = ORBIS_OK;
|
||||
if (gai_result != 0) {
|
||||
// handle more errors
|
||||
LOG_ERROR(Lib_Net, "address resolution for {} failed: {}", hostname, gai_result);
|
||||
ret = ORBIS_NET_ERETURN;
|
||||
} else {
|
||||
ASSERT(info && info->ai_addr);
|
||||
in_addr resolved_addr = ((sockaddr_in*)info->ai_addr)->sin_addr;
|
||||
LOG_DEBUG(Lib_Net, "resolved address for {}: {}", hostname, inet_ntoa(resolved_addr));
|
||||
addr->inaddr_addr = resolved_addr.s_addr;
|
||||
}
|
||||
|
||||
freeaddrinfo(info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace NetUtil
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
struct OrbisNetInAddr;
|
||||
}
|
||||
|
||||
namespace NetUtil {
|
||||
|
||||
class NetUtilInternal {
|
||||
public:
|
||||
explicit NetUtilInternal() = default;
|
||||
~NetUtilInternal() = default;
|
||||
|
||||
private:
|
||||
std::array<u8, 6> ether_address{};
|
||||
std::string default_gateway{};
|
||||
std::string netmask{};
|
||||
std::string ip{};
|
||||
u32 external_ip{0};
|
||||
u32 nat_type{0};
|
||||
std::mutex m_mutex;
|
||||
|
||||
public:
|
||||
const std::array<u8, 6>& GetEthernetAddr() const;
|
||||
const std::string& GetDefaultGateway() const;
|
||||
const std::string& GetNetmask() const;
|
||||
const std::string& GetIp() const;
|
||||
u32 GetExternalIp() const;
|
||||
void SetExternalIp(u32 addr);
|
||||
u32 GetNatType() const;
|
||||
bool RetrieveEthernetAddr();
|
||||
bool RetrieveDefaultGateway();
|
||||
bool RetrieveNetmask();
|
||||
bool RetrieveIp();
|
||||
int ResolveHostname(const char* hostname, Libraries::Net::OrbisNetInAddr* addr);
|
||||
};
|
||||
} // namespace NetUtil
|
||||
@@ -0,0 +1,758 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#include <Ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <common/singleton.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/network/net_ctl_codes.h"
|
||||
#include "core/libraries/network/netctl.h"
|
||||
#include "net_util.h"
|
||||
|
||||
namespace Libraries::NetCtl {
|
||||
|
||||
static NetCtlInternal netctl;
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweClearEventIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweFinishInternetConnectionTestIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweGetInfoIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweRegisterCallbackIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweSetInternetConnectionTestResultIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweStartInternetConnectionTestBandwidthTestIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweStartInternetConnectionTestIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweUnregisterCallbackIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoV6() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultV6() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateV6() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackV6() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallback() {
|
||||
LOG_DEBUG(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallbackForLibIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventForLibIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlConnectConfIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlConnectIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlConnectWithRetryIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlDisableBandwidthManagementIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlDisconnectIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlEnableBandwidthManagementIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetBandwidthInfoIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetEtherLinkMode() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetIfStat() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
|
||||
LOG_DEBUG(Lib_NetCtl, "code = {}", code);
|
||||
if (!EmulatorSettings.IsConnectedToNetwork()) {
|
||||
return ORBIS_NET_CTL_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
|
||||
|
||||
switch (code) {
|
||||
case ORBIS_NET_CTL_INFO_DEVICE:
|
||||
info->device = ORBIS_NET_CTL_DEVICE_WIRED;
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_ETHER_ADDR: {
|
||||
netinfo->RetrieveEthernetAddr();
|
||||
memcpy(info->ether_addr.data, netinfo->GetEthernetAddr().data(), 6);
|
||||
} break;
|
||||
case ORBIS_NET_CTL_INFO_MTU:
|
||||
info->mtu = 1500; // default value
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_LINK:
|
||||
info->link = EmulatorSettings.IsConnectedToNetwork() ? ORBIS_NET_CTL_LINK_CONNECTED
|
||||
: ORBIS_NET_CTL_LINK_DISCONNECTED;
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_IP_ADDRESS: {
|
||||
strcpy(info->ip_address,
|
||||
"127.0.0.1"); // placeholder in case ip retrieval failed
|
||||
auto success = netinfo->RetrieveIp();
|
||||
if (success) {
|
||||
strncpy(info->ip_address, netinfo->GetIp().data(), sizeof(info->ip_address));
|
||||
} else {
|
||||
LOG_WARNING(Lib_NetCtl, "local ip: failed to retrieve");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ORBIS_NET_CTL_INFO_NETMASK: {
|
||||
auto success = netinfo->RetrieveNetmask();
|
||||
if (success) {
|
||||
strncpy(info->netmask, netinfo->GetNetmask().data(), sizeof(info->netmask));
|
||||
LOG_DEBUG(Lib_NetCtl, "netmask: {}", info->netmask);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NetCtl, "netmask: failed to retrieve");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ORBIS_NET_CTL_INFO_DEFAULT_ROUTE: {
|
||||
auto success = netinfo->RetrieveDefaultGateway();
|
||||
if (success) {
|
||||
strncpy(info->default_route, netinfo->GetDefaultGateway().data(),
|
||||
sizeof(info->default_route));
|
||||
LOG_DEBUG(Lib_NetCtl, "default gateway: {}", info->default_route);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NetCtl, "default gateway: failed to retrieve");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ORBIS_NET_CTL_INFO_HTTP_PROXY_CONFIG:
|
||||
info->http_proxy_config = 0; // off
|
||||
LOG_DEBUG(Lib_NetCtl, "http proxy config: {}", info->http_proxy_config);
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_PRIMARY_DNS:
|
||||
strcpy(info->primary_dns, "1.1.1.1");
|
||||
LOG_DEBUG(Lib_NetCtl, "http primary dns: {}", info->primary_dns);
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_SECONDARY_DNS:
|
||||
strcpy(info->secondary_dns, "1.1.1.1");
|
||||
LOG_DEBUG(Lib_NetCtl, "http secondary dns: {}", info->secondary_dns);
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_HTTP_PROXY_SERVER:
|
||||
info->http_proxy_server[0] = '\0';
|
||||
LOG_DEBUG(Lib_NetCtl, "http proxy server: none");
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_HTTP_PROXY_PORT:
|
||||
info->http_proxy_port = 0;
|
||||
LOG_DEBUG(Lib_NetCtl, "http proxy config: {}", info->http_proxy_port);
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_IP_CONFIG:
|
||||
info->ip_config = 1; // static
|
||||
LOG_DEBUG(Lib_NetCtl, "ip config: {}", info->ip_config);
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_DHCP_HOSTNAME:
|
||||
LOG_DEBUG(Lib_NetCtl, "dhcp hostname: none");
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Lib_NetCtl, "{} unsupported code", code);
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoV6IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetNatInfo(OrbisNetCtlNatInfo* nat_info) {
|
||||
if (!nat_info) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
|
||||
}
|
||||
if (nat_info->size != sizeof(OrbisNetCtlNatInfo)) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
|
||||
nat_info->nat_type = netinfo->GetNatType();
|
||||
const u32 ext_ip = netinfo->GetExternalIp();
|
||||
if (ext_ip != 0) {
|
||||
nat_info->stun_status = 1;
|
||||
nat_info->mapped_addr = ext_ip;
|
||||
} else {
|
||||
nat_info->stun_status = 0;
|
||||
nat_info->mapped_addr = inet_addr("127.0.0.1");
|
||||
if (netinfo->RetrieveIp()) {
|
||||
nat_info->mapped_addr = inet_addr(netinfo->GetIp().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_NetCtl, "stun_status={} nat_type={} mapped_addr={:#x}", nat_info->stun_status,
|
||||
nat_info->nat_type, nat_info->mapped_addr);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetNatInfoIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetNetEvConfigInfoIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetResult(int eventType, int* errorCode) {
|
||||
if (!errorCode) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
|
||||
}
|
||||
LOG_DEBUG(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType);
|
||||
*errorCode = 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultV6IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoBssidForSsidListScanIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoBssidIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoByBssidIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoForSsidListScanIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoForSsidScanIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetState(int* state) {
|
||||
const auto connected = EmulatorSettings.IsConnectedToNetwork();
|
||||
LOG_DEBUG(Lib_NetCtl, "connected = {}", connected);
|
||||
const auto current_state =
|
||||
connected ? ORBIS_NET_CTL_STATE_IPOBTAINED : ORBIS_NET_CTL_STATE_DISCONNECTED;
|
||||
*state = current_state;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetState2IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateV6IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetWifiType() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlInit() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlIsBandwidthManagementEnabledIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallback(OrbisNetCtlCallback func, void* arg, int* cid) {
|
||||
if (!func || !cid) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
|
||||
}
|
||||
s32 result = netctl.RegisterCallback(func, arg);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
} else {
|
||||
*cid = result;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackForLibIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackV6IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlScanIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlSetErrorNotificationEnabledIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlSetStunWithPaddingFlagIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlTerm() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackForLibIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6IpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnsetStunWithPaddingFlagIpcInt() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_D8DCB6973537A3DC() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit() {
|
||||
LOG_DEBUG(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventForNpToolkit() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackForNpToolkit(OrbisNetCtlCallbackForNpToolkit func,
|
||||
void* arg, int* cid) {
|
||||
if (!func || !cid) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
|
||||
}
|
||||
s32 result = netctl.RegisterNpToolkitCallback(func, arg);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
} else {
|
||||
*cid = result;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackForNpToolkit() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApCheckCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApClearEvent() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApGetConnectInfo() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApGetInfo() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApGetResult() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApGetState() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApInit() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRegisterCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApStop() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApTerm() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApUnregisterCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApAppInitWpaKey() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApAppInitWpaKeyForQa() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApAppStartWithRetry() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApAppStartWithRetryPid() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRestart() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpCheckCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpClearEvent() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetInfo() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetResult() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetState() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpRegisterCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStart() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStartConf() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStartWithRetry() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStop() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlApRpUnregisterCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("XtClSOC1xcU", "libSceNetBwe", 1, "libSceNetCtl", sceNetBweCheckCallbackIpcInt);
|
||||
LIB_FUNCTION("YALqoY4aeY0", "libSceNetBwe", 1, "libSceNetCtl", sceNetBweClearEventIpcInt);
|
||||
LIB_FUNCTION("ouyROWhGUbM", "libSceNetBwe", 1, "libSceNetCtl",
|
||||
sceNetBweFinishInternetConnectionTestIpcInt);
|
||||
LIB_FUNCTION("G4vltQ0Vs+0", "libSceNetBwe", 1, "libSceNetCtl", sceNetBweGetInfoIpcInt);
|
||||
LIB_FUNCTION("GqETL5+INhU", "libSceNetBwe", 1, "libSceNetCtl", sceNetBweRegisterCallbackIpcInt);
|
||||
LIB_FUNCTION("mEUt-phGd5E", "libSceNetBwe", 1, "libSceNetCtl",
|
||||
sceNetBweSetInternetConnectionTestResultIpcInt);
|
||||
LIB_FUNCTION("pQLJV5SEAqk", "libSceNetBwe", 1, "libSceNetCtl",
|
||||
sceNetBweStartInternetConnectionTestBandwidthTestIpcInt);
|
||||
LIB_FUNCTION("c+aYh130SV0", "libSceNetBwe", 1, "libSceNetCtl",
|
||||
sceNetBweStartInternetConnectionTestIpcInt);
|
||||
LIB_FUNCTION("0lViPaTB-R8", "libSceNetBwe", 1, "libSceNetCtl",
|
||||
sceNetBweUnregisterCallbackIpcInt);
|
||||
LIB_FUNCTION("Jy1EO5GdlcM", "libSceNetCtlV6", 1, "libSceNetCtl", sceNetCtlGetInfoV6);
|
||||
LIB_FUNCTION("H5yARg37U5g", "libSceNetCtlV6", 1, "libSceNetCtl", sceNetCtlGetResultV6);
|
||||
LIB_FUNCTION("+lxqIKeU9UY", "libSceNetCtlV6", 1, "libSceNetCtl", sceNetCtlGetStateV6);
|
||||
LIB_FUNCTION("1NE9OWdBIww", "libSceNetCtlV6", 1, "libSceNetCtl", sceNetCtlRegisterCallbackV6);
|
||||
LIB_FUNCTION("hIUVeUNxAwc", "libSceNetCtlV6", 1, "libSceNetCtl", sceNetCtlUnregisterCallbackV6);
|
||||
LIB_FUNCTION("iQw3iQPhvUQ", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlCheckCallback);
|
||||
LIB_FUNCTION("UF6H6+kjyQs", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlCheckCallbackForLibIpcInt);
|
||||
LIB_FUNCTION("vv6g8zoanL4", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlClearEventForLibIpcInt);
|
||||
LIB_FUNCTION("8OJ86vFucfo", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlClearEventIpcInt);
|
||||
LIB_FUNCTION("HCD46HVTyQg", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlConnectConfIpcInt);
|
||||
LIB_FUNCTION("ID+Gq3Ddzbg", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlConnectIpcInt);
|
||||
LIB_FUNCTION("aPpic8K75YA", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlConnectWithRetryIpcInt);
|
||||
LIB_FUNCTION("9y4IcsJdTCc", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlDisableBandwidthManagementIpcInt);
|
||||
LIB_FUNCTION("qOefcpoSs0k", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlDisconnectIpcInt);
|
||||
LIB_FUNCTION("x9bSmRSE+hc", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlEnableBandwidthManagementIpcInt);
|
||||
LIB_FUNCTION("eCUIlA2t5CE", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetBandwidthInfoIpcInt);
|
||||
LIB_FUNCTION("2EfjPXVPk3s", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetEtherLinkMode);
|
||||
LIB_FUNCTION("teuK4QnJTGg", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetIfStat);
|
||||
LIB_FUNCTION("obuxdTiwkF8", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetInfo);
|
||||
LIB_FUNCTION("xstcTqAhTys", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetInfoIpcInt);
|
||||
LIB_FUNCTION("Jy1EO5GdlcM", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetInfoV6);
|
||||
LIB_FUNCTION("arAQRFlwqaA", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetInfoV6IpcInt);
|
||||
LIB_FUNCTION("JO4yuTuMoKI", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetNatInfo);
|
||||
LIB_FUNCTION("x+cnsAxKSHo", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetNatInfoIpcInt);
|
||||
LIB_FUNCTION("hhTsdv99azU", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlGetNetEvConfigInfoIpcInt);
|
||||
LIB_FUNCTION("0cBgduPRR+M", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetResult);
|
||||
LIB_FUNCTION("NEtnusbZyAs", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetResultIpcInt);
|
||||
LIB_FUNCTION("H5yARg37U5g", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetResultV6);
|
||||
LIB_FUNCTION("vdsTa93atXY", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetResultV6IpcInt);
|
||||
LIB_FUNCTION("wP0Ab2maR1Y", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlGetScanInfoBssidForSsidListScanIpcInt);
|
||||
LIB_FUNCTION("Wn-+887Lt2s", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetScanInfoBssidIpcInt);
|
||||
LIB_FUNCTION("FEdkOG1VbQo", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlGetScanInfoByBssidIpcInt);
|
||||
LIB_FUNCTION("irV8voIAHDw", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlGetScanInfoForSsidListScanIpcInt);
|
||||
LIB_FUNCTION("L97eAHI0xxs", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlGetScanInfoForSsidScanIpcInt);
|
||||
LIB_FUNCTION("uBPlr0lbuiI", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetState);
|
||||
LIB_FUNCTION("JXlI9EZVjf4", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetState2IpcInt);
|
||||
LIB_FUNCTION("gvnJPMkSoAY", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetStateIpcInt);
|
||||
LIB_FUNCTION("+lxqIKeU9UY", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetStateV6);
|
||||
LIB_FUNCTION("O8Fk4w5MWss", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetStateV6IpcInt);
|
||||
LIB_FUNCTION("BXW9b3R1Nw4", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlGetWifiType);
|
||||
LIB_FUNCTION("gky0+oaNM4k", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlInit);
|
||||
LIB_FUNCTION("YtAnCkTR0K4", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlIsBandwidthManagementEnabledIpcInt);
|
||||
LIB_FUNCTION("UJ+Z7Q+4ck0", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlRegisterCallback);
|
||||
LIB_FUNCTION("WRvDk2syatE", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlRegisterCallbackForLibIpcInt);
|
||||
LIB_FUNCTION("rqkh2kXvLSw", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlRegisterCallbackIpcInt);
|
||||
LIB_FUNCTION("1NE9OWdBIww", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlRegisterCallbackV6);
|
||||
LIB_FUNCTION("ipqlpcIqRsQ", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlRegisterCallbackV6IpcInt);
|
||||
LIB_FUNCTION("reIsHryCDx4", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlScanIpcInt);
|
||||
LIB_FUNCTION("LJYiiIS4HB0", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlSetErrorNotificationEnabledIpcInt);
|
||||
LIB_FUNCTION("DjuqqqV08Nk", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlSetStunWithPaddingFlagIpcInt);
|
||||
LIB_FUNCTION("Z4wwCFiBELQ", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlTerm);
|
||||
LIB_FUNCTION("Rqm2OnZMCz0", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlUnregisterCallback);
|
||||
LIB_FUNCTION("urWaUWkEGZg", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlUnregisterCallbackForLibIpcInt);
|
||||
LIB_FUNCTION("by9cbB7JGJE", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlUnregisterCallbackIpcInt);
|
||||
LIB_FUNCTION("hIUVeUNxAwc", "libSceNetCtl", 1, "libSceNetCtl", sceNetCtlUnregisterCallbackV6);
|
||||
LIB_FUNCTION("Hjxpy28aID8", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlUnregisterCallbackV6IpcInt);
|
||||
LIB_FUNCTION("1HSvkN9oxO4", "libSceNetCtl", 1, "libSceNetCtl",
|
||||
sceNetCtlUnsetStunWithPaddingFlagIpcInt);
|
||||
LIB_FUNCTION("2Ny2lzU3o9w", "libSceNetCtl", 1, "libSceNetCtl", Func_D8DCB6973537A3DC);
|
||||
LIB_FUNCTION("u5oqtlIP+Fw", "libSceNetCtlForNpToolkit", 1, "libSceNetCtl",
|
||||
sceNetCtlCheckCallbackForNpToolkit);
|
||||
LIB_FUNCTION("saYB0b2ZWtI", "libSceNetCtlForNpToolkit", 1, "libSceNetCtl",
|
||||
sceNetCtlClearEventForNpToolkit);
|
||||
LIB_FUNCTION("wIsKy+TfeLs", "libSceNetCtlForNpToolkit", 1, "libSceNetCtl",
|
||||
sceNetCtlRegisterCallbackForNpToolkit);
|
||||
LIB_FUNCTION("2oUqKR5odGc", "libSceNetCtlForNpToolkit", 1, "libSceNetCtl",
|
||||
sceNetCtlUnregisterCallbackForNpToolkit);
|
||||
LIB_FUNCTION("19Ec7WkMFfQ", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApCheckCallback);
|
||||
LIB_FUNCTION("meFMaDpdsVI", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApClearEvent);
|
||||
LIB_FUNCTION("hfkLVdXmfnU", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApGetConnectInfo);
|
||||
LIB_FUNCTION("LXADzTIzM9I", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApGetInfo);
|
||||
LIB_FUNCTION("4jkLJc954+Q", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApGetResult);
|
||||
LIB_FUNCTION("AKZOzsb9whc", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApGetState);
|
||||
LIB_FUNCTION("FdN+edNRtiw", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApInit);
|
||||
LIB_FUNCTION("pmjobSVHuY0", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApRegisterCallback);
|
||||
LIB_FUNCTION("r-pOyN6AhsM", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApStop);
|
||||
LIB_FUNCTION("cv5Y2efOTeg", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApTerm);
|
||||
LIB_FUNCTION("NpTcFtaQ-0E", "libSceNetCtlAp", 1, "libSceNetCtl", sceNetCtlApUnregisterCallback);
|
||||
LIB_FUNCTION("R-4a9Yh4tG8", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApAppInitWpaKey);
|
||||
LIB_FUNCTION("5oLJoOVBbGU", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApAppInitWpaKeyForQa);
|
||||
LIB_FUNCTION("YtTwZ3pa4aQ", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApAppStartWithRetry);
|
||||
LIB_FUNCTION("sgWeDrEt24U", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApAppStartWithRetryPid);
|
||||
LIB_FUNCTION("amqSGH8l--s", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRestart);
|
||||
LIB_FUNCTION("DufQZgH5ISc", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApRpCheckCallback);
|
||||
LIB_FUNCTION("qhZbOi+2qLY", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpClearEvent);
|
||||
LIB_FUNCTION("VQl16Q+qXeY", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpGetInfo);
|
||||
LIB_FUNCTION("3pxwYqHzGcw", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpGetResult);
|
||||
LIB_FUNCTION("LEn8FGztKWc", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpGetState);
|
||||
LIB_FUNCTION("ofGsK+xoAaM", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApRpRegisterCallback);
|
||||
LIB_FUNCTION("mjFgpqNavHg", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpStart);
|
||||
LIB_FUNCTION("HMvaHoZWsn8", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpStartConf);
|
||||
LIB_FUNCTION("9Dxg7XSlr2s", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApRpStartWithRetry);
|
||||
LIB_FUNCTION("6uvAl4RlEyk", "libSceNetCtlApIpcInt", 1, "libSceNetCtl", sceNetCtlApRpStop);
|
||||
LIB_FUNCTION("8eyH37Ns8tk", "libSceNetCtlApIpcInt", 1, "libSceNetCtl",
|
||||
sceNetCtlApRpUnregisterCallback);
|
||||
};
|
||||
|
||||
} // namespace Libraries::NetCtl
|
||||
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/net_ctl_obj.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::NetCtl {
|
||||
|
||||
constexpr int ORBIS_NET_ETHER_ADDR_LEN = 6;
|
||||
|
||||
struct OrbisNetEtherAddr {
|
||||
u8 data[ORBIS_NET_ETHER_ADDR_LEN];
|
||||
};
|
||||
|
||||
constexpr int ORBIS_NET_CTL_SSID_LEN = 32 + 1;
|
||||
constexpr int ORBIS_NET_CTL_HOSTNAME_LEN = 255 + 1;
|
||||
constexpr int ORBIS_NET_CTL_AUTH_NAME_LEN = 127 + 1;
|
||||
constexpr int ORBIS_NET_CTL_IPV4_ADDR_STR_LEN = 16;
|
||||
|
||||
union OrbisNetCtlInfo {
|
||||
u32 device;
|
||||
OrbisNetEtherAddr ether_addr;
|
||||
u32 mtu;
|
||||
u32 link;
|
||||
OrbisNetEtherAddr bssid;
|
||||
char ssid[ORBIS_NET_CTL_SSID_LEN];
|
||||
u32 wifi_security;
|
||||
u8 rssi_dbm;
|
||||
uint8_t rssi_percentage;
|
||||
u8 channel;
|
||||
u32 ip_config;
|
||||
char dhcp_hostname[ORBIS_NET_CTL_HOSTNAME_LEN];
|
||||
char pppoe_auth_name[ORBIS_NET_CTL_AUTH_NAME_LEN];
|
||||
char ip_address[ORBIS_NET_CTL_IPV4_ADDR_STR_LEN];
|
||||
char netmask[ORBIS_NET_CTL_IPV4_ADDR_STR_LEN];
|
||||
char default_route[ORBIS_NET_CTL_IPV4_ADDR_STR_LEN];
|
||||
char primary_dns[ORBIS_NET_CTL_IPV4_ADDR_STR_LEN];
|
||||
char secondary_dns[ORBIS_NET_CTL_IPV4_ADDR_STR_LEN];
|
||||
u32 http_proxy_config;
|
||||
char http_proxy_server[ORBIS_NET_CTL_HOSTNAME_LEN];
|
||||
u16 http_proxy_port;
|
||||
};
|
||||
|
||||
struct OrbisNetCtlNatInfo {
|
||||
u32 size;
|
||||
s32 stun_status;
|
||||
s32 nat_type;
|
||||
u32 mapped_addr;
|
||||
};
|
||||
static_assert(sizeof(OrbisNetCtlNatInfo) == 0x10);
|
||||
|
||||
// GetInfo codes
|
||||
constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1;
|
||||
constexpr int ORBIS_NET_CTL_INFO_ETHER_ADDR = 2;
|
||||
constexpr int ORBIS_NET_CTL_INFO_MTU = 3;
|
||||
constexpr int ORBIS_NET_CTL_INFO_LINK = 4;
|
||||
constexpr int ORBIS_NET_CTL_INFO_BSSID = 5;
|
||||
constexpr int ORBIS_NET_CTL_INFO_SSID = 6;
|
||||
constexpr int ORBIS_NET_CTL_INFO_WIFI_SECURITY = 7;
|
||||
constexpr int ORBIS_NET_CTL_INFO_RSSI_DBM = 8;
|
||||
constexpr int ORBIS_NET_CTL_INFO_RSSI_PERCENTAGE = 9;
|
||||
constexpr int ORBIS_NET_CTL_INFO_CHANNEL = 10;
|
||||
constexpr int ORBIS_NET_CTL_INFO_IP_CONFIG = 11;
|
||||
constexpr int ORBIS_NET_CTL_INFO_DHCP_HOSTNAME = 12;
|
||||
constexpr int ORBIS_NET_CTL_INFO_PPPOE_AUTH_NAME = 13;
|
||||
constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14;
|
||||
constexpr int ORBIS_NET_CTL_INFO_NETMASK = 15;
|
||||
constexpr int ORBIS_NET_CTL_INFO_DEFAULT_ROUTE = 16;
|
||||
constexpr int ORBIS_NET_CTL_INFO_PRIMARY_DNS = 17;
|
||||
constexpr int ORBIS_NET_CTL_INFO_SECONDARY_DNS = 18;
|
||||
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_CONFIG = 19;
|
||||
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_SERVER = 20;
|
||||
constexpr int ORBIS_NET_CTL_INFO_HTTP_PROXY_PORT = 21;
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweClearEventIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweFinishInternetConnectionTestIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweGetInfoIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweRegisterCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweSetInternetConnectionTestResultIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweStartInternetConnectionTestBandwidthTestIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweStartInternetConnectionTestIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweUnregisterCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoV6();
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultV6();
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateV6();
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackV6();
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6();
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallbackForLibIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventForLibIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlConnectConfIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlConnectIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlConnectWithRetryIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlDisableBandwidthManagementIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlDisconnectIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlEnableBandwidthManagementIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetBandwidthInfoIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetEtherLinkMode();
|
||||
int PS4_SYSV_ABI sceNetCtlGetIfStat();
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info);
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfoV6IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetNatInfo(OrbisNetCtlNatInfo* nat_info);
|
||||
int PS4_SYSV_ABI sceNetCtlGetNatInfoIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetNetEvConfigInfoIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetResult(int eventType, int* errorCode);
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetResultV6IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoBssidForSsidListScanIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoBssidIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoByBssidIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoForSsidListScanIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetScanInfoForSsidScanIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetState(int* state);
|
||||
int PS4_SYSV_ABI sceNetCtlGetState2IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetStateV6IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlGetWifiType();
|
||||
int PS4_SYSV_ABI sceNetCtlInit();
|
||||
int PS4_SYSV_ABI sceNetCtlIsBandwidthManagementEnabledIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallback(OrbisNetCtlCallback func, void* arg, int* cid);
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackForLibIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackV6IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlScanIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlSetErrorNotificationEnabledIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlSetStunWithPaddingFlagIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlTerm();
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackForLibIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6IpcInt();
|
||||
int PS4_SYSV_ABI sceNetCtlUnsetStunWithPaddingFlagIpcInt();
|
||||
int PS4_SYSV_ABI Func_D8DCB6973537A3DC();
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallbackForNpToolkit();
|
||||
int PS4_SYSV_ABI sceNetCtlClearEventForNpToolkit();
|
||||
int PS4_SYSV_ABI sceNetCtlRegisterCallbackForNpToolkit(OrbisNetCtlCallbackForNpToolkit func,
|
||||
void* arg, int* cid);
|
||||
int PS4_SYSV_ABI sceNetCtlUnregisterCallbackForNpToolkit();
|
||||
int PS4_SYSV_ABI sceNetCtlApCheckCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlApClearEvent();
|
||||
int PS4_SYSV_ABI sceNetCtlApGetConnectInfo();
|
||||
int PS4_SYSV_ABI sceNetCtlApGetInfo();
|
||||
int PS4_SYSV_ABI sceNetCtlApGetResult();
|
||||
int PS4_SYSV_ABI sceNetCtlApGetState();
|
||||
int PS4_SYSV_ABI sceNetCtlApInit();
|
||||
int PS4_SYSV_ABI sceNetCtlApRegisterCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlApStop();
|
||||
int PS4_SYSV_ABI sceNetCtlApTerm();
|
||||
int PS4_SYSV_ABI sceNetCtlApUnregisterCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlApAppInitWpaKey();
|
||||
int PS4_SYSV_ABI sceNetCtlApAppInitWpaKeyForQa();
|
||||
int PS4_SYSV_ABI sceNetCtlApAppStartWithRetry();
|
||||
int PS4_SYSV_ABI sceNetCtlApAppStartWithRetryPid();
|
||||
int PS4_SYSV_ABI sceNetCtlApRestart();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpCheckCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpClearEvent();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetInfo();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetResult();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpGetState();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpRegisterCallback();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStart();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStartConf();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStartWithRetry();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpStop();
|
||||
int PS4_SYSV_ABI sceNetCtlApRpUnregisterCallback();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::NetCtl
|
||||
@@ -0,0 +1,996 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
#include "sockets.h"
|
||||
// clang-format on
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/network/net_util.h"
|
||||
#include "net.h"
|
||||
#include "net_error.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u16 kSignalingVPortNbo = 0xffff;
|
||||
constexpr size_t kVPortHeaderSize = 4;
|
||||
constexpr size_t kMaxUdpPayload = 65507;
|
||||
constexpr size_t kMaxP2PPayload = kMaxUdpPayload - kVPortHeaderSize;
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr net_socket kInvalidSocket = INVALID_SOCKET;
|
||||
#else
|
||||
constexpr net_socket kInvalidSocket = -1;
|
||||
#endif
|
||||
|
||||
enum class InternalChannel {
|
||||
Signaling,
|
||||
Control,
|
||||
Matching2,
|
||||
};
|
||||
|
||||
struct QueuedPacket {
|
||||
u32 source_addr{};
|
||||
u16 source_port{};
|
||||
std::vector<u8> payload;
|
||||
};
|
||||
|
||||
int FailWith(int error) {
|
||||
*Libraries::Kernel::__Error() = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsValidSocket(net_socket socket) {
|
||||
return socket != kInvalidSocket;
|
||||
}
|
||||
|
||||
int LastSocketError() {
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsRetryableSocketError(int error) {
|
||||
#ifdef _WIN32
|
||||
return error == WSAEWOULDBLOCK || error == WSAEINTR;
|
||||
#else
|
||||
return error == EAGAIN || error == EWOULDBLOCK || error == EINTR;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CloseSocket(net_socket& socket) {
|
||||
if (!IsValidSocket(socket)) {
|
||||
return;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
::closesocket(socket);
|
||||
#else
|
||||
::close(socket);
|
||||
#endif
|
||||
socket = kInvalidSocket;
|
||||
}
|
||||
|
||||
bool SetSocketNonBlocking(net_socket socket) {
|
||||
#ifdef _WIN32
|
||||
u_long enabled = 1;
|
||||
return ::ioctlsocket(socket, FIONBIO, &enabled) == 0;
|
||||
#else
|
||||
int enabled = 1;
|
||||
return ::ioctl(socket, FIONBIO, &enabled) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool CreateReadinessPair(net_socket* read_socket, net_socket* signal_socket) {
|
||||
*read_socket = kInvalidSocket;
|
||||
*signal_socket = kInvalidSocket;
|
||||
|
||||
net_socket reader = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (!IsValidSocket(reader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_in address{};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = 0;
|
||||
if (::bind(reader, reinterpret_cast<const sockaddr*>(&address), sizeof(address)) < 0) {
|
||||
CloseSocket(reader);
|
||||
return false;
|
||||
}
|
||||
|
||||
socklen_t address_len = sizeof(address);
|
||||
if (::getsockname(reader, reinterpret_cast<sockaddr*>(&address), &address_len) < 0) {
|
||||
CloseSocket(reader);
|
||||
return false;
|
||||
}
|
||||
|
||||
net_socket signaler = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (!IsValidSocket(signaler) ||
|
||||
::connect(signaler, reinterpret_cast<const sockaddr*>(&address), sizeof(address)) < 0 ||
|
||||
!SetSocketNonBlocking(reader) || !SetSocketNonBlocking(signaler)) {
|
||||
CloseSocket(signaler);
|
||||
CloseSocket(reader);
|
||||
return false;
|
||||
}
|
||||
|
||||
*read_socket = reader;
|
||||
*signal_socket = signaler;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
class P2PTransport {
|
||||
public:
|
||||
static P2PTransport& Instance() {
|
||||
static P2PTransport transport;
|
||||
return transport;
|
||||
}
|
||||
|
||||
~P2PTransport() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool Start() {
|
||||
#if !defined(__linux__) && !defined(_WIN32)
|
||||
LOG_ERROR(Lib_Net, "P2P transport is currently supported on Linux and Windows");
|
||||
return false;
|
||||
#else
|
||||
std::lock_guard lock(start_mutex);
|
||||
if (running.load(std::memory_order_acquire)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
host_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (!IsValidSocket(host_socket)) {
|
||||
LOG_ERROR(Lib_Net, "P2P transport socket() failed error={}", LastSocketError());
|
||||
return false;
|
||||
}
|
||||
|
||||
int one = 1;
|
||||
(void)::setsockopt(host_socket, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char*>(&one), sizeof(one));
|
||||
if (!SetSocketNonBlocking(host_socket)) {
|
||||
LOG_ERROR(Lib_Net, "P2P transport failed to make socket nonblocking error={}",
|
||||
LastSocketError());
|
||||
CloseSocket(host_socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
u16 requested_port = 0;
|
||||
if (const char* env = std::getenv("SHADPS4_P2P_PORT"); env != nullptr && *env != '\0') {
|
||||
char* end = nullptr;
|
||||
const long parsed = std::strtol(env, &end, 10);
|
||||
if (end == env || *end != '\0' || parsed < 1 || parsed > 65535) {
|
||||
LOG_ERROR(Lib_Net, "Invalid SHADPS4_P2P_PORT='{}'", env);
|
||||
CloseSocket(host_socket);
|
||||
return false;
|
||||
}
|
||||
requested_port = static_cast<u16>(parsed);
|
||||
}
|
||||
|
||||
sockaddr_in bind_addr{};
|
||||
bind_addr.sin_family = AF_INET;
|
||||
bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
bind_addr.sin_port = htons(requested_port);
|
||||
|
||||
if (::bind(host_socket, reinterpret_cast<sockaddr*>(&bind_addr), sizeof(bind_addr)) < 0) {
|
||||
LOG_ERROR(Lib_Net, "P2P transport bind(port={}) failed error={}", requested_port,
|
||||
LastSocketError());
|
||||
CloseSocket(host_socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_in actual{};
|
||||
socklen_t actual_len = sizeof(actual);
|
||||
if (::getsockname(host_socket, reinterpret_cast<sockaddr*>(&actual), &actual_len) < 0) {
|
||||
LOG_ERROR(Lib_Net, "P2P transport getsockname() failed error={}", LastSocketError());
|
||||
CloseSocket(host_socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
physical_port = ntohs(actual.sin_port);
|
||||
running.store(true, std::memory_order_release);
|
||||
receive_thread = std::thread(&P2PTransport::ReceiveMain, this);
|
||||
|
||||
LOG_INFO(Lib_Net,
|
||||
"P2P transport ready: UDP physical_port={} (set SHADPS4_P2P_PORT to pin it)",
|
||||
physical_port);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
std::lock_guard lock(start_mutex);
|
||||
if (!running.exchange(false, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (receive_thread.joinable()) {
|
||||
receive_thread.join();
|
||||
}
|
||||
|
||||
CloseSocket(host_socket);
|
||||
physical_port = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsReady() const {
|
||||
return running.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
u16 PhysicalPort() const {
|
||||
return physical_port;
|
||||
}
|
||||
|
||||
bool RegisterSocket(P2PSocket* socket, u16 requested_vport_nbo, u16* actual_vport_nbo) {
|
||||
if (socket == nullptr || actual_vport_nbo == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard lock(registry_mutex);
|
||||
u16 vport = requested_vport_nbo;
|
||||
|
||||
// vport 0 acts as an ephemeral request in this PoC.
|
||||
if (vport == 0) {
|
||||
for (u32 attempt = 0; attempt < 35000; ++attempt) {
|
||||
if (next_ephemeral_vport >= 65535) {
|
||||
next_ephemeral_vport = 30000;
|
||||
}
|
||||
const u16 candidate_host = next_ephemeral_vport++;
|
||||
const u16 candidate_nbo = htons(candidate_host);
|
||||
if (candidate_nbo == kSignalingVPortNbo || game_sockets.contains(candidate_nbo)) {
|
||||
continue;
|
||||
}
|
||||
vport = candidate_nbo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vport == 0 || vport == kSignalingVPortNbo || game_sockets.contains(vport)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
game_sockets.emplace(vport, socket);
|
||||
*actual_vport_nbo = vport;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnregisterSocket(P2PSocket* socket, u16 vport_nbo) {
|
||||
std::lock_guard lock(registry_mutex);
|
||||
const auto it = game_sockets.find(vport_nbo);
|
||||
if (it != game_sockets.end() && it->second == socket) {
|
||||
game_sockets.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
int SendFrame(u16 source_vport_nbo, u16 dest_vport_nbo, const void* data, u32 len,
|
||||
u32 dest_addr_nbo, u16 dest_port_nbo) {
|
||||
#if !defined(__linux__) && !defined(_WIN32)
|
||||
return -1;
|
||||
#else
|
||||
if (!Start() || !IsValidSocket(host_socket) || (data == nullptr && len != 0) ||
|
||||
len > kMaxP2PPayload || dest_addr_nbo == 0 || dest_port_nbo == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<u8> framed(kVPortHeaderSize + len);
|
||||
std::memcpy(framed.data(), &source_vport_nbo, sizeof(source_vport_nbo));
|
||||
std::memcpy(framed.data() + sizeof(source_vport_nbo), &dest_vport_nbo,
|
||||
sizeof(dest_vport_nbo));
|
||||
if (len != 0) {
|
||||
std::memcpy(framed.data() + kVPortHeaderSize, data, len);
|
||||
}
|
||||
|
||||
sockaddr_in dest{};
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_addr.s_addr = dest_addr_nbo;
|
||||
dest.sin_port = dest_port_nbo;
|
||||
|
||||
const auto rc = ::sendto(host_socket, reinterpret_cast<const char*>(framed.data()),
|
||||
static_cast<int>(framed.size()), 0,
|
||||
reinterpret_cast<const sockaddr*>(&dest), sizeof(dest));
|
||||
if (rc < 0) {
|
||||
LOG_DEBUG(Lib_Net, "P2P sendto failed error={} dst={:#x}:{}", LastSocketError(),
|
||||
dest_addr_nbo, ntohs(dest_port_nbo));
|
||||
return -1;
|
||||
}
|
||||
if (static_cast<size_t>(rc) != framed.size()) {
|
||||
LOG_WARNING(Lib_Net, "P2P short UDP send: {} of {} bytes", rc, framed.size());
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SendInternal(const void* data, u32 len, u32 dest_addr_nbo, u16 dest_port_nbo) {
|
||||
return SendFrame(kSignalingVPortNbo, kSignalingVPortNbo, data, len, dest_addr_nbo,
|
||||
dest_port_nbo);
|
||||
}
|
||||
|
||||
int ReceiveInternal(InternalChannel channel, void* buf, u32 len, u32* from_addr,
|
||||
u16* from_port) {
|
||||
std::lock_guard lock(internal_mutex);
|
||||
auto& queue = QueueFor(channel);
|
||||
if (queue.empty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QueuedPacket packet = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
|
||||
const u32 copy_len = static_cast<u32>(std::min<size_t>(len, packet.payload.size()));
|
||||
if (copy_len != 0 && buf != nullptr) {
|
||||
std::memcpy(buf, packet.payload.data(), copy_len);
|
||||
}
|
||||
if (from_addr != nullptr) {
|
||||
*from_addr = packet.source_addr;
|
||||
}
|
||||
if (from_port != nullptr) {
|
||||
*from_port = packet.source_port;
|
||||
}
|
||||
return static_cast<int>(copy_len);
|
||||
}
|
||||
|
||||
private:
|
||||
P2PTransport() = default;
|
||||
|
||||
std::deque<QueuedPacket>& QueueFor(InternalChannel channel) {
|
||||
switch (channel) {
|
||||
case InternalChannel::Signaling:
|
||||
return signaling_queue;
|
||||
case InternalChannel::Control:
|
||||
return control_queue;
|
||||
case InternalChannel::Matching2:
|
||||
return matching2_queue;
|
||||
}
|
||||
return signaling_queue;
|
||||
}
|
||||
|
||||
static InternalChannel ClassifyInternal(const u8* payload, size_t size) {
|
||||
if (size >= 5 && std::memcmp(payload, "SHAD", 4) == 0) {
|
||||
if (payload[4] == 0x21) {
|
||||
return InternalChannel::Matching2;
|
||||
}
|
||||
if (payload[4] == 0x10) {
|
||||
return InternalChannel::Control;
|
||||
}
|
||||
}
|
||||
// STUN echo (6 bytes) and generic signaling 0x05/0x06/0x07 land here.
|
||||
return InternalChannel::Signaling;
|
||||
}
|
||||
|
||||
void PushInternal(InternalChannel channel, u32 from_addr, u16 from_port, const u8* payload,
|
||||
size_t size) {
|
||||
QueuedPacket packet{};
|
||||
packet.source_addr = from_addr;
|
||||
packet.source_port = from_port;
|
||||
packet.payload.assign(payload, payload + size);
|
||||
|
||||
std::lock_guard lock(internal_mutex);
|
||||
QueueFor(channel).push_back(std::move(packet));
|
||||
}
|
||||
|
||||
void ReceiveMain() {
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
std::vector<u8> buffer(65536);
|
||||
while (running.load(std::memory_order_acquire)) {
|
||||
sockaddr_in from{};
|
||||
socklen_t from_len = sizeof(from);
|
||||
#ifdef _WIN32
|
||||
constexpr int receive_flags = 0;
|
||||
#else
|
||||
constexpr int receive_flags = MSG_DONTWAIT;
|
||||
#endif
|
||||
const auto rc = ::recvfrom(host_socket, reinterpret_cast<char*>(buffer.data()),
|
||||
static_cast<int>(buffer.size()), receive_flags,
|
||||
reinterpret_cast<sockaddr*>(&from), &from_len);
|
||||
|
||||
if (rc < 0) {
|
||||
const int error = LastSocketError();
|
||||
if (IsRetryableSocketError(error)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
continue;
|
||||
}
|
||||
LOG_WARNING(Lib_Net, "P2P recvfrom failed error={}", error);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc < static_cast<decltype(rc)>(kVPortHeaderSize)) {
|
||||
LOG_DEBUG(Lib_Net, "P2P dropping too-short UDP frame size={}", rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
u16 source_vport_nbo = 0;
|
||||
u16 dest_vport_nbo = 0;
|
||||
std::memcpy(&source_vport_nbo, buffer.data(), sizeof(source_vport_nbo));
|
||||
std::memcpy(&dest_vport_nbo, buffer.data() + sizeof(source_vport_nbo),
|
||||
sizeof(dest_vport_nbo));
|
||||
|
||||
const u8* payload = buffer.data() + kVPortHeaderSize;
|
||||
const size_t payload_size = static_cast<size_t>(rc) - kVPortHeaderSize;
|
||||
|
||||
if (dest_vport_nbo == kSignalingVPortNbo) {
|
||||
PushInternal(ClassifyInternal(payload, payload_size), from.sin_addr.s_addr,
|
||||
from.sin_port, payload, payload_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard lock(registry_mutex);
|
||||
const auto it = game_sockets.find(dest_vport_nbo);
|
||||
if (it == game_sockets.end() || it->second == nullptr) {
|
||||
LOG_TRACE(Lib_Net, "P2P RX no socket for dst_vport={}", ntohs(dest_vport_nbo));
|
||||
continue;
|
||||
}
|
||||
|
||||
P2PDatagram packet{};
|
||||
packet.source_addr = from.sin_addr.s_addr;
|
||||
packet.source_port = from.sin_port;
|
||||
packet.source_vport = source_vport_nbo;
|
||||
packet.payload.assign(payload, payload + payload_size);
|
||||
it->second->EnqueuePacket(std::move(packet));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mutable std::mutex start_mutex;
|
||||
std::atomic<bool> running{false};
|
||||
std::thread receive_thread;
|
||||
|
||||
net_socket host_socket{kInvalidSocket};
|
||||
u16 physical_port{0};
|
||||
|
||||
std::mutex registry_mutex;
|
||||
std::unordered_map<u16, P2PSocket*> game_sockets;
|
||||
u16 next_ephemeral_vport{30000};
|
||||
|
||||
std::mutex internal_mutex;
|
||||
std::deque<QueuedPacket> signaling_queue;
|
||||
std::deque<QueuedPacket> control_queue;
|
||||
std::deque<QueuedPacket> matching2_queue;
|
||||
};
|
||||
|
||||
int ReadIntegerOption(const void* optval, u32 optlen, int* out) {
|
||||
if (optval == nullptr || out == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (optlen < sizeof(int)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
std::memcpy(out, optval, sizeof(int));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WriteIntegerOption(void* optval, u32* optlen, int value) {
|
||||
if (optval == nullptr || optlen == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (*optlen < sizeof(int)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
std::memcpy(optval, &value, sizeof(int));
|
||||
*optlen = sizeof(int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
P2PSocket::P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {
|
||||
#ifdef _WIN32
|
||||
if (!CreateReadinessPair(&readiness_fd, &readiness_signal_fd)) {
|
||||
LOG_ERROR(Lib_Net, "P2P readiness socket pair failed error={}", LastSocketError());
|
||||
valid = false;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
readiness_fd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
if (readiness_fd < 0) {
|
||||
LOG_ERROR(Lib_Net, "P2P eventfd() failed errno={}", errno);
|
||||
valid = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
P2PSocket::~P2PSocket() {
|
||||
Close();
|
||||
}
|
||||
|
||||
int P2PSocket::Close() {
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (!valid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bound) {
|
||||
P2PTransport::Instance().UnregisterSocket(this, local_addr.sin_vport);
|
||||
bound = false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard receive_lock(receive_mutex);
|
||||
valid = false;
|
||||
}
|
||||
connected = false;
|
||||
receive_cv.notify_all();
|
||||
|
||||
#ifdef _WIN32
|
||||
CloseSocket(readiness_signal_fd);
|
||||
CloseSocket(readiness_fd);
|
||||
#elif defined(__linux__)
|
||||
if (readiness_fd >= 0) {
|
||||
::close(readiness_fd);
|
||||
readiness_fd = -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
|
||||
if (level != ORBIS_NET_SOL_SOCKET) {
|
||||
LOG_WARNING(Lib_Net, "P2P setsockopt ignoring non-SOL_SOCKET level={} opt={}", level,
|
||||
optname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int value = 0;
|
||||
switch (optname) {
|
||||
case ORBIS_NET_SO_NBIO:
|
||||
case ORBIS_NET_SO_USECRYPTO:
|
||||
case ORBIS_NET_SO_USESIGNATURE:
|
||||
case ORBIS_NET_SO_REUSEADDR:
|
||||
case ORBIS_NET_SO_REUSEPORT:
|
||||
case ORBIS_NET_SO_KEEPALIVE:
|
||||
case ORBIS_NET_SO_BROADCAST:
|
||||
case ORBIS_NET_SO_ONESBCAST:
|
||||
case ORBIS_NET_SO_SNDBUF:
|
||||
case ORBIS_NET_SO_RCVBUF:
|
||||
case ORBIS_NET_SO_PRIORITY:
|
||||
if (ReadIntegerOption(optval, optlen, &value) < 0) {
|
||||
return -1;
|
||||
}
|
||||
integer_socket_options[optname] = value;
|
||||
if (optname == ORBIS_NET_SO_NBIO) {
|
||||
std::lock_guard receive_lock(receive_mutex);
|
||||
sockopt_so_nbio = value;
|
||||
} else if (optname == ORBIS_NET_SO_USECRYPTO) {
|
||||
sockopt_so_usecrypto = value;
|
||||
} else if (optname == ORBIS_NET_SO_USESIGNATURE) {
|
||||
sockopt_so_usesignature = value;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
// For the first emulator-to-emulator PoC, remember that an option was requested
|
||||
// but do not fail the title merely because the Sony-specific behavior is absent.
|
||||
LOG_WARNING(Lib_Net, "P2P setsockopt opt={:#x} accepted as no-op", optname);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
|
||||
if (level != ORBIS_NET_SOL_SOCKET) {
|
||||
return FailWith(ORBIS_NET_ENOPROTOOPT);
|
||||
}
|
||||
|
||||
switch (optname) {
|
||||
case ORBIS_NET_SO_NBIO:
|
||||
return WriteIntegerOption(optval, optlen, sockopt_so_nbio);
|
||||
case ORBIS_NET_SO_USECRYPTO:
|
||||
return WriteIntegerOption(optval, optlen, sockopt_so_usecrypto);
|
||||
case ORBIS_NET_SO_USESIGNATURE:
|
||||
return WriteIntegerOption(optval, optlen, sockopt_so_usesignature);
|
||||
case ORBIS_NET_SO_TYPE:
|
||||
return WriteIntegerOption(optval, optlen, socket_type);
|
||||
case ORBIS_NET_SO_ERROR:
|
||||
case ORBIS_NET_SO_ERROR_EX:
|
||||
return WriteIntegerOption(optval, optlen, 0);
|
||||
default:
|
||||
if (const auto it = integer_socket_options.find(optname);
|
||||
it != integer_socket_options.end()) {
|
||||
return WriteIntegerOption(optval, optlen, it->second);
|
||||
}
|
||||
return FailWith(ORBIS_NET_ENOPROTOOPT);
|
||||
}
|
||||
}
|
||||
|
||||
int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
if (socket_type != ORBIS_NET_SOCK_DGRAM_P2P) {
|
||||
return FailWith(ORBIS_NET_EOPNOTSUPP);
|
||||
}
|
||||
if (addr == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (addrlen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
const auto* in = reinterpret_cast<const OrbisNetSockaddrIn*>(addr);
|
||||
if (in->sin_family != ORBIS_NET_AF_INET) {
|
||||
return FailWith(ORBIS_NET_EAFNOSUPPORT);
|
||||
}
|
||||
if (!EnsureP2PTransport()) {
|
||||
return FailWith(ORBIS_NET_ENETDOWN);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (!valid) {
|
||||
return FailWith(ORBIS_NET_EBADF);
|
||||
}
|
||||
if (bound) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
u16 actual_vport = 0;
|
||||
if (!P2PTransport::Instance().RegisterSocket(this, in->sin_vport, &actual_vport)) {
|
||||
return FailWith(ORBIS_NET_EADDRINUSE);
|
||||
}
|
||||
|
||||
local_addr = *in;
|
||||
local_addr.sin_len = sizeof(OrbisNetSockaddrIn);
|
||||
local_addr.sin_family = ORBIS_NET_AF_INET;
|
||||
local_addr.sin_port = htons(P2PTransport::Instance().PhysicalPort());
|
||||
local_addr.sin_vport = actual_vport;
|
||||
bound = true;
|
||||
|
||||
LOG_INFO(Lib_Net, "P2P bind: physical_port={} virtual_port={}",
|
||||
P2PTransport::Instance().PhysicalPort(), ntohs(local_addr.sin_vport));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int P2PSocket::Listen(int backlog) {
|
||||
LOG_WARNING(Lib_Net, "P2P STREAM listen not implemented (backlog={})", backlog);
|
||||
return FailWith(ORBIS_NET_EOPNOTSUPP);
|
||||
}
|
||||
|
||||
int P2PSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) {
|
||||
if (msg == nullptr || msg->msg_iovlen < 0) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < msg->msg_iovlen; ++i) {
|
||||
total += static_cast<size_t>(msg->msg_iov[i].iov_len);
|
||||
if (total > kMaxP2PPayload || total > std::numeric_limits<u32>::max()) {
|
||||
return FailWith(ORBIS_NET_EMSGSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> payload(total);
|
||||
size_t offset = 0;
|
||||
for (int i = 0; i < msg->msg_iovlen; ++i) {
|
||||
const auto len = static_cast<size_t>(msg->msg_iov[i].iov_len);
|
||||
if (len != 0) {
|
||||
std::memcpy(payload.data() + offset, msg->msg_iov[i].iov_base, len);
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return SendPacket(payload.data(), static_cast<u32>(payload.size()), flags,
|
||||
reinterpret_cast<const OrbisNetSockaddr*>(msg->msg_name), msg->msg_namelen);
|
||||
}
|
||||
|
||||
int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) {
|
||||
if (socket_type != ORBIS_NET_SOCK_DGRAM_P2P) {
|
||||
return FailWith(ORBIS_NET_EOPNOTSUPP);
|
||||
}
|
||||
if (msg == nullptr && len != 0) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (len > kMaxP2PPayload) {
|
||||
return FailWith(ORBIS_NET_EMSGSIZE);
|
||||
}
|
||||
if (!EnsureP2PTransport()) {
|
||||
return FailWith(ORBIS_NET_ENETDOWN);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (!valid) {
|
||||
return FailWith(ORBIS_NET_EBADF);
|
||||
}
|
||||
|
||||
// UDP permits implicit local binding. Give an unbound guest socket an ephemeral vport.
|
||||
if (!bound) {
|
||||
u16 actual_vport = 0;
|
||||
if (!P2PTransport::Instance().RegisterSocket(this, 0, &actual_vport)) {
|
||||
return FailWith(ORBIS_NET_EADDRINUSE);
|
||||
}
|
||||
local_addr = {};
|
||||
local_addr.sin_len = sizeof(local_addr);
|
||||
local_addr.sin_family = ORBIS_NET_AF_INET;
|
||||
local_addr.sin_port = htons(P2PTransport::Instance().PhysicalPort());
|
||||
local_addr.sin_vport = actual_vport;
|
||||
bound = true;
|
||||
}
|
||||
|
||||
OrbisNetSockaddrIn target{};
|
||||
if (to != nullptr) {
|
||||
if (tolen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
target = *reinterpret_cast<const OrbisNetSockaddrIn*>(to);
|
||||
if (target.sin_family != ORBIS_NET_AF_INET) {
|
||||
return FailWith(ORBIS_NET_EAFNOSUPPORT);
|
||||
}
|
||||
} else {
|
||||
if (!connected) {
|
||||
return FailWith(ORBIS_NET_ENOTCONN);
|
||||
}
|
||||
target = peer_addr;
|
||||
}
|
||||
|
||||
const int rc = P2PTransport::Instance().SendFrame(local_addr.sin_vport, target.sin_vport, msg,
|
||||
len, target.sin_addr, target.sin_port);
|
||||
if (rc < 0) {
|
||||
return FailWith(ORBIS_NET_EHOSTUNREACH);
|
||||
}
|
||||
|
||||
LOG_TRACE(Lib_Net, "P2P TX {} bytes vport {} -> {} dst={:#x}:{}", len,
|
||||
ntohs(local_addr.sin_vport), ntohs(target.sin_vport), target.sin_addr,
|
||||
ntohs(target.sin_port));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int P2PSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) {
|
||||
if (msg == nullptr || msg->msg_iovlen < 0) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
size_t capacity = 0;
|
||||
for (int i = 0; i < msg->msg_iovlen; ++i) {
|
||||
capacity += static_cast<size_t>(msg->msg_iov[i].iov_len);
|
||||
if (capacity > std::numeric_limits<u32>::max()) {
|
||||
return FailWith(ORBIS_NET_EMSGSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> payload(capacity);
|
||||
u32 name_len = msg->msg_namelen;
|
||||
const int rc = ReceivePacket(payload.data(), static_cast<u32>(payload.size()), flags,
|
||||
reinterpret_cast<OrbisNetSockaddr*>(msg->msg_name),
|
||||
msg->msg_name != nullptr ? &name_len : nullptr);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
msg->msg_namelen = name_len;
|
||||
msg->msg_flags = 0;
|
||||
|
||||
size_t remaining = static_cast<size_t>(rc);
|
||||
size_t offset = 0;
|
||||
for (int i = 0; i < msg->msg_iovlen && remaining != 0; ++i) {
|
||||
const size_t amount =
|
||||
std::min<size_t>(remaining, static_cast<size_t>(msg->msg_iov[i].iov_len));
|
||||
if (amount != 0) {
|
||||
std::memcpy(msg->msg_iov[i].iov_base, payload.data() + offset, amount);
|
||||
}
|
||||
offset += amount;
|
||||
remaining -= amount;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) {
|
||||
if (buf == nullptr && len != 0) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
|
||||
std::unique_lock lock(receive_mutex);
|
||||
while (receive_queue.empty() && valid) {
|
||||
if (sockopt_so_nbio != 0 || (flags & ORBIS_NET_MSG_DONTWAIT) != 0) {
|
||||
return FailWith(ORBIS_NET_EWOULDBLOCK);
|
||||
}
|
||||
receive_cv.wait(lock);
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return FailWith(ORBIS_NET_EBADF);
|
||||
}
|
||||
|
||||
const P2PDatagram& packet = receive_queue.front();
|
||||
const u32 amount = static_cast<u32>(std::min<size_t>(len, packet.payload.size()));
|
||||
if (amount != 0) {
|
||||
std::memcpy(buf, packet.payload.data(), amount);
|
||||
}
|
||||
|
||||
if (from != nullptr && fromlen != nullptr) {
|
||||
if (*fromlen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
auto* out = reinterpret_cast<OrbisNetSockaddrIn*>(from);
|
||||
*out = {};
|
||||
out->sin_len = sizeof(OrbisNetSockaddrIn);
|
||||
out->sin_family = ORBIS_NET_AF_INET;
|
||||
out->sin_addr = packet.source_addr;
|
||||
out->sin_port = packet.source_port;
|
||||
out->sin_vport = packet.source_vport;
|
||||
*fromlen = sizeof(OrbisNetSockaddrIn);
|
||||
}
|
||||
|
||||
const bool peek = (flags & ORBIS_NET_MSG_PEEK) != 0;
|
||||
const bool peek_len =
|
||||
(flags & ORBIS_NET_MSG_PEEKLEN) == static_cast<int>(ORBIS_NET_MSG_PEEKLEN);
|
||||
const int result =
|
||||
peek_len ? static_cast<int>(packet.payload.size()) : static_cast<int>(amount);
|
||||
|
||||
if (!peek) {
|
||||
receive_queue.pop_front();
|
||||
#ifdef _WIN32
|
||||
if (receive_queue.empty() && IsValidSocket(readiness_fd)) {
|
||||
char signal = 0;
|
||||
(void)::recv(readiness_fd, &signal, sizeof(signal), 0);
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (receive_queue.empty() && readiness_fd >= 0) {
|
||||
u64 event_count = 0;
|
||||
(void)::read(readiness_fd, &event_count, sizeof(event_count));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
|
||||
LOG_WARNING(Lib_Net, "P2P STREAM accept not implemented");
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EOPNOTSUPP;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
|
||||
if (socket_type != ORBIS_NET_SOCK_DGRAM_P2P) {
|
||||
return FailWith(ORBIS_NET_EOPNOTSUPP);
|
||||
}
|
||||
if (addr == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (namelen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
const auto* in = reinterpret_cast<const OrbisNetSockaddrIn*>(addr);
|
||||
if (in->sin_family != ORBIS_NET_AF_INET) {
|
||||
return FailWith(ORBIS_NET_EAFNOSUPPORT);
|
||||
}
|
||||
if (!EnsureP2PTransport()) {
|
||||
return FailWith(ORBIS_NET_ENETDOWN);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_mutex);
|
||||
peer_addr = *in;
|
||||
connected = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
|
||||
if (name == nullptr || namelen == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (*namelen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
|
||||
auto out = local_addr;
|
||||
out.sin_len = sizeof(out);
|
||||
out.sin_family = ORBIS_NET_AF_INET;
|
||||
if (!bound && EnsureP2PTransport()) {
|
||||
out.sin_port = htons(GetP2PConfiguredPort());
|
||||
}
|
||||
std::memcpy(name, &out, sizeof(out));
|
||||
*namelen = sizeof(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int P2PSocket::GetPeerName(OrbisNetSockaddr* addr, u32* namelen) {
|
||||
if (!connected) {
|
||||
return FailWith(ORBIS_NET_ENOTCONN);
|
||||
}
|
||||
if (addr == nullptr || namelen == nullptr) {
|
||||
return FailWith(ORBIS_NET_EFAULT);
|
||||
}
|
||||
if (*namelen < sizeof(OrbisNetSockaddrIn)) {
|
||||
return FailWith(ORBIS_NET_EINVAL);
|
||||
}
|
||||
std::memcpy(addr, &peer_addr, sizeof(peer_addr));
|
||||
*namelen = sizeof(peer_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int P2PSocket::fstat(Libraries::Kernel::OrbisKernelStat* stat) {
|
||||
LOG_DEBUG(Lib_Net, "P2P fstat called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void P2PSocket::EnqueuePacket(P2PDatagram packet) {
|
||||
std::lock_guard lock(receive_mutex);
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool was_empty = receive_queue.empty();
|
||||
LOG_TRACE(Lib_Net, "P2P RX {} bytes vport {} <- {} src={:#x}:{}", packet.payload.size(),
|
||||
ntohs(local_addr.sin_vport), ntohs(packet.source_vport), packet.source_addr,
|
||||
ntohs(packet.source_port));
|
||||
receive_queue.push_back(std::move(packet));
|
||||
|
||||
#ifdef _WIN32
|
||||
if (was_empty && IsValidSocket(readiness_signal_fd)) {
|
||||
constexpr char signal = 1;
|
||||
if (::send(readiness_signal_fd, &signal, sizeof(signal), 0) < 0) {
|
||||
LOG_WARNING(Lib_Net, "P2P readiness signal failed error={}", LastSocketError());
|
||||
}
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (was_empty && readiness_fd >= 0) {
|
||||
const u64 one = 1;
|
||||
(void)::write(readiness_fd, &one, sizeof(one));
|
||||
}
|
||||
#endif
|
||||
receive_cv.notify_all();
|
||||
}
|
||||
|
||||
u16 GetP2PConfiguredPort() {
|
||||
return P2PTransport::Instance().PhysicalPort();
|
||||
}
|
||||
|
||||
u32 GetP2PAdvertisedAddr() {
|
||||
auto* netinfo = Common::Singleton<NetUtil::NetUtilInternal>::Instance();
|
||||
return netinfo->GetExternalIp();
|
||||
}
|
||||
|
||||
bool EnsureP2PTransport() {
|
||||
return P2PTransport::Instance().Start();
|
||||
}
|
||||
|
||||
bool P2PTransportIsReady() {
|
||||
return P2PTransport::Instance().IsReady();
|
||||
}
|
||||
|
||||
int P2PSignalingSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
|
||||
return P2PTransport::Instance().SendInternal(data, len, dest_addr, dest_port);
|
||||
}
|
||||
|
||||
int P2PSignalingRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
|
||||
return P2PTransport::Instance().ReceiveInternal(InternalChannel::Signaling, buf, len, from_addr,
|
||||
from_port);
|
||||
}
|
||||
|
||||
int P2PControlSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
|
||||
return P2PTransport::Instance().SendInternal(data, len, dest_addr, dest_port);
|
||||
}
|
||||
|
||||
int P2PControlRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
|
||||
return P2PTransport::Instance().ReceiveInternal(InternalChannel::Control, buf, len, from_addr,
|
||||
from_port);
|
||||
}
|
||||
|
||||
int P2PMatching2SendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
|
||||
return P2PTransport::Instance().SendInternal(data, len, dest_addr, dest_port);
|
||||
}
|
||||
|
||||
int P2PMatching2RecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
|
||||
return P2PTransport::Instance().ReceiveInternal(InternalChannel::Matching2, buf, len, from_addr,
|
||||
from_port);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,712 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <vector>
|
||||
#include <common/assert.h>
|
||||
#include "common/error.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "net.h"
|
||||
#ifndef _WIN32
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#include "net_error.h"
|
||||
#include "sockets.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ERROR_CASE(errname) \
|
||||
case (WSA##errname): \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_##errname; \
|
||||
return -1;
|
||||
#else
|
||||
#define ERROR_CASE(errname) \
|
||||
case (errname): \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_##errname; \
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
static int ConvertReturnErrorCode(int retval) {
|
||||
if (retval < 0) {
|
||||
#ifdef _WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
#else
|
||||
switch (errno) {
|
||||
#endif
|
||||
#ifndef _WIN32 // These errorcodes don't exist in WinSock
|
||||
ERROR_CASE(EPERM)
|
||||
ERROR_CASE(ENOENT)
|
||||
// ERROR_CASE(ESRCH)
|
||||
// ERROR_CASE(EIO)
|
||||
// ERROR_CASE(ENXIO)
|
||||
// ERROR_CASE(E2BIG)
|
||||
// ERROR_CASE(ENOEXEC)
|
||||
// ERROR_CASE(EDEADLK)
|
||||
ERROR_CASE(ENOMEM)
|
||||
// ERROR_CASE(ECHILD)
|
||||
// ERROR_CASE(EBUSY)
|
||||
ERROR_CASE(EEXIST)
|
||||
// ERROR_CASE(EXDEV)
|
||||
ERROR_CASE(ENODEV)
|
||||
// ERROR_CASE(ENOTDIR)
|
||||
// ERROR_CASE(EISDIR)
|
||||
ERROR_CASE(ENFILE)
|
||||
// ERROR_CASE(ENOTTY)
|
||||
// ERROR_CASE(ETXTBSY)
|
||||
// ERROR_CASE(EFBIG)
|
||||
ERROR_CASE(ENOSPC)
|
||||
// ERROR_CASE(ESPIPE)
|
||||
// ERROR_CASE(EROFS)
|
||||
// ERROR_CASE(EMLINK)
|
||||
ERROR_CASE(EPIPE)
|
||||
// ERROR_CASE(EDOM)
|
||||
// ERROR_CASE(ERANGE)
|
||||
// ERROR_CASE(ENOLCK)
|
||||
// ERROR_CASE(ENOSYS)
|
||||
// ERROR_CASE(EIDRM)
|
||||
// ERROR_CASE(EOVERFLOW)
|
||||
// ERROR_CASE(EILSEQ)
|
||||
// ERROR_CASE(ENOTSUP)
|
||||
ERROR_CASE(ECANCELED)
|
||||
// ERROR_CASE(EBADMSG)
|
||||
ERROR_CASE(ENODATA)
|
||||
// ERROR_CASE(ENOSR)
|
||||
// ERROR_CASE(ENOSTR)
|
||||
// ERROR_CASE(ETIME)
|
||||
#endif
|
||||
ERROR_CASE(EINTR)
|
||||
ERROR_CASE(EBADF)
|
||||
ERROR_CASE(EACCES)
|
||||
ERROR_CASE(EFAULT)
|
||||
ERROR_CASE(EINVAL)
|
||||
ERROR_CASE(EMFILE)
|
||||
ERROR_CASE(EWOULDBLOCK)
|
||||
ERROR_CASE(EINPROGRESS)
|
||||
ERROR_CASE(EALREADY)
|
||||
ERROR_CASE(ENOTSOCK)
|
||||
ERROR_CASE(EDESTADDRREQ)
|
||||
ERROR_CASE(EMSGSIZE)
|
||||
ERROR_CASE(EPROTOTYPE)
|
||||
ERROR_CASE(ENOPROTOOPT)
|
||||
ERROR_CASE(EPROTONOSUPPORT)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
ERROR_CASE(EOPNOTSUPP)
|
||||
#endif
|
||||
ERROR_CASE(EAFNOSUPPORT)
|
||||
ERROR_CASE(EADDRINUSE)
|
||||
ERROR_CASE(EADDRNOTAVAIL)
|
||||
ERROR_CASE(ENETDOWN)
|
||||
ERROR_CASE(ENETUNREACH)
|
||||
ERROR_CASE(ENETRESET)
|
||||
ERROR_CASE(ECONNABORTED)
|
||||
ERROR_CASE(ECONNRESET)
|
||||
ERROR_CASE(ENOBUFS)
|
||||
ERROR_CASE(EISCONN)
|
||||
ERROR_CASE(ENOTCONN)
|
||||
ERROR_CASE(ETIMEDOUT)
|
||||
ERROR_CASE(ECONNREFUSED)
|
||||
ERROR_CASE(ELOOP)
|
||||
ERROR_CASE(ENAMETOOLONG)
|
||||
ERROR_CASE(EHOSTUNREACH)
|
||||
ERROR_CASE(ENOTEMPTY)
|
||||
}
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINTERNAL;
|
||||
return -1;
|
||||
}
|
||||
// if it is 0 or positive return it as it is
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ConvertLevels(int level) {
|
||||
switch (level) {
|
||||
case ORBIS_NET_SOL_SOCKET:
|
||||
return SOL_SOCKET;
|
||||
case ORBIS_NET_IPPROTO_IP:
|
||||
return IPPROTO_IP;
|
||||
case ORBIS_NET_IPPROTO_TCP:
|
||||
return IPPROTO_TCP;
|
||||
case ORBIS_NET_IPPROTO_IPV6:
|
||||
return IPPROTO_IPV6;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst) {
|
||||
if (src == nullptr || dst == nullptr)
|
||||
return;
|
||||
memset(dst, 0, sizeof(sockaddr));
|
||||
const OrbisNetSockaddrIn* src_in = (const OrbisNetSockaddrIn*)src;
|
||||
sockaddr_in* dst_in = (sockaddr_in*)dst;
|
||||
dst_in->sin_family = src_in->sin_family;
|
||||
dst_in->sin_port = src_in->sin_port;
|
||||
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
|
||||
}
|
||||
|
||||
static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) {
|
||||
if (src == nullptr || dst == nullptr)
|
||||
return;
|
||||
memset(dst, 0, sizeof(OrbisNetSockaddr));
|
||||
OrbisNetSockaddrIn* dst_in = (OrbisNetSockaddrIn*)dst;
|
||||
sockaddr_in* src_in = (sockaddr_in*)src;
|
||||
dst_in->sin_family = static_cast<unsigned char>(src_in->sin_family);
|
||||
dst_in->sin_port = src_in->sin_port;
|
||||
memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4);
|
||||
}
|
||||
|
||||
bool PosixSocket::IsValid() const {
|
||||
#ifdef _WIN32
|
||||
return sock != INVALID_SOCKET;
|
||||
#else
|
||||
return sock != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int PosixSocket::Close() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
#ifdef _WIN32
|
||||
auto out = closesocket(sock);
|
||||
#else
|
||||
auto out = ::close(sock);
|
||||
#endif
|
||||
return ConvertReturnErrorCode(out);
|
||||
}
|
||||
|
||||
int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr addr2;
|
||||
convertOrbisNetSockaddrToPosix(addr, &addr2);
|
||||
return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in)));
|
||||
}
|
||||
|
||||
int PosixSocket::Listen(int backlog) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return ConvertReturnErrorCode(::listen(sock, backlog));
|
||||
}
|
||||
|
||||
static int convertOrbisFlagsToPosix(int sock_type, int sce_flags) {
|
||||
int posix_flags = 0;
|
||||
|
||||
if (sce_flags & ORBIS_NET_MSG_PEEK)
|
||||
posix_flags |= MSG_PEEK;
|
||||
#ifndef _WIN32
|
||||
if (sce_flags & ORBIS_NET_MSG_DONTWAIT)
|
||||
posix_flags |= MSG_DONTWAIT;
|
||||
#endif
|
||||
// MSG_WAITALL is only valid for stream sockets
|
||||
if ((sce_flags & ORBIS_NET_MSG_WAITALL) &&
|
||||
((sock_type == ORBIS_NET_SOCK_STREAM) || (sock_type == ORBIS_NET_SOCK_STREAM_P2P)))
|
||||
posix_flags |= MSG_WAITALL;
|
||||
|
||||
return posix_flags;
|
||||
}
|
||||
|
||||
// On Windows, MSG_DONTWAIT is not handled natively by recv/send.
|
||||
// This function uses select() with zero timeout to simulate non-blocking behavior.
|
||||
static int socket_is_ready(int sock, bool is_read = true) {
|
||||
fd_set fds{};
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
timeval timeout{0, 0};
|
||||
int res =
|
||||
select(sock + 1, is_read ? &fds : nullptr, is_read ? nullptr : &fds, nullptr, &timeout);
|
||||
if (res == 0) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EWOULDBLOCK;
|
||||
return -1;
|
||||
} else if (res < 0) {
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int PosixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
#ifdef _WIN32
|
||||
int totalSent = 0;
|
||||
bool waitAll = (flags & ORBIS_NET_MSG_WAITALL) != 0;
|
||||
bool dontWait = (flags & ORBIS_NET_MSG_DONTWAIT) != 0;
|
||||
|
||||
// stream socket with multiple buffers
|
||||
bool use_wsamsg =
|
||||
(socket_type == ORBIS_NET_SOCK_STREAM || socket_type == ORBIS_NET_SOCK_STREAM_P2P) &&
|
||||
msg->msg_iovlen > 1;
|
||||
|
||||
for (int i = 0; i < msg->msg_iovlen; ++i) {
|
||||
char* buf = (char*)msg->msg_iov[i].iov_base;
|
||||
size_t remaining = msg->msg_iov[i].iov_len;
|
||||
|
||||
while (remaining > 0) {
|
||||
if (dontWait) {
|
||||
int ready = socket_is_ready(sock, false);
|
||||
if (ready <= 0)
|
||||
return ready;
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
if (use_wsamsg) {
|
||||
// only call WSASendMsg if we have multiple buffers
|
||||
LPFN_WSASENDMSG wsasendmsg = nullptr;
|
||||
GUID guid = WSAID_WSASENDMSG;
|
||||
DWORD bytes = 0;
|
||||
if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
|
||||
&wsasendmsg, sizeof(wsasendmsg), &bytes, nullptr, nullptr) != 0) {
|
||||
// fallback to send()
|
||||
sent = ::send(sock, buf, remaining, 0);
|
||||
} else {
|
||||
DWORD bytesSent = 0;
|
||||
int res = wsasendmsg(
|
||||
sock, reinterpret_cast<LPWSAMSG>(const_cast<OrbisNetMsghdr*>(msg)), 0,
|
||||
&bytesSent, nullptr, nullptr);
|
||||
if (res == SOCKET_ERROR)
|
||||
return ConvertReturnErrorCode(WSAGetLastError());
|
||||
sent = bytesSent;
|
||||
}
|
||||
} else {
|
||||
sent = ::send(sock, buf, remaining, 0);
|
||||
if (sent == SOCKET_ERROR)
|
||||
return ConvertReturnErrorCode(WSAGetLastError());
|
||||
}
|
||||
|
||||
totalSent += sent;
|
||||
remaining -= sent;
|
||||
buf += sent;
|
||||
|
||||
if (!waitAll)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalSent;
|
||||
|
||||
#else
|
||||
int native_flags = convertOrbisFlagsToPosix(socket_type, flags);
|
||||
int res = sendmsg(sock, reinterpret_cast<const msghdr*>(msg), native_flags);
|
||||
return ConvertReturnErrorCode(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
int res = 0;
|
||||
#ifdef _WIN32
|
||||
if (flags & ORBIS_NET_MSG_DONTWAIT) {
|
||||
res = socket_is_ready(sock, false);
|
||||
if (res <= 0)
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
const auto posix_flags = convertOrbisFlagsToPosix(socket_type, flags);
|
||||
if (to == nullptr) {
|
||||
res = send(sock, (const char*)msg, len, posix_flags);
|
||||
} else {
|
||||
sockaddr addr{};
|
||||
convertOrbisNetSockaddrToPosix(to, &addr);
|
||||
res = sendto(sock, (const char*)msg, len, posix_flags, &addr, tolen);
|
||||
}
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
int PosixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) {
|
||||
std::scoped_lock lock{receive_mutex};
|
||||
|
||||
#ifdef _WIN32
|
||||
int totalReceived = 0;
|
||||
bool waitAll = (flags & ORBIS_NET_MSG_WAITALL) != 0;
|
||||
bool dontWait = (flags & ORBIS_NET_MSG_DONTWAIT) != 0;
|
||||
|
||||
// stream socket with multiple buffers
|
||||
bool use_wsarecvmsg =
|
||||
(socket_type == ORBIS_NET_SOCK_STREAM || socket_type == ORBIS_NET_SOCK_STREAM_P2P) &&
|
||||
msg->msg_iovlen > 1;
|
||||
|
||||
for (int i = 0; i < msg->msg_iovlen; ++i) {
|
||||
char* buf = (char*)msg->msg_iov[i].iov_base;
|
||||
size_t remaining = msg->msg_iov[i].iov_len;
|
||||
|
||||
while (remaining > 0) {
|
||||
// emulate DONTWAIT
|
||||
if (dontWait) {
|
||||
int ready = socket_is_ready(sock, true);
|
||||
if (ready <= 0)
|
||||
return ready; // returns ORBIS_NET_ERROR_EWOULDBLOCK or error
|
||||
}
|
||||
|
||||
int received = 0;
|
||||
if (use_wsarecvmsg) {
|
||||
// only call WSARecvMsg if multiple buffers + stream
|
||||
LPFN_WSARECVMSG wsarecvmsg = nullptr;
|
||||
GUID guid = WSAID_WSARECVMSG;
|
||||
DWORD bytes = 0;
|
||||
if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
|
||||
&wsarecvmsg, sizeof(wsarecvmsg), &bytes, nullptr, nullptr) != 0) {
|
||||
// fallback to recv()
|
||||
received = ::recv(sock, buf, remaining, 0);
|
||||
if (received == SOCKET_ERROR)
|
||||
return ConvertReturnErrorCode(WSAGetLastError());
|
||||
} else {
|
||||
DWORD bytesReceived = 0;
|
||||
int res = wsarecvmsg(sock, reinterpret_cast<LPWSAMSG>(msg), &bytesReceived,
|
||||
nullptr, nullptr);
|
||||
if (res == SOCKET_ERROR)
|
||||
return ConvertReturnErrorCode(WSAGetLastError());
|
||||
received = bytesReceived;
|
||||
}
|
||||
} else {
|
||||
// fallback to recv() for UDP or single-buffer
|
||||
received = ::recv(sock, buf, remaining, 0);
|
||||
if (received == SOCKET_ERROR)
|
||||
return ConvertReturnErrorCode(WSAGetLastError());
|
||||
}
|
||||
|
||||
totalReceived += received;
|
||||
remaining -= received;
|
||||
buf += received;
|
||||
|
||||
// stop after first receive if WAITALL is not set
|
||||
if (!waitAll)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalReceived;
|
||||
|
||||
#else
|
||||
int native_flags = convertOrbisFlagsToPosix(socket_type, flags);
|
||||
int res = recvmsg(sock, reinterpret_cast<msghdr*>(msg), native_flags);
|
||||
return ConvertReturnErrorCode(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
|
||||
u32* fromlen) {
|
||||
std::scoped_lock lock{receive_mutex};
|
||||
int res = 0;
|
||||
#ifdef _WIN32
|
||||
if (flags & ORBIS_NET_MSG_DONTWAIT) {
|
||||
res = socket_is_ready(sock);
|
||||
if (res <= 0)
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
const auto posix_flags = convertOrbisFlagsToPosix(socket_type, flags);
|
||||
if (from == nullptr) {
|
||||
res = recv(sock, (char*)buf, len, posix_flags);
|
||||
} else {
|
||||
sockaddr addr{};
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
res = recvfrom(sock, (char*)buf, len, posix_flags, &addr,
|
||||
(fromlen && *fromlen <= sizeof(addr) ? (socklen_t*)fromlen : &addrlen));
|
||||
if (res > 0)
|
||||
convertPosixSockaddrToOrbis(&addr, from);
|
||||
}
|
||||
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr addr2;
|
||||
socklen_t len = sizeof(addr2);
|
||||
net_socket new_socket = ::accept(sock, &addr2, &len);
|
||||
#ifdef _WIN32
|
||||
if (new_socket != INVALID_SOCKET) {
|
||||
#else
|
||||
if (new_socket >= 0) {
|
||||
#endif
|
||||
if (addr && addrlen) {
|
||||
convertPosixSockaddrToOrbis(&addr2, addr);
|
||||
*addrlen = sizeof(OrbisNetSockaddrIn);
|
||||
}
|
||||
return std::make_shared<PosixSocket>(new_socket);
|
||||
}
|
||||
ConvertReturnErrorCode(new_socket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr addr2;
|
||||
convertOrbisNetSockaddrToPosix(addr, &addr2);
|
||||
int result = ::connect(sock, &addr2, sizeof(sockaddr_in));
|
||||
#ifdef _WIN32
|
||||
// Winsock returns EWOULDBLOCK where real hardware returns EINPROGRESS
|
||||
// Step in here on errors to address this.
|
||||
if (result == -1) {
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK) {
|
||||
WSASetLastError(WSAEINPROGRESS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
LOG_DEBUG(Lib_Net, "raw connect result = {}, errno = {}", result,
|
||||
result == -1 ? Common::GetLastErrorMsg() : "none");
|
||||
return ConvertReturnErrorCode(result);
|
||||
}
|
||||
|
||||
int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr addr;
|
||||
convertOrbisNetSockaddrToPosix(name, &addr);
|
||||
if (name != nullptr) {
|
||||
*namelen = sizeof(sockaddr_in);
|
||||
}
|
||||
int res = getsockname(sock, &addr, (socklen_t*)namelen);
|
||||
if (res >= 0) {
|
||||
convertPosixSockaddrToOrbis(&addr, name);
|
||||
*namelen = sizeof(OrbisNetSockaddrIn);
|
||||
}
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
#define CASE_SETSOCKOPT(opt) \
|
||||
case ORBIS_NET_##opt: \
|
||||
return ConvertReturnErrorCode( \
|
||||
setsockopt(sock, native_level, opt, (const char*)optval, optlen))
|
||||
|
||||
#define CASE_SETSOCKOPT_VALUE(opt, value) \
|
||||
case opt: \
|
||||
if (optlen != sizeof(*value)) { \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \
|
||||
return -1; \
|
||||
} \
|
||||
memcpy(value, optval, optlen); \
|
||||
return 0
|
||||
|
||||
int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
s32 native_level = ConvertLevels(level);
|
||||
::linger native_linger;
|
||||
if (native_level == SOL_SOCKET) {
|
||||
switch (optname) {
|
||||
CASE_SETSOCKOPT(SO_REUSEADDR);
|
||||
CASE_SETSOCKOPT(SO_KEEPALIVE);
|
||||
CASE_SETSOCKOPT(SO_BROADCAST);
|
||||
CASE_SETSOCKOPT(SO_SNDBUF);
|
||||
CASE_SETSOCKOPT(SO_RCVBUF);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_CONNECTTIMEO, &sockopt_so_connecttimeo);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, &sockopt_so_reuseport);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature);
|
||||
case ORBIS_NET_SO_SNDTIMEO:
|
||||
case ORBIS_NET_SO_RCVTIMEO: {
|
||||
if (optlen != sizeof(int)) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EFAULT;
|
||||
return -1;
|
||||
}
|
||||
std::vector<char> val;
|
||||
const auto optname_nat = (optname == ORBIS_NET_SO_SNDTIMEO) ? SO_SNDTIMEO : SO_RCVTIMEO;
|
||||
int timeout_us = *(const int*)optval;
|
||||
#ifdef _WIN32
|
||||
DWORD timeout = timeout_us / 1000;
|
||||
#else
|
||||
timeval timeout{.tv_sec = timeout_us / 1000000, .tv_usec = timeout_us % 1000000};
|
||||
#endif
|
||||
val.insert(val.end(), (char*)&timeout, (char*)&timeout + sizeof(timeout));
|
||||
optlen = sizeof(timeout);
|
||||
return ConvertReturnErrorCode(
|
||||
setsockopt(sock, native_level, optname_nat, val.data(), optlen));
|
||||
}
|
||||
case ORBIS_NET_SO_ONESBCAST: {
|
||||
|
||||
if (optlen != sizeof(sockopt_so_onesbcast)) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EFAULT;
|
||||
return -1;
|
||||
}
|
||||
memcpy(&sockopt_so_onesbcast, optval, optlen);
|
||||
return ConvertReturnErrorCode(
|
||||
setsockopt(sock, native_level, SO_BROADCAST, (const char*)optval, optlen));
|
||||
}
|
||||
case ORBIS_NET_SO_TYPE:
|
||||
case ORBIS_NET_SO_ERROR: {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_ENOPROTOOPT;
|
||||
return -1;
|
||||
}
|
||||
case ORBIS_NET_SO_LINGER: {
|
||||
if (socket_type != ORBIS_NET_SOCK_STREAM) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROCUNAVAIL;
|
||||
return -1;
|
||||
}
|
||||
if (optlen < sizeof(OrbisNetLinger)) {
|
||||
LOG_ERROR(Lib_Net, "size missmatched! optlen = {} OrbisNetLinger={}", optlen,
|
||||
sizeof(OrbisNetLinger));
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const void* native_val = &native_linger;
|
||||
u32 native_len = sizeof(native_linger);
|
||||
native_linger.l_onoff = reinterpret_cast<const OrbisNetLinger*>(optval)->l_onoff;
|
||||
native_linger.l_linger = reinterpret_cast<const OrbisNetLinger*>(optval)->l_linger;
|
||||
return ConvertReturnErrorCode(
|
||||
setsockopt(sock, native_level, SO_LINGER, (const char*)native_val, native_len));
|
||||
}
|
||||
|
||||
case ORBIS_NET_SO_NAME:
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINVAL;
|
||||
return -1; // don't support set for name
|
||||
case ORBIS_NET_SO_NBIO: {
|
||||
if (optlen < sizeof(sockopt_so_nbio)) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINVAL;
|
||||
return -1;
|
||||
} else {
|
||||
memcpy(&sockopt_so_nbio, optval, sizeof(sockopt_so_nbio));
|
||||
}
|
||||
#ifdef _WIN32
|
||||
static_assert(sizeof(u_long) == sizeof(sockopt_so_nbio),
|
||||
"type used for ioctlsocket value does not have the expected size");
|
||||
return ConvertReturnErrorCode(ioctlsocket(sock, FIONBIO, (u_long*)&sockopt_so_nbio));
|
||||
#else
|
||||
return ConvertReturnErrorCode(ioctl(sock, FIONBIO, &sockopt_so_nbio));
|
||||
#endif
|
||||
}
|
||||
case ORBIS_NET_SO_ACCEPTTIMEO:
|
||||
LOG_ERROR(Lib_Net, "Unhandled option ORBIS_NET_SO_ACCEPTTIMEO");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
} else if (native_level == IPPROTO_IP) {
|
||||
switch (optname) {
|
||||
// CASE_SETSOCKOPT(IP_HDRINCL);
|
||||
CASE_SETSOCKOPT(IP_TOS);
|
||||
CASE_SETSOCKOPT(IP_TTL);
|
||||
CASE_SETSOCKOPT(IP_MULTICAST_IF);
|
||||
CASE_SETSOCKOPT(IP_MULTICAST_TTL);
|
||||
CASE_SETSOCKOPT(IP_MULTICAST_LOOP);
|
||||
CASE_SETSOCKOPT(IP_ADD_MEMBERSHIP);
|
||||
CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl);
|
||||
case ORBIS_NET_IP_HDRINCL: {
|
||||
if (socket_type != ORBIS_NET_SOCK_RAW) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROCUNAVAIL;
|
||||
return -1;
|
||||
}
|
||||
return ConvertReturnErrorCode(
|
||||
setsockopt(sock, native_level, optname, (const char*)optval, optlen));
|
||||
}
|
||||
}
|
||||
} else if (native_level == IPPROTO_TCP) {
|
||||
switch (optname) {
|
||||
CASE_SETSOCKOPT(TCP_NODELAY);
|
||||
CASE_SETSOCKOPT(TCP_MAXSEG);
|
||||
CASE_SETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, &sockopt_tcp_mss_to_advertise);
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CASE_GETSOCKOPT(opt) \
|
||||
case ORBIS_NET_##opt: { \
|
||||
socklen_t optlen_temp = *optlen; \
|
||||
auto retval = ConvertReturnErrorCode( \
|
||||
getsockopt(sock, native_level, opt, (char*)optval, &optlen_temp)); \
|
||||
*optlen = optlen_temp; \
|
||||
return retval; \
|
||||
}
|
||||
#define CASE_GETSOCKOPT_VALUE(opt, value) \
|
||||
case opt: \
|
||||
if (*optlen < sizeof(value)) { \
|
||||
*optlen = sizeof(value); \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \
|
||||
return -1; \
|
||||
} \
|
||||
*optlen = sizeof(value); \
|
||||
*(decltype(value)*)optval = value; \
|
||||
return 0;
|
||||
|
||||
int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
s32 native_level = ConvertLevels(level);
|
||||
if (native_level == SOL_SOCKET) {
|
||||
switch (optname) {
|
||||
CASE_GETSOCKOPT(SO_REUSEADDR);
|
||||
CASE_GETSOCKOPT(SO_KEEPALIVE);
|
||||
CASE_GETSOCKOPT(SO_BROADCAST);
|
||||
CASE_GETSOCKOPT(SO_LINGER);
|
||||
CASE_GETSOCKOPT(SO_SNDBUF);
|
||||
CASE_GETSOCKOPT(SO_RCVBUF);
|
||||
CASE_GETSOCKOPT(SO_SNDTIMEO);
|
||||
CASE_GETSOCKOPT(SO_RCVTIMEO);
|
||||
CASE_GETSOCKOPT(SO_ERROR);
|
||||
CASE_GETSOCKOPT(SO_TYPE);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NBIO, sockopt_so_nbio);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_CONNECTTIMEO, sockopt_so_connecttimeo);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_REUSEPORT, sockopt_so_reuseport);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, sockopt_so_onesbcast);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, sockopt_so_usecrypto);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, sockopt_so_usesignature);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_SO_NAME,
|
||||
(char)0); // writes an empty string to the output buffer
|
||||
case ORBIS_NET_SO_ERROR_EX: {
|
||||
socklen_t optlen_temp = *optlen;
|
||||
auto retval = ConvertReturnErrorCode(
|
||||
getsockopt(sock, level, SO_ERROR, (char*)optval, &optlen_temp));
|
||||
*optlen = optlen_temp;
|
||||
if (retval < 0) {
|
||||
s32 r = *Libraries::Kernel::__Error();
|
||||
*Libraries::Kernel::__Error() = 0;
|
||||
return r;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
} else if (native_level == IPPROTO_IP) {
|
||||
switch (optname) {
|
||||
CASE_GETSOCKOPT(IP_HDRINCL);
|
||||
CASE_GETSOCKOPT(IP_TOS);
|
||||
CASE_GETSOCKOPT(IP_TTL);
|
||||
CASE_GETSOCKOPT(IP_MULTICAST_IF);
|
||||
CASE_GETSOCKOPT(IP_MULTICAST_TTL);
|
||||
CASE_GETSOCKOPT(IP_MULTICAST_LOOP);
|
||||
CASE_GETSOCKOPT(IP_ADD_MEMBERSHIP);
|
||||
CASE_GETSOCKOPT(IP_DROP_MEMBERSHIP);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, sockopt_ip_ttlchk);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, sockopt_ip_maxttl);
|
||||
}
|
||||
} else if (native_level == IPPROTO_TCP) {
|
||||
switch (optname) {
|
||||
CASE_GETSOCKOPT(TCP_NODELAY);
|
||||
CASE_GETSOCKOPT(TCP_MAXSEG);
|
||||
CASE_GETSOCKOPT_VALUE(ORBIS_NET_TCP_MSS_TO_ADVERTISE, sockopt_tcp_mss_to_advertise);
|
||||
}
|
||||
}
|
||||
UNREACHABLE_MSG("Unknown level ={} optname ={}", level, optname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PosixSocket::GetPeerName(OrbisNetSockaddr* name, u32* namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
sockaddr addr;
|
||||
convertOrbisNetSockaddrToPosix(name, &addr);
|
||||
if (name != nullptr) {
|
||||
*namelen = sizeof(sockaddr_in);
|
||||
}
|
||||
int res = ::getpeername(sock, &addr, (socklen_t*)namelen);
|
||||
if (res >= 0) {
|
||||
convertPosixSockaddrToOrbis(&addr, name);
|
||||
*namelen = sizeof(OrbisNetSockaddrIn);
|
||||
}
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
int PosixSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
#ifdef _WIN32
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
sb->st_mode = 0000777u | 0140000u;
|
||||
return 0;
|
||||
#else
|
||||
struct stat st{};
|
||||
int result = ::fstat(sock, &st);
|
||||
sb->st_mode = 0000777u | 0140000u;
|
||||
sb->st_size = st.st_size;
|
||||
sb->st_blocks = st.st_blocks;
|
||||
sb->st_blksize = st.st_blksize;
|
||||
// sb->st_flags = st.st_flags;
|
||||
return ConvertReturnErrorCode(result);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,242 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
// clang-format off
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <afunix.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <mstcpip.h>
|
||||
// clang-format on
|
||||
typedef SOCKET net_socket;
|
||||
typedef int socklen_t;
|
||||
#ifndef LPFN_WSASENDMSG
|
||||
typedef INT(PASCAL* LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
|
||||
LPDWORD lpNumberOfBytesSent, LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
|
||||
#endif
|
||||
#ifndef WSAID_WSASENDMSG
|
||||
static const GUID WSAID_WSASENDMSG = {
|
||||
0xa441e712, 0x754f, 0x43ca, {0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d}};
|
||||
#endif
|
||||
#ifndef LPFN_WSARECVMSG
|
||||
typedef INT(PASCAL* LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg, LPDWORD lpdwNumberOfBytesRecvd,
|
||||
LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
|
||||
#endif
|
||||
|
||||
#ifndef WSAID_WSARECVMSG
|
||||
static const GUID WSAID_WSARECVMSG = {
|
||||
0xf689d7c8, 0x6f1f, 0x436b, {0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22}};
|
||||
#endif
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
typedef int net_socket;
|
||||
#endif
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "net.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
struct OrbisKernelStat;
|
||||
}
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
struct Socket;
|
||||
|
||||
typedef std::shared_ptr<Socket> SocketPtr;
|
||||
|
||||
struct OrbisNetLinger {
|
||||
s32 l_onoff;
|
||||
s32 l_linger;
|
||||
};
|
||||
struct Socket {
|
||||
explicit Socket(int domain, int type, int protocol) : socket_type(type) {}
|
||||
virtual ~Socket() = default;
|
||||
virtual bool IsValid() const = 0;
|
||||
virtual int Close() = 0;
|
||||
virtual int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) = 0;
|
||||
virtual int GetSocketOptions(int level, int optname, void* optval, u32* optlen) = 0;
|
||||
virtual int Bind(const OrbisNetSockaddr* addr, u32 addrlen) = 0;
|
||||
virtual int Listen(int backlog) = 0;
|
||||
virtual int SendMessage(const OrbisNetMsghdr* msg, int flags) = 0;
|
||||
virtual int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) = 0;
|
||||
virtual SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) = 0;
|
||||
virtual int ReceiveMessage(OrbisNetMsghdr* msg, int flags) = 0;
|
||||
virtual int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
|
||||
u32* fromlen) = 0;
|
||||
virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0;
|
||||
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
|
||||
virtual int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) = 0;
|
||||
virtual int fstat(Libraries::Kernel::OrbisKernelStat* stat) = 0;
|
||||
virtual std::optional<net_socket> Native() = 0;
|
||||
std::mutex m_mutex;
|
||||
std::mutex receive_mutex;
|
||||
int socket_type;
|
||||
};
|
||||
|
||||
struct PosixSocket : public Socket {
|
||||
net_socket sock;
|
||||
int sockopt_so_connecttimeo = 0;
|
||||
int sockopt_so_reuseport = 0;
|
||||
int sockopt_so_onesbcast = 0;
|
||||
int sockopt_so_usecrypto = 0;
|
||||
int sockopt_so_usesignature = 0;
|
||||
int sockopt_so_nbio = 0;
|
||||
int sockopt_ip_ttlchk = 0;
|
||||
int sockopt_ip_maxttl = 0;
|
||||
int sockopt_tcp_mss_to_advertise = 0;
|
||||
int socket_type;
|
||||
explicit PosixSocket(int domain, int type, int protocol)
|
||||
: Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {
|
||||
socket_type = type;
|
||||
}
|
||||
explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {}
|
||||
bool IsValid() const override;
|
||||
int Close() override;
|
||||
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
|
||||
int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override;
|
||||
int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override;
|
||||
int Listen(int backlog) override;
|
||||
int SendMessage(const OrbisNetMsghdr* msg, int flags) override;
|
||||
int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) override;
|
||||
int ReceiveMessage(OrbisNetMsghdr* msg, int flags) override;
|
||||
int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override;
|
||||
SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override;
|
||||
int Connect(const OrbisNetSockaddr* addr, u32 namelen) override;
|
||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||
std::optional<net_socket> Native() override {
|
||||
return sock;
|
||||
}
|
||||
};
|
||||
|
||||
struct P2PDatagram {
|
||||
u32 source_addr{};
|
||||
u16 source_port{};
|
||||
u16 source_vport{};
|
||||
std::vector<u8> payload;
|
||||
};
|
||||
|
||||
struct P2PSocket : public Socket {
|
||||
explicit P2PSocket(int domain, int type, int protocol);
|
||||
~P2PSocket() override;
|
||||
bool IsValid() const override {
|
||||
return valid;
|
||||
}
|
||||
int Close() override;
|
||||
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
|
||||
int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override;
|
||||
int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override;
|
||||
int Listen(int backlog) override;
|
||||
int SendMessage(const OrbisNetMsghdr* msg, int flags) override;
|
||||
int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) override;
|
||||
int ReceiveMessage(OrbisNetMsghdr* msg, int flags) override;
|
||||
int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override;
|
||||
SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override;
|
||||
int Connect(const OrbisNetSockaddr* addr, u32 namelen) override;
|
||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||
std::optional<net_socket> Native() override {
|
||||
#ifdef _WIN32
|
||||
if (readiness_fd != INVALID_SOCKET) {
|
||||
return readiness_fd;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (readiness_fd >= 0) {
|
||||
return readiness_fd;
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
// Called by the shared UDP transport after it demultiplexes a virtual port.
|
||||
void EnqueuePacket(P2PDatagram packet);
|
||||
|
||||
private:
|
||||
bool valid{true};
|
||||
bool bound{false};
|
||||
bool connected{false};
|
||||
OrbisNetSockaddrIn local_addr{};
|
||||
OrbisNetSockaddrIn peer_addr{};
|
||||
|
||||
int sockopt_so_nbio{0};
|
||||
int sockopt_so_usecrypto{0};
|
||||
int sockopt_so_usesignature{0};
|
||||
std::map<int, int> integer_socket_options;
|
||||
|
||||
std::condition_variable receive_cv;
|
||||
std::deque<P2PDatagram> receive_queue;
|
||||
|
||||
#ifdef _WIN32
|
||||
// wepoll requires a Winsock handle. A private loopback pair carries readiness bytes only.
|
||||
net_socket readiness_fd{INVALID_SOCKET};
|
||||
net_socket readiness_signal_fd{INVALID_SOCKET};
|
||||
#elif defined(__linux__)
|
||||
// eventfd carries readiness only; network data stays on the shared UDP transport.
|
||||
int readiness_fd{-1};
|
||||
#endif
|
||||
};
|
||||
|
||||
struct UnixSocket : public Socket {
|
||||
net_socket sock;
|
||||
int socket_type;
|
||||
explicit UnixSocket(int domain, int type, int protocol)
|
||||
: Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {
|
||||
socket_type = type;
|
||||
}
|
||||
explicit UnixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {}
|
||||
bool IsValid() const override;
|
||||
int Close() override;
|
||||
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
|
||||
int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override;
|
||||
int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override;
|
||||
int Listen(int backlog) override;
|
||||
int SendMessage(const OrbisNetMsghdr* msg, int flags) override;
|
||||
int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) override;
|
||||
int ReceiveMessage(OrbisNetMsghdr* msg, int flags) override;
|
||||
int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override;
|
||||
SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override;
|
||||
int Connect(const OrbisNetSockaddr* addr, u32 namelen) override;
|
||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||
std::optional<net_socket> Native() override {
|
||||
return sock;
|
||||
}
|
||||
};
|
||||
|
||||
u16 GetP2PConfiguredPort();
|
||||
u32 GetP2PAdvertisedAddr();
|
||||
bool EnsureP2PTransport();
|
||||
bool P2PTransportIsReady();
|
||||
int P2PSignalingSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
|
||||
int P2PSignalingRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
|
||||
int P2PControlSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
|
||||
int P2PControlRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
|
||||
int P2PMatching2SendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
|
||||
int P2PMatching2RecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
|
||||
|
||||
} // namespace Libraries::Net
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,231 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Ssl {
|
||||
|
||||
struct OrbisSslCaCerts;
|
||||
|
||||
struct OrbisSslCaList {
|
||||
void* certs;
|
||||
int certsNum;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI CA_MGMT_allocCertDistinguishedName();
|
||||
int PS4_SYSV_ABI CA_MGMT_certDistinguishedNameCompare();
|
||||
int PS4_SYSV_ABI CA_MGMT_convertKeyBlobToPKCS8Key();
|
||||
int PS4_SYSV_ABI CA_MGMT_convertKeyDER();
|
||||
int PS4_SYSV_ABI CA_MGMT_convertKeyPEM();
|
||||
int PS4_SYSV_ABI CA_MGMT_convertPKCS8KeyToKeyBlob();
|
||||
int PS4_SYSV_ABI CA_MGMT_convertProtectedPKCS8KeyToKeyBlob();
|
||||
int PS4_SYSV_ABI CA_MGMT_decodeCertificate();
|
||||
int PS4_SYSV_ABI CA_MGMT_enumAltName();
|
||||
int PS4_SYSV_ABI CA_MGMT_enumCrl();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractAllCertDistinguishedName();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractBasicConstraint();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractCertASN1Name();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractCertTimes();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractKeyBlobEx();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractKeyBlobTypeEx();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractPublicKeyInfo();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractSerialNum();
|
||||
int PS4_SYSV_ABI CA_MGMT_extractSignature();
|
||||
int PS4_SYSV_ABI CA_MGMT_free();
|
||||
int PS4_SYSV_ABI CA_MGMT_freeCertDistinguishedName();
|
||||
int PS4_SYSV_ABI CA_MGMT_freeCertDistinguishedNameOnStack();
|
||||
int PS4_SYSV_ABI CA_MGMT_freeCertificate();
|
||||
int PS4_SYSV_ABI CA_MGMT_freeKeyBlob();
|
||||
int PS4_SYSV_ABI CA_MGMT_freeSearchDetails();
|
||||
int PS4_SYSV_ABI CA_MGMT_getCertSignAlgoType();
|
||||
int PS4_SYSV_ABI CA_MGMT_keyBlobToDER();
|
||||
int PS4_SYSV_ABI CA_MGMT_keyBlobToPEM();
|
||||
int PS4_SYSV_ABI CA_MGMT_makeKeyBlobEx();
|
||||
int PS4_SYSV_ABI CA_MGMT_rawVerifyOID();
|
||||
int PS4_SYSV_ABI CA_MGMT_reorderChain();
|
||||
int PS4_SYSV_ABI CA_MGMT_returnCertificatePrints();
|
||||
int PS4_SYSV_ABI CA_MGMT_verifyCertWithKeyBlob();
|
||||
int PS4_SYSV_ABI CA_MGMT_verifySignature();
|
||||
int PS4_SYSV_ABI CERT_checkCertificateIssuer();
|
||||
int PS4_SYSV_ABI CERT_checkCertificateIssuer2();
|
||||
int PS4_SYSV_ABI CERT_checkCertificateIssuerSerialNumber();
|
||||
int PS4_SYSV_ABI CERT_CompSubjectAltNames();
|
||||
int PS4_SYSV_ABI CERT_CompSubjectAltNames2();
|
||||
int PS4_SYSV_ABI CERT_CompSubjectAltNamesExact();
|
||||
int PS4_SYSV_ABI CERT_CompSubjectCommonName();
|
||||
int PS4_SYSV_ABI CERT_CompSubjectCommonName2();
|
||||
int PS4_SYSV_ABI CERT_ComputeBufferHash();
|
||||
int PS4_SYSV_ABI CERT_decryptRSASignature();
|
||||
int PS4_SYSV_ABI CERT_decryptRSASignatureBuffer();
|
||||
int PS4_SYSV_ABI CERT_enumerateAltName();
|
||||
int PS4_SYSV_ABI CERT_enumerateAltName2();
|
||||
int PS4_SYSV_ABI CERT_enumerateCRL();
|
||||
int PS4_SYSV_ABI CERT_enumerateCRL2();
|
||||
int PS4_SYSV_ABI CERT_enumerateCRLAux();
|
||||
int PS4_SYSV_ABI CERT_extractAllDistinguishedNames();
|
||||
int PS4_SYSV_ABI CERT_extractDistinguishedNames();
|
||||
int PS4_SYSV_ABI CERT_extractDistinguishedNames2();
|
||||
int PS4_SYSV_ABI CERT_extractDistinguishedNamesFromName();
|
||||
int PS4_SYSV_ABI CERT_extractRSAKey();
|
||||
int PS4_SYSV_ABI CERT_extractSerialNum();
|
||||
int PS4_SYSV_ABI CERT_extractSerialNum2();
|
||||
int PS4_SYSV_ABI CERT_extractValidityTime();
|
||||
int PS4_SYSV_ABI CERT_extractValidityTime2();
|
||||
int PS4_SYSV_ABI CERT_getCertExtension();
|
||||
int PS4_SYSV_ABI CERT_getCertificateExtensions();
|
||||
int PS4_SYSV_ABI CERT_getCertificateExtensions2();
|
||||
int PS4_SYSV_ABI CERT_getCertificateIssuerSerialNumber();
|
||||
int PS4_SYSV_ABI CERT_getCertificateIssuerSerialNumber2();
|
||||
int PS4_SYSV_ABI CERT_getCertificateKeyUsage();
|
||||
int PS4_SYSV_ABI CERT_getCertificateKeyUsage2();
|
||||
int PS4_SYSV_ABI CERT_getCertificateSubject();
|
||||
int PS4_SYSV_ABI CERT_getCertificateSubject2();
|
||||
int PS4_SYSV_ABI CERT_getCertSignAlgoType();
|
||||
int PS4_SYSV_ABI CERT_GetCertTime();
|
||||
int PS4_SYSV_ABI CERT_getNumberOfChild();
|
||||
int PS4_SYSV_ABI CERT_getRSASignatureAlgo();
|
||||
int PS4_SYSV_ABI CERT_getSignatureItem();
|
||||
int PS4_SYSV_ABI CERT_getSubjectCommonName();
|
||||
int PS4_SYSV_ABI CERT_getSubjectCommonName2();
|
||||
int PS4_SYSV_ABI CERT_isRootCertificate();
|
||||
int PS4_SYSV_ABI CERT_isRootCertificate2();
|
||||
int PS4_SYSV_ABI CERT_rawVerifyOID();
|
||||
int PS4_SYSV_ABI CERT_rawVerifyOID2();
|
||||
int PS4_SYSV_ABI CERT_setKeyFromSubjectPublicKeyInfo();
|
||||
int PS4_SYSV_ABI CERT_setKeyFromSubjectPublicKeyInfoCert();
|
||||
int PS4_SYSV_ABI CERT_STORE_addCertAuthority();
|
||||
int PS4_SYSV_ABI CERT_STORE_addIdentity();
|
||||
int PS4_SYSV_ABI CERT_STORE_addIdentityNakedKey();
|
||||
int PS4_SYSV_ABI CERT_STORE_addIdentityPSK();
|
||||
int PS4_SYSV_ABI CERT_STORE_addIdentityWithCertificateChain();
|
||||
int PS4_SYSV_ABI CERT_STORE_addTrustPoint();
|
||||
int PS4_SYSV_ABI CERT_STORE_createStore();
|
||||
int PS4_SYSV_ABI CERT_STORE_findCertBySubject();
|
||||
int PS4_SYSV_ABI CERT_STORE_findIdentityByTypeFirst();
|
||||
int PS4_SYSV_ABI CERT_STORE_findIdentityByTypeNext();
|
||||
int PS4_SYSV_ABI CERT_STORE_findIdentityCertChainFirst();
|
||||
int PS4_SYSV_ABI CERT_STORE_findIdentityCertChainNext();
|
||||
int PS4_SYSV_ABI CERT_STORE_findPskByIdentity();
|
||||
int PS4_SYSV_ABI CERT_STORE_releaseStore();
|
||||
int PS4_SYSV_ABI CERT_STORE_traversePskListHead();
|
||||
int PS4_SYSV_ABI CERT_STORE_traversePskListNext();
|
||||
int PS4_SYSV_ABI CERT_validateCertificate();
|
||||
int PS4_SYSV_ABI CERT_validateCertificateWithConf();
|
||||
int PS4_SYSV_ABI CERT_VerifyCertificatePolicies();
|
||||
int PS4_SYSV_ABI CERT_VerifyCertificatePolicies2();
|
||||
int PS4_SYSV_ABI CERT_verifySignature();
|
||||
int PS4_SYSV_ABI CERT_VerifyValidityTime();
|
||||
int PS4_SYSV_ABI CERT_VerifyValidityTime2();
|
||||
int PS4_SYSV_ABI CERT_VerifyValidityTimeWithConf();
|
||||
int PS4_SYSV_ABI CRYPTO_initAsymmetricKey();
|
||||
int PS4_SYSV_ABI CRYPTO_uninitAsymmetricKey();
|
||||
int PS4_SYSV_ABI GC_createInstanceIDs();
|
||||
int PS4_SYSV_ABI getCertSigAlgo();
|
||||
int PS4_SYSV_ABI MOCANA_freeMocana();
|
||||
int PS4_SYSV_ABI MOCANA_initMocana();
|
||||
int PS4_SYSV_ABI RSA_verifySignature();
|
||||
int PS4_SYSV_ABI sceSslCheckRecvPending();
|
||||
int PS4_SYSV_ABI sceSslClose();
|
||||
int PS4_SYSV_ABI sceSslConnect();
|
||||
int PS4_SYSV_ABI sceSslCreateSslConnection();
|
||||
int PS4_SYSV_ABI sceSslDeleteSslConnection();
|
||||
int PS4_SYSV_ABI sceSslDisableOption();
|
||||
int PS4_SYSV_ABI sceSslDisableOptionInternal();
|
||||
int PS4_SYSV_ABI sceSslDisableOptionInternalInsecure();
|
||||
int PS4_SYSV_ABI sceSslEnableOption();
|
||||
int PS4_SYSV_ABI sceSslEnableOptionInternal();
|
||||
int PS4_SYSV_ABI sceSslFreeCaCerts();
|
||||
int PS4_SYSV_ABI sceSslFreeCaList();
|
||||
int PS4_SYSV_ABI sceSslFreeSslCertName();
|
||||
int PS4_SYSV_ABI sceSslGetCaCerts(int sslCtxId, OrbisSslCaCerts* certs);
|
||||
int PS4_SYSV_ABI sceSslGetCaList();
|
||||
int PS4_SYSV_ABI sceSslGetIssuerName();
|
||||
int PS4_SYSV_ABI sceSslGetMemoryPoolStats();
|
||||
int PS4_SYSV_ABI sceSslGetNameEntryCount();
|
||||
int PS4_SYSV_ABI sceSslGetNameEntryInfo();
|
||||
int PS4_SYSV_ABI sceSslGetNanoSSLModuleId();
|
||||
int PS4_SYSV_ABI sceSslGetNotAfter();
|
||||
int PS4_SYSV_ABI sceSslGetNotBefore();
|
||||
int PS4_SYSV_ABI sceSslGetSerialNumber();
|
||||
int PS4_SYSV_ABI sceSslGetSslError();
|
||||
int PS4_SYSV_ABI sceSslGetSubjectName();
|
||||
int PS4_SYSV_ABI sceSslInit(std::size_t poolSize);
|
||||
int PS4_SYSV_ABI sceSslLoadCert();
|
||||
int PS4_SYSV_ABI sceSslLoadRootCACert();
|
||||
int PS4_SYSV_ABI sceSslRecv();
|
||||
int PS4_SYSV_ABI sceSslReuseConnection();
|
||||
int PS4_SYSV_ABI sceSslSend();
|
||||
int PS4_SYSV_ABI sceSslSetMinSslVersion();
|
||||
int PS4_SYSV_ABI sceSslSetSslVersion();
|
||||
int PS4_SYSV_ABI sceSslSetVerifyCallback();
|
||||
int PS4_SYSV_ABI sceSslShowMemoryStat();
|
||||
int PS4_SYSV_ABI sceSslTerm();
|
||||
int PS4_SYSV_ABI sceSslUnloadCert();
|
||||
int PS4_SYSV_ABI SSL_acceptConnection();
|
||||
int PS4_SYSV_ABI SSL_acceptConnectionCommon();
|
||||
int PS4_SYSV_ABI SSL_assignCertificateStore();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_acceptConnection();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_closeConnection();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_connect();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_connectCommon();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_getRecvBuffer();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_getSendBuffer();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_init();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_initServer();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_recvMessage();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_recvMessage2();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_sendMessage();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_sendMessagePending();
|
||||
int PS4_SYSV_ABI SSL_ASYNC_start();
|
||||
int PS4_SYSV_ABI SSL_closeConnection();
|
||||
int PS4_SYSV_ABI SSL_connect();
|
||||
int PS4_SYSV_ABI SSL_connectWithCfgParam();
|
||||
int PS4_SYSV_ABI SSL_enableCiphers();
|
||||
int PS4_SYSV_ABI SSL_findConnectionInstance();
|
||||
int PS4_SYSV_ABI SSL_getCipherInfo();
|
||||
int PS4_SYSV_ABI SSL_getClientRandom();
|
||||
int PS4_SYSV_ABI SSL_getClientSessionInfo();
|
||||
int PS4_SYSV_ABI SSL_getCookie();
|
||||
int PS4_SYSV_ABI SSL_getNextSessionId();
|
||||
int PS4_SYSV_ABI SSL_getServerRandom();
|
||||
int PS4_SYSV_ABI SSL_getSessionCache();
|
||||
int PS4_SYSV_ABI SSL_getSessionFlags();
|
||||
int PS4_SYSV_ABI SSL_getSessionInfo();
|
||||
int PS4_SYSV_ABI SSL_getSessionStatus();
|
||||
int PS4_SYSV_ABI SSL_getSocketId();
|
||||
int PS4_SYSV_ABI SSL_getSSLTLSVersion();
|
||||
int PS4_SYSV_ABI SSL_init();
|
||||
int PS4_SYSV_ABI SSL_initiateRehandshake();
|
||||
int PS4_SYSV_ABI SSL_initServerCert();
|
||||
int PS4_SYSV_ABI SSL_ioctl();
|
||||
int PS4_SYSV_ABI SSL_isSessionSSL();
|
||||
int PS4_SYSV_ABI SSL_lockSessionCacheMutex();
|
||||
int PS4_SYSV_ABI SSL_lookupAlert();
|
||||
int PS4_SYSV_ABI SSL_negotiateConnection();
|
||||
int PS4_SYSV_ABI SSL_recv();
|
||||
int PS4_SYSV_ABI SSL_recvPending();
|
||||
int PS4_SYSV_ABI SSL_releaseTables();
|
||||
int PS4_SYSV_ABI SSL_retrieveServerNameList();
|
||||
int PS4_SYSV_ABI SSL_rngFun();
|
||||
int PS4_SYSV_ABI SSL_send();
|
||||
int PS4_SYSV_ABI SSL_sendAlert();
|
||||
int PS4_SYSV_ABI SSL_sendPending();
|
||||
int PS4_SYSV_ABI SSL_setCookie();
|
||||
int PS4_SYSV_ABI SSL_setServerCert();
|
||||
int PS4_SYSV_ABI SSL_setServerNameList();
|
||||
int PS4_SYSV_ABI SSL_setSessionFlags();
|
||||
int PS4_SYSV_ABI SSL_shutdown();
|
||||
int PS4_SYSV_ABI SSL_sslSettings();
|
||||
int PS4_SYSV_ABI SSL_validateCertParam();
|
||||
int PS4_SYSV_ABI VLONG_freeVlongQueue();
|
||||
int PS4_SYSV_ABI Func_22E76E60BC0587D7();
|
||||
int PS4_SYSV_ABI Func_28F8791A771D39C7();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Ssl
|
||||
@@ -0,0 +1,375 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/network/ssl2.h"
|
||||
#include "core/libraries/network/ssl2_error.h"
|
||||
|
||||
namespace Libraries::Ssl2 {
|
||||
|
||||
int PS4_SYSV_ABI CA_MGMT_extractKeyBlobEx() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI CA_MGMT_extractPublicKeyInfo() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI CA_MGMT_freeKeyBlob() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI CRYPTO_initAsymmetricKey() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI CRYPTO_uninitAsymmetricKey() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI RSA_verifySignature() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslCheckRecvPending() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslClose() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslConnect() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslCreateConnection() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslCreateSslConnection() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDeleteConnection() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDeleteSslConnection() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDisableOption() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDisableOptionInternal() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDisableOptionInternalInsecure() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslDisableVerifyOption() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslEnableOption() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslEnableOptionInternal() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslEnableVerifyOption() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslFreeCaCerts(s32 ssl_ctx_id, OrbisSslCaCerts* certs) {
|
||||
LOG_ERROR(Lib_Ssl2, "(DUMMY) called");
|
||||
if (certs == nullptr) {
|
||||
return ORBIS_SSL_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (certs->certs != nullptr) {
|
||||
for (s32 data = 0; data < certs->num; data++) {
|
||||
free(certs->certs[data].ptr);
|
||||
}
|
||||
delete (certs->certs);
|
||||
}
|
||||
|
||||
// delete (certs->pool);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslFreeCaList() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslFreeSslCertName() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetAlpnSelected() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetCaCerts(s32 ssl_ctx_id, OrbisSslCaCerts* certs) {
|
||||
LOG_ERROR(Lib_Ssl2, "(DUMMY) called");
|
||||
if (certs == nullptr) {
|
||||
return ORBIS_SSL_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
// Allocate a buffer to store dummy data in.
|
||||
const char* dummy_data = "dummy";
|
||||
u64 dummy_length = strlen(dummy_data) + 1;
|
||||
char* data = static_cast<char*>(malloc(dummy_length));
|
||||
strncpy(data, dummy_data, dummy_length);
|
||||
certs->certs = new OrbisSslData{data, dummy_length};
|
||||
certs->num = 1;
|
||||
certs->pool = nullptr;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetCaList() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetFingerprint() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetIssuerName() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetMemoryPoolStats() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetNameEntryCount() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetNameEntryInfo() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetNanoSSLModuleId() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetNotAfter() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetNotBefore() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetPeerCert() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetPem() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetSerialNumber() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetSslError() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslGetSubjectName() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslInit(std::size_t poolSize) {
|
||||
LOG_ERROR(Lib_Ssl2, "(DUMMY) called poolSize = {}", poolSize);
|
||||
// return a value >1
|
||||
static int id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslLoadCert() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslLoadRootCACert() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslRead() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslRecv() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslReuseConnection() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslSend() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslSetAlpn() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslSetMinSslVersion() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslSetSslVersion() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslSetVerifyCallback() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslTerm() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslUnloadCert() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSslWrite() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI VLONG_freeVlongQueue() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_22E76E60BC0587D7() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_28F8791A771D39C7() {
|
||||
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("Md+HYkCBZB4", "libSceSsl", 1, "libSceSsl", CA_MGMT_extractKeyBlobEx);
|
||||
LIB_FUNCTION("9bKYzKP6kYU", "libSceSsl", 1, "libSceSsl", CA_MGMT_extractPublicKeyInfo);
|
||||
LIB_FUNCTION("ipLIammTj2Q", "libSceSsl", 1, "libSceSsl", CA_MGMT_freeKeyBlob);
|
||||
LIB_FUNCTION("PRWr3-ytpdg", "libSceSsl", 1, "libSceSsl", CRYPTO_initAsymmetricKey);
|
||||
LIB_FUNCTION("cW7VCIMCh9A", "libSceSsl", 1, "libSceSsl", CRYPTO_uninitAsymmetricKey);
|
||||
LIB_FUNCTION("pBwtarKd7eg", "libSceSsl", 1, "libSceSsl", RSA_verifySignature);
|
||||
LIB_FUNCTION("1VM0h1JrUfA", "libSceSsl", 1, "libSceSsl", sceSslCheckRecvPending);
|
||||
LIB_FUNCTION("viRXSHZYd0c", "libSceSsl", 1, "libSceSsl", sceSslClose);
|
||||
LIB_FUNCTION("zXvd6iNyfgc", "libSceSsl", 1, "libSceSsl", sceSslConnect);
|
||||
LIB_FUNCTION("tuscfitnhEo", "libSceSsl", 1, "libSceSsl", sceSslCreateConnection);
|
||||
LIB_FUNCTION("P14ATpXc4J8", "libSceSsl", 1, "libSceSsl", sceSslCreateSslConnection);
|
||||
LIB_FUNCTION("HJ1n138CQ2g", "libSceSsl", 1, "libSceSsl", sceSslDeleteConnection);
|
||||
LIB_FUNCTION("hwrHV6Pprk4", "libSceSsl", 1, "libSceSsl", sceSslDeleteSslConnection);
|
||||
LIB_FUNCTION("iLKz4+ukLqk", "libSceSsl", 1, "libSceSsl", sceSslDisableOption);
|
||||
LIB_FUNCTION("-WqxBRAUVM4", "libSceSsl", 1, "libSceSsl", sceSslDisableOptionInternal);
|
||||
LIB_FUNCTION("w1+L-27nYas", "libSceSsl", 1, "libSceSsl", sceSslDisableOptionInternalInsecure);
|
||||
LIB_FUNCTION("PwsHbErG+e8", "libSceSsl", 1, "libSceSsl", sceSslDisableVerifyOption);
|
||||
LIB_FUNCTION("m-zPyAsIpco", "libSceSsl", 1, "libSceSsl", sceSslEnableOption);
|
||||
LIB_FUNCTION("g-zCwUKstEQ", "libSceSsl", 1, "libSceSsl", sceSslEnableOptionInternal);
|
||||
LIB_FUNCTION("po1X86mgHDU", "libSceSsl", 1, "libSceSsl", sceSslEnableVerifyOption);
|
||||
LIB_FUNCTION("qIvLs0gYxi0", "libSceSsl", 1, "libSceSsl", sceSslFreeCaCerts);
|
||||
LIB_FUNCTION("+DzXseDVkeI", "libSceSsl", 1, "libSceSsl", sceSslFreeCaList);
|
||||
LIB_FUNCTION("RwXD8grHZHM", "libSceSsl", 1, "libSceSsl", sceSslFreeSslCertName);
|
||||
LIB_FUNCTION("4O7+bRkRUe8", "libSceSsl", 1, "libSceSsl", sceSslGetAlpnSelected);
|
||||
LIB_FUNCTION("TDfQqO-gMbY", "libSceSsl", 1, "libSceSsl", sceSslGetCaCerts);
|
||||
LIB_FUNCTION("qOn+wm28wmA", "libSceSsl", 1, "libSceSsl", sceSslGetCaList);
|
||||
LIB_FUNCTION("brRtwGBu4A8", "libSceSsl", 1, "libSceSsl", sceSslGetFingerprint);
|
||||
LIB_FUNCTION("7whYpYfHP74", "libSceSsl", 1, "libSceSsl", sceSslGetIssuerName);
|
||||
LIB_FUNCTION("-PoIzr3PEk0", "libSceSsl", 1, "libSceSsl", sceSslGetMemoryPoolStats);
|
||||
LIB_FUNCTION("R1ePzopYPYM", "libSceSsl", 1, "libSceSsl", sceSslGetNameEntryCount);
|
||||
LIB_FUNCTION("7RBSTKGrmDA", "libSceSsl", 1, "libSceSsl", sceSslGetNameEntryInfo);
|
||||
LIB_FUNCTION("AzUipl-DpIw", "libSceSsl", 1, "libSceSsl", sceSslGetNanoSSLModuleId);
|
||||
LIB_FUNCTION("xHpt6+2pGYk", "libSceSsl", 1, "libSceSsl", sceSslGetNotAfter);
|
||||
LIB_FUNCTION("Eo0S65Jy28Q", "libSceSsl", 1, "libSceSsl", sceSslGetNotBefore);
|
||||
LIB_FUNCTION("-TbZc8pwPNc", "libSceSsl", 1, "libSceSsl", sceSslGetPeerCert);
|
||||
LIB_FUNCTION("kLB5aGoUJXg", "libSceSsl", 1, "libSceSsl", sceSslGetPem);
|
||||
LIB_FUNCTION("DOwXL+FQMEY", "libSceSsl", 1, "libSceSsl", sceSslGetSerialNumber);
|
||||
LIB_FUNCTION("0XcZknp7-Wc", "libSceSsl", 1, "libSceSsl", sceSslGetSslError);
|
||||
LIB_FUNCTION("dQReuBX9sD8", "libSceSsl", 1, "libSceSsl", sceSslGetSubjectName);
|
||||
LIB_FUNCTION("hdpVEUDFW3s", "libSceSsl", 1, "libSceSsl", sceSslInit);
|
||||
LIB_FUNCTION("Ab7+DH+gYyM", "libSceSsl", 1, "libSceSsl", sceSslLoadCert);
|
||||
LIB_FUNCTION("3-643mGVFJo", "libSceSsl", 1, "libSceSsl", sceSslLoadRootCACert);
|
||||
LIB_FUNCTION("jltWpVKtetg", "libSceSsl", 1, "libSceSsl", sceSslRead);
|
||||
LIB_FUNCTION("hi0veU3L2pU", "libSceSsl", 1, "libSceSsl", sceSslRecv);
|
||||
LIB_FUNCTION("50R2xYaYZwE", "libSceSsl", 1, "libSceSsl", sceSslReuseConnection);
|
||||
LIB_FUNCTION("p5bM5PPufFY", "libSceSsl", 1, "libSceSsl", sceSslSend);
|
||||
LIB_FUNCTION("TL86glUrmUw", "libSceSsl", 1, "libSceSsl", sceSslSetAlpn);
|
||||
LIB_FUNCTION("QWSxBzf6lAg", "libSceSsl", 1, "libSceSsl", sceSslSetMinSslVersion);
|
||||
LIB_FUNCTION("bKaEtQnoUuQ", "libSceSsl", 1, "libSceSsl", sceSslSetSslVersion);
|
||||
LIB_FUNCTION("E4a-ahM57QQ", "libSceSsl", 1, "libSceSsl", sceSslSetVerifyCallback);
|
||||
LIB_FUNCTION("0K1yQ6Lv-Yc", "libSceSsl", 1, "libSceSsl", sceSslTerm);
|
||||
LIB_FUNCTION("UQ+3Qu7v3cA", "libSceSsl", 1, "libSceSsl", sceSslUnloadCert);
|
||||
LIB_FUNCTION("iNjkt9Poblw", "libSceSsl", 1, "libSceSsl", sceSslWrite);
|
||||
LIB_FUNCTION("wcVuyTUr5ys", "libSceSsl", 1, "libSceSsl", VLONG_freeVlongQueue);
|
||||
LIB_FUNCTION("IuduYLwFh9c", "libSceSsl", 1, "libSceSsl", Func_22E76E60BC0587D7);
|
||||
LIB_FUNCTION("KPh5GncdOcc", "libSceSsl", 1, "libSceSsl", Func_28F8791A771D39C7);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ssl2
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Ssl2 {
|
||||
|
||||
struct OrbisSslData {
|
||||
char* ptr;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
struct OrbisSslCaCerts {
|
||||
OrbisSslData* certs;
|
||||
u64 num;
|
||||
void* pool;
|
||||
};
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Ssl2
|
||||
@@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
constexpr int ORBIS_SSL_ERROR_INVALID_ARGUMENT = 0x8095F007;
|
||||
@@ -0,0 +1,440 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <common/assert.h>
|
||||
#include <common/logging/log.h>
|
||||
#include <core/libraries/kernel/kernel.h>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include "common/error.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "net_error.h"
|
||||
#include "sockets.h"
|
||||
#include "sys_net.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
using FDTable = Common::Singleton<Core::FileSys::HandleTable>;
|
||||
|
||||
int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
int returncode = file->socket->Connect(addr, addrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
// Don't log EINPROGRESS or EISCONN, these are normal to see from non-blocking communication.
|
||||
if (error != ORBIS_NET_EINPROGRESS && error != ORBIS_NET_EISCONN) {
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
int returncode = file->socket->Bind(addr, addrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
auto new_sock = file->socket->Accept(addr, paddrlen);
|
||||
if (!new_sock) {
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
// Don't log EWOULDBLOCK, this is normal to see from non-blocking communication.
|
||||
if (error != ORBIS_NET_EWOULDBLOCK) {
|
||||
LOG_ERROR(Lib_Net,
|
||||
"s = {} ({}) returned error code creating new socket for accepting: {}", s,
|
||||
file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
auto fd = FDTable::Instance()->CreateHandle();
|
||||
auto* new_file = FDTable::Instance()->GetFile(fd);
|
||||
new_file->is_opened = true;
|
||||
new_file->type = Core::FileSys::FileType::Socket;
|
||||
new_file->socket = new_sock;
|
||||
return fd;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
int returncode = file->socket->GetPeerName(addr, paddrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
int returncode = file->socket->GetSocketAddress(addr, paddrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
auto name = [&] -> std::string_view {
|
||||
switch (level) {
|
||||
case ORBIS_NET_SOL_SOCKET:
|
||||
return NameOf((OrbisNetSocketSoOption)optname);
|
||||
case ORBIS_NET_IPPROTO_IP:
|
||||
return magic_enum::enum_name((OrbisNetSocketIpOption)optname);
|
||||
case ORBIS_NET_IPPROTO_TCP:
|
||||
return magic_enum::enum_name((OrbisNetSocketTcpOption)optname);
|
||||
default:
|
||||
return std::string_view{"unknown"};
|
||||
}
|
||||
}();
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), level = {}, optname = {}", s, file->m_guest_name,
|
||||
NameOf((OrbisNetProtocol)level), name);
|
||||
int returncode = file->socket->GetSocketOptions(level, optname, optval, optlen);
|
||||
if (returncode >= 0) {
|
||||
if (optname == ORBIS_NET_SO_ERROR) {
|
||||
LOG_DEBUG(Lib_Net, "so_error = {}", *reinterpret_cast<s32*>(optval));
|
||||
}
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), backlog = {}", s, file->m_guest_name, backlog);
|
||||
int returncode = file->socket->Listen(backlog);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval,
|
||||
u32 optlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
auto name = [&] -> std::string_view {
|
||||
switch (level) {
|
||||
case ORBIS_NET_SOL_SOCKET:
|
||||
return NameOf((OrbisNetSocketSoOption)optname);
|
||||
case ORBIS_NET_IPPROTO_IP:
|
||||
return magic_enum::enum_name((OrbisNetSocketIpOption)optname);
|
||||
case ORBIS_NET_IPPROTO_TCP:
|
||||
return magic_enum::enum_name((OrbisNetSocketTcpOption)optname);
|
||||
default:
|
||||
return std::string_view{"unknown"};
|
||||
}
|
||||
}();
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), level = {}, optname = {}", s, file->m_guest_name,
|
||||
NameOf((OrbisNetProtocol)level), name);
|
||||
int returncode = file->socket->SetSocketOptions(level, optname, optval, optlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol) {
|
||||
auto sname = name ? name : "anon";
|
||||
LOG_INFO(Lib_Net, "name = {} family = {} type = {} protocol = {}", std::string(sname),
|
||||
magic_enum::enum_name((OrbisNetFamily)family),
|
||||
magic_enum::enum_name((OrbisNetSocketType)type), protocol);
|
||||
SocketPtr socket;
|
||||
switch (type) {
|
||||
case ORBIS_NET_SOCK_STREAM:
|
||||
case ORBIS_NET_SOCK_DGRAM:
|
||||
if (family == ORBIS_NET_AF_UNIX) {
|
||||
socket = std::make_shared<UnixSocket>(family, type, protocol);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case ORBIS_NET_SOCK_RAW:
|
||||
socket = std::make_shared<PosixSocket>(family, type, protocol);
|
||||
break;
|
||||
case ORBIS_NET_SOCK_DGRAM_P2P:
|
||||
case ORBIS_NET_SOCK_STREAM_P2P:
|
||||
socket = std::make_shared<P2PSocket>(family, type, protocol);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown type {}", type);
|
||||
}
|
||||
if (!socket->IsValid()) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto fd = FDTable::Instance()->CreateHandle();
|
||||
auto* sock = FDTable::Instance()->GetFile(fd);
|
||||
sock->is_opened = true;
|
||||
sock->type = Core::FileSys::FileType::Socket;
|
||||
sock->socket = socket;
|
||||
sock->m_guest_name = sname;
|
||||
return fd;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_socket(int family, int type, int protocol) {
|
||||
return sys_socketex(nullptr, family, type, protocol);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int socketpair(int family, int type, int protocol, net_socket fd[2]) {
|
||||
if (family != AF_UNIX && family != AF_INET) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
if (type != SOCK_STREAM) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SOCKET listener = INVALID_SOCKET;
|
||||
SOCKET sock1 = INVALID_SOCKET;
|
||||
SOCKET sock2 = INVALID_SOCKET;
|
||||
sockaddr_in addr{};
|
||||
int addrlen = sizeof(addr);
|
||||
|
||||
listener = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (listener == INVALID_SOCKET)
|
||||
goto fail1;
|
||||
|
||||
ZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
if (bind(listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
|
||||
goto fail1;
|
||||
if (listen(listener, 1) == SOCKET_ERROR)
|
||||
goto fail1;
|
||||
|
||||
if (getsockname(listener, (sockaddr*)&addr, &addrlen) == SOCKET_ERROR)
|
||||
goto fail1;
|
||||
|
||||
sock1 = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock1 == INVALID_SOCKET)
|
||||
goto fail2;
|
||||
|
||||
if (connect(sock1, (sockaddr*)&addr, addrlen) == SOCKET_ERROR)
|
||||
goto fail2;
|
||||
|
||||
sock2 = accept(listener, nullptr, nullptr);
|
||||
if (sock2 == INVALID_SOCKET)
|
||||
goto fail3;
|
||||
|
||||
closesocket(listener);
|
||||
fd[0] = sock1;
|
||||
fd[1] = sock2;
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
closesocket(sock2);
|
||||
fail2:
|
||||
closesocket(sock1);
|
||||
fail1:
|
||||
closesocket(listener);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int PS4_SYSV_ABI sys_socketpair(int family, int type, int protocol, int sv[2]) {
|
||||
if (sv == nullptr) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (family != ORBIS_NET_AF_UNIX) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
net_socket fd[2];
|
||||
if (socketpair(family, type, protocol, fd) != 0) {
|
||||
LOG_ERROR(Lib_Net, "socketpair failed with {}", Common::GetLastErrorMsg());
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto fd1 = FDTable::Instance()->CreateHandle();
|
||||
auto fd2 = FDTable::Instance()->CreateHandle();
|
||||
auto* sock = FDTable::Instance()->GetFile(fd1);
|
||||
sock->is_opened = true;
|
||||
sock->type = Core::FileSys::FileType::Socket;
|
||||
sock->socket = std::make_shared<UnixSocket>(fd[0]);
|
||||
sock->m_guest_name = "anon_sock0";
|
||||
sock = FDTable::Instance()->GetFile(fd2);
|
||||
sock->is_opened = true;
|
||||
sock->type = Core::FileSys::FileType::Socket;
|
||||
sock->socket = std::make_shared<UnixSocket>(fd[1]);
|
||||
sock->m_guest_name = "anon_sock1";
|
||||
sv[0] = fd1;
|
||||
sv[1] = fd2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags) {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_socketclose(OrbisNetId s) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({})", s, file->m_guest_name);
|
||||
int returncode = file->socket->Close();
|
||||
if (returncode >= 0) {
|
||||
FDTable::Instance()->DeleteHandle(s);
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_send(OrbisNetId s, const void* buf, u64 len, int flags) {
|
||||
return sys_sendto(s, buf, len, flags, nullptr, 0);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags,
|
||||
const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), len = {}, flags = {:#x}", s, file->m_guest_name, len, flags);
|
||||
int returncode = file->socket->SendPacket(buf, len, flags, addr, addrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), flags = {:#x}", s, file->m_guest_name, flags);
|
||||
int returncode = file->socket->SendMessage(msg, flags);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "error code returned: {}", (u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI sys_recv(OrbisNetId s, void* buf, u64 len, int flags) {
|
||||
return sys_recvfrom(s, buf, len, flags, nullptr, nullptr);
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
|
||||
u32* paddrlen) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), len = {}, flags = {:#x}", s, file->m_guest_name, len, flags);
|
||||
int returncode = file->socket->ReceivePacket(buf, len, flags, addr, paddrlen);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
// Don't log EWOULDBLOCK, this is normal to see from non-blocking communication.
|
||||
u32 error = *Libraries::Kernel::__Error();
|
||||
if (error != ORBIS_NET_EWOULDBLOCK) {
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name, error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags) {
|
||||
auto file = FDTable::Instance()->GetSocket(s);
|
||||
if (!file) {
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EBADF;
|
||||
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
|
||||
return -1;
|
||||
}
|
||||
LOG_DEBUG(Lib_Net, "s = {} ({}), flags = {:#x}", s, file->m_guest_name, flags);
|
||||
int returncode = file->socket->ReceiveMessage(msg, flags);
|
||||
if (returncode >= 0) {
|
||||
return returncode;
|
||||
}
|
||||
LOG_ERROR(Lib_Net, "s = {} ({}) returned error code: {}", s, file->m_guest_name,
|
||||
(u32)*Libraries::Kernel::__Error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_htons(u16 v) {
|
||||
return ((v << 8) & 0xff00) | ((v >> 8) & 0x00ff);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sys_htonl(u32 v) {
|
||||
return (v << 24) | ((v & 0xff00) << 8) | ((v >> 8) & 0xff00) | (v >> 24);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "net.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
int PS4_SYSV_ABI sys_connect(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sys_bind(OrbisNetId s, const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sys_accept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sys_getpeername(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sys_getsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen);
|
||||
int PS4_SYSV_ABI sys_getsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen);
|
||||
int PS4_SYSV_ABI sys_listen(OrbisNetId s, int backlog);
|
||||
int PS4_SYSV_ABI sys_setsockopt(OrbisNetId s, int level, int optname, const void* optval,
|
||||
u32 optlen);
|
||||
int PS4_SYSV_ABI sys_shutdown(OrbisNetId s, int how);
|
||||
int PS4_SYSV_ABI sys_socketex(const char* name, int family, int type, int protocol);
|
||||
int PS4_SYSV_ABI sys_socket(int family, int type, int protocol);
|
||||
int PS4_SYSV_ABI sys_socketpair(int family, int type, int protocol, int sv[2]);
|
||||
int PS4_SYSV_ABI sys_netabort(OrbisNetId s, int flags);
|
||||
int PS4_SYSV_ABI sys_socketclose(OrbisNetId s);
|
||||
int PS4_SYSV_ABI sys_send(OrbisNetId s, const void* buf, u64 len, int flags);
|
||||
int PS4_SYSV_ABI sys_sendto(OrbisNetId s, const void* buf, u64 len, int flags,
|
||||
const OrbisNetSockaddr* addr, u32 addrlen);
|
||||
int PS4_SYSV_ABI sys_sendmsg(OrbisNetId s, const OrbisNetMsghdr* msg, int flags);
|
||||
s64 PS4_SYSV_ABI sys_recv(OrbisNetId s, void* buf, u64 len, int flags);
|
||||
s64 PS4_SYSV_ABI sys_recvfrom(OrbisNetId s, void* buf, u64 len, int flags, OrbisNetSockaddr* addr,
|
||||
u32* paddrlen);
|
||||
s64 PS4_SYSV_ABI sys_recvmsg(OrbisNetId s, OrbisNetMsghdr* msg, int flags);
|
||||
int PS4_SYSV_ABI sys_htons(u16 v);
|
||||
int PS4_SYSV_ABI sys_htonl(u32 v);
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,393 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <common/assert.h>
|
||||
#include "common/error.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "net.h"
|
||||
#ifndef _WIN32
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
#include "net_error.h"
|
||||
#include "sockets.h"
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ERROR_CASE(errname) \
|
||||
case (WSA##errname): \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_##errname; \
|
||||
return -1;
|
||||
#else
|
||||
#define ERROR_CASE(errname) \
|
||||
case (errname): \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_##errname; \
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
static int ConvertReturnErrorCode(int retval) {
|
||||
if (retval < 0) {
|
||||
#ifdef _WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
#else
|
||||
switch (errno) {
|
||||
#endif
|
||||
#ifndef _WIN32 // These errorcodes don't exist in WinSock
|
||||
ERROR_CASE(EPERM)
|
||||
ERROR_CASE(ENOENT)
|
||||
// ERROR_CASE(ESRCH)
|
||||
// ERROR_CASE(EIO)
|
||||
// ERROR_CASE(ENXIO)
|
||||
// ERROR_CASE(E2BIG)
|
||||
// ERROR_CASE(ENOEXEC)
|
||||
// ERROR_CASE(EDEADLK)
|
||||
ERROR_CASE(ENOMEM)
|
||||
// ERROR_CASE(ECHILD)
|
||||
// ERROR_CASE(EBUSY)
|
||||
ERROR_CASE(EEXIST)
|
||||
// ERROR_CASE(EXDEV)
|
||||
ERROR_CASE(ENODEV)
|
||||
// ERROR_CASE(ENOTDIR)
|
||||
// ERROR_CASE(EISDIR)
|
||||
ERROR_CASE(ENFILE)
|
||||
// ERROR_CASE(ENOTTY)
|
||||
// ERROR_CASE(ETXTBSY)
|
||||
// ERROR_CASE(EFBIG)
|
||||
ERROR_CASE(ENOSPC)
|
||||
// ERROR_CASE(ESPIPE)
|
||||
// ERROR_CASE(EROFS)
|
||||
// ERROR_CASE(EMLINK)
|
||||
ERROR_CASE(EPIPE)
|
||||
// ERROR_CASE(EDOM)
|
||||
// ERROR_CASE(ERANGE)
|
||||
// ERROR_CASE(ENOLCK)
|
||||
// ERROR_CASE(ENOSYS)
|
||||
// ERROR_CASE(EIDRM)
|
||||
// ERROR_CASE(EOVERFLOW)
|
||||
// ERROR_CASE(EILSEQ)
|
||||
// ERROR_CASE(ENOTSUP)
|
||||
ERROR_CASE(ECANCELED)
|
||||
// ERROR_CASE(EBADMSG)
|
||||
ERROR_CASE(ENODATA)
|
||||
// ERROR_CASE(ENOSR)
|
||||
// ERROR_CASE(ENOSTR)
|
||||
// ERROR_CASE(ETIME)
|
||||
#endif
|
||||
ERROR_CASE(EINTR)
|
||||
ERROR_CASE(EBADF)
|
||||
ERROR_CASE(EACCES)
|
||||
ERROR_CASE(EFAULT)
|
||||
ERROR_CASE(EINVAL)
|
||||
ERROR_CASE(EMFILE)
|
||||
ERROR_CASE(EWOULDBLOCK)
|
||||
ERROR_CASE(EINPROGRESS)
|
||||
ERROR_CASE(EALREADY)
|
||||
ERROR_CASE(ENOTSOCK)
|
||||
ERROR_CASE(EDESTADDRREQ)
|
||||
ERROR_CASE(EMSGSIZE)
|
||||
ERROR_CASE(EPROTOTYPE)
|
||||
ERROR_CASE(ENOPROTOOPT)
|
||||
ERROR_CASE(EPROTONOSUPPORT)
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
ERROR_CASE(EOPNOTSUPP)
|
||||
#endif
|
||||
ERROR_CASE(EAFNOSUPPORT)
|
||||
ERROR_CASE(EADDRINUSE)
|
||||
ERROR_CASE(EADDRNOTAVAIL)
|
||||
ERROR_CASE(ENETDOWN)
|
||||
ERROR_CASE(ENETUNREACH)
|
||||
ERROR_CASE(ENETRESET)
|
||||
ERROR_CASE(ECONNABORTED)
|
||||
ERROR_CASE(ECONNRESET)
|
||||
ERROR_CASE(ENOBUFS)
|
||||
ERROR_CASE(EISCONN)
|
||||
ERROR_CASE(ENOTCONN)
|
||||
ERROR_CASE(ETIMEDOUT)
|
||||
ERROR_CASE(ECONNREFUSED)
|
||||
ERROR_CASE(ELOOP)
|
||||
ERROR_CASE(ENAMETOOLONG)
|
||||
ERROR_CASE(EHOSTUNREACH)
|
||||
ERROR_CASE(ENOTEMPTY)
|
||||
}
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EINTERNAL;
|
||||
return -1;
|
||||
}
|
||||
// if it is 0 or positive return it as it is
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ConvertLevels(int level) {
|
||||
switch (level) {
|
||||
case ORBIS_NET_SOL_SOCKET:
|
||||
return SOL_SOCKET;
|
||||
case ORBIS_NET_IPPROTO_IP:
|
||||
return IPPROTO_IP;
|
||||
case ORBIS_NET_IPPROTO_TCP:
|
||||
return IPPROTO_TCP;
|
||||
case ORBIS_NET_IPPROTO_IPV6:
|
||||
return IPPROTO_IPV6;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void convertOrbisNetSockaddrToUnix(const OrbisNetSockaddr* src, sockaddr_un* dst) {
|
||||
if (src == nullptr || dst == nullptr)
|
||||
return;
|
||||
memset(dst, 0, sizeof(sockaddr_un));
|
||||
const OrbisNetSockaddrUn* src_in = (const OrbisNetSockaddrUn*)src;
|
||||
sockaddr_un* dst_in = (sockaddr_un*)dst;
|
||||
dst_in->sun_family = src_in->sun_family;
|
||||
memcpy(&dst_in->sun_path, &src_in->sun_path, src_in->sun_len);
|
||||
}
|
||||
|
||||
static void convertUnixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) {
|
||||
if (src == nullptr || dst == nullptr)
|
||||
return;
|
||||
memset(dst, 0, sizeof(OrbisNetSockaddrUn));
|
||||
OrbisNetSockaddrUn* dst_in = (OrbisNetSockaddrUn*)dst;
|
||||
sockaddr_un* src_in = (sockaddr_un*)src;
|
||||
dst_in->sun_len = strnlen(src_in->sun_path, 108);
|
||||
dst_in->sun_family = src_in->sun_family;
|
||||
memcpy(&dst_in->sun_path, &src_in->sun_path, dst_in->sun_len);
|
||||
}
|
||||
|
||||
bool UnixSocket::IsValid() const {
|
||||
#ifdef _WIN32
|
||||
return sock != INVALID_SOCKET;
|
||||
#else
|
||||
return sock != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int UnixSocket::Close() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
#ifdef _WIN32
|
||||
auto out = closesocket(sock);
|
||||
#else
|
||||
auto out = ::close(sock);
|
||||
#endif
|
||||
return ConvertReturnErrorCode(out);
|
||||
}
|
||||
|
||||
int UnixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr_un addr2;
|
||||
convertOrbisNetSockaddrToUnix(addr, &addr2);
|
||||
return ConvertReturnErrorCode(::bind(sock, (const sockaddr*)&addr2, sizeof(sockaddr_un)));
|
||||
}
|
||||
|
||||
int UnixSocket::Listen(int backlog) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return ConvertReturnErrorCode(::listen(sock, backlog));
|
||||
}
|
||||
|
||||
int UnixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
#ifdef _WIN32
|
||||
DWORD bytesSent = 0;
|
||||
LPFN_WSASENDMSG wsasendmsg = nullptr;
|
||||
GUID guid = WSAID_WSASENDMSG;
|
||||
DWORD bytes = 0;
|
||||
|
||||
if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsasendmsg,
|
||||
sizeof(wsasendmsg), &bytes, nullptr, nullptr) != 0) {
|
||||
return ConvertReturnErrorCode(-1);
|
||||
}
|
||||
|
||||
int res = wsasendmsg(sock, reinterpret_cast<LPWSAMSG>(const_cast<OrbisNetMsghdr*>(msg)), flags,
|
||||
&bytesSent, nullptr, nullptr);
|
||||
|
||||
if (res == SOCKET_ERROR) {
|
||||
return ConvertReturnErrorCode(-1);
|
||||
}
|
||||
return static_cast<int>(bytesSent);
|
||||
#else
|
||||
int res = sendmsg(sock, reinterpret_cast<const msghdr*>(msg), flags);
|
||||
return ConvertReturnErrorCode(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
int UnixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
|
||||
u32 tolen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
LOG_ERROR(Lib_Net, "called");
|
||||
if (to != nullptr) {
|
||||
sockaddr_un addr;
|
||||
convertOrbisNetSockaddrToUnix(to, &addr);
|
||||
return ConvertReturnErrorCode(
|
||||
sendto(sock, (const char*)msg, len, flags, (sockaddr*)&addr, sizeof(sockaddr_un)));
|
||||
} else {
|
||||
return ConvertReturnErrorCode(send(sock, (const char*)msg, len, flags));
|
||||
}
|
||||
}
|
||||
|
||||
int UnixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) {
|
||||
std::scoped_lock lock{receive_mutex};
|
||||
#ifdef _WIN32
|
||||
LPFN_WSARECVMSG wsarecvmsg = nullptr;
|
||||
GUID guid = WSAID_WSARECVMSG;
|
||||
DWORD bytes = 0;
|
||||
|
||||
if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &wsarecvmsg,
|
||||
sizeof(wsarecvmsg), &bytes, nullptr, nullptr) != 0) {
|
||||
return ConvertReturnErrorCode(-1);
|
||||
}
|
||||
|
||||
DWORD bytesReceived = 0;
|
||||
int res = wsarecvmsg(sock, reinterpret_cast<LPWSAMSG>(msg), &bytesReceived, nullptr, nullptr);
|
||||
|
||||
if (res == SOCKET_ERROR) {
|
||||
return ConvertReturnErrorCode(-1);
|
||||
}
|
||||
return static_cast<int>(bytesReceived);
|
||||
#else
|
||||
int res = recvmsg(sock, reinterpret_cast<msghdr*>(msg), flags);
|
||||
return ConvertReturnErrorCode(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
int UnixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) {
|
||||
std::scoped_lock lock{receive_mutex};
|
||||
LOG_ERROR(Lib_Net, "called");
|
||||
if (from != nullptr) {
|
||||
sockaddr_un addr;
|
||||
int res = recvfrom(sock, (char*)buf, len, flags, (sockaddr*)&addr, (socklen_t*)fromlen);
|
||||
convertUnixSockaddrToOrbis((sockaddr*)&addr, from);
|
||||
*fromlen = sizeof(OrbisNetSockaddrUn);
|
||||
return ConvertReturnErrorCode(res);
|
||||
} else {
|
||||
return ConvertReturnErrorCode(recv(sock, (char*)buf, len, flags));
|
||||
}
|
||||
}
|
||||
|
||||
SocketPtr UnixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr_un addr2;
|
||||
socklen_t len = sizeof(addr2);
|
||||
net_socket new_socket = ::accept(sock, (sockaddr*)&addr2, &len);
|
||||
#ifdef _WIN32
|
||||
if (new_socket != INVALID_SOCKET) {
|
||||
#else
|
||||
if (new_socket >= 0) {
|
||||
#endif
|
||||
if (addr && addrlen) {
|
||||
convertUnixSockaddrToOrbis((sockaddr*)&addr2, addr);
|
||||
*addrlen = sizeof(OrbisNetSockaddrUn);
|
||||
}
|
||||
return std::make_shared<UnixSocket>(new_socket);
|
||||
}
|
||||
ConvertReturnErrorCode(new_socket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int UnixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
sockaddr_un addr2;
|
||||
convertOrbisNetSockaddrToUnix(addr, &addr2);
|
||||
int result = 0;
|
||||
do {
|
||||
result = ::connect(sock, (sockaddr*)&addr2, sizeof(sockaddr_un));
|
||||
LOG_DEBUG(Lib_Net, "raw connect result = {}, errno = {}", result,
|
||||
result == -1 ? Common::GetLastErrorMsg() : "none");
|
||||
} while (result == -1 && (errno == EINTR || errno == EINPROGRESS));
|
||||
return ConvertReturnErrorCode(result);
|
||||
}
|
||||
|
||||
int UnixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
sockaddr_un addr;
|
||||
convertOrbisNetSockaddrToUnix(name, &addr);
|
||||
if (name != nullptr) {
|
||||
*namelen = sizeof(sockaddr_un);
|
||||
}
|
||||
int res = getsockname(sock, (sockaddr*)&addr, (socklen_t*)namelen);
|
||||
if (res >= 0) {
|
||||
convertUnixSockaddrToOrbis((sockaddr*)&addr, name);
|
||||
*namelen = sizeof(OrbisNetSockaddrUn);
|
||||
}
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
#define CASE_SETSOCKOPT(opt) \
|
||||
case ORBIS_NET_##opt: \
|
||||
return ConvertReturnErrorCode( \
|
||||
setsockopt(sock, native_level, opt, (const char*)optval, optlen))
|
||||
|
||||
#define CASE_SETSOCKOPT_VALUE(opt, value) \
|
||||
case opt: \
|
||||
if (optlen != sizeof(*value)) { \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \
|
||||
return -1; \
|
||||
} \
|
||||
memcpy(value, optval, optlen); \
|
||||
return 0
|
||||
|
||||
int UnixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
LOG_ERROR(Lib_Net, "Unknown level = {} optname = {}", level, optname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define CASE_GETSOCKOPT(opt) \
|
||||
case ORBIS_NET_##opt: { \
|
||||
socklen_t optlen_temp = *optlen; \
|
||||
auto retval = ConvertReturnErrorCode( \
|
||||
getsockopt(sock, native_level, opt, (char*)optval, &optlen_temp)); \
|
||||
*optlen = optlen_temp; \
|
||||
return retval; \
|
||||
}
|
||||
#define CASE_GETSOCKOPT_VALUE(opt, value) \
|
||||
case opt: \
|
||||
if (*optlen < sizeof(value)) { \
|
||||
*optlen = sizeof(value); \
|
||||
*Libraries::Kernel::__Error() = ORBIS_NET_EFAULT; \
|
||||
return -1; \
|
||||
} \
|
||||
*optlen = sizeof(value); \
|
||||
*(decltype(value)*)optval = value; \
|
||||
return 0;
|
||||
|
||||
int UnixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
LOG_ERROR(Lib_Net, "Unknown level = {} optname = {}", level, optname);
|
||||
return ConvertReturnErrorCode(-1);
|
||||
}
|
||||
|
||||
int UnixSocket::GetPeerName(OrbisNetSockaddr* name, u32* namelen) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
LOG_DEBUG(Lib_Net, "called");
|
||||
|
||||
sockaddr_un addr;
|
||||
convertOrbisNetSockaddrToUnix(name, &addr);
|
||||
if (name != nullptr) {
|
||||
*namelen = sizeof(sockaddr_un);
|
||||
}
|
||||
int res = ::getpeername(sock, (sockaddr*)&addr, (socklen_t*)namelen);
|
||||
if (res >= 0) {
|
||||
convertUnixSockaddrToOrbis((sockaddr*)&addr, name);
|
||||
*namelen = sizeof(OrbisNetSockaddrUn);
|
||||
}
|
||||
return ConvertReturnErrorCode(res);
|
||||
}
|
||||
|
||||
int UnixSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
#ifdef _WIN32
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
sb->st_mode = 0000777u | 0140000u;
|
||||
return 0;
|
||||
#else
|
||||
struct stat st{};
|
||||
int result = ::fstat(sock, &st);
|
||||
sb->st_mode = 0000777u | 0140000u;
|
||||
sb->st_size = st.st_size;
|
||||
sb->st_blocks = st.st_blocks;
|
||||
sb->st_blksize = st.st_blksize;
|
||||
// sb->st_flags = st.st_flags;
|
||||
return ConvertReturnErrorCode(result);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Libraries::Net
|
||||
@@ -0,0 +1,388 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_auth.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Libraries::Np::NpAuth {
|
||||
|
||||
static bool g_shadnet_enabled = false;
|
||||
static s32 g_active_auth_requests = 0;
|
||||
static std::mutex g_auth_request_mutex;
|
||||
|
||||
// Internal types for storing request-related information
|
||||
enum class NpAuthRequestState {
|
||||
None = 0,
|
||||
Ready = 1,
|
||||
Aborted = 2,
|
||||
Complete = 3,
|
||||
};
|
||||
|
||||
struct NpAuthRequest {
|
||||
NpAuthRequestState state;
|
||||
bool async;
|
||||
s32 result;
|
||||
};
|
||||
|
||||
static std::vector<NpAuthRequest> g_auth_requests;
|
||||
|
||||
s32 CreateNpAuthRequest(bool async) {
|
||||
if (g_active_auth_requests == ORBIS_NP_AUTH_REQUEST_LIMIT) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_MAX;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
s32 req_index = 0;
|
||||
while (req_index < g_auth_requests.size()) {
|
||||
// Find first nonexistant request
|
||||
if (g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
// There is no request at this index, set the index to ready then break.
|
||||
g_auth_requests[req_index].state = NpAuthRequestState::Ready;
|
||||
g_auth_requests[req_index].async = async;
|
||||
break;
|
||||
}
|
||||
req_index++;
|
||||
}
|
||||
|
||||
if (req_index == g_auth_requests.size()) {
|
||||
// There are no requests to replace.
|
||||
NpAuthRequest new_request{NpAuthRequestState::Ready, async, 0};
|
||||
g_auth_requests.emplace_back(new_request);
|
||||
}
|
||||
|
||||
// Offset by one, first returned ID is 0x10000001
|
||||
g_active_auth_requests++;
|
||||
LOG_DEBUG(Lib_NpAuth, "called, async = {}", async);
|
||||
return req_index + ORBIS_NP_AUTH_REQUEST_ID_OFFSET + 1;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthCreateRequest() {
|
||||
return CreateNpAuthRequest(false);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthCreateAsyncRequest(const OrbisNpAuthCreateAsyncRequestParameter* param) {
|
||||
if (param == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (param->size != sizeof(OrbisNpAuthCreateAsyncRequestParameter)) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
return CreateNpAuthRequest(true);
|
||||
}
|
||||
|
||||
s32 GetAuthorizationCode(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
|
||||
s32 flag, OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
|
||||
if (param == nullptr || auth_code == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (param->size != sizeof(OrbisNpAuthGetAuthorizationCodeParameter)) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
|
||||
}
|
||||
if (param->user_id == -1 || param->client_id == nullptr || param->scope == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
// From here the actual authorization code request is performed.
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto& request = g_auth_requests[req_index];
|
||||
if (request.state == NpAuthRequestState::Complete) {
|
||||
request.result = ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
} else if (request.state == NpAuthRequestState::Aborted) {
|
||||
request.result = ORBIS_NP_AUTH_ERROR_ABORTED;
|
||||
return ORBIS_NP_AUTH_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
request.state = NpAuthRequestState::Complete;
|
||||
if (!g_shadnet_enabled) {
|
||||
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
// If the request is processed in some form, and it's an async request, then it returns OK.
|
||||
if (request.async) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
|
||||
LOG_ERROR(Lib_NpAuth, "(STUBBED) called, req_id = {:#x}, async = {}", req_id, request.async);
|
||||
|
||||
// Not sure what values are expected here, so zeroing these for now.
|
||||
std::memset(auth_code, 0, sizeof(OrbisNpAuthorizationCode));
|
||||
std::strncpy(auth_code->code, "AUTHCODE", 9);
|
||||
if (issuer_id != nullptr) {
|
||||
*issuer_id = 100;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI
|
||||
sceNpAuthGetAuthorizationCode(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameter* param,
|
||||
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
|
||||
if (param == nullptr || auth_code == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (param->size != sizeof(OrbisNpAuthGetAuthorizationCodeParameter)) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
|
||||
}
|
||||
if (param->online_id == nullptr || param->client_id == nullptr || param->scope == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!g_shadnet_enabled) {
|
||||
// Calls sceNpManagerIntGetUserIdByOnlineId to get a user id, returning any errors.
|
||||
// This call will not succeed while signed out because games cannot retrieve an online id.
|
||||
return ORBIS_NP_ERROR_USER_NOT_FOUND;
|
||||
}
|
||||
|
||||
// For simplicity, call sceUserServiceGetInitialUser to get the user_id.
|
||||
s32 user_id = 0;
|
||||
ASSERT(UserService::sceUserServiceGetInitialUser(&user_id) == 0);
|
||||
|
||||
// Library constructs an OrbisNpAuthGetAuthorizationCodeParameterA struct,
|
||||
// then uses that to call GetAuthorizationCode.
|
||||
OrbisNpAuthGetAuthorizationCodeParameterA internal_params;
|
||||
std::memset(&internal_params, 0, sizeof(internal_params));
|
||||
internal_params.size = sizeof(internal_params);
|
||||
internal_params.client_id = param->client_id;
|
||||
internal_params.user_id = user_id;
|
||||
internal_params.scope = param->scope;
|
||||
|
||||
return GetAuthorizationCode(req_id, &internal_params, 0, auth_code, issuer_id);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI
|
||||
sceNpAuthGetAuthorizationCodeA(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
|
||||
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
|
||||
return GetAuthorizationCode(req_id, param, 0, auth_code, issuer_id);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI
|
||||
sceNpAuthGetAuthorizationCodeV3(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
|
||||
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
|
||||
return GetAuthorizationCode(req_id, param, 1, auth_code, issuer_id);
|
||||
}
|
||||
|
||||
s32 GetIdToken(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param, s32 flag,
|
||||
OrbisNpIdToken* token) {
|
||||
if (param == nullptr || token == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (param->size != sizeof(OrbisNpAuthGetIdTokenParameterA)) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
|
||||
}
|
||||
if (param->user_id == -1 || param->client_id == nullptr || param->client_secret == nullptr ||
|
||||
param->scope == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
// From here the actual authorization code request is performed.
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto& request = g_auth_requests[req_index];
|
||||
if (request.state == NpAuthRequestState::Complete) {
|
||||
request.result = ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
} else if (request.state == NpAuthRequestState::Aborted) {
|
||||
request.result = ORBIS_NP_AUTH_ERROR_ABORTED;
|
||||
return ORBIS_NP_AUTH_ERROR_ABORTED;
|
||||
}
|
||||
|
||||
request.state = NpAuthRequestState::Complete;
|
||||
if (!g_shadnet_enabled) {
|
||||
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
// If the request is processed in some form, and it's an async request, then it returns OK.
|
||||
if (request.async) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
|
||||
LOG_ERROR(Lib_NpAuth, "(STUBBED) called, req_id = {:#x}, async = {}", req_id, request.async);
|
||||
|
||||
// Not sure what values are expected here, so zeroing this for now.
|
||||
std::memset(token, 0, sizeof(OrbisNpIdToken));
|
||||
std::strncpy(token->token, "TOKEN", 6);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthGetIdToken(s32 req_id, const OrbisNpAuthGetIdTokenParameter* param,
|
||||
OrbisNpIdToken* token) {
|
||||
if (param == nullptr || token == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (param->size != sizeof(OrbisNpAuthGetIdTokenParameter)) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
|
||||
}
|
||||
if (param->online_id == nullptr || param->client_id == nullptr ||
|
||||
param->client_secret == nullptr || param->scope == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!g_shadnet_enabled) {
|
||||
// Calls sceNpManagerIntGetUserIdByOnlineId to get a user id, returning any errors.
|
||||
// This call will not succeed while signed out because games cannot retrieve an online id.
|
||||
return ORBIS_NP_ERROR_USER_NOT_FOUND;
|
||||
}
|
||||
|
||||
// For simplicity, call sceUserServiceGetInitialUser to get the user_id.
|
||||
s32 user_id = 0;
|
||||
ASSERT(UserService::sceUserServiceGetInitialUser(&user_id) == 0);
|
||||
|
||||
// Library constructs an OrbisNpAuthGetIdTokenParameterA struct,
|
||||
// then uses that to call GetIdToken.
|
||||
OrbisNpAuthGetIdTokenParameterA internal_params;
|
||||
std::memset(&internal_params, 0, sizeof(internal_params));
|
||||
internal_params.size = sizeof(internal_params);
|
||||
internal_params.user_id = user_id;
|
||||
internal_params.client_id = param->client_id;
|
||||
internal_params.client_secret = param->client_secret;
|
||||
internal_params.scope = param->scope;
|
||||
|
||||
return GetIdToken(req_id, &internal_params, 0, token);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthGetIdTokenA(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param,
|
||||
OrbisNpIdToken* token) {
|
||||
return GetIdToken(req_id, param, 0, token);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthGetIdTokenV3(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param,
|
||||
OrbisNpIdToken* token) {
|
||||
return GetIdToken(req_id, param, 1, token);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthSetTimeout(s32 req_id, s32 resolve_retry, u32 resolve_timeout,
|
||||
u32 conn_timeout, u32 send_timeout, u32 recv_timeout) {
|
||||
LOG_ERROR(Lib_NpAuth, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthAbortRequest(s32 req_id) {
|
||||
LOG_DEBUG(Lib_NpAuth, "called req_id = {:#x}", req_id);
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (g_auth_requests[req_index].state == NpAuthRequestState::Complete) {
|
||||
// If the request is already complete, abort is ignored.
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
g_auth_requests[req_index].state = NpAuthRequestState::Aborted;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthWaitAsync(s32 req_id, s32* result) {
|
||||
if (result == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!g_auth_requests[req_index].async ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::Ready) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
// Since we're not actually performing any sort of network request here,
|
||||
// we can just set result based on the request and return.
|
||||
*result = g_auth_requests[req_index].result;
|
||||
LOG_WARNING(Lib_NpAuth, "called req_id = {:#x}, returning result = {:#x}", req_id,
|
||||
static_cast<u32>(*result));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthPollAsync(s32 req_id, s32* result) {
|
||||
if (result == nullptr) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!g_auth_requests[req_index].async ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::Ready) {
|
||||
return ORBIS_NP_AUTH_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
// Since we're not actually performing any sort of network request here,
|
||||
// we can just set result based on the request and return.
|
||||
*result = g_auth_requests[req_index].result;
|
||||
LOG_WARNING(Lib_NpAuth, "called req_id = {:#x}, returning result = {:#x}", req_id,
|
||||
static_cast<u32>(*result));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpAuthDeleteRequest(s32 req_id) {
|
||||
LOG_DEBUG(Lib_NpAuth, "called req_id = {:#x}", req_id);
|
||||
|
||||
std::scoped_lock lk{g_auth_request_mutex};
|
||||
|
||||
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
|
||||
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
|
||||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
|
||||
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
|
||||
}
|
||||
|
||||
g_active_auth_requests--;
|
||||
g_auth_requests[req_index].state = NpAuthRequestState::None;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
g_shadnet_enabled = EmulatorSettings.IsShadNetEnabled();
|
||||
|
||||
LIB_FUNCTION("6bwFkosYRQg", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateRequest);
|
||||
LIB_FUNCTION("N+mr7GjTvr8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateAsyncRequest);
|
||||
LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCode);
|
||||
LIB_FUNCTION("qAUXQ9GdWp8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCodeA);
|
||||
LIB_FUNCTION("KI4dHLlTNl0", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCodeV3);
|
||||
LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdToken);
|
||||
LIB_FUNCTION("CocbHVIKPE8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdTokenA);
|
||||
LIB_FUNCTION("RdsFVsgSpZY", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdTokenV3);
|
||||
LIB_FUNCTION("PM3IZCw-7m0", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthSetTimeout);
|
||||
LIB_FUNCTION("cE7wIsqXdZ8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthAbortRequest);
|
||||
LIB_FUNCTION("SK-S7daqJSE", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthWaitAsync);
|
||||
LIB_FUNCTION("gjSyfzSsDcE", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthPollAsync);
|
||||
LIB_FUNCTION("H8wG9Bk-nPc", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthDeleteRequest);
|
||||
|
||||
LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuthCompat", 1, "libSceNpAuth",
|
||||
sceNpAuthGetAuthorizationCode);
|
||||
LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuthCompat", 1, "libSceNpAuth", sceNpAuthGetIdToken);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np::NpAuth
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Np::NpAuth {
|
||||
|
||||
constexpr s32 ORBIS_NP_AUTH_REQUEST_LIMIT = 0x10;
|
||||
constexpr s32 ORBIS_NP_AUTH_REQUEST_ID_OFFSET = 0x10000000;
|
||||
|
||||
struct OrbisNpAuthCreateAsyncRequestParameter {
|
||||
u64 size;
|
||||
u64 cpu_affinity_mask;
|
||||
s32 thread_priority;
|
||||
u8 padding[4];
|
||||
};
|
||||
|
||||
struct OrbisNpAuthGetAuthorizationCodeParameter {
|
||||
u64 size;
|
||||
const OrbisNpOnlineId* online_id;
|
||||
const OrbisNpClientId* client_id;
|
||||
const char* scope;
|
||||
};
|
||||
|
||||
struct OrbisNpAuthGetAuthorizationCodeParameterA {
|
||||
u64 size;
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id;
|
||||
u8 padding[4];
|
||||
const OrbisNpClientId* client_id;
|
||||
const char* scope;
|
||||
};
|
||||
|
||||
struct OrbisNpAuthGetIdTokenParameter {
|
||||
u64 size;
|
||||
const OrbisNpOnlineId* online_id;
|
||||
const OrbisNpClientId* client_id;
|
||||
const OrbisNpClientSecret* client_secret;
|
||||
const char* scope;
|
||||
};
|
||||
|
||||
struct OrbisNpAuthGetIdTokenParameterA {
|
||||
u64 size;
|
||||
Libraries::UserService::OrbisUserServiceUserId user_id;
|
||||
u8 padding[4];
|
||||
const OrbisNpClientId* client_id;
|
||||
const OrbisNpClientSecret* client_secret;
|
||||
const char* scope;
|
||||
};
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Np::NpAuth
|
||||
@@ -0,0 +1,192 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_commerce/np_commerce_ui.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "np_commerce.h"
|
||||
|
||||
namespace Libraries::Np::NpCommerce {
|
||||
|
||||
static Status g_dialog_status = Status::NONE;
|
||||
static s32 g_result_code = ORBIS_COMMON_DIALOG_RESULT_USER_CANCELED;
|
||||
static CommerceDialogState g_state;
|
||||
static CommerceDialogUi g_dialog;
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogInitialize() {
|
||||
LOG_INFO(Lib_NpCommerce, "called");
|
||||
if (g_dialog_status != Status::NONE) {
|
||||
return static_cast<s32>(Error::ALREADY_INITIALIZED);
|
||||
}
|
||||
g_dialog_status = Status::INITIALIZED;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogTerminate() {
|
||||
LOG_INFO(Lib_NpCommerce, "called");
|
||||
if (g_dialog_status == Status::NONE) {
|
||||
return static_cast<s32>(Error::NOT_INITIALIZED);
|
||||
}
|
||||
g_dialog.Finish(CommerceResult::OK);
|
||||
g_dialog_status = Status::NONE;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogClose() {
|
||||
LOG_INFO(Lib_NpCommerce, "called");
|
||||
switch (g_dialog_status) {
|
||||
case Status::NONE:
|
||||
return static_cast<s32>(Error::NOT_INITIALIZED);
|
||||
case Status::INITIALIZED:
|
||||
return static_cast<s32>(Error::NOT_RUNNING);
|
||||
case Status::FINISHED:
|
||||
return static_cast<s32>(Error::ALREADY_CLOSE);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_dialog.Finish(CommerceResult::OK);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s8 PS4_SYSV_ABI sceNpCommerceDialogGetStatus() {
|
||||
LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast<u32>(g_dialog_status));
|
||||
return static_cast<s8>(g_dialog_status);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogInitializeInternal() {
|
||||
LOG_INFO(Lib_NpCommerce, "called");
|
||||
return sceNpCommerceDialogInitialize();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogUpdateStatus() {
|
||||
LOG_DEBUG(Lib_NpCommerce, "called, status = {}", static_cast<u32>(g_dialog_status));
|
||||
return static_cast<s32>(g_dialog_status);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogGetResult(OrbisNpCommerceDialogResult* result) {
|
||||
if (result == nullptr) {
|
||||
return static_cast<s32>(Error::ARG_NULL);
|
||||
}
|
||||
if (g_dialog_status != Status::FINISHED) {
|
||||
return static_cast<s32>(Error::NOT_FINISHED);
|
||||
}
|
||||
result->result = g_result_code;
|
||||
// authorized is only meaningful in PLUS mode
|
||||
result->authorized = g_state.mode == CommerceMode::PLUS &&
|
||||
g_result_code == ORBIS_NP_COMMERCE_DIALOG_RESULT_PURCHASED;
|
||||
result->userData = g_state.user_data;
|
||||
LOG_INFO(Lib_NpCommerce, "result={} authorized={}", result->result, result->authorized);
|
||||
return g_result_code;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceDialogOpen(const OrbisNpCommerceDialogParam* param) {
|
||||
LOG_INFO(Lib_NpCommerce, "called, param = {}", static_cast<const void*>(param));
|
||||
if (g_dialog_status != Status::INITIALIZED && g_dialog_status != Status::FINISHED) {
|
||||
LOG_WARNING(Lib_NpCommerce, "Open in bad state {}", static_cast<u32>(g_dialog_status));
|
||||
return static_cast<s32>(Error::NOT_INITIALIZED);
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return static_cast<s32>(Error::ARG_NULL);
|
||||
}
|
||||
if (param->mode < ORBIS_NP_COMMERCE_DIALOG_MODE_CATEGORY ||
|
||||
param->mode > ORBIS_NP_COMMERCE_DIALOG_MODE_PLUS) {
|
||||
LOG_ERROR(Lib_NpCommerce, "SceNpCommerceDialog Parameter Error (mode={})", param->mode);
|
||||
g_result_code = static_cast<s32>(Error::PARAM_INVALID);
|
||||
g_dialog_status = Status::FINISHED;
|
||||
return static_cast<s32>(Error::PARAM_INVALID);
|
||||
}
|
||||
|
||||
g_state = CommerceDialogState{};
|
||||
g_state.mode = static_cast<CommerceMode>(param->mode);
|
||||
g_state.user_id = param->userId;
|
||||
g_state.service_label = param->serviceLabel;
|
||||
g_state.user_data = param->userData;
|
||||
// features is only meaningful in PLUS mode
|
||||
g_state.features = g_state.mode == CommerceMode::PLUS ? param->features : 0;
|
||||
|
||||
// Resolve the calling user's name for display
|
||||
char name_buf[Libraries::UserService::ORBIS_USER_SERVICE_MAX_USER_NAME_LENGTH + 1] = {};
|
||||
if (Libraries::UserService::sceUserServiceGetUserName(param->userId, name_buf,
|
||||
sizeof(name_buf)) >= 0) {
|
||||
g_state.username = name_buf;
|
||||
}
|
||||
|
||||
if (param->numTargets > ORBIS_NP_COMMERCE_DIALOG_NUM_TARGETS_MAX) {
|
||||
LOG_WARNING(Lib_NpCommerce, "numTargets {} exceeds max {}, clamping", param->numTargets,
|
||||
ORBIS_NP_COMMERCE_DIALOG_NUM_TARGETS_MAX);
|
||||
}
|
||||
const u32 count = std::min(param->numTargets, ORBIS_NP_COMMERCE_DIALOG_NUM_TARGETS_MAX);
|
||||
if (count > 0 && param->targets != nullptr) {
|
||||
g_state.targets.reserve(count);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const char* label = param->targets[i];
|
||||
if (label != nullptr) {
|
||||
g_state.targets.emplace_back(label, strnlen(label, MAX_LABEL_LEN));
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_INFO(Lib_NpCommerce,
|
||||
"open: mode={} user_id={} serviceLabel={:#x} numTargets={} features={:#x} first='{}'",
|
||||
param->mode, g_state.user_id, g_state.service_label, param->numTargets,
|
||||
g_state.features, g_state.targets.empty() ? "" : g_state.targets.front());
|
||||
|
||||
g_dialog_status = Status::RUNNING;
|
||||
g_result_code = ORBIS_COMMON_DIALOG_RESULT_USER_CANCELED;
|
||||
g_dialog = CommerceDialogUi(&g_state, &g_dialog_status, &g_result_code);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceHidePsStoreIcon() {
|
||||
LOG_INFO(Lib_NpCommerce, "called");
|
||||
PsStoreIconLayer::Instance().Hide();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceSetPsStoreIconLayout(s32 layout) {
|
||||
LOG_INFO(Lib_NpCommerce, "called, layout = {}", layout);
|
||||
if (layout < 0 || layout > 2) {
|
||||
LOG_WARNING(Lib_NpCommerce, "invalid layout {}, ignoring", layout);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
PsStoreIconLayer::Instance().SetLayout(static_cast<PsStoreIconLayout>(layout));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCommerceShowPsStoreIcon(s32 pos) {
|
||||
LOG_INFO(Lib_NpCommerce, "called, pos = {}", pos);
|
||||
const auto p =
|
||||
(pos >= 0 && pos <= 2) ? static_cast<PsStoreIconPos>(pos) : PsStoreIconPos::CENTER;
|
||||
PsStoreIconLayer::Instance().Show(p);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("NU3ckGHMFXo", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogClose);
|
||||
LIB_FUNCTION("r42bWcQbtZY", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogGetResult);
|
||||
LIB_FUNCTION("CCbC+lqqvF0", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogGetStatus);
|
||||
LIB_FUNCTION("0aR2aWmQal4", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogInitialize);
|
||||
LIB_FUNCTION("9ZiLXAGG5rg", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogInitializeInternal);
|
||||
LIB_FUNCTION("DfSCDRA3EjY", "libSceNpCommerce", 1, "libSceNpCommerce", sceNpCommerceDialogOpen);
|
||||
LIB_FUNCTION("m-I92Ab50W8", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogTerminate);
|
||||
LIB_FUNCTION("LR5cwFMMCVE", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceDialogUpdateStatus);
|
||||
LIB_FUNCTION("dsqCVsNM0Zg", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceHidePsStoreIcon);
|
||||
LIB_FUNCTION("uKTDW8hk-ts", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceSetPsStoreIconLayout);
|
||||
LIB_FUNCTION("DHmwsa6S8Tc", "libSceNpCommerce", 1, "libSceNpCommerce",
|
||||
sceNpCommerceShowPsStoreIcon);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np::NpCommerce
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Np::NpCommerce {
|
||||
|
||||
using CommonDialog::BaseParam;
|
||||
using CommonDialog::Error;
|
||||
using CommonDialog::Status;
|
||||
|
||||
static constexpr u64 MAX_LABEL_LEN = 128;
|
||||
static constexpr u32 ORBIS_NP_COMMERCE_DIALOG_NUM_TARGETS_MAX = 10;
|
||||
|
||||
struct OrbisNpCommerceDialogParam {
|
||||
BaseParam baseParam;
|
||||
s32 size;
|
||||
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||
s32 mode;
|
||||
u32 serviceLabel;
|
||||
const char* const* targets;
|
||||
u32 numTargets;
|
||||
s32 : 32;
|
||||
u64 features;
|
||||
void* userData;
|
||||
u8 reserved[32];
|
||||
};
|
||||
static_assert(sizeof(OrbisNpCommerceDialogParam) == 0x80);
|
||||
|
||||
struct OrbisNpCommerceDialogResult {
|
||||
s32 result;
|
||||
bool authorized;
|
||||
char : 8;
|
||||
short : 16;
|
||||
void* userData;
|
||||
u8 reserved[32];
|
||||
};
|
||||
static_assert(sizeof(OrbisNpCommerceDialogResult) == 0x30);
|
||||
|
||||
// OrbisNpCommerceDialogResult return codes
|
||||
static constexpr s32 ORBIS_COMMON_DIALOG_RESULT_OK = 0;
|
||||
static constexpr s32 ORBIS_COMMON_DIALOG_RESULT_USER_CANCELED = 1;
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_RESULT_PURCHASED = 2;
|
||||
|
||||
// OrbisNpCommerceDialogMode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_CATEGORY = 0; // Browse category mode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_PRODUCT = 1; // Browse product mode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_PRODUCT_CODE = 2; // promotion code mode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_CHECKOUT = 3; // Checkout mode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_DOWNLOADLIST = 4; // Download mode
|
||||
static constexpr s32 ORBIS_NP_COMMERCE_DIALOG_MODE_PLUS = 5; // PlayStation Plus mode
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Np::NpCommerce
|
||||
@@ -0,0 +1,358 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <imgui.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/np/np_commerce/np_commerce_ui.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace Libraries::CommonDialog;
|
||||
using namespace Libraries::Np::NpCommerce;
|
||||
|
||||
static constexpr ImVec2 BUTTON_SIZE{160.0f, 30.0f};
|
||||
|
||||
static const char* ModeTitle(CommerceMode m) {
|
||||
switch (m) {
|
||||
case CommerceMode::CATEGORY:
|
||||
return "shadNet Store";
|
||||
case CommerceMode::PRODUCT:
|
||||
return "Product";
|
||||
case CommerceMode::PRODUCT_CODE:
|
||||
return "Redeem Code";
|
||||
case CommerceMode::CHECKOUT:
|
||||
return "Purchase Confirmation";
|
||||
case CommerceMode::DOWNLOADLIST:
|
||||
return "My Downloads";
|
||||
case CommerceMode::PLUS:
|
||||
return "shadNet Plus";
|
||||
default:
|
||||
return "shadNet Store";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* ModeActionLabel(CommerceMode m) {
|
||||
switch (m) {
|
||||
case CommerceMode::CATEGORY:
|
||||
case CommerceMode::PRODUCT:
|
||||
case CommerceMode::CHECKOUT:
|
||||
return "Simulate purchase";
|
||||
case CommerceMode::PRODUCT_CODE:
|
||||
return "Redeem code";
|
||||
case CommerceMode::PLUS:
|
||||
return "Join SN Plus";
|
||||
case CommerceMode::DOWNLOADLIST:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* ModeEmptyText(CommerceMode m) {
|
||||
switch (m) {
|
||||
case CommerceMode::CATEGORY:
|
||||
return "Top store category.";
|
||||
case CommerceMode::PRODUCT_CODE:
|
||||
return "Enter a promotion code.";
|
||||
case CommerceMode::DOWNLOADLIST:
|
||||
return "All downloadable content.";
|
||||
case CommerceMode::PLUS:
|
||||
return "Join shadNet Plus.";
|
||||
default:
|
||||
return "No product specified.";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* ModeDescription(CommerceMode m) {
|
||||
switch (m) {
|
||||
case CommerceMode::CATEGORY:
|
||||
return "Browsing a store category.";
|
||||
case CommerceMode::PRODUCT:
|
||||
return "Viewing a product.";
|
||||
case CommerceMode::PRODUCT_CODE:
|
||||
return "Redeeming a promotion code.";
|
||||
case CommerceMode::CHECKOUT:
|
||||
return "Confirming a purchase.";
|
||||
case CommerceMode::DOWNLOADLIST:
|
||||
return "Viewing owned downloads.";
|
||||
case CommerceMode::PLUS:
|
||||
return "Joining shadNet Plus.";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string DescribeFeatures(u64 features) {
|
||||
if (features == 0) {
|
||||
return "none";
|
||||
}
|
||||
std::string out;
|
||||
if (features & 0x1) { // ORBIS_NP_PLUS_FEATURE_REALTIME_MULTIPLAY
|
||||
out += "Realtime Multiplay";
|
||||
}
|
||||
if (out.empty()) {
|
||||
return "unknown";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Decomposed CHECKOUT/DOWNLOADLIST element "[serviceLabel:]product[-sku]".
|
||||
struct ParsedTarget {
|
||||
std::string service;
|
||||
std::string product;
|
||||
std::string sku;
|
||||
};
|
||||
|
||||
static ParsedTarget ParseTarget(const std::string& t) {
|
||||
ParsedTarget out;
|
||||
std::string s = t;
|
||||
if (const auto colon = s.find(':'); colon != std::string::npos) {
|
||||
out.service = s.substr(0, colon);
|
||||
s = s.substr(colon + 1);
|
||||
}
|
||||
if (const auto dash = s.find('-'); dash != std::string::npos) {
|
||||
out.product = s.substr(0, dash);
|
||||
out.sku = s.substr(dash + 1);
|
||||
} else {
|
||||
out.product = s;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
CommerceDialogUi::CommerceDialogUi(CommerceDialogState* state, Status* status, s32* result)
|
||||
: state(state), status(status), result(result) {
|
||||
if (status && *status == Status::RUNNING) {
|
||||
first_render = true;
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
CommerceDialogUi::~CommerceDialogUi() {
|
||||
Finish(CommerceResult::USER_CANCELED);
|
||||
}
|
||||
|
||||
CommerceDialogUi::CommerceDialogUi(CommerceDialogUi&& other) noexcept
|
||||
: Layer(other), first_render(other.first_render), state(other.state), status(other.status),
|
||||
result(other.result) {
|
||||
other.state = nullptr;
|
||||
other.status = nullptr;
|
||||
other.result = nullptr;
|
||||
}
|
||||
|
||||
CommerceDialogUi& CommerceDialogUi::operator=(CommerceDialogUi&& other) noexcept {
|
||||
if (this != &other) {
|
||||
first_render = other.first_render;
|
||||
state = other.state;
|
||||
status = other.status;
|
||||
result = other.result;
|
||||
other.state = nullptr;
|
||||
other.status = nullptr;
|
||||
other.result = nullptr;
|
||||
if (status && *status == Status::RUNNING) {
|
||||
first_render = true;
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CommerceDialogUi::Finish(CommerceResult r) {
|
||||
if (result) {
|
||||
*result = static_cast<s32>(r);
|
||||
}
|
||||
if (status) {
|
||||
*status = Status::FINISHED;
|
||||
}
|
||||
state = nullptr;
|
||||
status = nullptr;
|
||||
result = nullptr;
|
||||
RemoveLayer(this);
|
||||
}
|
||||
|
||||
void CommerceDialogUi::Draw() {
|
||||
if (status == nullptr || *status != Status::RUNNING || state == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto& io = GetIO();
|
||||
const ImVec2 window_size{std::min(io.DisplaySize.x, 520.0f),
|
||||
std::min(io.DisplaySize.y, 380.0f)};
|
||||
|
||||
CentralizeNextWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowCollapsed(false);
|
||||
if (first_render || !io.NavActive) {
|
||||
SetNextWindowFocus();
|
||||
}
|
||||
KeepNavHighlight();
|
||||
if (Begin("CommerceDialog##NpCommerceDialog", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
std::string title = ModeTitle(state->mode);
|
||||
if (state->targets.size() == 1 &&
|
||||
(state->mode == CommerceMode::PRODUCT || state->mode == CommerceMode::CATEGORY)) {
|
||||
title += ": " + state->targets.front();
|
||||
}
|
||||
{
|
||||
const float tw = CalcTextSize(title.c_str()).x;
|
||||
SetCursorPosX(std::max(10.0f, (GetWindowSize().x - tw) * 0.5f));
|
||||
TextUnformatted(title.c_str());
|
||||
}
|
||||
Separator();
|
||||
|
||||
TextWrapped("%s", ModeDescription(state->mode));
|
||||
Spacing();
|
||||
|
||||
// Calling user.
|
||||
if (!state->username.empty()) {
|
||||
Text("User: %s (id %d)", state->username.c_str(), state->user_id);
|
||||
} else {
|
||||
Text("User id: %d", state->user_id);
|
||||
}
|
||||
// Service label is meaningful for category/product/checkout/downloadlist.
|
||||
if (state->mode != CommerceMode::PRODUCT_CODE && state->mode != CommerceMode::PLUS) {
|
||||
Text("Service label: %u", state->service_label);
|
||||
}
|
||||
// PS Plus feature only applies in PLUS mode.
|
||||
if (state->mode == CommerceMode::PLUS) {
|
||||
Text("PS Plus feature: %s", DescribeFeatures(state->features).c_str());
|
||||
}
|
||||
|
||||
Spacing();
|
||||
if (state->targets.empty()) {
|
||||
TextWrapped("%s", ModeEmptyText(state->mode));
|
||||
} else {
|
||||
const bool checkout_form =
|
||||
state->mode == CommerceMode::CHECKOUT || state->mode == CommerceMode::DOWNLOADLIST;
|
||||
TextDisabled(state->targets.size() == 1 ? "Target:" : "Targets:");
|
||||
for (const auto& t : state->targets) {
|
||||
if (checkout_form) {
|
||||
const ParsedTarget pt = ParseTarget(t);
|
||||
if (!pt.sku.empty() || !pt.service.empty()) {
|
||||
BulletText("Product %s", pt.product.c_str());
|
||||
if (!pt.sku.empty()) {
|
||||
Indent();
|
||||
Text("SKU: %s", pt.sku.c_str());
|
||||
Unindent();
|
||||
}
|
||||
if (!pt.service.empty()) {
|
||||
Indent();
|
||||
Text("Service: %s", pt.service.c_str());
|
||||
Unindent();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BulletText("%s", t.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Spacing();
|
||||
|
||||
const char* action = ModeActionLabel(state->mode);
|
||||
const auto ws = GetWindowSize();
|
||||
const float n = action ? 2.0f : 1.0f;
|
||||
SetCursorPos({ws.x / 2.0f - (BUTTON_SIZE.x * n + 10.0f * (n - 1.0f)) / 2.0f,
|
||||
ws.y - 10.0f - BUTTON_SIZE.y});
|
||||
BeginGroup();
|
||||
if (action) {
|
||||
if (Button(action, BUTTON_SIZE)) {
|
||||
LOG_INFO(Lib_NpCommerce, "CommerceDialog: action '{}' mode={} target='{}'", action,
|
||||
static_cast<s32>(state->mode),
|
||||
state->targets.empty() ? "" : state->targets.front());
|
||||
Finish(CommerceResult::PURCHASED);
|
||||
}
|
||||
SameLine();
|
||||
}
|
||||
if (Button("Close", BUTTON_SIZE)) {
|
||||
Finish(CommerceResult::USER_CANCELED);
|
||||
}
|
||||
// Default focus on Close so a stray confirm press dismisses, never buys.
|
||||
if (first_render) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
End();
|
||||
|
||||
first_render = false;
|
||||
}
|
||||
|
||||
static float LayoutScale(PsStoreIconLayout l) {
|
||||
switch (l) {
|
||||
case PsStoreIconLayout::FIXED_90:
|
||||
return 0.90f;
|
||||
case PsStoreIconLayout::FOLLOW_SAFE_AREA:
|
||||
return 0.95f;
|
||||
case PsStoreIconLayout::DEFAULT:
|
||||
default:
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
PsStoreIconLayer& PsStoreIconLayer::Instance() {
|
||||
static PsStoreIconLayer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void PsStoreIconLayer::Show(PsStoreIconPos p) {
|
||||
pos.store(p);
|
||||
if (!shown.exchange(true)) {
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PsStoreIconLayer::Hide() {
|
||||
if (shown.exchange(false)) {
|
||||
RemoveLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PsStoreIconLayer::SetLayout(PsStoreIconLayout l) {
|
||||
layout.store(l);
|
||||
}
|
||||
|
||||
void PsStoreIconLayer::Draw() {
|
||||
if (!shown.load()) {
|
||||
return;
|
||||
}
|
||||
const auto& io = GetIO();
|
||||
const ImVec2 ds = io.DisplaySize;
|
||||
const float scale = LayoutScale(layout.load());
|
||||
const float inset_x = ds.x * (1.0f - scale) * 0.5f;
|
||||
const float inset_y = ds.y * (1.0f - scale) * 0.5f;
|
||||
|
||||
constexpr ImVec2 icon{52.0f, 52.0f};
|
||||
constexpr float pad = 16.0f;
|
||||
|
||||
float x;
|
||||
switch (pos.load()) {
|
||||
case PsStoreIconPos::LEFT:
|
||||
x = inset_x + pad;
|
||||
break;
|
||||
case PsStoreIconPos::RIGHT:
|
||||
x = ds.x - inset_x - icon.x - pad;
|
||||
break;
|
||||
case PsStoreIconPos::CENTER:
|
||||
default:
|
||||
x = (ds.x - icon.x) * 0.5f;
|
||||
break;
|
||||
}
|
||||
const float y = ds.y - inset_y - icon.y - pad;
|
||||
|
||||
SetNextWindowPos({x, y});
|
||||
SetNextWindowSize(icon);
|
||||
if (Begin("##PsStoreIcon", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
|
||||
auto* dl = GetWindowDrawList();
|
||||
const ImVec2 p0 = GetWindowPos();
|
||||
const ImVec2 p1{p0.x + icon.x, p0.y + icon.y};
|
||||
dl->AddRectFilled(p0, p1, IM_COL32(0, 55, 145, 230), 10.0f);
|
||||
dl->AddRect(p0, p1, IM_COL32(255, 255, 255, 210), 10.0f, 0, 2.0f);
|
||||
const char* label = "SN";
|
||||
const ImVec2 ts = CalcTextSize(label);
|
||||
dl->AddText({p0.x + (icon.x - ts.x) * 0.5f, p0.y + (icon.y - ts.y) * 0.5f},
|
||||
IM_COL32(255, 255, 255, 255), label);
|
||||
}
|
||||
End();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Libraries::Np::NpCommerce {
|
||||
|
||||
// OrbisNpCommerceDialogMode
|
||||
enum class CommerceMode : s32 {
|
||||
CATEGORY = 0,
|
||||
PRODUCT = 1,
|
||||
PRODUCT_CODE = 2,
|
||||
CHECKOUT = 3,
|
||||
DOWNLOADLIST = 4,
|
||||
PLUS = 5,
|
||||
};
|
||||
|
||||
// OrbisNpCommerceDialogResult result values
|
||||
enum class CommerceResult : s32 {
|
||||
OK = 0,
|
||||
USER_CANCELED = 1,
|
||||
PURCHASED = 2,
|
||||
};
|
||||
|
||||
struct CommerceDialogState {
|
||||
std::vector<std::string> targets; // product / category labels
|
||||
std::string username; // resolved from userId, may be empty
|
||||
CommerceMode mode{CommerceMode::CATEGORY};
|
||||
s32 user_id{-1};
|
||||
u32 service_label{0};
|
||||
u64 features{0}; // ORBIS_NP_PLUS_FEATURE_* (PLUS mode only)
|
||||
void* user_data{nullptr}; // echoed back into the result struct
|
||||
};
|
||||
|
||||
class CommerceDialogUi final : public ImGui::Layer {
|
||||
bool first_render{false};
|
||||
CommerceDialogState* state{};
|
||||
CommonDialog::Status* status{};
|
||||
s32* result{};
|
||||
|
||||
public:
|
||||
explicit CommerceDialogUi(CommerceDialogState* state = nullptr,
|
||||
CommonDialog::Status* status = nullptr, s32* result = nullptr);
|
||||
~CommerceDialogUi() override;
|
||||
CommerceDialogUi(const CommerceDialogUi&) = delete;
|
||||
CommerceDialogUi& operator=(const CommerceDialogUi&) = delete;
|
||||
CommerceDialogUi(CommerceDialogUi&& other) noexcept;
|
||||
CommerceDialogUi& operator=(CommerceDialogUi&& other) noexcept;
|
||||
|
||||
void Finish(CommerceResult r);
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
// OrbisNpCommercePsStoreIconPos
|
||||
enum class PsStoreIconPos : s32 {
|
||||
CENTER = 0, // screen lower center
|
||||
LEFT = 1, // screen lower left
|
||||
RIGHT = 2, // screen lower right
|
||||
};
|
||||
|
||||
// OrbisNpCommercePsStoreIconLayout
|
||||
enum class PsStoreIconLayout : s32 {
|
||||
DEFAULT = 0, // full screen (100%)
|
||||
FOLLOW_SAFE_AREA = 1, // follow display safe area (90%-100%)
|
||||
FIXED_90 = 2, // fixed 90% of screen
|
||||
};
|
||||
|
||||
class PsStoreIconLayer final : public ImGui::Layer {
|
||||
std::atomic<bool> shown{false};
|
||||
std::atomic<PsStoreIconPos> pos{PsStoreIconPos::CENTER};
|
||||
std::atomic<PsStoreIconLayout> layout{PsStoreIconLayout::DEFAULT};
|
||||
|
||||
public:
|
||||
static PsStoreIconLayer& Instance();
|
||||
|
||||
void Show(PsStoreIconPos p);
|
||||
void Hide();
|
||||
void SetLayout(PsStoreIconLayout l);
|
||||
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np::NpCommerce
|
||||
@@ -0,0 +1,617 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/kernel/threads.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
|
||||
namespace Libraries::Np::NpCommon {
|
||||
|
||||
namespace {
|
||||
static_assert(sizeof(OrbisNpCalloutEntry) == 0x20);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, thread) == 0x00);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, mutex) == 0x08);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, cond) == 0x10);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, active) == 0x18);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, stop_requested) == 0x1c);
|
||||
static_assert(offsetof(OrbisNpCalloutContext, head) == 0x20);
|
||||
static_assert(sizeof(OrbisNpCalloutContext) == 0x28);
|
||||
|
||||
s32 NormalizeNpCommonResult(int rc) {
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
return Libraries::Kernel::ErrnoToSceKernelError(rc);
|
||||
}
|
||||
|
||||
u32 NormalizeNpTryLockResult(int rc) {
|
||||
const s32 sce_rc = NormalizeNpCommonResult(rc);
|
||||
if (sce_rc == ORBIS_KERNEL_ERROR_EBUSY) {
|
||||
return ORBIS_NP_LW_MUTEX_ERROR_BUSY;
|
||||
}
|
||||
return static_cast<u32>(sce_rc);
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI NpCalloutThreadMain(void* arg) {
|
||||
auto* ctx = static_cast<OrbisNpCalloutContext*>(arg);
|
||||
const auto mutex = &ctx->mutex;
|
||||
sceNpMutexLock(mutex);
|
||||
|
||||
for (;;) {
|
||||
s64 now_usec = 0;
|
||||
sceNpGetSystemClockUsec(&now_usec);
|
||||
|
||||
auto* entry = ctx->head;
|
||||
u32 wait_usec = 0;
|
||||
|
||||
if (entry == nullptr) {
|
||||
if (ctx->stop_requested > 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const u64 deadline = static_cast<u64>(entry->deadline_usec);
|
||||
const u64 now = static_cast<u64>(now_usec);
|
||||
const u64 remaining = deadline - now;
|
||||
if (deadline < now || remaining == 0) {
|
||||
ctx->head = entry->next;
|
||||
sceNpMutexUnlock(mutex);
|
||||
if (entry->handler != nullptr) {
|
||||
entry->handler(entry->arg);
|
||||
}
|
||||
sceNpMutexLock(mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->stop_requested > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
wait_usec = static_cast<u32>(remaining > 0xffffffffULL ? 0xffffffffULL : remaining);
|
||||
}
|
||||
|
||||
sceNpCondTimedwait(&ctx->cond, mutex, wait_usec);
|
||||
if (ctx->stop_requested > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sceNpMutexUnlock(mutex);
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCmpNpId(OrbisNpId* np_id1, OrbisNpId* np_id2) {
|
||||
if (np_id1 == nullptr || np_id2 == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (std::strncmp(np_id1->handle.data, np_id2->handle.data, ORBIS_NP_ONLINEID_MAX_LENGTH) != 0) {
|
||||
return ORBIS_NP_UTIL_ERROR_NOT_MATCH;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
if (np_id1->opt[i] != np_id2->opt[i]) {
|
||||
return ORBIS_NP_UTIL_ERROR_NOT_MATCH;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
if (np_id1->reserved[i] != np_id2->reserved[i]) {
|
||||
return ORBIS_NP_UTIL_ERROR_NOT_MATCH;
|
||||
}
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCmpNpIdInOrder(OrbisNpId* np_id1, OrbisNpId* np_id2, u32* out_result) {
|
||||
if (np_id1 == nullptr || np_id2 == nullptr || out_result == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
u32 compare =
|
||||
std::strncmp(np_id1->handle.data, np_id2->handle.data, ORBIS_NP_ONLINEID_MAX_LENGTH);
|
||||
if (compare < 0) {
|
||||
*out_result = -1;
|
||||
return ORBIS_OK;
|
||||
} else if (compare > 0) {
|
||||
*out_result = 1;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
if (np_id1->opt[i] < np_id2->opt[i]) {
|
||||
*out_result = -1;
|
||||
return ORBIS_OK;
|
||||
} else if (np_id1->opt[i] > np_id2->opt[i]) {
|
||||
*out_result = 1;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
if (np_id1->reserved[i] < np_id2->reserved[i]) {
|
||||
*out_result = -1;
|
||||
return ORBIS_OK;
|
||||
} else if (np_id1->reserved[i] > np_id2->reserved[i]) {
|
||||
*out_result = 1;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
*out_result = 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCmpOnlineId(OrbisNpOnlineId* online_id1, OrbisNpOnlineId* online_id2) {
|
||||
if (online_id1 == nullptr || online_id2 == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (std::strncmp(online_id1->data, online_id2->data, ORBIS_NP_ONLINEID_MAX_LENGTH) != 0) {
|
||||
return ORBIS_NP_UTIL_ERROR_NOT_MATCH;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpMutexLock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return static_cast<u32>(
|
||||
NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_mutex_lock(mutex)));
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpMutexUnlock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return static_cast<u32>(
|
||||
NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_mutex_unlock(mutex)));
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpMutexInit(Libraries::Kernel::PthreadMutexT* mutex, const char* name,
|
||||
u64 flags) {
|
||||
LOG_DEBUG(Lib_NpCommon, "mutex={:p} name={:p} flags={:#x}", fmt::ptr(mutex), fmt::ptr(name),
|
||||
flags);
|
||||
|
||||
Libraries::Kernel::PthreadMutexAttrT attr = nullptr;
|
||||
int rc = Libraries::Kernel::posix_pthread_mutexattr_init(&attr);
|
||||
if (rc == 0) {
|
||||
if ((flags & 1) != 0) {
|
||||
rc = Libraries::Kernel::posix_pthread_mutexattr_settype(
|
||||
&attr, Libraries::Kernel::PthreadMutexType::Recursive);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = Libraries::Kernel::scePthreadMutexInit(mutex, &attr, name);
|
||||
}
|
||||
Libraries::Kernel::posix_pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
return static_cast<u32>(NormalizeNpCommonResult(rc));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpMutexDestroy(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_DEBUG(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_mutex_destroy(mutex));
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpMutexTryLock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return NormalizeNpTryLockResult(Libraries::Kernel::posix_pthread_mutex_trylock(mutex));
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexLock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return sceNpMutexLock(mutex);
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexUnlock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return sceNpMutexUnlock(mutex);
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexInit(Libraries::Kernel::PthreadMutexT* mutex, const char* name,
|
||||
u64 flags) {
|
||||
LOG_DEBUG(Lib_NpCommon, "mutex={:p} name={:p} flags={:#x}", fmt::ptr(mutex), fmt::ptr(name),
|
||||
flags);
|
||||
return sceNpMutexInit(mutex, name, flags);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpLwMutexDestroy(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_DEBUG(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return sceNpMutexDestroy(mutex);
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexTryLock(Libraries::Kernel::PthreadMutexT* mutex) {
|
||||
LOG_TRACE(Lib_NpCommon, "mutex={:p}", fmt::ptr(mutex));
|
||||
return sceNpMutexTryLock(mutex);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCondInit(Libraries::Kernel::PthreadCondT* cond, const char* name, u64 flags) {
|
||||
LOG_DEBUG(Lib_NpCommon, "cond={:p} name={:p} flags={:#x}", fmt::ptr(cond), fmt::ptr(name),
|
||||
flags);
|
||||
|
||||
Libraries::Kernel::PthreadCondAttrT attr = nullptr;
|
||||
int rc = Libraries::Kernel::posix_pthread_condattr_init(&attr);
|
||||
if (rc == 0) {
|
||||
rc = Libraries::Kernel::scePthreadCondInit(cond, &attr, name);
|
||||
Libraries::Kernel::posix_pthread_condattr_destroy(&attr);
|
||||
}
|
||||
return NormalizeNpCommonResult(rc);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCondDestroy(Libraries::Kernel::PthreadCondT* cond) {
|
||||
LOG_DEBUG(Lib_NpCommon, "cond={:p}", fmt::ptr(cond));
|
||||
return NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_cond_destroy(cond));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCondSignal(Libraries::Kernel::PthreadCondT* cond) {
|
||||
LOG_DEBUG(Lib_NpCommon, "cond={:p}", fmt::ptr(cond));
|
||||
return NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_cond_signal(cond));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCondTimedwait(Libraries::Kernel::PthreadCondT* cond,
|
||||
Libraries::Kernel::PthreadMutexT* mutex, u32 usec) {
|
||||
LOG_DEBUG(Lib_NpCommon, "cond={:p} mutex={:p} usec={:#x}", fmt::ptr(cond), fmt::ptr(mutex),
|
||||
usec);
|
||||
if (usec == 0) {
|
||||
return NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_cond_wait(cond, mutex));
|
||||
}
|
||||
const s32 rc = NormalizeNpCommonResult(
|
||||
Libraries::Kernel::posix_pthread_cond_reltimedwait_np(cond, mutex, usec));
|
||||
if (rc == ORBIS_KERNEL_ERROR_ETIMEDOUT) {
|
||||
return ORBIS_NP_LW_COND_ERROR_TIMEDOUT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCreateThread(Libraries::Kernel::PthreadT* thread, NpThreadEntry start_routine,
|
||||
void* arg, s32 priority, u64 stack_size, u64 affinity_mask,
|
||||
const char* name) {
|
||||
LOG_DEBUG(Lib_NpCommon,
|
||||
"thread={:p} start={:#x} arg={:p} priority={} stack={:#x} "
|
||||
"affinity={:#x} name={:p}",
|
||||
fmt::ptr(thread), reinterpret_cast<u64>(start_routine), fmt::ptr(arg), priority,
|
||||
stack_size, affinity_mask, fmt::ptr(name));
|
||||
|
||||
Libraries::Kernel::PthreadAttrT attr = nullptr;
|
||||
int rc = Libraries::Kernel::posix_pthread_attr_init(&attr);
|
||||
if (rc != 0) {
|
||||
return NormalizeNpCommonResult(rc);
|
||||
}
|
||||
|
||||
rc = Libraries::Kernel::posix_pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size));
|
||||
if (rc == 0) {
|
||||
if (priority != 0) {
|
||||
s32 sdk_version = 0;
|
||||
rc = Libraries::Kernel::sceKernelGetCompiledSdkVersion(&sdk_version);
|
||||
if (rc >= 0 && sdk_version >= 0x2500000) {
|
||||
rc = Libraries::Kernel::posix_pthread_attr_setinheritsched(&attr, 0);
|
||||
if (rc == 0) {
|
||||
rc = Libraries::Kernel::posix_pthread_attr_setschedpolicy(
|
||||
&attr, Libraries::Kernel::SchedPolicy::Fifo);
|
||||
}
|
||||
}
|
||||
if (rc >= 0) {
|
||||
Libraries::Kernel::SchedParam param{.sched_priority = priority};
|
||||
rc = Libraries::Kernel::posix_pthread_attr_setschedparam(&attr, ¶m);
|
||||
}
|
||||
}
|
||||
if (rc == 0 && affinity_mask != 0) {
|
||||
rc = Libraries::Kernel::scePthreadAttrSetaffinity(&attr, affinity_mask);
|
||||
}
|
||||
if (rc == 0) {
|
||||
rc = Libraries::Kernel::posix_pthread_create_name_np(thread, &attr, start_routine, arg,
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
Libraries::Kernel::posix_pthread_attr_destroy(&attr);
|
||||
return NormalizeNpCommonResult(rc);
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpJoinThread(Libraries::Kernel::PthreadT thread, void** ret) {
|
||||
LOG_DEBUG(Lib_NpCommon, "thread={:p} ret={:p}", fmt::ptr(thread), fmt::ptr(ret));
|
||||
return static_cast<u32>(
|
||||
NormalizeNpCommonResult(Libraries::Kernel::posix_pthread_join(thread, ret)));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpGetSystemClockUsec(s64* usec) {
|
||||
LOG_DEBUG(Lib_NpCommon, "usec={:p}", fmt::ptr(usec));
|
||||
Libraries::Kernel::OrbisKernelTimespec ts{};
|
||||
const s32 rc =
|
||||
Libraries::Kernel::sceKernelClockGettime(Libraries::Kernel::ORBIS_CLOCK_MONOTONIC, &ts);
|
||||
if (rc == ORBIS_OK) {
|
||||
*usec = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpGetPlatformType(const OrbisNpId* npid) {
|
||||
LOG_DEBUG(Lib_NpCommon, "np_id={:p}", fmt::ptr(npid));
|
||||
|
||||
if (npid == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
static constexpr u32 kTagPs3 = 0x00337370; // "ps3"
|
||||
static constexpr u32 kTagVita = 0x32707370; // "psp2(VITA)"
|
||||
static constexpr u32 kTagPs4 = 0x00347370; // "ps4"
|
||||
|
||||
if (npid->opt[4] == 0) {
|
||||
return OrbisNpPlatformType::None;
|
||||
}
|
||||
u32 tag = 0;
|
||||
std::memcpy(&tag, npid->opt + 4, sizeof(tag));
|
||||
switch (tag) {
|
||||
case kTagPs3:
|
||||
return OrbisNpPlatformType::PS3;
|
||||
case kTagVita:
|
||||
return OrbisNpPlatformType::Vita;
|
||||
case kTagPs4:
|
||||
return OrbisNpPlatformType::PS4;
|
||||
default:
|
||||
return ORBIS_NP_ERROR_INVALID_PLATFORM_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* id) {
|
||||
LOG_DEBUG(Lib_NpCommon, "online_id={:p}", fmt::ptr(id));
|
||||
|
||||
if (id == nullptr || id->term != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t len = strnlen(id->data, ORBIS_NP_ONLINEID_MAX_LENGTH + 1);
|
||||
if (len < 3 || len > ORBIS_NP_ONLINEID_MAX_LENGTH) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const unsigned char first = static_cast<unsigned char>(id->data[0]);
|
||||
if (!std::isalnum(first)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < len; ++i) {
|
||||
const unsigned char c = static_cast<unsigned char>(id->data[i]);
|
||||
if (std::isalnum(c) || c == '_' || c == '-') {
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI sceNpGetSdkVersion(char* version_buf) {
|
||||
LOG_DEBUG(Lib_NpCommon, "called");
|
||||
// Real library has no parameter checks, so I guess this is purely for internal use?
|
||||
Libraries::Kernel::SwVersionStruct sw_version{};
|
||||
sw_version.struct_size = 0x28;
|
||||
Libraries::Kernel::sceKernelGetSystemSwVersion(&sw_version);
|
||||
u32 sw_hex = sw_version.hex_representation;
|
||||
snprintf(version_buf, 8, "%d.%02d", ((sw_hex >> 0x18) & 0xf) + ((sw_hex >> 0x1c) * 10),
|
||||
((sw_hex >> 0x10) & 0xf) + (((sw_hex >> 0x14) & 0xf) * 10));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* callout_ctx, const char* name,
|
||||
u64 stack_size, s32 priority, u64 affinity_mask) {
|
||||
LOG_DEBUG(Lib_NpCommon, "ctx={:p} name={:p} stack={:#x} priority={} affinity={:#x}",
|
||||
fmt::ptr(callout_ctx), fmt::ptr(name), stack_size, priority, affinity_mask);
|
||||
|
||||
s32 rc = ORBIS_NP_CALLOUT_ERROR_ALREADY_INITIALIZED;
|
||||
if (callout_ctx->active < 1) {
|
||||
callout_ctx->stop_requested = 0;
|
||||
callout_ctx->head = nullptr;
|
||||
rc = static_cast<s32>(sceNpMutexInit(&callout_ctx->mutex, name, 1));
|
||||
if (rc >= 0) {
|
||||
rc = sceNpCondInit(&callout_ctx->cond, name, 0);
|
||||
if (rc >= 0) {
|
||||
rc = sceNpCreateThread(&callout_ctx->thread, NpCalloutThreadMain, callout_ctx,
|
||||
priority, stack_size, affinity_mask, name);
|
||||
if (rc >= 0) {
|
||||
callout_ctx->active = 1;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
sceNpCondDestroy(&callout_ctx->cond);
|
||||
}
|
||||
sceNpMutexDestroy(&callout_ctx->mutex);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx(OrbisNpCalloutContext* callout_ctx,
|
||||
OrbisNpCalloutEntry* entry, u64 delay_usec, u64 handler,
|
||||
u64 arg) {
|
||||
LOG_DEBUG(Lib_NpCommon, "ctx={:p} callout={:p} delay={:#x} handler={:p} arg={:#x}",
|
||||
fmt::ptr(callout_ctx), fmt::ptr(entry), delay_usec,
|
||||
fmt::ptr(reinterpret_cast<void*>(static_cast<uintptr_t>(handler))), arg);
|
||||
|
||||
if (callout_ctx->active == 0) {
|
||||
return ORBIS_NP_CALLOUT_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
entry->next = nullptr;
|
||||
entry->handler = reinterpret_cast<OrbisNpCalloutHandler>(handler);
|
||||
entry->arg = arg;
|
||||
sceNpGetSystemClockUsec(&entry->deadline_usec);
|
||||
entry->deadline_usec += static_cast<s64>(static_cast<u32>(delay_usec));
|
||||
|
||||
sceNpMutexLock(&callout_ctx->mutex);
|
||||
|
||||
for (auto* cur = callout_ctx->head; cur != nullptr; cur = cur->next) {
|
||||
if (cur == entry) {
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
return ORBIS_NP_CALLOUT_ERROR_DUPLICATE_ENTRY;
|
||||
}
|
||||
}
|
||||
|
||||
OrbisNpCalloutEntry** prev_next = &callout_ctx->head;
|
||||
for (auto* cur = callout_ctx->head; cur != nullptr; cur = cur->next) {
|
||||
if (cur->deadline_usec >= entry->deadline_usec) {
|
||||
break;
|
||||
}
|
||||
prev_next = &cur->next;
|
||||
}
|
||||
|
||||
entry->next = *prev_next;
|
||||
*prev_next = entry;
|
||||
sceNpCondSignal(&callout_ctx->cond);
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx64(OrbisNpCalloutContext* callout_ctx,
|
||||
OrbisNpCalloutEntry* entry, s64 delay_usec, u64 handler,
|
||||
u64 arg) {
|
||||
LOG_DEBUG(Lib_NpCommon, "ctx={:p} callout={:p} delay={} handler={:p} arg={:#x}",
|
||||
fmt::ptr(callout_ctx), fmt::ptr(entry), delay_usec,
|
||||
fmt::ptr(reinterpret_cast<void*>(static_cast<uintptr_t>(handler))), arg);
|
||||
|
||||
if (callout_ctx->active == 0) {
|
||||
return ORBIS_NP_CALLOUT_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
entry->next = nullptr;
|
||||
entry->handler = reinterpret_cast<OrbisNpCalloutHandler>(handler);
|
||||
entry->arg = arg;
|
||||
sceNpGetSystemClockUsec(&entry->deadline_usec);
|
||||
entry->deadline_usec += delay_usec;
|
||||
|
||||
sceNpMutexLock(&callout_ctx->mutex);
|
||||
|
||||
for (auto* cur = callout_ctx->head; cur != nullptr; cur = cur->next) {
|
||||
if (cur == entry) {
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
return ORBIS_NP_CALLOUT_ERROR_DUPLICATE_ENTRY;
|
||||
}
|
||||
}
|
||||
|
||||
OrbisNpCalloutEntry** prev_next = &callout_ctx->head;
|
||||
for (auto* cur = callout_ctx->head; cur != nullptr; cur = cur->next) {
|
||||
if (cur->deadline_usec >= entry->deadline_usec) {
|
||||
break;
|
||||
}
|
||||
prev_next = &cur->next;
|
||||
}
|
||||
|
||||
entry->next = *prev_next;
|
||||
*prev_next = entry;
|
||||
sceNpCondSignal(&callout_ctx->cond);
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStopOnCtx(OrbisNpCalloutContext* callout_ctx,
|
||||
OrbisNpCalloutEntry* entry, u32* removed) {
|
||||
LOG_DEBUG(Lib_NpCommon, "ctx={:p} callout={:p} removed={:p}", fmt::ptr(callout_ctx),
|
||||
fmt::ptr(entry), fmt::ptr(removed));
|
||||
|
||||
if (callout_ctx->active == 0) {
|
||||
return ORBIS_NP_CALLOUT_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
sceNpMutexLock(&callout_ctx->mutex);
|
||||
u32 found = 0;
|
||||
OrbisNpCalloutEntry** prev_next = &callout_ctx->head;
|
||||
for (auto* cur = callout_ctx->head; cur != nullptr; cur = cur->next) {
|
||||
if (cur == entry) {
|
||||
*prev_next = cur->next;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
prev_next = &cur->next;
|
||||
}
|
||||
if (removed != nullptr) {
|
||||
*removed = found;
|
||||
}
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI sceNpCalloutTermCtx(OrbisNpCalloutContext* callout_ctx) {
|
||||
LOG_DEBUG(Lib_NpCommon, "ctx={:p}", fmt::ptr(callout_ctx));
|
||||
|
||||
if (callout_ctx->active != 0) {
|
||||
sceNpMutexLock(&callout_ctx->mutex);
|
||||
callout_ctx->stop_requested = 1;
|
||||
sceNpCondSignal(&callout_ctx->cond);
|
||||
sceNpMutexUnlock(&callout_ctx->mutex);
|
||||
sceNpJoinThread(callout_ctx->thread, nullptr);
|
||||
sceNpCondDestroy(&callout_ctx->cond);
|
||||
sceNpMutexDestroy(&callout_ctx->mutex);
|
||||
callout_ctx->active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("i8UmXTSq7N4", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCmpNpId);
|
||||
LIB_FUNCTION("TcwEFnakiSc", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCmpNpIdInOrder);
|
||||
LIB_FUNCTION("dj+O5aD2a0Q", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCmpOnlineId);
|
||||
LIB_FUNCTION("1a+iY5YUJcI", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCondDestroy);
|
||||
LIB_FUNCTION("9+m5nRdJ-wQ", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCalloutInitCtx);
|
||||
LIB_FUNCTION("AqJ4xkWsV+I", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCalloutTermCtx);
|
||||
LIB_FUNCTION("18j+qk6dRwk", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpLwMutexLock);
|
||||
LIB_FUNCTION("1CiXI-MyEKs", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpLwMutexInit);
|
||||
LIB_FUNCTION("4zxevggtYrQ", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpLwMutexDestroy);
|
||||
LIB_FUNCTION("CQG2oyx1-nM", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpLwMutexUnlock);
|
||||
LIB_FUNCTION("DuslmoqQ+nk", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpMutexTryLock);
|
||||
LIB_FUNCTION("EjMsfO3GCIA", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpJoinThread);
|
||||
LIB_FUNCTION("fClnlkZmA6k", "libSceNpCommonCompat", 1, "libSceNpCommon",
|
||||
sceNpCalloutStartOnCtx);
|
||||
LIB_FUNCTION("fhJ5uKzcn0w", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCreateThread);
|
||||
LIB_FUNCTION("hkeX9iuCwlI", "libSceNpCommonCompat", 1, "libSceNpCommon",
|
||||
sceNpIntIsValidOnlineId);
|
||||
LIB_FUNCTION("hp0kVgu5Fxw", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpLwMutexTryLock);
|
||||
LIB_FUNCTION("in19gH7G040", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCalloutStopOnCtx);
|
||||
LIB_FUNCTION("lQ11BpMM4LU", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpMutexDestroy);
|
||||
LIB_FUNCTION("lpr66Gby8dQ", "libSceNpCommonCompat", 1, "libSceNpCommon",
|
||||
sceNpCalloutStartOnCtx64);
|
||||
LIB_FUNCTION("oZyb9ktuCpA", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpMutexUnlock);
|
||||
LIB_FUNCTION("PVVsRmMkO1g", "libSceNpCommonCompat", 1, "libSceNpCommon",
|
||||
sceNpGetSystemClockUsec);
|
||||
LIB_FUNCTION("q2tsVO3lM4A", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCondInit);
|
||||
LIB_FUNCTION("r9Bet+s6fKc", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpMutexLock);
|
||||
LIB_FUNCTION("sXVQUIGmk2U", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpGetPlatformType);
|
||||
LIB_FUNCTION("ss2xO9IJxKQ", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCondTimedwait);
|
||||
LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpMutexInit);
|
||||
LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommonCompat", 1, "libSceNpCommon", sceNpCondSignal);
|
||||
LIB_FUNCTION("i8UmXTSq7N4", "libSceNpCommon", 1, "libSceNpCommon", sceNpCmpNpId);
|
||||
LIB_FUNCTION("TcwEFnakiSc", "libSceNpCommon", 1, "libSceNpCommon", sceNpCmpNpIdInOrder);
|
||||
LIB_FUNCTION("dj+O5aD2a0Q", "libSceNpCommon", 1, "libSceNpCommon", sceNpCmpOnlineId);
|
||||
LIB_FUNCTION("1a+iY5YUJcI", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondDestroy);
|
||||
LIB_FUNCTION("9+m5nRdJ-wQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpCalloutInitCtx);
|
||||
LIB_FUNCTION("AqJ4xkWsV+I", "libSceNpCommon", 1, "libSceNpCommon", sceNpCalloutTermCtx);
|
||||
LIB_FUNCTION("18j+qk6dRwk", "libSceNpCommon", 1, "libSceNpCommon", sceNpLwMutexLock);
|
||||
LIB_FUNCTION("1CiXI-MyEKs", "libSceNpCommon", 1, "libSceNpCommon", sceNpLwMutexInit);
|
||||
LIB_FUNCTION("4zxevggtYrQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpLwMutexDestroy);
|
||||
LIB_FUNCTION("CQG2oyx1-nM", "libSceNpCommon", 1, "libSceNpCommon", sceNpLwMutexUnlock);
|
||||
LIB_FUNCTION("DuslmoqQ+nk", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexTryLock);
|
||||
LIB_FUNCTION("EjMsfO3GCIA", "libSceNpCommon", 1, "libSceNpCommon", sceNpJoinThread);
|
||||
LIB_FUNCTION("fClnlkZmA6k", "libSceNpCommon", 1, "libSceNpCommon", sceNpCalloutStartOnCtx);
|
||||
LIB_FUNCTION("fhJ5uKzcn0w", "libSceNpCommon", 1, "libSceNpCommon", sceNpCreateThread);
|
||||
LIB_FUNCTION("hkeX9iuCwlI", "libSceNpCommon", 1, "libSceNpCommon", sceNpIntIsValidOnlineId);
|
||||
LIB_FUNCTION("hp0kVgu5Fxw", "libSceNpCommon", 1, "libSceNpCommon", sceNpLwMutexTryLock);
|
||||
LIB_FUNCTION("in19gH7G040", "libSceNpCommon", 1, "libSceNpCommon", sceNpCalloutStopOnCtx);
|
||||
LIB_FUNCTION("lQ11BpMM4LU", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexDestroy);
|
||||
LIB_FUNCTION("lpr66Gby8dQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpCalloutStartOnCtx64);
|
||||
LIB_FUNCTION("oZyb9ktuCpA", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexUnlock);
|
||||
LIB_FUNCTION("PVVsRmMkO1g", "libSceNpCommon", 1, "libSceNpCommon", sceNpGetSystemClockUsec);
|
||||
LIB_FUNCTION("q2tsVO3lM4A", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondInit);
|
||||
LIB_FUNCTION("r9Bet+s6fKc", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexLock);
|
||||
LIB_FUNCTION("sXVQUIGmk2U", "libSceNpCommon", 1, "libSceNpCommon", sceNpGetPlatformType);
|
||||
LIB_FUNCTION("ss2xO9IJxKQ", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondTimedwait);
|
||||
LIB_FUNCTION("uEwag-0YZPc", "libSceNpCommon", 1, "libSceNpCommon", sceNpMutexInit);
|
||||
LIB_FUNCTION("uMJFOA62mVU", "libSceNpCommon", 1, "libSceNpCommon", sceNpCondSignal);
|
||||
LIB_FUNCTION("Pglk7zFj0DI", "libSceNpCommon", 1, "libSceNpCommon", sceNpGetSdkVersion);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np::NpCommon
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Np::NpCommon {
|
||||
|
||||
struct OrbisNpFreeKernelMemoryArgs {
|
||||
u64 length;
|
||||
u64 unk;
|
||||
void* addr;
|
||||
};
|
||||
|
||||
using NpThreadEntry = void* PS4_SYSV_ABI (*)(void*);
|
||||
using OrbisNpCalloutHandler = void PS4_SYSV_ABI (*)(u64);
|
||||
|
||||
struct OrbisNpCalloutEntry {
|
||||
OrbisNpCalloutEntry* next;
|
||||
OrbisNpCalloutHandler handler;
|
||||
s64 deadline_usec;
|
||||
u64 arg;
|
||||
};
|
||||
|
||||
struct OrbisNpCalloutContext {
|
||||
Libraries::Kernel::PthreadT thread;
|
||||
Libraries::Kernel::PthreadMutexT mutex;
|
||||
Libraries::Kernel::PthreadCondT cond;
|
||||
s32 active;
|
||||
s32 stop_requested;
|
||||
OrbisNpCalloutEntry* head;
|
||||
};
|
||||
|
||||
u32 PS4_SYSV_ABI sceNpMutexLock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpMutexUnlock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpMutexInit(Libraries::Kernel::PthreadMutexT* mutex, const char* name,
|
||||
u64 flags);
|
||||
s32 PS4_SYSV_ABI sceNpMutexDestroy(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpMutexTryLock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexLock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexUnlock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexInit(Libraries::Kernel::PthreadMutexT* mutex, const char* name,
|
||||
u64 flags);
|
||||
s32 PS4_SYSV_ABI sceNpLwMutexDestroy(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
u32 PS4_SYSV_ABI sceNpLwMutexTryLock(Libraries::Kernel::PthreadMutexT* mutex);
|
||||
s32 PS4_SYSV_ABI sceNpCondInit(Libraries::Kernel::PthreadCondT* cond, const char* name, u64 flags);
|
||||
s32 PS4_SYSV_ABI sceNpCondDestroy(Libraries::Kernel::PthreadCondT* cond);
|
||||
s32 PS4_SYSV_ABI sceNpCondSignal(Libraries::Kernel::PthreadCondT* cond);
|
||||
s32 PS4_SYSV_ABI sceNpCondTimedwait(Libraries::Kernel::PthreadCondT* cond,
|
||||
Libraries::Kernel::PthreadMutexT* mutex, u32 usec);
|
||||
s32 PS4_SYSV_ABI sceNpCreateThread(Libraries::Kernel::PthreadT* thread, NpThreadEntry start_routine,
|
||||
void* arg, s32 priority, u64 stack_size, u64 affinity_mask,
|
||||
const char* name);
|
||||
u32 PS4_SYSV_ABI sceNpJoinThread(Libraries::Kernel::PthreadT thread, void** ret);
|
||||
s32 PS4_SYSV_ABI sceNpGetSystemClockUsec(s64* usec);
|
||||
s32 PS4_SYSV_ABI sceNpGetPlatformType(const OrbisNpId* np_id);
|
||||
s32 PS4_SYSV_ABI sceNpIntIsValidOnlineId(const OrbisNpOnlineId* id);
|
||||
void PS4_SYSV_ABI sceNpGetSdkVersion(char* version_buf);
|
||||
s32 PS4_SYSV_ABI sceNpCalloutInitCtx(OrbisNpCalloutContext* ctx, const char* name, u64 stack_size,
|
||||
s32 priority, u64 affinity_mask);
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout,
|
||||
u64 delay_usec, u64 handler, u64 arg);
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStartOnCtx64(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout,
|
||||
s64 delay_usec, u64 handler, u64 arg);
|
||||
s32 PS4_SYSV_ABI sceNpCalloutStopOnCtx(OrbisNpCalloutContext* ctx, OrbisNpCalloutEntry* callout,
|
||||
u32* removed);
|
||||
void PS4_SYSV_ABI sceNpCalloutTermCtx(OrbisNpCalloutContext* ctx);
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Np::NpCommon
|
||||
@@ -0,0 +1,784 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
// Base NP errors (0x80550001 - 0x8055001F)
|
||||
constexpr int ORBIS_NP_ERROR_ALREADY_INITIALIZED = 0x80550001;
|
||||
constexpr int ORBIS_NP_ERROR_NOT_INITIALIZED = 0x80550002;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003;
|
||||
constexpr int ORBIS_NP_ERROR_UNKNOWN_PLATFORM_TYPE = 0x80550004;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_PLATFORM_TYPE = 0x80550004;
|
||||
constexpr int ORBIS_NP_ERROR_OUT_OF_MEMORY = 0x80550005;
|
||||
constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006;
|
||||
constexpr int ORBIS_NP_ERROR_USER_NOT_FOUND = 0x80550007;
|
||||
constexpr int ORBIS_NP_ERROR_CALLBACK_ALREADY_REGISTERED = 0x80550008;
|
||||
constexpr int ORBIS_NP_ERROR_CALLBACK_NOT_REGISTERED = 0x80550009;
|
||||
constexpr int ORBIS_NP_ERROR_NOT_SIGNED_UP = 0x8055000A;
|
||||
constexpr int ORBIS_NP_ERROR_AGE_RESTRICTION = 0x8055000B;
|
||||
constexpr int ORBIS_NP_ERROR_LOGOUT = 0x8055000C;
|
||||
constexpr int ORBIS_NP_ERROR_LATEST_SYSTEM_SOFTWARE_EXIST = 0x8055000D;
|
||||
constexpr int ORBIS_NP_ERROR_LATEST_SYSTEM_SOFTWARE_EXIST_FOR_TITLE = 0x8055000E;
|
||||
constexpr int ORBIS_NP_ERROR_LATEST_PATCH_PKG_EXIST = 0x8055000F;
|
||||
constexpr int ORBIS_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED = 0x80550010;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_SIZE = 0x80550011;
|
||||
constexpr int ORBIS_NP_ERROR_ABORTED = 0x80550012;
|
||||
constexpr int ORBIS_NP_ERROR_REQUEST_MAX = 0x80550013;
|
||||
constexpr int ORBIS_NP_ERROR_REQUEST_NOT_FOUND = 0x80550014;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015;
|
||||
constexpr int ORBIS_NP_ERROR_NP_TITLE_DAT_NOT_FOUND = 0x80550016;
|
||||
constexpr int ORBIS_NP_ERROR_INCONSISTENT_NP_TITLE_ID = 0x80550017;
|
||||
constexpr int ORBIS_NP_ERROR_PATCH_NOT_CHECKED = 0x80550018;
|
||||
constexpr int ORBIS_NP_ERROR_TITLE_IS_BANNED = 0x80550019;
|
||||
constexpr int ORBIS_NP_ERROR_TIMEOUT = 0x8055001A;
|
||||
constexpr int ORBIS_NP_ERROR_TITLE_ID_IN_PARAM_SFO_NOT_MATCHED_TO_NP_TITLE_ID = 0x8055001B;
|
||||
constexpr int ORBIS_NP_ERROR_TITLE_ID_IN_PARAM_SFO_NOT_EXIST = 0x8055001C;
|
||||
constexpr int ORBIS_NP_ERROR_CALLBACK_MAX = 0x8055001D;
|
||||
constexpr int ORBIS_NP_ERROR_INVALID_NP_TITLE_ID = 0x8055001E;
|
||||
constexpr int ORBIS_NP_ERROR_ONLINE_ID_CHANGED = 0x8055001F;
|
||||
constexpr int ORBIS_NP_INT_ERROR_INVALID_ARGUMENT = 0x8055A283;
|
||||
constexpr int ORBIS_NP_SCORE_INVALID_ARGUMENT = 0x80550704;
|
||||
constexpr int ORBIS_NP_WEB_API_INVALID_ARGUMENT = 0x80552902;
|
||||
|
||||
// NP Util (0x80550601 - 0x8055060e)
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_ARGUMENT = 0x80550601;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INSUFFICIENT = 0x80550602;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_PARSER_FAILED = 0x80550603;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_PROTOCOL_ID = 0x80550604;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_NP_ID = 0x80550605;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_NP_ENV = 0x80550606;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_CHARACTER = 0x80550608;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_NOT_MATCH = 0x80550609;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_TITLEID = 0x8055060A;
|
||||
constexpr int ORBIS_NP_UTIL_ERROR_UNKNOWN = 0x8055060E;
|
||||
|
||||
// NP Auth errors (0x80550300 - 0x8055040F)
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT = 0x80550301;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_SIZE = 0x80550302;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_OUT_OF_MEMORY = 0x80550303;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ABORTED = 0x80550304;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_REQUEST_MAX = 0x80550305;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND = 0x80550306;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_ID = 0x80550307;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_NO_TOKEN_RECEIVED = 0x80550308;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_SERVICE_END = 0x80550400;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_SERVICE_DOWN = 0x80550401;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_SERVICE_BUSY = 0x80550402;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_SERVER_MAINTENANCE = 0x80550403;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_DATA_LENGTH = 0x80550410;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_USER_AGENT = 0x80550411;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_VERSION = 0x80550412;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_SERVICE_ID = 0x80550420;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_CREDENTIAL = 0x80550421;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_ENTITLEMENT_ID = 0x80550422;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_S_INVALID_CONSUMED_COUNT = 0x80550423;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_CONSOLE_ID = 0x80550424;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_CONSOLE_ID_SUSPENDED = 0x80550427;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_CLOSED = 0x80550430;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_SUSPENDED = 0x80550431;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_EULA = 0x80550432;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT1 = 0x80550440;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT2 = 0x80550441;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT3 = 0x80550442;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT4 = 0x80550443;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT5 = 0x80550444;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT6 = 0x80550445;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT7 = 0x80550446;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT8 = 0x80550447;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT9 = 0x80550448;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT10 = 0x80550449;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT11 = 0x8055044A;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT12 = 0x8055044B;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT13 = 0x8055044C;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT14 = 0x8055044D;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT15 = 0x8055044E;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT16 = 0x8055044F;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_SUB_ACCOUNT_RENEW_EULA = 0x8055044F;
|
||||
constexpr int ORBIS_NP_AUTH_ERROR_UNKNOWN = 0x80550480;
|
||||
|
||||
// NP Community / Score client errors (0x80550700 - 0x8055071D)
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_ALREADY_INITIALIZED = 0x80550701;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_NOT_INITIALIZED = 0x80550702;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_OUT_OF_MEMORY = 0x80550703;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT = 0x80550704;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_NO_LOGIN = 0x80550705;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS = 0x80550706;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_ABORTED = 0x80550707;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_BAD_RESPONSE = 0x80550708;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_BODY_TOO_LARGE = 0x80550709;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_HTTP_SERVER = 0x8055070A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_SIGNATURE = 0x8055070B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT = 0x8055070C;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_UNKNOWN_TYPE = 0x8055070D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ID = 0x8055070E;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID = 0x8055070F;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_TYPE = 0x80550711;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TRANSACTION_ALREADY_END = 0x80550712;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_PARTITION = 0x80550713;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT = 0x80550714;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_CLIENT_HANDLE_ALREADY_EXISTS = 0x80550715;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_NO_RESOURCE = 0x80550716;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_REQUEST_BEFORE_END = 0x80550717;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID = 0x80550718;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_NPID = 0x80550719;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_SCORE_INVALID_SAVEDATA_OWNER = 0x8055071A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_TUS_INVALID_SAVEDATA_OWNER = 0x8055071B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_GHOST_SERVER_RETURN_INVALID_STATUS_CODE = 0x8055071C;
|
||||
constexpr int ORBIS_NP_COMMUNITY_ERROR_UBS_ONLINE_ID_IN_XML_CREATED_PAST_IS_DIFFERENT_FROM_CURRENT =
|
||||
0x8055071D;
|
||||
|
||||
// NP Community / Score server errors (0x80550800 - 0x805508AB)
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BAD_REQUEST = 0x80550801;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_TICKET = 0x80550802;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_SIGNATURE = 0x80550803;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_NPID = 0x80550805;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN = 0x80550806;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INTERNAL_SERVER_ERROR = 0x80550807;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_VERSION_NOT_SUPPORTED = 0x80550808;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_SERVICE_UNAVAILABLE = 0x80550809;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_PLAYER_BANNED = 0x8055080A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_CENSORED = 0x8055080B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_RECORD_FORBIDDEN = 0x8055080C;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_USER_PROFILE_NOT_FOUND = 0x8055080D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UPLOADER_DATA_NOT_FOUND = 0x8055080E;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_QUOTA_MASTER_NOT_FOUND = 0x8055080F;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_TITLE_NOT_FOUND = 0x80550810;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BLACKLISTED_USER_ID = 0x80550811;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND = 0x80550812;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND = 0x80550814;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE = 0x80550815;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_LATEST_UPDATE_NOT_FOUND = 0x80550816;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_BOARD_MASTER_NOT_FOUND = 0x80550817;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND = 0x80550818;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ANTICHEAT_DATA = 0x80550819;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_LARGE_DATA = 0x8055081A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_USER_NPID = 0x8055081B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ENVIRONMENT = 0x8055081D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ONLINE_NAME_CHARACTER = 0x8055081F;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ONLINE_NAME_LENGTH = 0x80550820;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ABOUT_ME_CHARACTER = 0x80550821;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_ABOUT_ME_LENGTH = 0x80550822;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE = 0x80550823;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_OVER_THE_RANKING_LIMIT = 0x80550824;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_FAIL_TO_CREATE_SIGNATURE = 0x80550826;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_MASTER_INFO_NOT_FOUND = 0x80550827;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_OVER_THE_GAME_DATA_LIMIT = 0x80550828;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_SELF_DATA_NOT_FOUND = 0x8055082A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED = 0x8055082B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS = 0x8055082C;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_MANY_RESULTS = 0x8055082D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NOT_RECORDABLE_VERSION = 0x8055082E;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_USER_STORAGE_TITLE_MASTER_NOT_FOUND = 0x80550848;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_VIRTUAL_USER = 0x80550849;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_USER_STORAGE_DATA_NOT_FOUND = 0x8055084A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NON_PLUS_MEMBER = 0x8055085D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNMATCH_SEQUENCE = 0x8055085E;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_SAVEDATA_NOT_FOUND = 0x8055085F;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_MANY_SAVEDATA_FILES = 0x80550860;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_MUCH_TOTAL_SAVEDATA_SIZE = 0x80550861;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NOT_YET_DOWNLOADABLE = 0x80550862;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BLACKLISTED_TITLE = 0x80550868;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_LARGE_ICONDATA = 0x80550869;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_LARGE_SAVEDATA = 0x8055086A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNMATCH_SIGNATURE = 0x8055086B;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNMATCH_MD5SUM = 0x8055086C;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TOO_MUCH_SAVEDATA_SIZE = 0x8055086D;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RECORD_DATE_IS_NEWER_THAN_COMP_DATE = 0x8055086E;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED = 0x80550873;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNSUPPORTED_PLATFORM = 0x80550878;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_EXPIRED_SIGNATURE = 0x80550889;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_SAVEDATA_UPDATED = 0x8055088A;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_MATCHING_BEFORE_SERVICE = 0x805508A0;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_MATCHING_END_OF_SERVICE = 0x805508A1;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_MATCHING_MAINTENANCE = 0x805508A2;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_BEFORE_SERVICE = 0x805508A3;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_END_OF_SERVICE = 0x805508A4;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_RANKING_MAINTENANCE = 0x805508A5;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE = 0x805508A6;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_BEFORE_SERVICE = 0x805508AA;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_END_OF_SERVICE = 0x805508AB;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_MAINTENANCE = 0x805508AC;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_FSR_BEFORE_SERVICE = 0x805508AD;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_FSR_END_OF_SERVICE = 0x805508AE;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_FSR_MAINTENANCE = 0x805508AF;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UBS_BEFORE_SERVICE = 0x805508B0;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UBS_END_OF_SERVICE = 0x805508B1;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UBS_MAINTENANCE = 0x805508B2;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BASIC_BLACKLISTED_USER_ID = 0x805508B3;
|
||||
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x805508FF;
|
||||
|
||||
// NP Matching2 (0x80550c01 - 0x80550d33)
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_OUT_OF_MEMORY = 0x80550C01;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED = 0x80550C02;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED = 0x80550C03;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_MAX = 0x80550C04;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_ALREADY_EXISTS = 0x80550C05;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND = 0x80550C06;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_ALREADY_STARTED = 0x80550C07;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED = 0x80550C08;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SERVER_NOT_FOUND = 0x80550C09;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT = 0x80550C0A;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID = 0x80550C0B;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_SERVER_ID = 0x80550C0C;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_WORLD_ID = 0x80550C0D;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_LOBBY_ID = 0x80550C0E;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ROOM_ID = 0x80550C0F;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_MEMBER_ID = 0x80550C10;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ATTRIBUTE_ID = 0x80550C11;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_CASTTYPE = 0x80550C12;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_SORT_METHOD = 0x80550C13;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_MAX_SLOT = 0x80550C14;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_MATCHING_SPACE = 0x80550C16;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_BLOCK_KICK_FLAG = 0x80550C17;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_MESSAGE_TARGET = 0x80550C18;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_RANGE_FILTER_MAX = 0x80550C19;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INSUFFICIENT_BUFFER = 0x80550C1A;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_DESTINATION_DISAPPEARED = 0x80550C1B;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_REQUEST_TIMEOUT = 0x80550C1C;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ALIGNMENT = 0x80550C1D;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONNECTION_CLOSED_BY_SERVER = 0x80550C1E;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SSL_VERIFY_FAILED = 0x80550C1F;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SSL_HANDSHAKE = 0x80550C20;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SSL_SEND = 0x80550C21;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SSL_RECV = 0x80550C22;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_JOINED_SESSION_MAX = 0x80550C23;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_ALREADY_JOINED = 0x80550C24;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_SESSION_TYPE = 0x80550C25;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_NP_SIGNED_OUT = 0x80550C26;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_BUSY = 0x80550C27;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SERVER_NOT_AVAILABLE = 0x80550C28;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_NOT_ALLOWED = 0x80550C29;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_ABORTED = 0x80550C2A;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND = 0x80550C2B;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SESSION_DESTROYED = 0x80550C2C;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CONTEXT_STOPPED = 0x80550C2D;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_REQUEST_PARAMETER = 0x80550C2E;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_NOT_NP_SIGN_IN = 0x80550C2F;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_ROOM_NOT_FOUND = 0x80550C30;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_ROOM_MEMBER_NOT_FOUND = 0x80550C31;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_LOBBY_NOT_FOUND = 0x80550C32;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_LOBBY_MEMBER_NOT_FOUND = 0x80550C33;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_KEEPALIVE_TIMEOUT = 0x80550C34;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_TIMEOUT_TOO_SHORT = 0x80550C35;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_TIMEDOUT = 0x80550C36;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_SLOTGROUP = 0x80550C37;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_ATTRIBUTE_SIZE = 0x80550C38;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_CANNOT_ABORT = 0x80550C39;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_SESSION_NOT_FOUND = 0x80550C3A;
|
||||
constexpr int ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT = 0x80550C3B;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_BAD_REQUEST = 0x80550D01;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_SERVICE_UNAVAILABLE = 0x80550D02;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_BUSY = 0x80550D03;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_END_OF_SERVICE = 0x80550D04;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_INTERNAL_SERVER_ERROR = 0x80550D05;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_PLAYER_BANNED = 0x80550D06;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_FORBIDDEN = 0x80550D07;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_BLOCKED = 0x80550D08;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_UNSUPPORTED_NP_ENV = 0x80550D09;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_INVALID_TICKET = 0x80550D0A;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_INVALID_SIGNATURE = 0x80550D0B;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_EXPIRED_TICKET = 0x80550D0C;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ENTITLEMENT_REQUIRED = 0x80550D0D;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_CONTEXT = 0x80550D0E;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_CLOSED = 0x80550D0F;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_TITLE = 0x80550D10;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_WORLD = 0x80550D11;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_LOBBY = 0x80550D12;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM = 0x80550D13;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_LOBBY_INSTANCE = 0x80550D14;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM_INSTANCE = 0x80550D15;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_PASSWORD_MISMATCH = 0x80550D17;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_LOBBY_FULL = 0x80550D18;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ROOM_FULL = 0x80550D19;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_GROUP_FULL = 0x80550D1B;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER = 0x80550D1C;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_GROUP_PASSWORD_MISMATCH = 0x80550D1D;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_TITLE_PASSPHRASE_MISMATCH = 0x80550D1E;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_LOBBY_ALREADY_EXIST = 0x80550D25;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ROOM_ALREADY_EXIST = 0x80550D26;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_CONSOLE_BANNED = 0x80550D28;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_ROOMGROUP = 0x80550D29;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_SUCH_GROUP = 0x80550D2A;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NO_PASSWORD = 0x80550D2B;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_INVALID_GROUP_SLOT_NUM = 0x80550D2C;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_INVALID_PASSWORD_SLOT_MASK = 0x80550D2D;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_DUPLICATE_GROUP_LABEL = 0x80550D2E;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_REQUEST_OVERFLOW = 0x80550D2F;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ALREADY_JOINED = 0x80550D30;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH = 0x80550D31;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ROOM_INCONSISTENCY = 0x80550D32;
|
||||
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_BLOCKED_USER_IN_ROOM = 0x80550D33;
|
||||
|
||||
// NP Matching2 Signaling (0x80550e01 - 0x80550e1a)
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_NOT_INITIALIZED = 0x80550E01;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80550E02;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_OUT_OF_MEMORY = 0x80550E03;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CTXID_NOT_AVAILABLE = 0x80550E04;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CTX_NOT_FOUND = 0x80550E05;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_REQID_NOT_AVAILABLE = 0x80550E06;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_REQ_NOT_FOUND = 0x80550E07;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_PARSER_CREATE_FAILED = 0x80550E08;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_PARSER_FAILED = 0x80550E09;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_INVALID_NAMESPACE = 0x80550E0A;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_NETINFO_NOT_AVAILABLE = 0x80550E0B;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_PEER_NOT_RESPONDING = 0x80550E0C;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CONNID_NOT_AVAILABLE = 0x80550E0D;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CONN_NOT_FOUND = 0x80550E0E;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_PEER_UNREACHABLE = 0x80550E0F;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_PEER = 0x80550E10;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_TIMEOUT = 0x80550E11;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CTX_MAX = 0x80550E12;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_RESULT_NOT_FOUND = 0x80550E13;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_CONN_IN_PROGRESS = 0x80550E14;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_INVALID_ARGUMENT = 0x80550E15;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_OWN_NP_ID = 0x80550E16;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_TOO_MANY_CONN = 0x80550E17;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_MYSELF = 0x80550E18;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_MATCHING2_PEER_NOT_FOUND = 0x80550E19;
|
||||
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_OWN_PEER_ADDRESS = 0x80550E1A;
|
||||
|
||||
// NP Trophy (0x80551600 - 0x805516c2)
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_TITLE_ID = 0x80551620;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TITLE = 0x8055162A;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_GOTO_PROCESS_SUSPEND = 0x8055162C;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_ON_MUTE = 0x8055162E;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_TOO_BIG = 0x8055162F;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_RETRY_COUNT_MAX = 0x80551630;
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_NOT_FOUND = 0x805516C2;
|
||||
|
||||
// NP Bandwidth Test (0x80551f02 - 0x80551f09)
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_NOT_INITIALIZED = 0x80551F02;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_BAD_RESPONSE = 0x80551F03;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_OUT_OF_MEMORY = 0x80551F04;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_INVALID_ARGUMENT = 0x80551F05;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_INVALID_SIZE = 0x80551F06;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_CONTEXT_NOT_AVAILABLE = 0x80551F07;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_ABORTED = 0x80551F08;
|
||||
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_TIMEOUT = 0x80551F09;
|
||||
|
||||
// NP Party (0x80552501 - 0x80552516)
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_UNKNOWN = 0x80552501;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_ALREADY_INITIALIZED = 0x80552502;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_NOT_INITIALIZED = 0x80552503;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_ARGUMENT = 0x80552504;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_OUT_OF_MEMORY = 0x80552505;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_NOT_IN_PARTY = 0x80552506;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_VOICE_NOT_ENABLED = 0x80552507;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_MEMBER_NOT_FOUND = 0x80552508;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_SEND_BUSY = 0x80552509;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_SEND_OUT_OF_CONTEXT = 0x80552510;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_STATE = 0x80552511;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_LOCAL_PARTY_MEMBER = 0x80552512;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_PROCESS_TYPE = 0x80552513;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_GAME_SESSION_NOT_ENABLED = 0x80552514;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_PARTY_NO_FRIENDS = 0x80552515;
|
||||
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_PARTY_IS_GAME_SESSION = 0x80552516;
|
||||
|
||||
// NP Signaling (0x80552701 - 0x8055271b)
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_NOT_INITIALIZED = 0x80552701;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80552702;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_OUT_OF_MEMORY = 0x80552703;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CTXID_NOT_AVAILABLE = 0x80552704;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CTX_NOT_FOUND = 0x80552705;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_REQID_NOT_AVAILABLE = 0x80552706;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_REQ_NOT_FOUND = 0x80552707;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_PARSER_CREATE_FAILED = 0x80552708;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_PARSER_FAILED = 0x80552709;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_INVALID_NAMESPACE = 0x8055270A;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_NETINFO_NOT_AVAILABLE = 0x8055270B;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_PEER_NOT_RESPONDING = 0x8055270C;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CONNID_NOT_AVAILABLE = 0x8055270D;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CONN_NOT_FOUND = 0x8055270E;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_PEER_UNREACHABLE = 0x8055270F;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_TERMINATED_BY_PEER = 0x80552710;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_TIMEOUT = 0x80552711;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CTX_MAX = 0x80552712;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_RESULT_NOT_FOUND = 0x80552713;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_CONN_IN_PROGRESS = 0x80552714;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_INVALID_ARGUMENT = 0x80552715;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_OWN_NP_ID = 0x80552716;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_TOO_MANY_CONN = 0x80552717;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_TERMINATED_BY_MYSELF = 0x80552718;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_PROHIBITED_TO_USE = 0x80552719;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_EXCEED_RATE_LIMIT = 0x8055271A;
|
||||
constexpr int ORBIS_NP_SIGNALING_ERROR_OWN_PEER_ADDRESS = 0x8055271B;
|
||||
|
||||
// NP WebAPI (0x80552901 - 0x8055291f)
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_OUT_OF_MEMORY = 0x80552901;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT = 0x80552902;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID = 0x80552903;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_NOT_FOUND = 0x80552904;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_NOT_FOUND = 0x80552905;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_REQUEST_NOT_FOUND = 0x80552906;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_NOT_SIGNED_IN = 0x80552907;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_CONTENT_PARAMETER = 0x80552908;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_ABORTED = 0x80552909;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_ALREADY_EXIST = 0x8055290A;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055290B;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055290C;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_HANDLE_NOT_FOUND = 0x8055290D;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055290E;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_SERVICE_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055290F;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x80552910;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_BUSY = 0x80552911;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_BUSY = 0x80552912;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_REQUEST_BUSY = 0x80552913;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_HTTP_STATUS_CODE = 0x80552914;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_PROHIBITED_HTTP_HEADER = 0x80552915;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_PROHIBITED_FUNCTION_CALL = 0x80552916;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_MULTIPART_PART_NOT_FOUND = 0x80552917;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_PARAMETER_TOO_LONG = 0x80552918;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_HANDLE_BUSY = 0x80552919;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_LIB_CONTEXT_MAX = 0x8055291A;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_USER_CONTEXT_MAX = 0x8055291B;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055291C;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055291D;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_AFTER_SEND = 0x8055291E;
|
||||
constexpr int ORBIS_NP_WEBAPI_ERROR_TIMEOUT = 0x8055291F;
|
||||
|
||||
// NP In-Game Message (0x80552b01 - 0x80552b09)
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_OUT_OF_MEMORY = 0x80552B01;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_INVALID_ARGUMENT = 0x80552B02;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_LIB_CONTEXT_NOT_FOUND = 0x80552B03;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_NOT_SIGNED_IN = 0x80552B04;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_HANDLE_NOT_FOUND = 0x80552B05;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_ABORTED = 0x80552B06;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x80552B07;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_NOT_PREPARED = 0x80552B08;
|
||||
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_EXCEED_RATE_LIMIT = 0x80552B09;
|
||||
|
||||
// NP ID Mapper (0x80553000 - 0x80553003)
|
||||
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ABORTED = 0x80553000;
|
||||
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ACCOUNT_ID_NOT_FOUND = 0x80553001;
|
||||
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ONLINE_ID_NOT_FOUND = 0x80553002;
|
||||
constexpr int ORBIS_NP_ID_MAPPER_ERROR_NP_ID_NOT_FOUND = 0x80553003;
|
||||
|
||||
// NP Data Communication (0x80553200 - 0x80553218)
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_UNKNOWN = 0x80553200;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_NOT_INITIALIZED = 0x80553201;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_ALREADY_INITIALIZED = 0x80553202;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_OUT_OF_MEMORY = 0x80553203;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_SIZE = 0x80553204;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_ARGUMENT = 0x80553205;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_CONTEXT_ID = 0x80553206;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_USER_ID = 0x80553207;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_SERVICE_LABEL = 0x80553208;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_EVENT_HANDLER = 0x80553209;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_PEER_ADDRESS = 0x8055320A;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_ACCOUNT_ID = 0x8055320B;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_PLATFORM_TYPE = 0x8055320C;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_SIGNALING_CONNECTION_ID = 0x8055320D;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_PEER_CONNECTION_ID = 0x8055320E;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_DATA_CHANNEL_ID = 0x8055320F;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_DATA_CHANNEL_NUMBER = 0x80553210;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_DATA_CHANNEL_OPTION = 0x80553211;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INVALID_DATA_CHANNEL_OPTION_VALUE = 0x80553212;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_USER_NOT_LOGGED_IN = 0x80553213;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_USER_NOT_SIGNED_IN = 0x80553214;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_CONTEXT_EXCEEDS_MAX = 0x80553215;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_SIGNALING_EXCEEDS_MAX = 0x80553216;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_DATA_CHANNEL_EXCEEDS_MAX = 0x80553217;
|
||||
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INSUFFICIENT_BUFFER = 0x80553218;
|
||||
|
||||
// NP Session Signaling (0x80553301 - 0x80553312)
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED = 0x80553301;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80553302;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT = 0x80553303;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_OWN_PEER_ADDRESS = 0x80553304;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_OUT_OF_MEMORY = 0x80553305;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TIMEOUT = 0x80553306;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_CTXID_NOT_AVAILABLE = 0x80553307;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_CTX_NOT_FOUND = 0x80553308;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_GRPID_NOT_AVAILABLE = 0x80553309;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_GRP_NOT_FOUND = 0x8055330A;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_CONNID_NOT_AVAILABLE = 0x8055330B;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND = 0x8055330C;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_PEER_UNREACHABLE = 0x8055330D;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_CONN_IN_PROGRESS = 0x8055330E;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TERMINATED_BY_PEER = 0x8055330F;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TERMINATED_BY_MYSELF = 0x80553310;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TOO_MANY_CONN = 0x80553311;
|
||||
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_GROUP_SIGNALING_ALREADY_ACTIVATED = 0x80553312;
|
||||
|
||||
// NP WEBAPI2 (0x80553401 - 0x8055341c)
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_OUT_OF_MEMORY = 0x80553401;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT = 0x80553402;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID = 0x80553403;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND = 0x80553404;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND = 0x80553405;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND = 0x80553406;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_NOT_SIGNED_IN = 0x80553407;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_CONTENT_PARAMETER = 0x80553408;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_ABORTED = 0x80553409;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_ALREADY_EXIST = 0x8055340a;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_FILTER_NOT_FOUND = 0x8055340b;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x8055340c;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_NOT_FOUND = 0x8055340d;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x8055340e;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_BUSY = 0x8055340f;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_BUSY = 0x80553410;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_REQUEST_BUSY = 0x80553411;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_HTTP_STATUS_CODE = 0x80553412;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_HTTP_HEADER = 0x80553413;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PROHIBITED_FUNCTION_CALL = 0x80553414;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_MULTIPART_PART_NOT_FOUND = 0x80553415;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PARAMETER_TOO_LONG = 0x80553416;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_HANDLE_BUSY = 0x80553417;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_MAX = 0x80553418;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_MAX = 0x80553419;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND = 0x8055341a;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_TIMEOUT = 0x8055341b;
|
||||
constexpr int ORBIS_NP_WEBAPI2_ERROR_PUSH_CONTEXT_NOT_FOUND = 0x8055341c;
|
||||
|
||||
// NP Session Management Client (0x80553600 - 0x8055361e)
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ALREADY_INITIALIZED = 0x80553600;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_NOT_INITIALIZED = 0x80553601;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_OUT_OF_MEMORY = 0x80553602;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_ARGUMENT = 0x80553603;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_USER_ID = 0x80553604;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_ACCOUNT_ID = 0x80553605;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_PLATFORM_TYPE = 0x80553606;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_ONLINE_ID = 0x80553607;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_SESSION_ID = 0x80553608;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_BRIDGE_ID = 0x80553609;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_BRIDGE_TOKEN = 0x8055360A;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_ETAG = 0x8055360B;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_PEER_ADDRESS = 0x8055360C;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_CHANNEL = 0x8055360D;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_MESSAGE = 0x8055360E;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_PUSH_CONTEXT_ID = 0x8055360F;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_SESSION_CUSTOM_DATA = 0x80553610;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_MEMBER_CUSTOM_DATA = 0x80553611;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_FIELDS = 0x80553612;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ABORTED = 0x80553613;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_UNKNOWN = 0x80553614;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INSUFFICIENT_BUFFER = 0x80553615;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ITEM_NOT_FOUND = 0x80553616;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_DATA_MALFORMED = 0x80553617;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_TOPOLOGY = 0x80553618;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_SESSION_NAME = 0x80553619;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_DEVICE_ID = 0x8055361A;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_GROUP_ID = 0x8055361B;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_VIEW_NAME = 0x8055361C;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_EVENT = 0x8055361D;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_MEMBER_ID = 0x8055361E;
|
||||
|
||||
// NP Session Management Manager (0x80553700 - 0x80553736)
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_ALREADY_INITIALIZED = 0x80553700;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_NOT_INITIALIZED = 0x80553701;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_OUT_OF_MEMORY = 0x80553702;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_ARGUMENT = 0x80553703;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_INITIALIZE_PARAMETER = 0x80553704;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_CONTEXT_PARAMETER = 0x80553705;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_HANDLER = 0x80553706;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_CONTEXT_ID = 0x80553707;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_OPTION = 0x80553708;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_ACCOUNT_ID = 0x80553709;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_PLATFORM_TYPE = 0x8055370A;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_ID = 0x8055370B;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_BRIDGE_INFO = 0x8055370C;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_BRIDGE_ID = 0x8055370D;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_BRIDGE_TOKEN = 0x8055370E;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_ETAG = 0x8055370F;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_CHANNEL = 0x80553710;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_MESSAGE = 0x80553711;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_REQUEST = 0x80553712;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_REQUEST_ID = 0x80553713;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_REQUEST_CALLBACK = 0x80553714;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_REQUEST_CALLBACK_ALREADY_REGISTERED =
|
||||
0x80553715;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_DATA_VALUE_TYPE =
|
||||
0x80553716;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_MEMBER_DATA_VALUE_TYPE = 0x80553717;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_DATA_VALUE = 0x80553718;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_MEMBER_DATA_VALUE = 0x80553719;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_MEMBER_ID = 0x8055371A;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_ABORTED = 0x8055371B;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_CONTEXT_NOT_STARTED = 0x8055371C;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_CONTEXT_ALREADY_STARTED = 0x8055371D;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_UNKNOWN = 0x8055371E;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INSUFFICIENT_BUFFER = 0x8055371F;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_REQUEST_NOT_FOUND = 0x80553720;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_SESSION_NOT_FOUND = 0x80553721;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_SESSION_ALREADY_EXISTS = 0x80553722;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_CONTEXT_SLOT_EXCEEDS_MAX = 0x80553723;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_UNEXPECTED_DEACTIVATED_ERRCODE = 0x80553724;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_CMD_HANDLER_STOPPED = 0x80553725;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_MEMBER_NOT_FOUND = 0x80553726;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_MEMBER_ALREADY_EXISTS = 0x80553727;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_STATE = 0x80553728;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_SESSION_RELEASED = 0x80553729;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_MEMBER_FULL = 0x8055372A;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_CONTEXT_TYPE = 0x8055372B;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_REQUEST_NOT_FINISHED = 0x8055372C;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_QUEUE_EMPTY = 0x8055372D;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_PRESENCE_OFFLINE_DETECTED = 0x8055372E;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_PROPERTY = 0x8055372F;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_PROPERTY_NOT_FOUND = 0x80553730;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_UNSUPPORTED_OPERATION = 0x80553731;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_GROUP_ID = 0x80553732;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_VIEW_NAME = 0x80553733;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_BRIDGE_INFO_NOT_FOUND = 0x80553734;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_ITEM_NOT_FOUND = 0x80553735;
|
||||
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_CUSTOM_DATA = 0x80553736;
|
||||
|
||||
// NP Game Intent (0x80553800 - 0x80553807)
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_UNKNOWN = 0x80553800;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_ALREADY_INITIALIZED = 0x80553801;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_NOT_INITIALIZED = 0x80553802;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_OUT_OF_MEMORY = 0x80553803;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_INVALID_ARGUMENT = 0x80553804;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_INSUFFICIENT_BUFFER = 0x80553805;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_INTENT_NOT_FOUND = 0x80553806;
|
||||
constexpr int ORBIS_NP_GAME_INTENT_ERROR_VALUE_NOT_FOUND = 0x80553807;
|
||||
|
||||
// NP ASM Client (0x8055a287 - 0x8055a289)
|
||||
constexpr int ORBIS_NP_ASM_CLIENT_ERROR_ABORTED = 0x8055A287;
|
||||
constexpr int ORBIS_NP_ASM_CLIENT_ERROR_NP_SERVICE_LAVEL_NOT_MATCH = 0x8055A289;
|
||||
|
||||
// NP Callout / LW Mutex (0x80558001 - 0x8055800F)
|
||||
constexpr int ORBIS_NP_CALLOUT_ERROR_NOT_INITIALIZED = 0x80558001;
|
||||
constexpr int ORBIS_NP_CALLOUT_ERROR_ALREADY_INITIALIZED = 0x80558002;
|
||||
constexpr int ORBIS_NP_CALLOUT_ERROR_DUPLICATE_ENTRY = 0x80558006;
|
||||
constexpr int ORBIS_NP_LW_COND_ERROR_TIMEDOUT = 0x8055800B;
|
||||
constexpr int ORBIS_NP_LW_MUTEX_ERROR_BUSY = 0x8055800F;
|
||||
|
||||
// NP Signaling internal errors (0x80559601 - 0x8055960F)
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_INVALID_ARGUMENT = 0x80559601;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_DATA_TOO_BIG = 0x80559602;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_INVALID_ACTION = 0x80559603;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_CONN_NOT_ACTIVATED = 0x80559604;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_OUT_OF_MEMORY = 0x80559605;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_PARSER_FAILED = 0x80559606;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_INVALID_NAMESPACE = 0x80559607;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_ALLOCATOR = 0x80559608;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_IPC_CONTEXT_NOT_FOUND = 0x80559609;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_ALREADY_INITIALIZED = 0x8055960A;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_NOT_INITIALIZED = 0x8055960B;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_XMPP_IPC_INVALID_MSG_TYPE = 0x8055960C;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_XMPP_IPC_INVALID_ID = 0x8055960D;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_INVALID_DATA_TYPE = 0x8055960E;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_ERROR_CALLBACK_QUEUE_OVERFLOW = 0x8055960F;
|
||||
|
||||
// NP Signaling internal socket / core errors
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_CORE_ERROR_80550E81 = 0x80550E81;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_CORE_ERROR_80550E82 = 0x80550E82;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_CORE_ERROR_80550E83 = 0x80550E83;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SERVICE_ERROR_TRANSIENT_SOCKET_ALLOC = 0x80559611;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SERVICE_ERROR_POLL_PENDING = 0x80559D1B;
|
||||
|
||||
// NP Signaling internal socket errors
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SOCKET_ERROR_BASE = 0x80410100;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SOCKET_ERROR_SHORT_IO = 0x80410104;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SOCKET_ERROR_ECHO_RECV_REBUILD = 0x80410123;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SOCKET_ERROR_80410124 = 0x80410124;
|
||||
|
||||
// NP Signaling internal STUN errors
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_ALREADY_INITIALIZED = 0x80412401;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_NOT_INITIALIZED = 0x80412402;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_TIMEOUT = 0x80412403;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_BAD_RESPONSE_HEADER = 0x80412404;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_BAD_ATTRIBUTE_LENGTH = 0x80412405;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_PARSE_FAILED = 0x80412406;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_UNSUPPORTED_ADDRESS_FAMILY = 0x80412407;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_NO_SLOT = 0x80412409;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_BAD_QUEUE_INDEX = 0x8041240A;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_MESSAGE_INTEGRITY = 0x8041240B;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_BAD_XOR_MAPPED_ADDRESS = 0x8041240C;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_ALLOC_FAILED = 0x8041240D;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_CANCELLED = 0x8041240E;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_NO_RESULT = 0x8041240F;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_QUEUE_SEND_FAILED = 0x80412410;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_BAD_SERVICE_FLAGS = 0x80412411;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_STUN_ERROR_SERVICE_UNAVAILABLE = 0x80412412;
|
||||
|
||||
// NP Signaling internal UPNP errors
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_INITIALIZATION_FAILED = 0x80414101;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_NET_UNAVAILABLE = 0x80414102;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_INVALID_ARGUMENT = 0x80414105;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_DISCOVERY_PENDING = 0x80414108;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_HTTP_SOCKET_FAILED = 0x8041410A;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SERVICE_NOT_FOUND = 0x8041410B;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_INVALID_ACTION = 0x80414171;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_INVALID_ARGS = 0x80414172;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_INVALID_VAR = 0x80414173;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_ACTION_FAILED = 0x80414174;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_WILDCARD_NOT_PERMITTED = 0x80414175;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_EXTERNAL_PORT_WILDCARD_ONLY =
|
||||
0x80414176;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_CONFLICT_MAPPING_ENTRY = 0x80414177;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_SAME_PORT_VALUES_REQUIRED =
|
||||
0x80414178;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_ONLY_PERMANENT_LEASE_SUPPORTED =
|
||||
0x80414179;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_REMOTE_HOST_WILDCARD_ONLY =
|
||||
0x8041417A;
|
||||
constexpr int
|
||||
ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_EXTERNAL_PORT_WILDCARD_NOT_PERMITTED =
|
||||
0x8041417B;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_NO_PORT_MAPS_AVAILABLE = 0x8041417C;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SOAP_FAULT_GENERIC = 0x8041417D;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_8041417E = 0x8041417E;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_80414181 = 0x80414181;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_80414184 = 0x80414184;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_FORMAT_OR_BUFFER = 0x80414185;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SSDP_ADDR = 0x80414186;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SSDP_TIMEOUT = 0x80414187;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_80414188 = 0x80414188;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_HTTP_URL_INVALID = 0x80414189;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_SSDP_IGNORED = 0x8041418A;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_8041418B = 0x8041418B;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_CONTENT_LENGTH_MISMATCH = 0x8041418D;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_UNSUPPORTED_ACTION = 0x8041418F;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_80414190 = 0x80414190;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_XML_ARGUMENT = 0x804141A0;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_XML_INPUT = 0x804141A1;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_UPNP_ERROR_XML_SPACE = 0x804141A2;
|
||||
|
||||
// NP Signaling internal misc errors
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SHA1_ERROR_INVALID_ARGUMENT = 0x80480300;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_SHA1_ERROR_INACTIVE_CONTEXT = 0x80480302;
|
||||
constexpr int ORBIS_NP_SIGNALING_INTERNAL_HMAC_ERROR_INVALID_ARGUMENT = 0x80481000;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,325 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_score/np_score.h"
|
||||
#include "core/libraries/np/np_score/np_score_ctx.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "shadnet/client.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
class NpHandler {
|
||||
public:
|
||||
static NpHandler& GetInstance();
|
||||
|
||||
NpHandler(const NpHandler&) = delete;
|
||||
NpHandler& operator=(const NpHandler&) = delete;
|
||||
|
||||
// Connect every currently-logged-in user that has shadNet credentials.
|
||||
void Initialize();
|
||||
|
||||
// Disconnect all clients, stop worker threads, fire SignedOut for each.
|
||||
void Shutdown();
|
||||
|
||||
// Connect/disconnect a single user in response to a user service login/logout event.
|
||||
void OnUserLoggedIn(s32 user_id);
|
||||
void OnUserLoggedOut(s32 user_id);
|
||||
|
||||
// True if this specific user is authenticated to the shadNet server.
|
||||
bool IsPsnSignedIn(s32 user_id) const;
|
||||
|
||||
/// True if any user is currently signed in
|
||||
bool IsAnySignedIn() const;
|
||||
|
||||
// Set the Appear-Offline preference for all signed-in users (and future logins). While
|
||||
// enabled, shadNet handles the user as offline for everyone else. Call from the UI/config.
|
||||
void SetAppearOffline(bool enable);
|
||||
bool IsAppearOffline() const {
|
||||
return m_appear_offline.load();
|
||||
}
|
||||
|
||||
/// Full NP ID for this user, built once from shadnet_npid after login.
|
||||
OrbisNpId GetNpId(s32 user_id) const;
|
||||
|
||||
/// The Online ID embedded in the NP ID (npid.handle).
|
||||
OrbisNpOnlineId GetOnlineId(s32 user_id) const;
|
||||
|
||||
// Avatar URL returned by the server.
|
||||
std::string GetAvatarUrl(s32 user_id) const;
|
||||
|
||||
// 64-bit account ID assigned by the server.
|
||||
OrbisNpAccountId GetAccountId(s32 user_id) const;
|
||||
|
||||
// WebAPI bearer token
|
||||
std::string GetBearerToken(s32 user_id) const;
|
||||
|
||||
// Sends a session invitation via the WebAPI (POST /v1/sessions/<id>/invitations). Each 'to'
|
||||
// entry is an online ID (npid) or a decimal account ID
|
||||
bool SendSessionInvitation(s32 user_id, const std::string& session_id,
|
||||
const std::vector<std::string>& to, const std::string& message);
|
||||
|
||||
// Raises ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION (0x10000002) so titles that watch the
|
||||
// system-service event learn about the invite.
|
||||
void PostSessionInvitationEvent(const std::string& session_id, const std::string& invitation_id,
|
||||
const std::string& accepter_online_id);
|
||||
|
||||
// A session invitation surfaced from a shadNet push, stashed until the user acts on it.
|
||||
struct PendingInvitation {
|
||||
std::string session_id;
|
||||
std::string invitation_id;
|
||||
std::string from_npid; // sender (for display)
|
||||
std::string to_npid; // local recipient / accepter
|
||||
int64_t valid_until = 0; // ms since epoch; 0 = never expires
|
||||
};
|
||||
// Pending invitations stashed for a local user (newest last), populated on arrival.
|
||||
std::vector<PendingInvitation> GetPendingInvitations(s32 user_id) const;
|
||||
// Consumes an invitation server-side (PUT usedFlag=true) and drops it from the stash. Returns
|
||||
// true if the invite was found and the consume succeeded.
|
||||
bool AcceptSessionInvitation(s32 user_id, const std::string& invitation_id);
|
||||
// Dismisses an invitation locally by dropping it from the stash. No server call (a declined
|
||||
// invite is left unconsumed and simply ages out server-side).
|
||||
void DeclineSessionInvitation(s32 user_id, const std::string& invitation_id);
|
||||
|
||||
// Local IP address (network byte order) as seen at connect time.
|
||||
u32 GetLocalIpAddr(s32 user_id) const;
|
||||
|
||||
// Reverse lookup: server account_id to local user_id.
|
||||
// Returns -1 if no connected user owns that account_id.
|
||||
s32 GetUserIdByAccountId(u64 account_id) const;
|
||||
|
||||
// Reverse lookup: OrbisNpOnlineId to local user_id.
|
||||
// Scans m_np_ids for a matching handle.data string.
|
||||
// Returns -1 if no connected user has that Online ID.
|
||||
s32 GetUserIdByOnlineId(const OrbisNpOnlineId& online_id) const;
|
||||
|
||||
// Friend list
|
||||
u32 GetNumFriends(s32 user_id) const;
|
||||
std::optional<std::string> GetFriendNpid(s32 user_id, u32 index) const;
|
||||
|
||||
// ---- Friend list / requests / blocked (shadNet) ----
|
||||
struct FriendInfo {
|
||||
std::string npid;
|
||||
bool online = false;
|
||||
};
|
||||
struct FriendListSnapshot {
|
||||
std::vector<FriendInfo> friends;
|
||||
std::vector<std::string> requests_received;
|
||||
std::vector<std::string> requests_sent;
|
||||
std::vector<std::string> blocked;
|
||||
};
|
||||
|
||||
// Local users that currently have an authenticated shadNet session.
|
||||
std::vector<s32> GetConnectedUsers() const;
|
||||
|
||||
// user's friend state for UI display.
|
||||
FriendListSnapshot GetFriendList(s32 user_id) const;
|
||||
|
||||
// Friend/block actions
|
||||
s32 SendFriendRequest(s32 user_id, const std::string& npid);
|
||||
s32 RemoveFriend(s32 user_id, const std::string& npid);
|
||||
s32 BlockUser(s32 user_id, const std::string& npid);
|
||||
s32 UnblockUser(s32 user_id, const std::string& npid);
|
||||
|
||||
// Submit a RecordScore request to the shadNet server.
|
||||
s32 RecordScore(s32 user_id, s32 service_label, u32 boardId, s32 pcId, s64 score,
|
||||
const char* comment, size_t commentLen, const u8* gameInfoData,
|
||||
size_t gameInfoSize, std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
s32 RecordGameData(s32 user_id, s32 service_label, u32 boardId, s32 pcId, s64 score,
|
||||
const u8* data, size_t size, std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
s32 GetGameData(s32 user_id, s32 service_label, u32 boardId, const std::string& npId, s32 pcId,
|
||||
void* dataOut, u64 recvSize, u64* totalSizeOut,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
s32 GetGameDataByAccountId(s32 user_id, s32 service_label, u32 boardId, u64 accountId, s32 pcId,
|
||||
void* dataOut, u64 recvSize, u64* totalSizeOut,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
s32 GetBoardInfo(s32 user_id, s32 service_label, u32 boardId,
|
||||
NpScore::OrbisNpScoreBoardInfo* boardInfo,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// Submit a GetRankingByNpId request to the shadNet server.
|
||||
s32 GetRankingByNpId(s32 user_id, s32 service_label, u32 boardId,
|
||||
const std::vector<std::string>& npIds, const std::vector<s32>& pcIds,
|
||||
NpScore::OrbisNpScorePlayerRankData* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// Submit a GetRankingByRange request to the shadNet server.
|
||||
s32 GetRankingByRange(s32 user_id, s32 service_label, u32 boardId, u32 startSerialRank,
|
||||
u32 arrayNum, NpScore::OrbisNpScoreRankData* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// A-variant of GetRankingByRange.
|
||||
s32 GetRankingByRangeA(s32 user_id, s32 service_label, u32 boardId, u32 startSerialRank,
|
||||
u32 arrayNum, NpScore::OrbisNpScoreRankDataA* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
s32 GetRankingByAccountId(s32 user_id, s32 service_label, u32 boardId,
|
||||
const std::vector<u64>& accountIds, const std::vector<s32>& pcIds,
|
||||
NpScore::OrbisNpScorePlayerRankDataA* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// Submit a GetFriendsRanking request to the shadNet server.
|
||||
s32 GetFriendsRanking(s32 user_id, s32 service_label, u32 boardId, bool includeSelf,
|
||||
u32 arrayNum, NpScore::OrbisNpScoreRankData* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// A-variant of GetFriendsRanking
|
||||
s32 GetFriendsRankingA(s32 user_id, s32 service_label, u32 boardId, bool includeSelf,
|
||||
u32 arrayNum, NpScore::OrbisNpScoreRankDataA* rankArray,
|
||||
NpScore::OrbisNpScoreComment* commentArray,
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray,
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate, u32* totalRecord,
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req);
|
||||
|
||||
// State callbacks
|
||||
using StateCallback = std::function<void(Libraries::UserService::OrbisUserServiceUserId user_id,
|
||||
NpManager::OrbisNpState state)>;
|
||||
|
||||
s32 RegisterStateCallback(StateCallback cb, void* userdata);
|
||||
void UnregisterStateCallback(s32 handle);
|
||||
|
||||
private:
|
||||
NpHandler() = default;
|
||||
~NpHandler() = default;
|
||||
|
||||
/// Connect one user. Blocks until connected+authenticated or failed.
|
||||
bool ConnectUser(s32 user_id, const std::string& host, u16 port, const std::string& npid,
|
||||
const std::string& password, const std::string& token);
|
||||
|
||||
// Connect a single logged-in user by id (looks up credentials, parses server).
|
||||
bool ConnectUserById(s32 user_id);
|
||||
|
||||
// Parse the configured shadNet server "host:port" (default port 31313).
|
||||
std::pair<std::string, u16> ParseServerAddress() const;
|
||||
|
||||
// Start the health-monitor worker thread if not already running (idempotent).
|
||||
void StartWorker();
|
||||
|
||||
// Disconnect and remove one user's client.
|
||||
void DisconnectUser(s32 user_id);
|
||||
|
||||
void WorkerThread();
|
||||
// Transparent reconnect after a network drop
|
||||
void MarkForReconnect(s32 user_id);
|
||||
void TryReconnect();
|
||||
void FireStateCallback(s32 user_id, NpManager::OrbisNpState state);
|
||||
|
||||
// Notification forwarders wired into each client
|
||||
void OnFriendQuery(s32 user_id, const ShadNet::NotifyFriendQuery& n);
|
||||
void OnFriendNew(s32 user_id, const ShadNet::NotifyFriendNew& n);
|
||||
void OnFriendLost(s32 user_id, const ShadNet::NotifyFriendLost& n);
|
||||
void OnFriendStatus(s32 user_id, const ShadNet::NotifyFriendStatus& n);
|
||||
void OnWebApiPushEvent(s32 user_id, const ShadNet::NotifyWebApiPushEvent& n);
|
||||
void OnLoginResult(s32 user_id, const ShadNet::LoginResult& res);
|
||||
|
||||
// General async reply dispatch. Routes a reply to the owning subsystem by
|
||||
// command. Called from the per-user ShadNetClient on the reader thread.
|
||||
void OnAsyncReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body);
|
||||
|
||||
void OnScoreReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body);
|
||||
|
||||
// 12-byte NP Communication ID
|
||||
std::string GetNpCommId(s32 service_label) const;
|
||||
|
||||
// Appear-Offline preference, applied to every client at login and on change.
|
||||
std::atomic<bool> m_appear_offline{false};
|
||||
|
||||
// Per-user client map
|
||||
mutable std::mutex m_mutex_clients;
|
||||
std::map<s32, std::shared_ptr<ShadNet::ShadNetClient>> m_clients;
|
||||
// Per-user NP ID built once from shadnet_npid after login.
|
||||
std::map<s32, OrbisNpId> m_np_ids;
|
||||
|
||||
// Score requests awaiting a reply, keyed by the submit packet id.
|
||||
struct PendingScoreRequest {
|
||||
std::shared_ptr<NpScore::ScoreRequestCtx> req;
|
||||
ShadNet::CommandType cmd;
|
||||
std::vector<std::string> requestedNpIds;
|
||||
NpScore::OrbisNpScorePlayerRankData* rankArray = nullptr;
|
||||
NpScore::OrbisNpScoreRankData* plainRankArray = nullptr;
|
||||
NpScore::OrbisNpScoreRankDataA* aRankArray = nullptr;
|
||||
NpScore::OrbisNpScorePlayerRankDataA* aPlayerRankArray = nullptr;
|
||||
NpScore::OrbisNpScoreComment* commentArray = nullptr;
|
||||
NpScore::OrbisNpScoreGameInfo* infoArray = nullptr;
|
||||
NpScore::OrbisNpScoreBoardInfo* boardInfo = nullptr;
|
||||
// GetGameData / GetGameDataByAccountId output buffers.
|
||||
void* dataOut = nullptr;
|
||||
u64 recvSize = 0;
|
||||
u64* totalSizeOut = nullptr;
|
||||
Libraries::Rtc::OrbisRtcTick* lastSortDate = nullptr;
|
||||
u32* totalRecord = nullptr;
|
||||
u64 arrayNum = 0;
|
||||
};
|
||||
mutable std::mutex m_mutex_pending_score;
|
||||
std::map<u64, PendingScoreRequest> m_pending_score;
|
||||
|
||||
// Worker thread
|
||||
std::atomic<bool> m_initialized{false};
|
||||
std::atomic<bool> m_worker_running{false};
|
||||
std::thread m_worker_thread;
|
||||
|
||||
// Users dropped by a network error and awaiting transparent reconnect
|
||||
struct ReconnectState {
|
||||
std::chrono::steady_clock::time_point next_attempt{};
|
||||
std::chrono::milliseconds backoff{0};
|
||||
};
|
||||
std::unordered_map<s32, ReconnectState> m_reconnect;
|
||||
|
||||
mutable std::mutex m_mutex_pending_invites;
|
||||
std::unordered_map<s32, std::vector<PendingInvitation>> m_pending_invites;
|
||||
|
||||
// State callbacks
|
||||
struct CbEntry {
|
||||
s32 handle;
|
||||
StateCallback cb;
|
||||
void* userdata;
|
||||
};
|
||||
mutable std::mutex m_mutex_cbs;
|
||||
std::vector<CbEntry> m_state_cbs;
|
||||
s32 m_next_handle{1};
|
||||
|
||||
// Friend state per user, seeded from LoginReply and updated by notifications/actions.
|
||||
mutable std::mutex m_mutex_friend_state;
|
||||
std::map<s32, FriendListSnapshot> m_friend_state;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 220 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user