Static Makefile, ParaLLEl GX2, and libretro-common for the network

The core builds with a static Makefile (Makefile + Makefile.common) in the
manner of the other ParaLLEl cores, for every target .gitlab-ci.yml has:
Linux x86_64/aarch64, Windows (MinGW, native or cross), macOS, iOS, tvOS,
Android arm64/x86_64 and webOS. CMake, vcpkg and their overlay ports are
gone; the libraries the core needs are submodules built with it (fmt
12.1.0, glm 1.0.1, glslang 15.1.0, libusb 1.0.29, pugixml 1.15, zlib
1.3.1, zstd 1.5.7), with the generated files and the graphic packs
produced by the Makefile itself (tools/pack_graphic_packs.c), and a
precompiled header for gcc and clang.

The core is renamed parallel_gx2 (parallel_gx2_libretro.info, library
name "ParaLLEl GX2").

HTTP and TLS go through RetroArch's libretro-common (net_http with the
per-request TLS settings, the built-in TLS client, x509, the hashes and
the KDF) in place of libcurl, OpenSSL and mbedTLS: dependencies/curl-compat
is the small libcurl API nlibcurl and napi use, on one transfer thread with
lock-free submission and per-handle event queues, with its own tests
against local HTTP and TLS servers, plain and under ASan/UBSan and TSan,
in both CIs. The libretro-common files are plain copies from RetroArch.

boost is replaced by the few headers the core uses (boost-compat), and
the CI files build with the Makefile.
This commit is contained in:
libretroadmin committed 2026-11-07 06:02:30 +01:00
1 parent 91fb753ab8
commit a2de2322c8
243 files changed
+70435 -8375

No files matched your search

+52 -149
View File
@@ -1,10 +1,10 @@
name: Build libretro core name: Build libretro core
# Builds cemu_libretro for every target the core currently supports and uploads # Builds parallel_gx2_libretro with the Makefile for every target the core
# each result as an artifact, so a release can be assembled from them. # currently supports and uploads each result as an artifact, so a release
# # can be assembled from them. The dependencies are submodules built with
# The Android jobs mirror .gitlab-ci.yml, whose android-cmake template has # the core, so a job installs no libraries beyond the system's GL and X11
# nowhere to run (this core is not on git.libretro.com). # headers on Linux. Also runs the network layer's tests.
on: on:
push: push:
@@ -19,9 +19,6 @@ on:
type: string type: string
default: all default: all
env:
VCPKG_ROOT: "${{github.workspace}}/dependencies/vcpkg"
# Test builds can be limited to the platforms their testers use: the # Test builds can be limited to the platforms their testers use: the
# platforms input (workflow_dispatch) lists them. Pushes and "all" build # platforms input (workflow_dispatch) lists them. Pushes and "all" build
# everything. # everything.
@@ -30,7 +27,7 @@ jobs:
if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'linux') if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'linux')
name: linux-${{ matrix.arch }} name: linux-${{ matrix.arch }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 350 timeout-minutes: 120
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -48,52 +45,44 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: "Install dependencies" - name: "Install dependencies"
# the headers the OpenGL and Vulkan surfaces are declared with
run: | run: |
sudo apt update -qq sudo apt update -qq
sudo apt install -y --no-install-recommends \ sudo apt install -y --no-install-recommends g++-12 \
cmake ninja-build nasm autoconf automake libtool pkg-config python3 \ libgl-dev libegl-dev libx11-dev libxrender-dev libx11-xcb-dev
libglm-dev libgl1-mesa-dev libegl1-mesa-dev freeglut3-dev \
libx11-dev libxext-dev libxrandr-dev libxi-dev libxcursor-dev \
libxss-dev libxkbcommon-dev libwayland-dev wayland-protocols \
libudev-dev libdbus-1-dev libgcrypt20-dev libpulse-dev \
libsecret-1-dev libsystemd-dev libbluetooth-dev
- name: "Bootstrap vcpkg"
run: bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
- name: "Configure"
# Flags as documented in README.md, plus the two the GitLab job turns
# off because the core does not use them.
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_LIBRETRO=ON \
-DENABLE_WXWIDGETS=OFF \
-DENABLE_DISCORD_RPC=OFF \
-DENABLE_CUBEB=OFF \
-DENABLE_BLUEZ=OFF \
-DENABLE_FERAL_GAMEMODE=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
- name: "Build" - name: "Build"
run: cmake --build build --target cemu_libretro -- -j $(nproc) run: make -j $(nproc) CC=gcc-12 CXX=g++-12
- name: "Upload artifact" - name: "Upload artifact"
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: cemu_libretro-linux-${{ matrix.arch }} name: parallel_gx2_libretro-linux-${{ matrix.arch }}
path: ./bin/cemu_libretro.so path: ./parallel_gx2_libretro.so
tests:
if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'linux')
name: tests
runs-on: ubuntu-22.04
timeout-minutes: 30
steps:
- name: "Checkout repo"
uses: actions/checkout@v6
- name: "curl_compat against local servers"
# plain, then under AddressSanitizer + UBSan, then ThreadSanitizer
run: |
./dependencies/curl-compat/test/run_tests.sh
make -C dependencies/curl-compat/test clean
SANITIZER=address,undefined ./dependencies/curl-compat/test/run_tests.sh
make -C dependencies/curl-compat/test clean
SANITIZER=thread ./dependencies/curl-compat/test/run_tests.sh
macos: macos:
if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'macos') if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'macos')
name: macos-${{ matrix.arch }} name: macos-${{ matrix.arch }}
runs-on: macos-14 runs-on: macos-14
# First macOS build of the core: src/CMakeLists.txt already keeps the X11/GL timeout-minutes: 120
# link line to `UNIX AND NOT APPLE` and defines VK_USE_PLATFORM_MACOS_MVK,
# but nothing here has ever been through a compiler. Same treatment as the
# Windows job until it has.
continue-on-error: true
timeout-minutes: 350
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -106,58 +95,23 @@ jobs:
submodules: "recursive" submodules: "recursive"
fetch-depth: 0 fetch-depth: 0
- name: "Install dependencies"
run: brew install nasm automake libtool
- name: "Install molten-vk"
# The Vulkan renderer links against the loader; on macOS that is
# MoltenVK, the release upstream's macOS build uses.
run: |
curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar
tar xf MoltenVK-macos-privateapi.tar
sudo mkdir -p /usr/local/lib
sudo cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
- name: "Configure"
# Same flags as the Linux job; BLUEZ and FERAL_GAMEMODE are Linux-only
# options that are already off here, and the architecture is passed the
# way the standalone macOS build passes it, so vcpkg picks the matching
# triplet for the x86_64 slice on this arm64 runner.
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \
-DENABLE_LIBRETRO=ON \
-DENABLE_WXWIDGETS=OFF \
-DENABLE_DISCORD_RPC=OFF \
-DENABLE_CUBEB=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
- name: "Build" - name: "Build"
run: cmake --build build --target cemu_libretro -- -j $(sysctl -n hw.ncpu) run: make -j $(sysctl -n hw.ncpu) platform=osx arch=${{ matrix.arch }}
- name: "Upload artifact" - name: "Upload artifact"
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: cemu_libretro-macos-${{ matrix.arch }} name: parallel_gx2_libretro-macos-${{ matrix.arch }}
path: ./bin/cemu_libretro.dylib path: ./parallel_gx2_libretro.dylib
windows: windows:
if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'windows') if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'windows')
name: windows-x64 name: windows-x64
runs-on: windows-2022 runs-on: windows-2022
# The WGL path in the glue layer has never been through a compiler; give it timeout-minutes: 120
# a run without failing the whole workflow over it. defaults:
continue-on-error: true run:
timeout-minutes: 350 shell: msys2 {0}
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
@@ -166,54 +120,34 @@ jobs:
submodules: "recursive" submodules: "recursive"
fetch-depth: 0 fetch-depth: 0
- name: "Setup cmake" - name: "Set up MSYS2"
uses: jwlawson/actions-setup-cmake@v2 uses: msys2/setup-msys2@v2
with: with:
cmake-version: '3.29.0' msystem: MINGW64
install: make git mingw-w64-x86_64-gcc
- name: "Bootstrap vcpkg"
run: ./dependencies/vcpkg/bootstrap-vcpkg.bat
- name: "Configure"
# RelWithDebInfo instead of Release: same optimization level for MSVC,
# but keeps a PDB around so a crash dump/live debug session resolves to
# real function names instead of the nearest export + a huge offset.
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_LIBRETRO=ON -DENABLE_WXWIDGETS=OFF -DENABLE_DISCORD_RPC=OFF -DENABLE_CUBEB=OFF -DVCPKG_INSTALL_OPTIONS="--clean-after-build"
- name: "Build" - name: "Build"
run: cmake --build build --target cemu_libretro --config RelWithDebInfo run: make -j $(nproc)
- name: "Upload artifact" - name: "Upload artifact"
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: cemu_libretro-windows-x64 name: parallel_gx2_libretro-windows-x64
path: ./bin/cemu_libretro.dll path: ./parallel_gx2_libretro.dll
# The symbols, for reading crash offsets, apart from the core so that
# downloading the core does not mean downloading them too
- name: "Upload symbols"
uses: actions/upload-artifact@v6
with:
name: cemu_libretro-windows-x64-pdb
path: ./bin/cemu_libretro.pdb
android: android:
if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'android') if: github.event_name != 'workflow_dispatch' || inputs.platforms == 'all' || contains(inputs.platforms, 'android')
name: android-${{ matrix.abi }} name: android-${{ matrix.abi }}
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
# This target has never been built: the whole vcpkg set has to come up for a timeout-minutes: 120
# cross triplet first, so do not fail the run over it.
continue-on-error: true
timeout-minutes: 350
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- abi: arm64-v8a - abi: arm64-v8a
triplet: arm64-android platform: android-arm64
- abi: x86_64 - abi: x86_64
triplet: x64-android platform: android-x86_64
steps: steps:
- name: "Checkout repo" - name: "Checkout repo"
@@ -222,43 +156,12 @@ jobs:
submodules: "recursive" submodules: "recursive"
fetch-depth: 0 fetch-depth: 0
- name: "Install host dependencies"
run: |
sudo apt update -qq
sudo apt install -y --no-install-recommends \
nasm autoconf automake libtool pkg-config python3 ninja-build
- name: "Bootstrap vcpkg"
run: bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
- name: "Configure"
# API level 28, as in the GitLab job: bionic gained iconv (which SDL2
# links against) and aligned_alloc there, and the template's default of
# 16 is useless for C++20 anyway. CMAKE_TOOLCHAIN_FILE is the NDK's -
# the top-level CMakeLists turns it into a vcpkg chainload toolchain.
run: |
cmake -DCMAKE_BUILD_TYPE=Release \
-DENABLE_LIBRETRO=ON \
-DENABLE_BLUEZ=OFF \
-DENABLE_FERAL_GAMEMODE=OFF \
-DENABLE_WAYLAND=OFF \
-DENABLE_DISCORD_RPC=OFF \
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
-DVCPKG_HOST_TRIPLET=x64-linux \
-DANDROID_PLATFORM=android-28 \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_STL=c++_static \
-DANDROID_ABI=${{ matrix.abi }} \
. -Bbuild/android-${{ matrix.abi }}
- name: "Build" - name: "Build"
run: cmake --build build/android-${{ matrix.abi }} --target cemu_libretro --config Release -- -j $(nproc) # API level 28: bionic has aligned_alloc there, and C++20 wants it
run: make -j $(nproc) platform=${{ matrix.platform }} ANDROID_API=28 NDK_ROOT=$ANDROID_NDK_LATEST_HOME
- name: "Prepare artifact"
run: mv bin/cemu_libretro.so cemu_libretro_android.so
- name: "Upload artifact" - name: "Upload artifact"
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: cemu_libretro-android-${{ matrix.abi }} name: parallel_gx2_libretro-android-${{ matrix.abi }}
path: ./cemu_libretro_android.so path: ./parallel_gx2_libretro_android.so
+14 -78
View File
@@ -1,14 +1,10 @@
name: Preflight Apple embedded name: Preflight Apple embedded
# Not for upstream, and not a build anyone should download yet: this is the # Not for upstream, and not a build anyone should download yet: the core
# first attempt at the core for iOS and tvOS, and it exists to find out what # for iOS and tvOS, built to find out what stops it.
# stops it. vcpkg has arm64-ios and arm64-tvos triplets of its own, so the
# dependency set is at least addressable; what is unknown is which of the
# twenty-odd ports the manifest asks for actually build against those SDKs, and
# what the emulator itself reaches for that is macOS rather than Apple.
# #
# Vulkan is off - MoltenVK ships as a dylib and a loose dylib beside a core is # Vulkan is off - MoltenVK ships as a dylib and a loose dylib beside a core is
# not something these systems load - so the GS is Metal here, with the software # not something these systems load - so the GPU is Metal here, with the software
# readback present path the core already has for frontends that hand out no # readback present path the core already has for frontends that hand out no
# hardware context. # hardware context.
@@ -22,19 +18,17 @@ jobs:
preflight: preflight:
name: ${{ matrix.platform }} name: ${{ matrix.platform }}
runs-on: macos-15 runs-on: macos-15
timeout-minutes: 360 timeout-minutes: 120
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- platform: ios - platform: ios
system_name: iOS target: ios-arm64
sysroot: iphoneos core: parallel_gx2_libretro_ios.dylib
triplet: arm64-ios
- platform: tvos - platform: tvos
system_name: tvOS target: tvos-arm64
sysroot: appletvos core: parallel_gx2_libretro_tvos.dylib
triplet: arm64-tvos
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -47,75 +41,17 @@ jobs:
xcodebuild -version xcodebuild -version
xcodebuild -showsdks xcodebuild -showsdks
- name: Install the tools the ports need
run: |
brew list nasm >/dev/null 2>&1 || brew install nasm
brew list automake >/dev/null 2>&1 || brew install automake
brew list libtool >/dev/null 2>&1 || brew install libtool
- name: Cache vcpkg binaries
uses: actions/cache@v4
with:
path: ~/.cache/vcpkg/archives
key: vcpkg-${{ matrix.triplet }}-${{ hashFiles('vcpkg.json') }}
restore-keys: vcpkg-${{ matrix.triplet }}-
- name: Configure
run: |
# The libretro core's own options, plus what the platform decides:
# no OpenGL (these have GLES through EAGL and nothing the emulator's
# GL backend could use), no Vulkan (see the note at the top), Metal.
cmake -S . -B build/${{ matrix.platform }} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SYSTEM_NAME=${{ matrix.system_name }} \
-DCMAKE_OSX_SYSROOT=${{ matrix.sysroot }} \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_SYSTEM_PROCESSOR=arm64 \
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
-DENABLE_LIBRETRO=ON \
-DENABLE_OPENGL=OFF \
-DENABLE_VULKAN=OFF \
-DENABLE_METAL=ON \
-DENABLE_DISCORD_RPC=OFF \
-DENABLE_BLUEZ=OFF \
-DENABLE_FERAL_GAMEMODE=OFF \
-DENABLE_WXWIDGETS=OFF \
-DCMAKE_MACOSX_BUNDLE=OFF
- name: Build the core - name: Build the core
run: | run: make -j $(sysctl -n hw.ncpu) platform=${{ matrix.target }} IOS_MINVER=13.4 MINVER=13.4
cmake --build build/${{ matrix.platform }} --target cemu_libretro --config Release -- -j $(sysctl -n hw.ncpu)
- name: What came out - name: What came out
run: | run: |
find build/${{ matrix.platform }} bin -name 'cemu_libretro*' -print -exec ls -l {} \; 2>/dev/null || true ls -l ${{ matrix.core }}
CORE=$(find . -name 'cemu_libretro.dylib' | head -1) vtool -show ${{ matrix.core }} || true
test -n "$CORE" || { echo "no core was produced"; exit 1; } otool -L ${{ matrix.core }} || true
vtool -show "$CORE" || true
otool -L "$CORE" || true
- name: The vcpkg logs, when a port is what stopped it
if: failure()
run: |
# vcpkg writes the failing port and its logs here; the manifest log is
# mostly the manifest itself, so the error is what to grep for.
LOG=build/${{ matrix.platform }}/vcpkg-manifest-install.log
if [ -f "$LOG" ]; then
echo "=== the part of $LOG that is not the manifest"
grep -vE '^\s*[]["{},]' "$LOG" | tail -100
fi
for f in $(find build/${{ matrix.platform }} -name 'issue_body.md' 2>/dev/null | head -2); do
echo "=== $f"; head -120 "$f"
done
# vcpkg's buildtrees live under the vcpkg root, not under the CMake
# build directory, which is why the port's own log went missing the
# first time this step ran.
for f in $(find dependencies/vcpkg/buildtrees build/${{ matrix.platform }} -name '*-err.log' -o -name '*-out.log' 2>/dev/null | head -8); do
echo "=== $f"; tail -50 "$f"
done
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: success() if: success()
with: with:
name: cemu_libretro_${{ matrix.platform }} name: parallel_gx2_libretro_${{ matrix.platform }}
path: bin/cemu_libretro* path: ${{ matrix.core }}
+3 -1
View File
@@ -53,4 +53,6 @@ bin/gameProfiles/*
bin/graphicPacks/* bin/graphicPacks/*
# Ignore Finder view option files created by OS X # Ignore Finder view option files created by OS X
.DS_Store .DS_Store
# the Makefile build
/obj/
+105 -527
View File
@@ -7,589 +7,167 @@
# Core definitions # Core definitions
.core-defs: .core-defs:
variables: variables:
CORENAME: cemu GIT_SUBMODULE_STRATEGY: recursive
# CMake writes cemu_libretro.so to <source>/bin (see src/CMakeLists.txt), CORENAME: parallel_gx2
# which is outside of BUILD_DIR, so climb back out of build/linux-$ARCH.
EXTRA_PATH: ../../bin
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
# Cemu is C++20; the image default compiler is too old for it.
CC: gcc-12
CXX: g++-12
# arm64-linux is a community triplet - vcpkg ships no prebuilt tools for it
# and has to use the ones from the image.
VCPKG_FORCE_SYSTEM_BINARIES: 1
# vcpkg builds every port from source; its binary cache keeps what it built,
# and GitLab's cache keeps that between pipelines, so a commit that leaves
# the dependencies alone does not build them again. vcpkg looks each port
# up by the hash of everything it is built from, so a cache from before a
# change of ports or of vcpkg is simply not used for what changed. The
# directory is in the repository (it has to exist before vcpkg runs).
VCPKG_DEFAULT_BINARY_CACHE: $CI_PROJECT_DIR/vcpkg-cache
cache:
key: vcpkg-$CI_JOB_NAME
paths:
- vcpkg-cache/
# Kept from a failed job too: what it did build is still good
when: always
variables:
GIT_SUBMODULE_STRATEGY: recursive
# Inclusion templates, required for the build to work # Inclusion templates, required for the build to work
include: include:
################################## DESKTOPS ################################ ################################## DESKTOPS ################################
# Linux
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-cmake.yml'
# Windows # Windows
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/windows-cmake-mingw.yml' file: '/windows-x64-mingw.yml'
# macOS
# Linux 64-bit
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/osx-cmake-x86.yml' file: '/linux-x64.yml'
# Linux ARM 64-bit
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/osx-cmake-arm64.yml' file: '/linux-aarch64.yml'
# Apple embedded (iOS and tvOS, both arm64)
# MacOS x64
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/ios-cmake.yml' file: '/osx-x64.yml'
# MacOS arm64
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/tvos-cmake.yml' file: '/osx-arm64.yml'
################################## CELLULAR ################################ ################################## CELLULAR ################################
# Android # Android
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/android-cmake.yml' file: '/android-make.yml'
################################### TELEVISION #############################
# webOS. aarch64 only - see the job at the end for why. # iOS arm64
- project: 'libretro-infrastructure/ci-templates' - project: 'libretro-infrastructure/ci-templates'
file: '/webos-cmake.yml' file: '/ios-arm64.yml'
# tvOS arm64
- project: 'libretro-infrastructure/ci-templates'
file: '/tvos-arm64.yml'
################################## MISC ####################################
# webOS aarch64
- project: 'libretro-infrastructure/ci-templates'
file: '/webos-make.yml'
# Stages for building # Stages for building
stages: stages:
- build-prepare - build-prepare
- build-shared - build-shared
- test
# libretro-runner-02 has been dropping jobs within seconds and then running the
# same job green on the next pipeline with nothing changed — windows-x64 after
# three 17-minute successes, both android ABIs, linux-x64. Retry rather than let
# a runner having a bad minute redden the pipeline; anything really broken still
# fails three times over.
default:
retry: 2
############################################################################## ##############################################################################
#################################### STAGES ################################## #################################### STAGES ##################################
############################################################################## ##############################################################################
# #
################################### DESKTOPS ################################# ################################### DESKTOPS #################################
# Both Linux jobs want the same packages and the same extra time, and a job # Linux wants the GLX, EGL, X11 and XCB headers the core's OpenGL and
# defining before_script replaces the template's instead of adding to it - so # Vulkan surfaces are declared with; a job defining before_script replaces
# the repeated part lives here. # the template's, so its one line is repeated here.
.core-defs-linux: .core-defs-linux:
extends: .core-defs extends: .core-defs
# vcpkg builds every dependency from source (~30 ports, Boost among them),
# which does not fit in the default one hour.
timeout: 3h
variables: variables:
# Wayland is dead weight in a core build and the x86_64 image cannot even # Cemu is C++20
# satisfy it: find_package(WaylandProtocols 1.15) fails there against the CC: gcc-12
# image's 1.13. HAS_WAYLAND only reaches wxgui - which ENABLE_LIBRETRO CXX: g++-12
# forces off - and VulkanRenderer's surface creation, which returns before
# any of it in libretro mode, where the frontend hands the core a surface.
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
-DENABLE_WAYLAND=OFF
before_script: before_script:
# Repeated from .libretro-linux-cmake, whose before_script this replaces.
- export NUMPROC=$(($(nproc)/5)) - export NUMPROC=$(($(nproc)/5))
- if builtin type -P ccache &> /dev/null; then ccache -V &> /dev/null; fi
# libdbus-1-dev is not optional here: dependencies/vcpkg_overlay_ports_linux/dbus
# is an empty package on purpose (see the portfile), so sdl2[dbus] has only
# the system headers to build against. The rest is what the other vcpkg
# ports (SDL2, libusb) and the core itself (GL/EGL/X11) need.
- apt-get update -qq - apt-get update -qq
- apt-get install -y --no-install-recommends - apt-get install -y --no-install-recommends
nasm autoconf automake libtool pkg-config python3 libgl-dev libegl-dev libx11-dev libxrender-dev libx11-xcb-dev
libglm-dev libgl1-mesa-dev libegl1-mesa-dev
libx11-dev libxext-dev libxrandr-dev libxi-dev libxcursor-dev
libxss-dev libxkbcommon-dev libwayland-dev wayland-protocols
libudev-dev libdbus-1-dev libgcrypt20-dev
# Linux aarch64 # Windows 64-bit
libretro-build-linux-aarch64:
extends:
- .libretro-linux-cmake-aarch64
- .core-defs-linux
# Linux x86_64
libretro-build-linux-x64:
extends:
- .libretro-linux-cmake-x86_64
- .core-defs-linux
# The template's x86_64 image is Ubuntu Xenial with GCC 9, which cannot build
# C++20 at all; backports is the same family of image with newer compilers,
# gcc-12 among them (the same one the aarch64 image is held to).
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
before_script:
- !reference [.core-defs-linux, before_script]
# x64-linux is a first-class triplet, so let vcpkg fetch its own cmake and
# ninja rather than holding it to whatever the image ships. That variable
# belongs to the aarch64 job, where the community triplet has no prebuilt
# tools to fetch; here it only gets in the way.
- unset VCPKG_FORCE_SYSTEM_BINARIES
# Windows x86_64
# Windows has only ever been built with MSVC so far (the GitHub Actions workflow
# does that on windows-2022, and the CMakeLists' Windows bits - SEH exception
# handler, /WHOLEARCHIVE, the MSVC runtime property - are written for it). This
# cross build with MXE's MinGW is new ground, on top of a vcpkg dependency set
# that has never been brought up for the mingw triplet, so keep it from failing
# the pipeline while it is worked out.
libretro-build-windows-x64: libretro-build-windows-x64:
extends: extends:
- .libretro-windows-cmake-x86_64 - .libretro-windows-x64-mingw-make-default
- .core-defs - .core-defs
allow_failure: true
# Same reason as the Linux jobs: everything is built from source.
timeout: 3h
before_script:
# Repeated from .libretro-windows-cmake, whose before_script this replaces.
- export NUMPROC=$(($(nproc)/5))
- if builtin type -P ccache &> /dev/null; then ccache -V &> /dev/null; fi
# The host here is x86_64 Linux, the same as in the Linux x64 job above.
- unset VCPKG_FORCE_SYSTEM_BINARIES
# .core-defs asks for gcc-12, a package name from the Ubuntu images. The
# compilers here come from MXE's cmake wrapper
# (x86_64-w64-mingw32.static-cmake), which the top-level CMakeLists hands to
# vcpkg as a chainload toolchain; leaving CC/CXX set would only mislead the
# ports that build with their own configure scripts.
- unset CC CXX
# What the ports configure with; the MXE image carries the cross toolchain
# and not much else.
- apt-get update -qq
- apt-get install -y --no-install-recommends
nasm autoconf automake libtool pkg-config python3
# vcpkg builds the ports with its own scripts/toolchains/mingw.cmake (the
# chainload set in CMakeLists only covers the core's own build), and that
# file looks the compiler up as x86_64-w64-mingw32-gcc. MXE spells its
# tools x86_64-w64-mingw32.static-gcc, so the find_program() comes up empty
# and CMake falls back to the host cc/c++ - which is then handed MinGW link
# flags and dies on "ld: unrecognized option '--major-image-version'"
# before the first port is built. Give every MXE tool the name vcpkg looks
# for.
- |
for tool in /usr/lib/mxe/usr/bin/x86_64-w64-mingw32.static-*; do
[ -e "$tool" ] || continue
name=$(basename "$tool")
ln -sf "$tool" "/usr/local/bin/${name/.static-/-}"
done
variables:
# CMakeLists picks x64-windows-static as soon as WIN32 is set, which is an
# MSVC triplet; MinGW needs its own. A -D from the command line wins over
# the set(... CACHE) there, and the triplet is also what tells that code
# which port overlay to use, as WIN32 is not set yet on a Unix host.
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_WXWIDGETS=OFF
-DENABLE_DISCORD_RPC=OFF
-DENABLE_CUBEB=OFF
-DVCPKG_TARGET_TRIPLET=x64-mingw-static
-DVCPKG_HOST_TRIPLET=x64-linux
################################### CELLULAR ################################# # Linux 64-bit
# Android is a fresh target for this core: unlike the Linux job it has to build libretro-build-linux-x64:
# the whole vcpkg dependency set for a cross triplet, and none of it has run
# before - so keep these from failing the pipeline while they are brought up.
.core-defs-android:
extends: .core-defs
allow_failure: true
# Same reason as the Linux job, plus a cross toolchain on top.
timeout: 3h
before_script:
# Repeated from the template, whose before_script this one replaces.
- export NUMPROC=$(($(nproc)/5))
# vcpkg needs a host compiler for the x64-linux ports it builds first, the
# autotools most of its ports configure with, and make - the generator the
# template's cmake line falls back to; the Android image has them all
# (Ubuntu 26.04, October 2026: gcc, g++, make, ninja, autoconf, automake,
# libtool, pkg-config, python3, unzip, curl, git, nasm, zip), so nothing is
# installed here.
# .core-defs asks for gcc-12, which the Linux image has and this one does
# not. Leaving CC/CXX set to anything at all is worse than dropping them:
# the cmake ports ignore them and take the NDK's clang, but openssl builds
# with its own Configure and a makefile, which honours $CC and so feeds the
# clang flags to the host gcc ("unrecognized command-line option
# '--target=aarch64-none-linux-android28'"). Unset, openssl picks up the
# NDK clang the port puts on PATH and the host triplet - which only builds
# vcpkg's own helper tools - finds the image's cc, the same as on GitHub
# Actions, where neither variable is set to begin with.
- unset CC CXX
# The host here is x86_64, so vcpkg can fetch its own cmake and ninja
# instead of being held to the image's. That flag belongs to the aarch64
# Linux job, where the community triplet has no prebuilt tools to fetch.
- unset VCPKG_FORCE_SYSTEM_BINARIES
# The ports are built with vcpkg's own scripts/toolchains/android.cmake -
# the chainload set in CMakeLists only covers the core's own build - and
# that file looks the NDK up in ANDROID_NDK_HOME, falling back to Windows
# paths that do not exist here. The image calls it NDK_ROOT instead, so
# "Detecting compiler hash for triplet arm64-android" dies right there.
# (GitHub Actions runners set ANDROID_NDK_HOME, hence the green build.)
- export ANDROID_NDK_HOME="$NDK_ROOT"
- export ANDROID_NDK_ROOT="$NDK_ROOT"
variables:
# The template defaults to API 16, which is nowhere near enough for C++20.
# 28 is the floor that works: bionic only got iconv (SDL2 links it) and
# aligned_alloc there.
API_LEVEL: 28
# Cemu's CMakeLists hands the template's NDK toolchain to vcpkg as a
# chainload toolchain (see the comment there); this picks the triplet the
# ports are built for.
VCPKG_DEFAULT_TRIPLET: $VCPKG_TRIPLET
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
-DENABLE_WAYLAND=OFF
-DENABLE_DISCORD_RPC=OFF
-DVCPKG_TARGET_TRIPLET=$VCPKG_TRIPLET
-DVCPKG_HOST_TRIPLET=x64-linux
# Android aarch64
# macOS. Cemu ships macOS builds of its own and the core now builds for both
# slices on GitHub Actions runners, so the only unknown left is this buildbot's
# Macs: the ones pcee2 runs on have cmake, ninja, git and clang but no Homebrew,
# and several vcpkg ports want nasm or the autotools. Rather than guess, the
# setup writes down what it finds and the job keeps that as an artifact —
# traces here need an account to read, artifacts do not.
.core-defs-macos:
extends: .core-defs
allow_failure: true
# vcpkg builds every dependency from source, as on the other platforms.
timeout: 3h
before_script:
# Replaces the osx template's before_script, which uses nproc — a coreutils
# binary that a stock macOS runner does not have.
- export NUMPROC=$(($(sysctl -n hw.ncpu)/5))
# .core-defs sets this for the arm64-linux community triplet, where vcpkg has
# no prebuilt tools to fetch. On macOS it is the opposite: holding vcpkg to
# the runner's binaries is what broke detect_compiler ("vcpkg was unable to
# detect the active compiler's information"), so let it bring its own.
- unset VCPKG_FORCE_SYSTEM_BINARIES
# .core-defs asks for gcc-12, which is a package name from the Ubuntu images;
# macOS has clang and nothing by that name, so vcpkg's detect_compiler port
# failed on "Could not find the compiler specified in the environment
# variable CC: gcc-12" before a single dependency was built. The Windows job
# unsets these for the same reason.
- unset CC CXX
# The runner's ninja is on an x86_64 Homebrew path on an arm64 machine and
# CMake could not use it; ask for Ninja only if it actually runs here.
- |
if ninja --version >/dev/null 2>&1; then GEN="-G Ninja"; else GEN=""; fi
export GEN
- |
{
echo "== $CI_JOB_NAME on $(uname -m)"; sw_vers || true; xcode-select -p || true
for t in git cmake ninja nasm brew automake autoconf libtool pkg-config python3 clang mono; do
printf '%-12s ' "$t"; command -v "$t" || echo MISSING
done
echo "generator: ${GEN:-default}"; ninja --version 2>&1 | head -1
} > ci-macos-debug.log 2>&1
- |
{
if command -v brew >/dev/null 2>&1; then
for pkg in nasm automake libtool; do brew list $pkg >/dev/null 2>&1 || brew install $pkg || echo "brew install $pkg FAILED"; done
else
echo "no brew on this runner - ports needing nasm or the autotools will say so below"
fi
} >> ci-macos-debug.log 2>&1
# The Vulkan renderer links the loader; on macOS that is MoltenVK, the same
# release the standalone macOS build uses.
- |
{
curl -L -o MoltenVK-macos-privateapi.tar https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar &&
tar xf MoltenVK-macos-privateapi.tar &&
mkdir -p "$CI_PROJECT_DIR/deps/lib" &&
cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib 2>/dev/null ||
cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib "$CI_PROJECT_DIR/deps/lib"
} >> ci-macos-debug.log 2>&1
- bash ./dependencies/vcpkg/bootstrap-vcpkg.sh 2>&1 | tail -20 | tee -a ci-macos-debug.log
# The template's script, with its output appended to the same artifact. The
# first run died here, a minute in, and the trace needs an account to read.
script:
- |
set -o pipefail
{
${CMAKE} $GEN -DCMAKE_BUILD_TYPE=Release $CORE_ARGS "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR $CMAKE_OSX_EXTRA &&
${CMAKE} --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
} 2>&1 | tail -300 | tee -a ci-macos-debug.log
- mv $BUILD_DIR/$EXTRA_PATH/$LIBNAME $LIBNAME
# vcpkg reports failures by pointing at logs the trace does not show, and the
# script stops at the first failing command — so collecting them belongs here,
# where it runs whatever happened.
after_script:
- |
for f in "$BUILD_DIR/vcpkg-manifest-install.log" \
dependencies/vcpkg/buildtrees/detect_compiler/*-err.log \
dependencies/vcpkg/buildtrees/detect_compiler/*-out.log; do
[ -f "$f" ] || continue
echo "===== $f (tail)" >> ci-macos-debug.log
tail -60 "$f" >> ci-macos-debug.log
done
for f in $(ls -t dependencies/vcpkg/buildtrees/*/*-err.log 2>/dev/null | head -2); do
echo "===== $f (tail)" >> ci-macos-debug.log
tail -60 "$f" >> ci-macos-debug.log
done
artifacts:
when: always
expire_in: 1 week
paths:
- ci-macos-debug.log
- $LIBNAME
# iOS and tvOS. Same core, and the platform decides three things for it: no
# OpenGL (the emulator's GL backend has nothing to run on where GL is EAGL and
# GLES), no Vulkan (MoltenVK is a dylib and these systems do not load a loose
# one), so the GS is Metal - with the readback present path the core already has
# for frontends that hand out no hardware context.
#
# JIT is the honest caveat: mapping executable pages needs an entitlement a
# stock install does not grant, which is the same bargain every JIT core on
# these platforms makes, and not something this build can fix.
#
# allow_failure while they settle, like the macOS jobs: nothing here has been
# run on a device yet, only built.
.core-defs-apple-embedded:
extends: .core-defs
allow_failure: true
# vcpkg builds every dependency from source, as on the other platforms.
timeout: 3h
variables:
# 13.4 is what CMakeLists asks for on Apple; the templates default to 8.0
# (iOS) and 12.0 (tvOS), which is older than the C++20 library the emulator
# needs. ios-cmake.yml reads IOS_MINVER, tvos-cmake.yml reads MINVER.
IOS_MINVER: "13.4"
MINVER: "13.4"
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
-DENABLE_OPENGL=OFF
-DENABLE_VULKAN=OFF
-DENABLE_METAL=ON
-DENABLE_DISCORD_RPC=OFF
-DENABLE_WXWIDGETS=OFF
-DCMAKE_MACOSX_BUNDLE=OFF
before_script:
# Replaces the template's before_script, which is where NUMPROC is normally
# set for its `-j $NUMPROC` build line. sysctl, not nproc: a stock macOS
# runner has no coreutils.
- export NUMPROC=$(($(sysctl -n hw.ncpu)/5))
# Same two as the macOS jobs: vcpkg must bring its own binaries, and the
# gcc-12 the Linux jobs ask for does not exist here.
- unset VCPKG_FORCE_SYSTEM_BINARIES
- unset CC CXX
- |
if command -v brew >/dev/null 2>&1; then
for pkg in nasm automake libtool; do brew list $pkg >/dev/null 2>&1 || brew install $pkg || echo "brew install $pkg FAILED"; done
fi
- bash ./dependencies/vcpkg/bootstrap-vcpkg.sh 2>&1 | tail -20
after_script:
# vcpkg reports failures by pointing at logs the trace does not show.
- |
for f in "$BUILD_DIR/vcpkg-manifest-install.log" \
$(ls -t dependencies/vcpkg/buildtrees/*/*-err.log 2>/dev/null | head -2); do
[ -f "$f" ] || continue
echo "===== $f (tail)"
tail -60 "$f"
done
libretro-build-osx-x64:
extends: extends:
# -x86_64, not -x86: the latter is the 32-bit profile and sets ARCH=x86. - .libretro-linux-x64-make-default
- .libretro-osx-cmake-x86_64 - .core-defs-linux
- .core-defs-macos # Ubuntu Xenial's GCC cannot build C++20; backports is the same family
# The Intel runner behind the template's macosx-packaging tag only carries the # of image with newer compilers, gcc-12 among them.
# macOS 10.15 SDK, and SDL3 needs the 11 one: UniformTypeIdentifiers.h is not image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
# there and GameController has no physicalInputProfile, so vcpkg cannot build
# sdl3 on it at any deployment target. Cross-compile the x86_64 slice on the # Linux ARM 64-bit
# Apple Silicon runner instead - CMakeLists.txt takes CEMU_ARCHITECTURE from libretro-build-linux-aarch64:
# CMAKE_OSX_ARCHITECTURES, and the GitHub macOS job builds this slice the same extends:
# way on an arm64 runner. - .libretro-linux-aarch64-make-default
- .core-defs-linux
# macOS Intel
libretro-build-osx-x64:
tags: tags:
- mac-apple-silicon - mac-apple-silicon
extends:
- .libretro-osx-x64-make-default
- .core-defs
variables: variables:
# The template targets 10.9, where std::filesystem does not exist yet, so arch: x86_64
# glslang stopped on "'path' is unavailable: introduced in macOS 10.15".
# Same floor the arm64 template asks for.
MACOSX_DEPLOYMENT_TARGET: "10.15"
CMAKE_OSX_EXTRA: -DCMAKE_OSX_ARCHITECTURES=x86_64
# macOS arm64
libretro-build-osx-arm64: libretro-build-osx-arm64:
extends: extends:
- .libretro-osx-cmake-arm64 - .libretro-osx-arm64-make-default
- .core-defs-macos - .core-defs
################################### CELLULAR #################################
# Android ARMv8a
android-arm64-v8a: android-arm64-v8a:
extends: extends:
- .libretro-android-cmake-arm64-v8a - .libretro-android-make-arm64-v8a
- .core-defs-android - .core-defs
variables:
VCPKG_TRIPLET: arm64-android
# Android x86_64 # Android x86_64 (for play only)
android-x86_64: android-x86_64:
extends: extends:
- .libretro-android-cmake-x86_64 - .libretro-android-make-x86_64
- .core-defs-android
variables:
VCPKG_TRIPLET: x64-android
##################################### APPLE ##################################
# iOS (arm64).
################################## TELEVISION ################################
# webOS, aarch64 only. The other half of the platform is armv7a, and the
# recompiler backends here are x86-64 and arm64 (BackendX64 and xbyak_aarch64) -
# there is nothing for a 32-bit ARM device to run Espresso code on, so that half
# is not offered rather than offered and broken.
#
# The renderer is Vulkan. Cemu's GL backend is desktop GL 4.5 with direct state
# access and the buildroot carries EGL and GLES and nothing else, so it is off
# here the same way it is on Android. The loader is dlopen()ed at run time
# (VulkanAPI.cpp) and the headers are vendored, so the build needs nothing from
# the SDK for it - but that also means the core lands on the device's own
# libvulkan, and a TV without one will load the core and fail at renderer
# creation. That is the device's property, not the build's.
#
# The dependency set is the interesting part: vcpkg builds its ports with the
# toolchain named by their triplet, not with the one CMakeLists chainloads, so
# this needs a triplet of its own (dependencies/vcpkg_triplets/arm64-webos.cmake)
# that points at the SDK's toolchain file.
#
# allow_failure while it is new: the SDK exists only on the buildbot, so the
# first pipeline is the first time this toolchain has seen the tree.
libretro-build-webos-aarch64:
allow_failure: true
extends:
- .libretro-webos-aarch64-cmake
- .core-defs - .core-defs
# vcpkg builds every dependency from source, as on the other platforms.
timeout: 3h
variables:
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DWEBOS=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
-DENABLE_WAYLAND=OFF
-DENABLE_OPENGL=OFF
-DENABLE_VULKAN=ON
-DENABLE_DISCORD_RPC=OFF
-DENABLE_WXWIDGETS=OFF
-DENABLE_CUBEB=OFF
-DENABLE_LIBUSB=OFF
-DVCPKG_TARGET_TRIPLET=arm64-webos
-DVCPKG_HOST_TRIPLET=x64-linux
before_script:
# Repeated from the template, whose before_script this one replaces.
- export NUMPROC=$(($(nproc)/5))
- export BUILD_DIR=build/webos-${WEBOS_ARCH}
- . "$SDK_ENV"
# The triplet reads the toolchain from the environment - see its comment.
- export WEBOS_CMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN_FILE"
# And the SDK's pkg-config, which the ports cannot find under its toolchain
# file (curl failed with "pkg-config tool not found").
- export WEBOS_PKG_CONFIG="$(command -v pkg-config)"
# environment-setup points every one of these at the cross toolchain, which
# is right for the core and wrong for everything vcpkg builds for the host
# triplet first (its own tools, and the ports x64-linux carries). The core's
# compiler comes from the toolchain file, not from here.
- unset CC CXX CPP CFLAGS CXXFLAGS CPPFLAGS LDFLAGS AR AS LD NM RANLIB OBJCOPY OBJDUMP
# It also exports the kernel build's ARCH, CROSS_COMPILE and KERNELDIR, and
# OpenSSL's Configure puts CROSS_COMPILE in front of the compiler the triplet
# gives it - an absolute path - so every object in the openssl port failed
# with "aarch64-webos-linux-gnu-/developer/.../aarch64-webos-linux-gnu-gcc:
# not found". Nothing here builds a kernel.
- unset ARCH CROSS_COMPILE KERNELDIR
# The host is x86_64, so vcpkg can fetch its own cmake and ninja rather than
# being held to the image's. That flag belongs to the aarch64 Linux job.
- unset VCPKG_FORCE_SYSTEM_BINARIES
# This image is built around the webOS SDK and has never run vcpkg, so write
# down what it does and does not carry, and install what is missing if that
# is allowed. Not every libretro image runs as root. The notes go to the
# trace (after_script), not to an artifact: the buildbot packages every
# artifact of this job as a core.
- |
{
head -2 /etc/os-release; id
for t in git curl zip unzip tar pkg-config python3 ninja cmake make gcc g++ autoconf automake libtool nasm; do
printf '%-12s ' "$t"; command -v "$t" || echo MISSING
done
} > ci-webos-debug.log 2>&1
- |
if [ "$(id -u)" = 0 ]; then
{
apt-get update -qq &&
apt-get install -y --no-install-recommends \
build-essential make ninja-build nasm autoconf automake libtool \
pkg-config python3 zip unzip curl git ca-certificates
} >> ci-webos-debug.log 2>&1 || echo "apt-get failed with $?" >> ci-webos-debug.log
else
echo "not root - taking the image as it is" >> ci-webos-debug.log
fi
- bash ./dependencies/vcpkg/bootstrap-vcpkg.sh 2>&1 | tail -20 | tee -a ci-webos-debug.log
# vcpkg reports failures by pointing at logs the trace does not show.
after_script:
- |
for f in "build/webos-aarch64/vcpkg-manifest-install.log" \
$(ls -t dependencies/vcpkg/buildtrees/*/*-err.log 2>/dev/null | head -3); do
[ -f "$f" ] || continue
echo "===== $f (tail)" >> ci-webos-debug.log
tail -60 "$f" >> ci-webos-debug.log
done
cat ci-webos-debug.log 2>/dev/null || true
artifacts:
expire_in: 1 week
paths:
- $LIBNAME
################################## APPLE EMBEDDED ############################ # iOS arm64: Metal only, the Vulkan renderer needs MoltenVK and a
# frontend surface the core does not get here
libretro-build-ios-arm64: libretro-build-ios-arm64:
extends: extends:
- .libretro-ios-cmake-arm64 - .libretro-ios-arm64-make-default
- .core-defs-apple-embedded - .core-defs
variables: variables:
VCPKG_DEFAULT_TRIPLET: arm64-ios IOS_MINVER: "13.4"
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON
-DENABLE_BLUEZ=OFF
-DENABLE_FERAL_GAMEMODE=OFF
-DENABLE_OPENGL=OFF
-DENABLE_VULKAN=OFF
-DENABLE_METAL=ON
-DENABLE_DISCORD_RPC=OFF
-DENABLE_WXWIDGETS=OFF
-DCMAKE_MACOSX_BUNDLE=OFF
-DVCPKG_TARGET_TRIPLET=arm64-ios
# tvOS (arm64). # tvOS arm64
libretro-build-tvos-arm64: libretro-build-tvos-arm64:
extends: extends:
- .libretro-tvos-cmake-arm64 - .libretro-tvos-arm64-make-default
- .core-defs-apple-embedded - .core-defs
variables: variables:
VCPKG_DEFAULT_TRIPLET: arm64-tvos MINVER: "13.4"
CORE_ARGS: >-
-DENABLE_LIBRETRO=ON ################################## MISC ####################################
-DENABLE_BLUEZ=OFF # webOS aarch64
-DENABLE_FERAL_GAMEMODE=OFF libretro-build-webos-aarch64:
-DENABLE_OPENGL=OFF extends:
-DENABLE_VULKAN=OFF - .libretro-webos-aarch64-make-default
-DENABLE_METAL=ON - .core-defs
-DENABLE_DISCORD_RPC=OFF
-DENABLE_WXWIDGETS=OFF #################################### TESTS ###################################
-DCMAKE_MACOSX_BUNDLE=OFF # The network layer that stands in for libcurl and OpenSSL
-DVCPKG_TARGET_TRIPLET=arm64-tvos # (dependencies/curl-compat), against local HTTP and TLS servers.
test-curl-compat:
stage: test
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
needs: []
variables:
GIT_SUBMODULE_STRATEGY: none
CC: gcc-12
CXX: g++-12
before_script:
- apt-get update -qq
- apt-get install -y --no-install-recommends python3 openssl
script:
- ./dependencies/curl-compat/test/run_tests.sh
- make -C dependencies/curl-compat/test clean
- SANITIZER=address,undefined ./dependencies/curl-compat/test/run_tests.sh
- make -C dependencies/curl-compat/test clean
- SANITIZER=thread ./dependencies/curl-compat/test/run_tests.sh
+21 -4
View File
@@ -3,10 +3,6 @@
url = https://github.com/WizzardSK/ZArchive url = https://github.com/WizzardSK/ZArchive
shallow = true shallow = true
branch = vfs-file-access branch = vfs-file-access
[submodule "dependencies/vcpkg"]
path = dependencies/vcpkg
url = https://github.com/microsoft/vcpkg
shallow = false
[submodule "dependencies/Vulkan-Headers"] [submodule "dependencies/Vulkan-Headers"]
path = dependencies/Vulkan-Headers path = dependencies/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers url = https://github.com/KhronosGroup/Vulkan-Headers
@@ -26,3 +22,24 @@
path = dependencies/cemu_graphic_packs path = dependencies/cemu_graphic_packs
url = https://github.com/cemu-project/cemu_graphic_packs.git url = https://github.com/cemu-project/cemu_graphic_packs.git
shallow = true shallow = true
[submodule "dependencies/fmt"]
path = dependencies/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "dependencies/glm"]
path = dependencies/glm
url = https://github.com/g-truc/glm.git
[submodule "dependencies/pugixml"]
path = dependencies/pugixml
url = https://github.com/zeux/pugixml.git
[submodule "dependencies/zlib"]
path = dependencies/zlib
url = https://github.com/madler/zlib.git
[submodule "dependencies/zstd"]
path = dependencies/zstd
url = https://github.com/facebook/zstd.git
[submodule "dependencies/glslang"]
path = dependencies/glslang
url = https://github.com/KhronosGroup/glslang.git
[submodule "dependencies/libusb"]
path = dependencies/libusb
url = https://github.com/libusb/libusb.git
+14
View File
@@ -33,3 +33,17 @@ BUILD.md
CONTRIBUTING.md CONTRIBUTING.md
CODING_STYLE.md CODING_STYLE.md
boost.natvis boost.natvis
:(glob)CMakeLists.txt
:(glob)**/CMakeLists.txt
CMakePresets.json
cmake
vcpkg.json
dependencies/vcpkg
dependencies/vcpkg_overlay_ports
dependencies/vcpkg_overlay_ports_linux
dependencies/vcpkg_overlay_ports_mac
dependencies/vcpkg_overlay_ports_webos
dependencies/vcpkg_overlay_ports_win
dependencies/vcpkg_triplets
src/Cemu/Tools/DownloadManager
src/util/helpers/ZipArchive.h
+3 -1
View File
@@ -4,7 +4,9 @@ What changed in the libretro core of this fork, newest first. Changes that come
## Unreleased ## Unreleased
- Linux: content loaded a second time in the same RetroArch, after closing it, no longer crashes RetroArch. The core's OpenSSL read the system's `openssl.cnf`, which loads the pkcs11 engine on many distributions; that system library stays loaded when the core is unloaded and kept pointers into the first copy of the core, so the second load crashed in it. The core's OpenSSL no longer reads the system's config. - The core is now ParaLLEl GX2, `parallel_gx2_libretro`. RetroArch keeps core options, remaps and overrides under the core's name, so they start from the defaults: to keep them, copy RetroArch's `config/Cemu/` to `config/ParaLLEl GX2/` and rename the files in it from `Cemu.*` to `ParaLLEl GX2.*`. The core's own files stay in `<system>/Cemu/` and `<save>/Cemu/`.
- Online play (Nintendo Network, Pretendo, SpotPass, Miiverse) goes through the HTTP and TLS client RetroArch's libretro-common has, in place of libcurl and OpenSSL: the client certificates and the RSA/AES-256-CBC suite the console's services ask for included.
- Linux: content loaded a second time in the same RetroArch, after closing it, no longer crashes RetroArch. The core's OpenSSL read the system's `openssl.cnf`, which loads the pkcs11 engine on many distributions; that system library stays loaded when the core is unloaded and kept pointers into the first copy of the core, so the second load crashed in it. The core no longer has OpenSSL at all.
- A crash is no longer the core's to end: Cemu's crash handlers still write their crash log to `log.txt`, and then hand the crash to the handler there was before, where they used to end RetroArch at once (`_Exit` on Linux and Android, `exit(0)` on Windows), so the system's crash report (Android's tombstone, Windows Error Reporting) is written and names the fault. Ctrl+C and SIGTERM are left to RetroArch, which the core used to end on them (sco8487). - A crash is no longer the core's to end: Cemu's crash handlers still write their crash log to `log.txt`, and then hand the crash to the handler there was before, where they used to end RetroArch at once (`_Exit` on Linux and Android, `exit(0)` on Windows), so the system's crash report (Android's tombstone, Windows Error Reporting) is written and names the fault. Ctrl+C and SIGTERM are left to RetroArch, which the core used to end on them (sco8487).
- New options Controller > Stick Dead Zone (Player 1) and (Players 2-4), 0% to 50%, 25% by default as standalone Cemu. The GamePad's sticks had no dead zone at all, so the slightest touch, or a stick resting a hair off centre, moved the character, while a Pro Controller had Cemu's 25% (demifiend9, Smash 4); now both follow the option, and sticks that rest at the centre can go lower (NNshi). - New options Controller > Stick Dead Zone (Player 1) and (Players 2-4), 0% to 50%, 25% by default as standalone Cemu. The GamePad's sticks had no dead zone at all, so the slightest touch, or a stick resting a hair off centre, moved the character, while a Pro Controller had Cemu's 25% (demifiend9, Smash 4); now both follow the option, and sticks that rest at the centre can go lower (NNshi).
- Merged up to upstream Cemu 2.6-372 (29f2f1fa): coreinit alarm fixes and the alarm queue functions, recompiler entry speed-up and a function-scanning fix, the GamePad (DRC) audio mix fix, a GX2 flush fix and graphic pack patch-group fixes. The core reports upstream's version. - Merged up to upstream Cemu 2.6-372 (29f2f1fa): coreinit alarm fixes and the alarm queue functions, recompiler entry speed-up and a function-scanning fix, the GamePad (DRC) audio mix fix, a GX2 flush fix and graphic pack patch-group fixes. The core reports upstream's version.
-324
View File
@@ -1,324 +0,0 @@
cmake_minimum_required(VERSION 3.21.1)
option(ENABLE_VCPKG "Enable the vcpkg package manager" ON)
# This repository builds the libretro core and nothing else - the standalone
# wx application is gone. ENABLE_LIBRETRO stays as a variable because the rest
# of the build, much of it shared with upstream Cemu, still asks for it.
set(ENABLE_LIBRETRO ON)
option(ALLOW_PORTABLE "Allow Cemu to be run in portable mode" ON)
# used by CI script to set version:
set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "")
set(EMULATOR_VERSION_MINOR "0" CACHE STRING "")
set(EMULATOR_VERSION_PATCH "0" CACHE STRING "")
set(THREADS_PREFER_PTHREAD_FLAG true) # needs to be set before vcpkg
execute_process(
COMMAND git log --format=%h -1
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
# webOS is a Linux by CMAKE_SYSTEM_NAME with a buildroot as bare as Android's:
# EGL and GLES and nothing else - no X11, no Wayland, no desktop GL, no session
# bus. It has to be settled here, before the vcpkg block below picks the overlay
# ports and before the option defaults further down are chosen - which is ahead
# of project(), so the compiler is not known yet and the SDK's toolchain file is
# what gives it away (/developer/aarch64-webos-linux-gnu_sdk-buildroot/...), the
# same tell CEMU_TARGET_ANDROID uses below. -DWEBOS=ON says it outright.
if(NOT WEBOS AND CMAKE_TOOLCHAIN_FILE MATCHES "webos")
set(WEBOS TRUE)
endif()
if (ENABLE_VCPKG)
# check if vcpkg is shallow and unshallow it if necessary
execute_process(
COMMAND git rev-parse --is-shallow-repository
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/dependencies/vcpkg
OUTPUT_VARIABLE is_vcpkg_shallow
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(is_vcpkg_shallow STREQUAL "true")
message(STATUS "vcpkg is shallow. Unshallowing it now...")
execute_process(
COMMAND git fetch --unshallow
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/dependencies/vcpkg"
RESULT_VARIABLE result
OUTPUT_VARIABLE output
)
endif()
# Cross builds hand cmake a toolchain file of their own - the NDK's on
# Android, MXE's for the MinGW Windows build - which would bypass vcpkg
# entirely and leave every find_package() below unsatisfied. Hand it to
# vcpkg as a chainload toolchain instead and let vcpkg's own toolchain take
# the primary slot. (A cache entry set from the command line wins over the
# plain set() below, so this has to be done here rather than there.)
if(CMAKE_TOOLCHAIN_FILE AND NOT VCPKG_CHAINLOAD_TOOLCHAIN_FILE
AND NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg.cmake$")
if(CMAKE_TOOLCHAIN_FILE MATCHES "android.toolchain.cmake$")
set(CEMU_TARGET_ANDROID ON)
endif()
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" CACHE STRING "" FORCE)
unset(CMAKE_TOOLCHAIN_FILE CACHE)
endif()
if(WEBOS)
# The Linux overlays take libpng, cairo and gtk3 from the system, which
# for a cross build would mean the host's x86_64 copies. Only the dbus
# one is worth carrying over - nothing links D-Bus, SDL dlopen()s it -
# and its port exists because upstream's tarball host answers 504 often
# enough to fail whole pipelines.
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_webos;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
# vcpkg builds its ports with the triplet's own toolchain, not with the
# one this file chainloads for the core (that reaches try_compile and
# nothing else), so the cross compiler has to be named in a triplet of
# its own. arm64-linux would build them for the host.
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_triplets")
elseif(CEMU_TARGET_ANDROID)
# no android-specific overlay ports at the moment - vcpkg refuses to start
# if an overlay path does not exist, so do not name one that is not there
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
elseif(VCPKG_TARGET_TRIPLET MATCHES "mingw")
# Cross-compiling to Windows from a Unix host: WIN32 is not set yet (no
# project() call so far, and the host is what counts before it), so the
# triplet is what tells the Windows overlay apart from the Linux one.
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_win;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
elseif(UNIX AND NOT APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
elseif(APPLE)
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_mac;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
else()
set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_win;${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports")
endif()
# libusb is a feature rather than a plain dependency because iOS and tvOS
# have no USB API to build it against: its Darwin backend is IOKit's USB
# family, and the SDK there does not even carry IOKit/IOCFBundle.h, so the
# port stops on a missing header before anything of ours is compiled.
# Nothing is lost - a phone or a TV box has no Wii U GamePad adapter to
# plug in - and ENABLE_LIBUSB below follows the same condition.
#
# This runs before project(), so APPLE is not set yet and the system name
# is what tells the platforms apart.
# webOS is left out for a different reason than the Apple ones: the port
# builds there, but its Linux backend wants libudev, which the buildroot
# does not carry - and a webOS app has no raw USB access to use it with.
if (NOT CMAKE_SYSTEM_NAME STREQUAL "iOS" AND NOT CMAKE_SYSTEM_NAME STREQUAL "tvOS" AND NOT WEBOS)
list(APPEND VCPKG_MANIFEST_FEATURES "usb")
endif()
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain file")
# Set this so that all the various find_package() calls don't need an explicit
# CONFIG option
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
if (WIN32)
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "")
endif()
endif()
project(Cemu VERSION 2.0.0)
# vcpkg hangs an applocal.ps1 post-build step on every Windows target, to copy
# the DLLs it built next to the binary. That is a PowerShell script, and the
# MinGW job cross-compiles on Linux, where linking cemu_libretro.dll then ends
# in "/bin/sh: 1: powershell.exe: not found" with everything else already done.
# A static triplet has no DLLs to copy anyway.
if(WIN32 AND NOT MSVC)
set(VCPKG_APPLOCAL_DEPS OFF)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT
add_definitions(-DEMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR})
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR})
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# enable link time optimization for release builds
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
if (MSVC)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT CemuBin)
# floating point model: precise, fiber safe optimizations
add_compile_options(/EHsc /fp:precise)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Speeds up static linking (especially helpful in incremental compilation)
if((CMAKE_LINKER MATCHES ".*lld-link.*") AND (CMAKE_AR MATCHES ".*llvm-lib.*"))
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY STATIC_LIBRARY_OPTIONS /llvmlibthin)
endif()
else()
add_compile_options(/GT)
endif()
# enable additional optimization flags for release builds
add_compile_options($<$<CONFIG:Release,RelWithDebInfo>:/Oi>) # enable intrinsic functions
add_compile_options($<$<CONFIG:Release,RelWithDebInfo>:/Ot>) # favor speed
endif()
if(CMAKE_GENERATOR STREQUAL "Xcode")
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY XCODE_STARTUP_PROJECT CemuBin)
endif()
if (APPLE)
enable_language(OBJC OBJCXX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4")
endif()
if (UNIX AND NOT APPLE)
# webOS: the buildroot carries no Wayland.
if (WEBOS)
set(CEMU_DESKTOP_SESSION_DEFAULT OFF)
else()
set(CEMU_DESKTOP_SESSION_DEFAULT ON)
endif()
option(ENABLE_WAYLAND "Build with Wayland support" ${CEMU_DESKTOP_SESSION_DEFAULT})
endif()
if (APPLE)
set(ENABLE_METAL_DEFAULT ON)
set(ENABLE_OPENGL_DEFAULT OFF)
else()
set(ENABLE_METAL_DEFAULT OFF)
# The GL backend here is desktop GL 4.5 with direct state access; webOS
# offers GLES through EGL and nothing more, so Vulkan is the backend that
# fits there - the same split as Android.
if (WEBOS)
set(ENABLE_OPENGL_DEFAULT OFF)
else()
set(ENABLE_OPENGL_DEFAULT ON)
endif()
endif()
option(ENABLE_OPENGL "Enables the OpenGL backend" ${ENABLE_OPENGL_DEFAULT})
option(ENABLE_VULKAN "Enables the Vulkan backend" ON)
option(ENABLE_METAL "Enables the Metal backend" ${ENABLE_METAL_DEFAULT})
if (ENABLE_METAL AND NOT APPLE)
message(FATAL_ERROR "Metal backend is only supported on Apple platforms")
endif()
# Input comes from the frontend (LibretroControllerProvider) and audio goes to
# it (LibretroAudioAPI); the standalone's own backends - SDL, the DSU client,
# the keyboard, real Wiimotes over hidapi/BlueZ, XInput, DirectInput, cubeb,
# XAudio2, DirectSound - are gone. libusb stays: nsyshid passes real USB toy
# portals through to the game with it, which the frontend cannot do.
#
# Off on the Apple embedded platforms, where there is no USB API to talk to and
# the dependency is not built either (see the manifest features above).
if (CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR WEBOS)
set(ENABLE_LIBUSB_DEFAULT OFF)
else()
set(ENABLE_LIBUSB_DEFAULT ON)
endif()
option(ENABLE_LIBUSB "Build with libusb support" ${ENABLE_LIBUSB_DEFAULT})
if (ENABLE_LIBUSB)
add_compile_definitions(HAS_LIBUSB)
endif()
if (ENABLE_LIBRETRO)
add_compile_definitions(ENABLE_LIBRETRO)
# All static libs must be compiled with -fPIC for shared library linking
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if(ENABLE_LIBRETRO)
add_compile_definitions(RETRO_CORE)
endif()
find_package(Threads REQUIRED)
find_package(CURL REQUIRED)
find_package(pugixml REQUIRED)
find_package(RapidJSON REQUIRED)
set(CEMU_BOOST_COMPONENTS program_options filesystem nowide)
if (ANDROID)
# Fiber/FiberAndroid.cpp needs fcontext
list(APPEND CEMU_BOOST_COMPONENTS context)
endif()
find_package(Boost COMPONENTS ${CEMU_BOOST_COMPONENTS} REQUIRED)
find_package(libzip REQUIRED)
find_package(glslang REQUIRED)
find_package(ZLIB REQUIRED)
find_package(zstd MODULE REQUIRED) # MODULE so that zstd::zstd is available
find_package(OpenSSL COMPONENTS Crypto SSL REQUIRED)
find_package(glm REQUIRED)
find_package(fmt 12.1 REQUIRED)
find_package(PNG REQUIRED)
# glslang versions older than 11.11.0 define targets without a namespace
if (NOT TARGET glslang::SPIRV AND TARGET SPIRV)
add_library(glslang::SPIRV ALIAS SPIRV)
endif()
# Android and webOS are UNIX as far as CMake is concerned, but have none of
# this: no X11 and no Wayland.
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT WEBOS)
find_package(X11 REQUIRED)
if (ENABLE_WAYLAND)
find_package(Wayland REQUIRED Client)
find_package(WaylandScanner REQUIRED)
find_package(WaylandProtocols 1.15 REQUIRED)
ecm_add_wayland_client_protocol(WAYLAND_PROTOCOL_SRCS
PROTOCOL "${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml"
BASENAME viewporter)
add_library(CemuWaylandProtocols STATIC ${WAYLAND_PROTOCOL_SRCS})
target_include_directories(CemuWaylandProtocols PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
target_include_directories(CemuWaylandProtocols PRIVATE ${Wayland_INCLUDE_DIRS})
add_compile_definitions(HAS_WAYLAND)
endif()
endif()
if (WEBOS)
add_compile_definitions(WEBOS)
endif()
if (ENABLE_VULKAN)
include_directories("dependencies/Vulkan-Headers/include")
add_compile_definitions(ENABLE_VULKAN)
add_compile_definitions(VK_NO_PROTOTYPES)
endif()
if (ENABLE_OPENGL AND NOT ANDROID AND NOT WEBOS)
# Android and webOS have no desktop OpenGL, only GLES/EGL - the core uses
# Vulkan on both.
find_package(OpenGL REQUIRED)
add_compile_definitions(ENABLE_OPENGL)
endif()
if (ENABLE_METAL)
include_directories(${CMAKE_SOURCE_DIR}/dependencies/metal-cpp)
add_compile_definitions(ENABLE_METAL)
endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
if (CMAKE_OSX_ARCHITECTURES)
set(CEMU_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(CEMU_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
endif()
find_package(ZArchive QUIET)
if (NOT ZArchive_FOUND)
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)
endif()
add_subdirectory(src)
+350
View File
@@ -0,0 +1,350 @@
DEBUG = 0
LINK_STATIC_LIBCPLUSPLUS = 1
LTO ?=
CORE_DIR := .
TARGET_NAME := parallel_gx2
ifeq ($(ECHO),1)
E := 1
endif
# Windows is recognised from the environment every Windows shell sets (all
# MSYS2 environments included, whatever uname -s reports there) or from a
# MinGW-w64 compiler, so a cross build needs only CC/CXX.
ifeq ($(platform),)
platform = unix
UNAME_S := $(shell uname -s 2>/dev/null)
ifeq ($(OS),Windows_NT)
platform = win
else ifeq ($(UNAME_S),)
platform = win
else ifneq ($(findstring Darwin,$(UNAME_S)),)
platform = osx
else ifneq (,$(findstring mingw,$(shell $(CC) -dumpmachine 2>/dev/null)))
platform = win
endif
endif
# make's built-in default compiler is cc, which MinGW-w64 does not ship.
ifneq (,$(filter win win64,$(platform)))
ifeq ($(origin CC),default)
CC := gcc
endif
ifeq ($(origin CXX),default)
CXX := g++
endif
endif
# webOS SDK toolchains are plain Linux cross compilers; their name is what
# tells them apart.
ifneq (,$(findstring webos,$(CC)))
ifneq (,$(findstring unix,$(platform)))
override platform := webos
endif
endif
# The graphic packs are packed by a tool built for, and run on, the
# build machine.
ifeq ($(OS),Windows_NT)
HOSTCC ?= gcc
HOST_EXE := .exe
else
HOSTCC ?= cc
endif
# ----------------------------------------------------------------------------
# Target architecture: taken from the compiler (or the Apple -arch/-target
# selection), not the host, so cross builds pick the right recompiler.
# ----------------------------------------------------------------------------
ifeq ($(platform), osx)
ifeq ($(CROSS_COMPILE),1)
arch := $(firstword $(subst -, ,$(LIBRETRO_APPLE_PLATFORM)))
endif
ifeq ($(arch),)
arch := $(shell uname -m)
endif
else ifneq (,$(filter ios-arm64 tvos-arm64 android-arm64,$(platform)))
arch := arm64
else ifeq ($(platform), android-x86_64)
arch := x86_64
else ifeq ($(arch),)
arch := $(firstword $(subst -, ,$(shell $(CC) -dumpmachine 2>/dev/null)))
ifeq ($(arch),)
arch := $(shell uname -m)
endif
endif
ifneq (,$(filter x86_64 amd64 x64,$(arch)))
ARCH_X86_64 := 1
arch := x86_64
else ifneq (,$(filter aarch64 arm64 arm,$(arch)))
ARCH_ARM64 := 1
arch := arm64
else
$(error Unsupported target architecture '$(arch)': the core builds for x86_64 and arm64)
endif
HAVE_LIBUSB := 1
HAVE_VULKAN := 1
HAVE_OPENGL := 0
HAVE_METAL := 0
FLAGS :=
CFLAGS :=
CXXFLAGS :=
LDFLAGS :=
LIBS :=
# Unix (Linux) and webOS
ifneq (,$(filter unix webos,$(platform)))
OS_UNIX := 1
OS_LINUX := 1
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--no-undefined -Wl,--version-script=$(CORE_DIR)/src/libretro/link.T -Wl,-z,noexecstack
ifeq ($(platform), webos)
FLAGS += -DWEBOS
HAVE_LIBUSB := 0
else
# EGL alone: GL's functions are loaded by the renderer, and GLX's and
# Xlib's are found at run time (CemuLibretro.cpp), so the core loads
# without X11
HAVE_OPENGL := 1
LIBS += -lEGL
endif
ifeq ($(LINK_STATIC_LIBCPLUSPLUS),1)
LDFLAGS += -static-libgcc -static-libstdc++
endif
LIBS += -lpthread -ldl -lrt
# macOS
else ifeq ($(platform), osx)
OS_UNIX := 1
OS_APPLE := 1
OS_MACOS := 1
TARGET := $(TARGET_NAME)_libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib
HAVE_METAL := 1
MACOS_MINVER ?= 13.4
ifeq ($(CROSS_COMPILE),1)
TARGET_RULE := -target $(arch)-apple-macos$(MACOS_MINVER) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
else
TARGET_RULE := -arch $(arch) -mmacosx-version-min=$(MACOS_MINVER)
endif
FLAGS += $(TARGET_RULE)
LDFLAGS += $(TARGET_RULE) -Wl,-dead_strip
LIBS += -framework AppKit -framework CoreFoundation -framework Foundation \
-framework IOKit -framework Security -framework Metal -framework QuartzCore \
-lobjc
# iOS / tvOS
else ifneq (,$(filter ios-arm64 tvos-arm64,$(platform)))
OS_UNIX := 1
OS_APPLE := 1
OS_IOS := 1
fpic := -fPIC
SHARED := -dynamiclib
HAVE_METAL := 1
HAVE_VULKAN := 0
HAVE_LIBUSB := 0
ifeq ($(platform), tvos-arm64)
TARGET := $(TARGET_NAME)_libretro_tvos.dylib
MINVER ?= 16.4
ifeq ($(IOSSDK),)
IOSSDK := $(shell xcrun -sdk appletvos -show-sdk-path)
endif
MINVER_FLAG := -mappletvos-version-min=$(MINVER)
else
TARGET := $(TARGET_NAME)_libretro_ios.dylib
IOS_MINVER ?= 16.4
ifeq ($(IOSSDK),)
IOSSDK := $(shell xcrun -sdk iphoneos -show-sdk-path)
endif
MINVER_FLAG := -miphoneos-version-min=$(IOS_MINVER)
endif
CC = cc -arch arm64 -isysroot $(IOSSDK)
CXX = c++ -arch arm64 -isysroot $(IOSSDK)
FLAGS += $(MINVER_FLAG) -DIPHONEOS
LDFLAGS += $(MINVER_FLAG)
LIBS += -framework CoreFoundation -framework Foundation -framework Metal \
-framework QuartzCore -framework IOKit -lobjc
# Android (NDK clang)
else ifneq (,$(filter android-arm64 android-x86_64,$(platform)))
OS_UNIX := 1
OS_ANDROID := 1
TARGET := $(TARGET_NAME)_libretro_android.so
fpic := -fPIC
SHARED := -shared -Wl,--no-undefined -Wl,--version-script=$(CORE_DIR)/src/libretro/link.T -Wl,-z,noexecstack
ANDROID_API ?= 28
ifeq ($(platform), android-arm64)
ANDROID_TRIPLE := aarch64-linux-android$(ANDROID_API)
else
ANDROID_TRIPLE := x86_64-linux-android$(ANDROID_API)
endif
NDK_ROOT ?= $(ANDROID_NDK_ROOT)
ANDROID_NDK_LLVM ?= $(NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64
CC = $(ANDROID_NDK_LLVM)/bin/clang --target=$(ANDROID_TRIPLE)
CXX = $(ANDROID_NDK_LLVM)/bin/clang++ --target=$(ANDROID_TRIPLE)
AR = $(ANDROID_NDK_LLVM)/bin/llvm-ar
FLAGS += -DANDROID -DVK_USE_PLATFORM_ANDROID_KHR -funwind-tables
LDFLAGS += -static-libstdc++
LIBS += -landroid -llog -lm -ldl
# Windows (MinGW-w64, native MSYS2 or cross)
else
OS_WIN := 1
TARGET := $(TARGET_NAME)_libretro.dll
SHARED := -shared -Wl,--no-undefined -Wl,--version-script=$(CORE_DIR)/src/libretro/link.T
FLAGS += -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00 -DWIN32_LEAN_AND_MEAN
HAVE_OPENGL := 1
LDFLAGS += -static-libgcc -static-libstdc++ -static
LIBS += -lopengl32 -ldbghelp -lgdi32 -lntdll -lpsapi -liphlpapi -lws2_32 -lbcrypt \
-ladvapi32 -lsetupapi -lole32 -luuid -lshell32 -luser32 -lkernel32 -lwinpthread
endif
GEN_DIR := $(CORE_DIR)/obj/gen
OBJ_DIR := $(CORE_DIR)/obj/$(platform)-$(arch)
include Makefile.common
# ----------------------------------------------------------------------------
# Compiler flags
# ----------------------------------------------------------------------------
ifneq (,$(filter 1,$(OS_LINUX) $(OS_APPLE)))
FLAGS += -D_FILE_OFFSET_BITS=64
endif
ifeq ($(OS_APPLE),1)
FLAGS += -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE
endif
FLAGS += -fno-strict-aliasing
# Everything the core does not export stays hidden; the libretro entry points
# carry visibility("default") through RETRO_API.
ifneq (,$(fpic))
FLAGS += $(fpic)
endif
FLAGS += -fvisibility=hidden
CXXFLAGS += -fvisibility-inlines-hidden
ifeq ($(DEBUG), 1)
FLAGS += -O0 -g -D_DEBUG -DCEMU_DEBUG_ASSERT
else
FLAGS += -O2 -DNDEBUG
endif
ifneq ($(LTO),)
FLAGS += $(LTO)
LDFLAGS += $(LTO)
endif
ifneq ($(SANITIZER),)
FLAGS += -fsanitize=$(SANITIZER)
LDFLAGS += -fsanitize=$(SANITIZER)
endif
CFLAGS := $(FLAGS) -std=gnu11 $(CFLAGS)
CXXFLAGS := $(FLAGS) -std=gnu++20 $(CXXFLAGS)
LDFLAGS += $(fpic) $(SHARED)
# ----------------------------------------------------------------------------
# Objects
# ----------------------------------------------------------------------------
# Objects live under obj/<platform>-<arch>, one tree per target, so the
# submodules stay clean and a second architecture never links the first
# one's objects.
obj_of = $(patsubst %,$(OBJ_DIR)/%.o,$(1))
define LIB_template
$(1)_OBJECTS := $$(call obj_of,$$($(1)_SOURCES_C) $$($(1)_SOURCES_CXX) $$($(1)_SOURCES_ASM) $$($(1)_SOURCES_OBJCXX))
$$($(1)_OBJECTS): GROUP := $(1)
OBJECTS += $$($(1)_OBJECTS)
endef
OBJECTS :=
$(foreach g,$(LIB_GROUPS),$(eval $(call LIB_template,$(g))))
DEPS := $(OBJECTS:.o=.d)
ifneq ($(CORE_PCH),)
$(CORE_OBJECTS): $(CORE_PCH)
DEPS += $(CORE_PCH_STUB).d
endif
# The toolchain and configuration a tree was built with; a change wipes it,
# since nothing in an object's name says which compiler produced it.
BUILD_TAG := $(CC)|$(CXX)|$(platform)|$(arch)|$(DEBUG)|$(LTO)|$(SANITIZER)|$(HAVE_VULKAN)|$(HAVE_OPENGL)|$(HAVE_METAL)|$(HAVE_LIBUSB)
BUILD_TAG_STATUS := $(shell \
if [ "`cat $(OBJ_DIR)/.build-tag 2>/dev/null`" != "$(BUILD_TAG)" ]; then \
rm -rf $(OBJ_DIR); mkdir -p $(OBJ_DIR); \
echo "$(BUILD_TAG)" > $(OBJ_DIR)/.build-tag; \
fi)
# after the line above, which may have emptied the tree
ifneq ($(CORE_PCH),)
CORE_PCH_FLAGS_STATUS := $(shell mkdir -p $(OBJ_DIR)/pch; \
echo '$(CORE_PCH_FLAGS)' > $(OBJ_DIR)/pch/flags.tmp; \
cmp -s $(OBJ_DIR)/pch/flags.tmp $(OBJ_DIR)/pch/flags || mv -f $(OBJ_DIR)/pch/flags.tmp $(OBJ_DIR)/pch/flags; \
rm -f $(OBJ_DIR)/pch/flags.tmp)
endif
.DEFAULT_GOAL := all
all: $(TARGET)
-include $(DEPS)
$(OBJECTS): | $(GEN_HEADERS)
# The object list outgrows the 32 KiB Windows command line, so there the
# linker reads it from a response file ($(file) needs GNU make 4.0, which
# MSYS2 and the cross toolchains have; the Xcode make is 3.81).
ifeq ($(OS_WIN),1)
LINK_OBJECTS = @$(OBJ_DIR)/link-objects.rsp
else
LINK_OBJECTS = $(OBJECTS)
endif
$(TARGET): $(OBJECTS)
@$(if $(E),echo LD $@,)
ifeq ($(OS_WIN),1)
$(file >$(OBJ_DIR)/link-objects.rsp,$(OBJECTS))
endif
$(CXX) -o $@ $(LINK_OBJECTS) $(LDFLAGS) $(LIBS)
$(OBJ_DIR)/%.c.o: %.c
@mkdir -p $(@D)
@$(if $(E),echo CC $<,)
$(CC) $(CFLAGS) $($(GROUP)_FLAGS) $($(GROUP)_CFLAGS) -MMD -MP -c $< -o $@
$(OBJ_DIR)/%.cpp.o: %.cpp
@mkdir -p $(@D)
@$(if $(E),echo CXX $<,)
$(CXX) $(CXXFLAGS) $($(GROUP)_FLAGS) $($(GROUP)_CXXFLAGS) -MMD -MP -c $< -o $@
$(OBJ_DIR)/%.cc.o: %.cc
@mkdir -p $(@D)
@$(if $(E),echo CXX $<,)
$(CXX) $(CXXFLAGS) $($(GROUP)_FLAGS) $($(GROUP)_CXXFLAGS) -MMD -MP -c $< -o $@
# Objective-C++ cannot take the C++ precompiled header: the header itself.
$(OBJ_DIR)/%.mm.o: %.mm
@mkdir -p $(@D)
@$(if $(E),echo OBJCXX $<,)
$(CXX) -x objective-c++ $(CXXFLAGS) $($(GROUP)_FLAGS) $(subst $(CORE_PCH_INCLUDE),$(CORE_PCH_HEADER),$($(GROUP)_CXXFLAGS)) -MMD -MP -c $< -o $@
$(OBJ_DIR)/%.S.o: %.S
@mkdir -p $(@D)
@$(if $(E),echo AS $<,)
$(CC) $(CFLAGS) $($(GROUP)_FLAGS) $($(GROUP)_ASFLAGS) -MMD -MP -c $< -o $@
$(OBJ_DIR)/%.s.o: %.s
@mkdir -p $(@D)
@$(if $(E),echo AS $<,)
$(CC) -x assembler-with-cpp $(CFLAGS) $($(GROUP)_FLAGS) $($(GROUP)_ASFLAGS) -c $< -o $@
clean:
rm -rf $(CORE_DIR)/obj
rm -f $(TARGET)
.PHONY: all clean
+702
View File
@@ -0,0 +1,702 @@
# Source lists and per-library flags for the static Makefile build of the
# parallel_gx2 libretro core. Included by Makefile, which sets the platform
# switches used here:
#
# ARCH_X86_64 / ARCH_ARM64 target architecture
# OS_WIN / OS_UNIX OS family
# OS_LINUX (Linux, webOS) / OS_ANDROID / OS_APPLE, with OS_MACOS or OS_IOS
# HAVE_LIBUSB HAVE_VULKAN HAVE_OPENGL HAVE_METAL optional components
#
# Units only the standalone emulator uses are left out.
DEP_DIR := $(CORE_DIR)/dependencies
SRC_DIR := $(CORE_DIR)/src
LRC_DIR := $(DEP_DIR)/libretro-common
# ----------------------------------------------------------------------------
# Generated files
# ----------------------------------------------------------------------------
GLSLANG_DIR := $(DEP_DIR)/glslang
GLSLANG_GEN_DIR := $(GEN_DIR)/glslang
GRAPHIC_PACKS_C := $(GEN_DIR)/libretro_graphic_packs.cpp
PACK_TOOL := $(GEN_DIR)/pack_graphic_packs$(HOST_EXE)
GEN_HEADERS := $(GLSLANG_GEN_DIR)/glslang/build_info.h $(GLSLANG_GEN_DIR)/glslang/SPIRV/GlslangToSpv.h
# glslang's version, as its build_info.py takes it from CHANGES.md
GLSLANG_VERSION := $(shell sed -n 's/^\#\# \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1 \2 \3/p' $(GLSLANG_DIR)/CHANGES.md | head -n 1)
$(GLSLANG_GEN_DIR)/glslang/build_info.h: $(GLSLANG_DIR)/build_info.h.tmpl $(GLSLANG_DIR)/CHANGES.md
@mkdir -p $(@D)
sed -e 's/@major@/$(word 1,$(GLSLANG_VERSION))/' -e 's/@minor@/$(word 2,$(GLSLANG_VERSION))/' \
-e 's/@patch@/$(word 3,$(GLSLANG_VERSION))/' -e 's/@flavor@//' $< > $@
# <glslang/SPIRV/...>, where an installed glslang has the SPIRV headers
$(GLSLANG_GEN_DIR)/glslang/SPIRV/GlslangToSpv.h:
@mkdir -p $(@D)
echo '#include "$(abspath $(GLSLANG_DIR)/SPIRV/GlslangToSpv.h)"' > $@
# The community graphic packs, compiled in (src/libretro/LibretroGraphicPacks.cpp)
GRAPHIC_PACKS_DIR := $(DEP_DIR)/cemu_graphic_packs
PACK_TOOL_SOURCES := $(CORE_DIR)/tools/pack_graphic_packs.c \
$(addprefix $(DEP_DIR)/zlib/,adler32.c compress.c crc32.c deflate.c trees.c zutil.c)
$(PACK_TOOL): $(PACK_TOOL_SOURCES)
@mkdir -p $(@D)
$(HOSTCC) -O2 -I$(DEP_DIR)/zlib $(PACK_TOOL_SOURCES) -o $@
# The packs' names have spaces, which make cannot take as prerequisites:
# a stamp with a checksum of the tree's listing stands in for them, and is
# rewritten only when that changes.
GRAPHIC_PACKS_STAMP := $(GEN_DIR)/graphic_packs.stamp
GRAPHIC_PACKS_STATUS := $(shell mkdir -p $(GEN_DIR); \
(cd $(GRAPHIC_PACKS_DIR) 2>/dev/null && ls -lR Enhancements Resolutions Mods Workarounds src LICENSE.md 2>/dev/null) \
| cksum > $(GRAPHIC_PACKS_STAMP).tmp; \
cmp -s $(GRAPHIC_PACKS_STAMP).tmp $(GRAPHIC_PACKS_STAMP) || mv -f $(GRAPHIC_PACKS_STAMP).tmp $(GRAPHIC_PACKS_STAMP); \
rm -f $(GRAPHIC_PACKS_STAMP).tmp)
$(GRAPHIC_PACKS_C): $(PACK_TOOL) $(GRAPHIC_PACKS_STAMP)
@mkdir -p $(@D)
$(PACK_TOOL) $(GRAPHIC_PACKS_DIR) $@
# The commit the core is built from, for log.txt
GIT_HASH := $(shell git -C $(CORE_DIR) log --format=%h -1 2>/dev/null)
ifeq ($(GIT_HASH),)
GIT_HASH := unknown
endif
# ----------------------------------------------------------------------------
# The core
# ----------------------------------------------------------------------------
CORE_SOURCES_C :=
CORE_SOURCES_CXX :=
CORE_SOURCES_ASM :=
CORE_SOURCES_OBJCXX :=
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Cafe/Account/Account.cpp \
$(CORE_DIR)/src/Cafe/CafeSystem.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/FST/FST.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/FST/KeyCache.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/WUD/wud.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/WUHB/WUHBReader.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fsc.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fscDeviceHostFS.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fscDeviceRedirect.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fscDeviceWua.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fscDeviceWud.cpp \
$(CORE_DIR)/src/Cafe/Filesystem/fscDeviceWuhb.cpp \
$(CORE_DIR)/src/Cafe/GamePatch.cpp \
$(CORE_DIR)/src/Cafe/GameProfile/GameProfile.cpp \
$(CORE_DIR)/src/Cafe/GraphicPack/GraphicPack2.cpp \
$(CORE_DIR)/src/Cafe/GraphicPack/GraphicPack2Patches.cpp \
$(CORE_DIR)/src/Cafe/GraphicPack/GraphicPack2PatchesApply.cpp \
$(CORE_DIR)/src/Cafe/GraphicPack/GraphicPack2PatchesParser.cpp \
$(CORE_DIR)/src/Cafe/HW/ACR/ACR.cpp \
$(CORE_DIR)/src/Cafe/HW/AI/AI.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Debugger/Debugger.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterFPU.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterImpl.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterMain.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterOPC.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterPS.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/PPCScheduler.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/PPCSchedulerLLE.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/PPCTimer.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLAnalyzer.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLDebug.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocator.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLRegisterAllocatorRanges.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/IML/IMLSegment.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/PPCRecompiler.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGen.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp \
$(CORE_DIR)/src/Cafe/HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Common/RegisterSerializer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Common/ShaderSerializer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/FetchShader.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteAsyncCommands.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteBufferData.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteGSCopyShaderParser.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteIndices.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteOverlay.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteQuery.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteRingBuffer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteShader.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteSoftware.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteStreamoutGPU.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTexture.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTextureLoader.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTextureView.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteThread.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Core/LatteTiming.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib_Coord.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLAttrDecoder.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerRegisterDataTypeTracker.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Renderer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/RendererCore.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/RendererShader.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/ShaderInfo/ShaderDescription.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/ShaderInfo/ShaderInstanceInfo.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Transcompiler/LatteTC.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Transcompiler/LatteTCGenIR.cpp \
$(CORE_DIR)/src/Cafe/HW/MMU/MMU.cpp \
$(CORE_DIR)/src/Cafe/HW/SI/SI.cpp \
$(CORE_DIR)/src/Cafe/HW/VI/VI.cpp \
$(CORE_DIR)/src/Cafe/IOSU/ODM/iosu_odm.cpp \
$(CORE_DIR)/src/Cafe/IOSU/PDM/iosu_pdm.cpp \
$(CORE_DIR)/src/Cafe/IOSU/ccr_nfc/iosu_ccr_nfc.cpp \
$(CORE_DIR)/src/Cafe/IOSU/fsa/iosu_fsa.cpp \
$(CORE_DIR)/src/Cafe/IOSU/kernel/iosu_kernel.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_acp.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_act.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_crypto.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_fpd.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_ioctl.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_mcp.cpp \
$(CORE_DIR)/src/Cafe/IOSU/legacy/iosu_nim.cpp \
$(CORE_DIR)/src/Cafe/IOSU/nn/boss/boss_common.cpp \
$(CORE_DIR)/src/Cafe/IOSU/nn/boss/boss_service.cpp \
$(CORE_DIR)/src/Cafe/IOSU/nn/iosu_nn_service.cpp \
$(CORE_DIR)/src/Cafe/OS/RPL/COSModule.cpp \
$(CORE_DIR)/src/Cafe/OS/RPL/elf.cpp \
$(CORE_DIR)/src/Cafe/OS/RPL/rpl.cpp \
$(CORE_DIR)/src/Cafe/OS/RPL/rpl_debug_symbols.cpp \
$(CORE_DIR)/src/Cafe/OS/RPL/rpl_symbol_storage.cpp \
$(CORE_DIR)/src/Cafe/OS/common/OSCommon.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/TCL/TCL.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/avm/avm.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/camera/camera.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Alarm.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Atomic.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_BSP.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Callbacks.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_CodeGen.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Coroutine.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_DynLoad.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_FG.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_GHS.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_HWInterface.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_IM.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_IOS.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_IPC.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_IPCBuf.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Init.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_LockedCache.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MEM.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MEM_BlockHeap.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MEM_ExpHeap.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MEM_FrmHeap.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MEM_UnitHeap.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MPQueue.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Memory.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MemoryMapping.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_MessageQueue.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_OverlayArena.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Scheduler.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Spinlock.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_SysHeap.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_SystemInfo.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_ThreadQueue.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/coreinit/coreinit_Time.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/dmae/dmae.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/drmapp/drmapp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/erreula/erreula.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_AddrTest.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Blit.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Command.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_ContextState.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Draw.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Event.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Memory.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Misc.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Query.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_RenderTarget.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Resource.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Shader.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_State.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Streamout.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Surface.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_Texture.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_TilingAperture.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/gx2/GX2_shader_legacy.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/h264_avc/H264Dec.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/h264_avc/H264DecBackendAVC.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/h264_avc/parser/H264Parser.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/mic/mic.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nfc/TLV.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nfc/TagV0.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nfc/ndef.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nfc/nfc.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nfc/stream.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nlibcurl/nlibcurl.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nlibnss/nlibnss.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_ac/nn_ac.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_acp/nn_acp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_act/nn_act.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_aoc/nn_aoc.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_boss/nn_boss.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_ccr/nn_ccr.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_cmpt/nn_cmpt.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_ec/nn_ec.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_fp/nn_fp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_ndm/nn_ndm.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_nfp/nn_nfp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_nim/nn_nim.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_Common.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_PostTypes.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_pdm/nn_pdm.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_save/nn_save.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_sl/nn_sl.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_spm/nn_spm.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_temp/nn_temp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nn_uds/nn_uds.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/BackendEmulated.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/Dimensions.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/Infinity.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/Skylander.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/SkylanderXbox360.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/Whitelist.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/g721/g721.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/nsyshid.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsyskbd/nsyskbd.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/nsysnet/nsysnet.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/ntag/ntag.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/padscore/padscore.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/proc_ui/proc_ui.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_aux.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_exports.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_ist.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_mix.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_multivoice.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_out.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_core/ax_voice.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/snd_user/snd_user.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/swkbd/swkbd.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/sysapp/sysapp.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/vpad/vpad.cpp \
$(CORE_DIR)/src/Cafe/OS/libs/zlib125/zlib125.cpp \
$(CORE_DIR)/src/Cafe/TitleList/GameInfo.cpp \
$(CORE_DIR)/src/Cafe/TitleList/SaveInfo.cpp \
$(CORE_DIR)/src/Cafe/TitleList/SaveList.cpp \
$(CORE_DIR)/src/Cafe/TitleList/TitleConverter.cpp \
$(CORE_DIR)/src/Cafe/TitleList/TitleInfo.cpp \
$(CORE_DIR)/src/Cafe/TitleList/TitleList.cpp \
$(CORE_DIR)/src/Cemu/ExpressionParser/ExpressionParser.cpp \
$(CORE_DIR)/src/Cemu/FileCache/FileCache.cpp \
$(CORE_DIR)/src/Cemu/Logging/CemuLogging.cpp \
$(CORE_DIR)/src/Cemu/PPCAssembler/ppcAssembler.cpp \
$(CORE_DIR)/src/Cemu/napi/napi.cpp \
$(CORE_DIR)/src/Cemu/napi/napi_act.cpp \
$(CORE_DIR)/src/Cemu/napi/napi_ec.cpp \
$(CORE_DIR)/src/Cemu/napi/napi_helper.cpp \
$(CORE_DIR)/src/Cemu/napi/napi_idbe.cpp \
$(CORE_DIR)/src/Cemu/napi/napi_version.cpp \
$(CORE_DIR)/src/Cemu/ncrypto/ncrypto.cpp \
$(CORE_DIR)/src/Cemu/nex/nex.cpp \
$(CORE_DIR)/src/Cemu/nex/nexFriends.cpp \
$(CORE_DIR)/src/Cemu/nex/nexThread.cpp \
$(CORE_DIR)/src/Cemu/nex/prudp.cpp \
$(CORE_DIR)/src/Common/ExceptionHandler/Breadcrumb.cpp \
$(CORE_DIR)/src/Common/ExceptionHandler/ExceptionHandler.cpp \
$(CORE_DIR)/src/Common/SysAllocator.cpp \
$(CORE_DIR)/src/Common/VFSFileStream.cpp \
$(CORE_DIR)/src/Common/cpu_features.cpp \
$(CORE_DIR)/src/Common/precompiled.cpp \
$(CORE_DIR)/src/audio/IAudioAPI.cpp \
$(CORE_DIR)/src/audio/IAudioInputAPI.cpp \
$(CORE_DIR)/src/config/ActiveSettings.cpp \
$(CORE_DIR)/src/config/CemuConfig.cpp \
$(CORE_DIR)/src/config/NetworkSettings.cpp \
$(CORE_DIR)/src/imgui/imgui_extension.cpp \
$(CORE_DIR)/src/input/ControllerFactory.cpp \
$(CORE_DIR)/src/input/InputManager.cpp \
$(CORE_DIR)/src/input/api/Controller.cpp \
$(CORE_DIR)/src/input/api/ControllerState.cpp \
$(CORE_DIR)/src/input/api/Libretro/LibretroController.cpp \
$(CORE_DIR)/src/input/api/Libretro/LibretroControllerProvider.cpp \
$(CORE_DIR)/src/input/emulated/ClassicController.cpp \
$(CORE_DIR)/src/input/emulated/EmulatedController.cpp \
$(CORE_DIR)/src/input/emulated/ProController.cpp \
$(CORE_DIR)/src/input/emulated/VPADController.cpp \
$(CORE_DIR)/src/input/emulated/WPADController.cpp \
$(CORE_DIR)/src/input/emulated/WiimoteController.cpp \
$(CORE_DIR)/src/libretro/CemuLibretro.cpp \
$(CORE_DIR)/src/libretro/LibretroAudioAPI.cpp \
$(CORE_DIR)/src/libretro/LibretroGraphicPacks.cpp \
$(CORE_DIR)/src/libretro/LibretroWindowSystem.cpp \
$(CORE_DIR)/src/main.cpp \
$(CORE_DIR)/src/mainLLE.cpp \
$(CORE_DIR)/src/resource/CafeDefaultFont.cpp \
$(CORE_DIR)/src/resource/embedded/resources.cpp \
$(CORE_DIR)/src/util/IniParser/IniParser.cpp \
$(CORE_DIR)/src/util/SystemInfo/SystemInfo.cpp \
$(CORE_DIR)/src/util/VirtualHeap/VirtualHeap.cpp \
$(CORE_DIR)/src/util/Zir/Core/IR.cpp \
$(CORE_DIR)/src/util/Zir/EmitterGLSL/ZpIREmitGLSL.cpp \
$(CORE_DIR)/src/util/Zir/Passes/RegisterAllocatorForGLSL.cpp \
$(CORE_DIR)/src/util/Zir/Passes/ZpIRRegisterAllocator.cpp \
$(CORE_DIR)/src/util/bootSound/BootSoundReader.cpp \
$(CORE_DIR)/src/util/crypto/aes128.cpp \
$(CORE_DIR)/src/util/crypto/crc32.cpp \
$(CORE_DIR)/src/util/crypto/md5.cpp \
$(CORE_DIR)/src/util/helpers/Serializer.cpp \
$(CORE_DIR)/src/util/helpers/helpers.cpp \
$(CORE_DIR)/src/util/highresolutiontimer/HighResolutionTimer.cpp \
$(CORE_DIR)/src/util/tinyxml2/tinyxml2.cpp
CORE_SOURCES_CXX += $(GRAPHIC_PACKS_C)
ifeq ($(OS_WIN),1)
CORE_SOURCES_CXX += \
$(SRC_DIR)/Common/windows/platform.cpp \
$(SRC_DIR)/Common/windows/FileStream_win32.cpp \
$(SRC_DIR)/Common/ExceptionHandler/ExceptionHandler_win32.cpp \
$(SRC_DIR)/util/Fiber/FiberWin.cpp \
$(SRC_DIR)/util/MemMapper/MemMapperWin.cpp \
$(SRC_DIR)/util/SystemInfo/SystemInfoWin.cpp
else
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Common/unix/platform.cpp \
$(CORE_DIR)/src/Common/unix/FileStream_unix.cpp \
$(CORE_DIR)/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp \
$(CORE_DIR)/src/util/MemMapper/MemMapperUnix.cpp \
$(CORE_DIR)/src/util/SystemInfo/SystemInfoUnix.cpp
endif
ifeq ($(OS_LINUX),1)
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Common/ExceptionHandler/ELFSymbolTable.cpp \
$(CORE_DIR)/src/util/Fiber/FiberUnix.cpp \
$(CORE_DIR)/src/util/SystemInfo/SystemInfoLinux.cpp
CORE_SOURCES_ASM += \
$(CORE_DIR)/src/resource/embedded/fontawesome.S
endif
ifeq ($(OS_ANDROID),1)
CORE_SOURCES_CXX += \
$(SRC_DIR)/util/Fiber/FiberAndroid.cpp \
$(CORE_DIR)/src/util/SystemInfo/SystemInfoLinux.cpp
CORE_SOURCES_ASM += \
$(CORE_DIR)/src/resource/embedded/fontawesome.S
endif
ifeq ($(OS_APPLE),1)
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/util/Fiber/FiberUnix.cpp \
$(SRC_DIR)/util/SystemInfo/SystemInfoMac.cpp
CORE_SOURCES_ASM += \
$(SRC_DIR)/resource/embedded/fontawesome_macos.S
endif
ifeq ($(HAVE_OPENGL),1)
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/LatteShaderGL.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLQuery.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererStreamout.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererUniformData.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp \
$(CORE_DIR)/src/imgui/imgui_impl_opengl3.cpp
endif
ifeq ($(HAVE_VULKAN),1)
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanQuery.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp \
$(CORE_DIR)/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp \
$(CORE_DIR)/src/imgui/imgui_impl_vulkan.cpp \
$(CORE_DIR)/src/libretro/LibretroVkQueue.cpp
endif
ifeq ($(HAVE_METAL),1)
CORE_SOURCES_CXX += \
$(addprefix $(SRC_DIR)/Cafe/HW/Latte/Renderer/Metal/, \
CachedFBOMtl.cpp LatteTextureMtl.cpp LatteTextureReadbackMtl.cpp \
LatteTextureViewMtl.cpp LatteToMtl.cpp MetalAttachmentsInfo.cpp \
MetalBufferAllocator.cpp MetalCppImpl.cpp MetalDepthStencilCache.cpp \
MetalLayerHandle.cpp MetalMemoryManager.cpp MetalOutputShaderCache.cpp \
MetalPipelineCache.cpp MetalPipelineCompiler.cpp MetalQuery.cpp \
MetalRenderer.cpp MetalSamplerCache.cpp MetalVoidVertexPipeline.cpp \
RendererShaderMtl.cpp) \
$(SRC_DIR)/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLAttrDecoder.cpp \
$(SRC_DIR)/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp
CORE_SOURCES_OBJCXX += \
$(SRC_DIR)/Cafe/HW/Latte/Renderer/Metal/MetalLayer.mm \
$(SRC_DIR)/imgui/imgui_impl_metal.mm
ifeq ($(OS_MACOS),1)
CORE_SOURCES_OBJCXX += \
$(SRC_DIR)/Cafe/HW/Latte/Renderer/MetalView.mm \
$(SRC_DIR)/Cafe/HW/Latte/Renderer/Vulkan/CocoaSurface.mm
endif
endif
ifeq ($(HAVE_LIBUSB),1)
CORE_SOURCES_CXX += \
$(CORE_DIR)/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp \
$(CORE_DIR)/src/util/libusbWrapper/libusbWrapper.cpp
endif
ifeq ($(ARCH_ARM64),1)
CORE_SOURCES_CXX += \
$(SRC_DIR)/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
endif
CORE_FLAGS := -DENABLE_LIBRETRO -DRETRO_CORE -DVK_NO_PROTOTYPES \
-DEMULATOR_VERSION_MAJOR=0 -DEMULATOR_VERSION_MINOR=0 -DEMULATOR_VERSION_PATCH=0 \
-DEMULATOR_HASH=$(GIT_HASH) \
-I$(SRC_DIR) -I$(SRC_DIR)/Cafe -I$(SRC_DIR)/Common -I$(SRC_DIR)/util -I$(SRC_DIR)/Cemu \
-I$(SRC_DIR)/input -I$(SRC_DIR)/config -I$(SRC_DIR)/audio -I$(SRC_DIR)/imgui \
-I$(SRC_DIR)/gui -I$(SRC_DIR)/gui/interface -I$(SRC_DIR)/libretro -I$(SRC_DIR)/resource \
-I$(DEP_DIR)/Vulkan-Headers/include -I$(DEP_DIR)/ZArchive/include \
-I$(DEP_DIR)/imgui -I$(DEP_DIR)/glm -I$(DEP_DIR)/fmt/include \
-I$(DEP_DIR)/pugixml/src -I$(DEP_DIR)/zlib -I$(DEP_DIR)/zstd/lib \
-I$(GLSLANG_DIR) -I$(GLSLANG_GEN_DIR) -I$(DEP_DIR)/boost-compat \
-I$(DEP_DIR)/curl-compat/include -I$(DEP_DIR)/libusb-config \
-I$(LRC_DIR)/include
CORE_WARNFLAGS := -Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes \
-Wno-deprecated-enum-enum-conversion
ifeq ($(HAVE_VULKAN),1)
CORE_FLAGS += -DENABLE_VULKAN
endif
ifeq ($(HAVE_OPENGL),1)
CORE_FLAGS += -DENABLE_OPENGL
endif
ifeq ($(HAVE_METAL),1)
CORE_FLAGS += -DENABLE_METAL -DIMGUI_IMPL_METAL_CPP -I$(DEP_DIR)/metal-cpp
endif
ifeq ($(OS_APPLE),1)
CORE_FLAGS += -DVK_USE_PLATFORM_MACOS_MVK -DVK_USE_PLATFORM_METAL_EXT
endif
# desktop Linux: the X11 surfaces (Xlib and XCB headers); webOS has none
ifeq ($(OS_LINUX),1)
ifneq ($(platform),webos)
CORE_FLAGS += -DVK_USE_PLATFORM_XLIB_KHR -DVK_USE_PLATFORM_XCB_KHR
endif
endif
ifeq ($(HAVE_LIBUSB),1)
CORE_FLAGS += -DHAS_LIBUSB
endif
ifeq ($(ARCH_ARM64),1)
CORE_FLAGS += -I$(DEP_DIR)/xbyak_aarch64 -I$(DEP_DIR)/xbyak_aarch64/xbyak_aarch64
endif
CORE_ASFLAGS := -I$(SRC_DIR)/resource
# Every unit of the core starts with Common/precompiled.h, read
# precompiled: a stub of that name is force-included, and the compiler
# takes the precompiled header next to it in its place (GCC a .gch, which
# it skips for the stub when the flags do not match; Clang a .pch).
CORE_PCH_HEADER := $(abspath $(SRC_DIR)/Common/precompiled.h)
CORE_PCH_INCLUDE := $(CORE_PCH_HEADER)
ifneq ($(NO_PCH),1)
CORE_PCH_STUB := $(OBJ_DIR)/pch/precompiled.h
ifneq (,$(findstring clang,$(shell $(CXX) --version 2>/dev/null)))
CORE_PCH := $(CORE_PCH_STUB).pch
else
CORE_PCH := $(CORE_PCH_STUB).gch
endif
# rewritten only when the flags change, which is when the PCH must be
CORE_PCH_FLAGS = $(CXXFLAGS) $(CORE_FLAGS) $(CORE_WARNFLAGS)
$(CORE_PCH_STUB):
@mkdir -p $(@D)
echo '#include "$(CORE_PCH_HEADER)"' > $@
$(CORE_PCH): $(CORE_PCH_STUB) $(OBJ_DIR)/pch/flags $(SRC_DIR)/Common/precompiled.h | $(GEN_HEADERS)
@$(if $(E),echo PCH $<,)
$(CXX) $(CORE_PCH_FLAGS) -MMD -MP -MF $(CORE_PCH_STUB).d -MT $@ -x c++-header -c $< -o $@
CORE_PCH_INCLUDE := $(abspath $(CORE_PCH_STUB))
endif
CORE_CXXFLAGS := -include $(CORE_PCH_INCLUDE) $(CORE_WARNFLAGS)
# ----------------------------------------------------------------------------
# libcurl's API over libretro-common's net_http, and the part of
# libretro-common the core uses
# ----------------------------------------------------------------------------
CURLCOMPAT_SOURCES_CXX := $(DEP_DIR)/curl-compat/curl_compat.cpp
CURLCOMPAT_FLAGS := -I$(DEP_DIR)/curl-compat/include -I$(LRC_DIR)/include \
-DHAVE_SSL -DHAVE_RETROSSL -DHAVE_CRYPTO
LRC_SOURCES_C := \
$(LRC_DIR)/compat/compat_posix_string.c \
$(LRC_DIR)/compat/compat_strcasestr.c \
$(LRC_DIR)/compat/compat_strl.c \
$(LRC_DIR)/compat/fopen_utf8.c \
$(LRC_DIR)/crypto/crypto.c \
$(LRC_DIR)/crypto/kdf.c \
$(LRC_DIR)/crypto/pk.c \
$(LRC_DIR)/crypto/x509.c \
$(LRC_DIR)/encodings/encoding_base64.c \
$(LRC_DIR)/encodings/encoding_utf.c \
$(LRC_DIR)/features/features_cpu.c \
$(LRC_DIR)/file/file_path.c \
$(LRC_DIR)/file/file_path_io.c \
$(LRC_DIR)/file/retro_dirent.c \
$(LRC_DIR)/hash/lrc_hash.c \
$(LRC_DIR)/lists/string_list.c \
$(LRC_DIR)/net/net_compat.c \
$(LRC_DIR)/net/net_http.c \
$(LRC_DIR)/net/net_socket.c \
$(LRC_DIR)/net/net_socket_ssl_retro.c \
$(LRC_DIR)/rthreads/retro_eventcount.c \
$(LRC_DIR)/rthreads/rthreads.c \
$(LRC_DIR)/streams/file_stream.c \
$(LRC_DIR)/string/rstrtod.c \
$(LRC_DIR)/string/stdstring.c \
$(LRC_DIR)/time/rtime.c \
$(LRC_DIR)/utils/md5.c \
$(LRC_DIR)/vfs/vfs_implementation.c
LRC_FLAGS := -I$(LRC_DIR)/include -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_CRYPTO -DHAVE_THREADS
ifeq ($(OS_ANDROID),1)
LRC_SOURCES_C += $(LRC_DIR)/libco/libco.c
endif
# ----------------------------------------------------------------------------
# Dependencies
# ----------------------------------------------------------------------------
IH264D_DIR := $(DEP_DIR)/ih264d
IH264D_SOURCES_C := $(addprefix $(IH264D_DIR)/, \
common/ih264_buf_mgr.c common/ih264_cabac_tables.c common/ih264_cavlc_tables.c common/ih264_chroma_intra_pred_filters.c common/ih264_common_tables.c common/ih264_deblk_edge_filters.c common/ih264_deblk_tables.c common/ih264_disp_mgr.c common/ih264_dpb_mgr.c common/ih264_ihadamard_scaling.c common/ih264_inter_pred_filters.c common/ih264_iquant_itrans_recon.c common/ih264_luma_intra_pred_filters.c common/ih264_mem_fns.c common/ih264_padding.c common/ih264_resi_trans_quant.c common/ih264_trans_data.c common/ih264_weighted_pred.c common/ithread.c decoder/ih264d_api.c decoder/ih264d_bitstrm.c decoder/ih264d_cabac.c decoder/ih264d_cabac_init_tables.c decoder/ih264d_compute_bs.c decoder/ih264d_deblocking.c decoder/ih264d_dpb_mgr.c decoder/ih264d_format_conv.c decoder/ih264d_function_selector_generic.c decoder/ih264d_inter_pred.c decoder/ih264d_mb_utils.c decoder/ih264d_mvpred.c decoder/ih264d_nal.c decoder/ih264d_parse_bslice.c decoder/ih264d_parse_cabac.c decoder/ih264d_parse_cavlc.c decoder/ih264d_parse_headers.c decoder/ih264d_parse_islice.c decoder/ih264d_parse_mb_header.c decoder/ih264d_parse_pslice.c decoder/ih264d_parse_slice.c decoder/ih264d_process_bslice.c decoder/ih264d_process_intra_mb.c decoder/ih264d_process_pslice.c decoder/ih264d_quant_scaling.c decoder/ih264d_sei.c decoder/ih264d_tables.c decoder/ih264d_thread_compute_bs.c decoder/ih264d_thread_parse_decode.c decoder/ih264d_utils.c decoder/ih264d_vui.c)
IH264D_SOURCES_ASM :=
# ih264_list.c is left out: the decoder does not use it, and outside
# Windows it calls an ithread_usleep() that ithread.c does not have.
IH264D_FLAGS := -I$(IH264D_DIR)/common -I$(IH264D_DIR)/decoder -w
ifeq ($(ARCH_X86_64),1)
IH264D_SOURCES_C += $(addprefix $(IH264D_DIR)/, \
common/x86/ih264_chroma_intra_pred_filters_ssse3.c common/x86/ih264_deblk_chroma_ssse3.c common/x86/ih264_deblk_luma_ssse3.c common/x86/ih264_ihadamard_scaling_sse42.c common/x86/ih264_ihadamard_scaling_ssse3.c common/x86/ih264_inter_pred_filters_ssse3.c common/x86/ih264_iquant_itrans_recon_dc_ssse3.c common/x86/ih264_iquant_itrans_recon_sse42.c common/x86/ih264_iquant_itrans_recon_ssse3.c common/x86/ih264_luma_intra_pred_filters_ssse3.c common/x86/ih264_mem_fns_ssse3.c common/x86/ih264_padding_ssse3.c common/x86/ih264_resi_trans_quant_sse42.c common/x86/ih264_weighted_pred_sse42.c decoder/x86/ih264d_function_selector.c decoder/x86/ih264d_function_selector_sse42.c decoder/x86/ih264d_function_selector_ssse3.c)
IH264D_FLAGS += -I$(IH264D_DIR)/common/x86 -I$(IH264D_DIR)/decoder/x86
else
IH264D_SOURCES_C += $(addprefix $(IH264D_DIR)/, \
decoder/arm/ih264d_function_selector_a9q.c decoder/arm/ih264d_function_selector_av8.c decoder/arm/ih264d_function_selector.c)
IH264D_SOURCES_ASM += $(addprefix $(IH264D_DIR)/, \
common/armv8/ih264_deblk_chroma_av8.s common/armv8/ih264_deblk_luma_av8.s common/armv8/ih264_default_weighted_pred_av8.s common/armv8/ih264_ihadamard_scaling_av8.s common/armv8/ih264_inter_pred_chroma_av8.s common/armv8/ih264_inter_pred_filters_luma_horz_av8.s common/armv8/ih264_inter_pred_filters_luma_vert_av8.s common/armv8/ih264_inter_pred_luma_copy_av8.s common/armv8/ih264_inter_pred_luma_horz_hpel_vert_hpel_av8.s common/armv8/ih264_inter_pred_luma_horz_hpel_vert_qpel_av8.s common/armv8/ih264_inter_pred_luma_horz_qpel_av8.s common/armv8/ih264_inter_pred_luma_horz_qpel_vert_hpel_av8.s common/armv8/ih264_inter_pred_luma_horz_qpel_vert_qpel_av8.s common/armv8/ih264_inter_pred_luma_vert_qpel_av8.s common/armv8/ih264_intra_pred_chroma_av8.s common/armv8/ih264_intra_pred_luma_16x16_av8.s common/armv8/ih264_intra_pred_luma_4x4_av8.s common/armv8/ih264_intra_pred_luma_8x8_av8.s common/armv8/ih264_iquant_itrans_recon_av8.s common/armv8/ih264_iquant_itrans_recon_dc_av8.s common/armv8/ih264_mem_fns_neon_av8.s common/armv8/ih264_padding_neon_av8.s common/armv8/ih264_resi_trans_quant_av8.s common/armv8/ih264_weighted_bi_pred_av8.s common/armv8/ih264_weighted_pred_av8.s common/armv8/macos_arm_symbol_aliases.s)
ifeq ($(OS_APPLE),1)
IH264D_SOURCES_ASM += $(IH264D_DIR)/common/armv8/macos_arm_symbol_aliases.s
endif
IH264D_FLAGS += -DARMV8 -I$(IH264D_DIR)/common/armv8 -I$(IH264D_DIR)/decoder/arm
endif
IH264D_ASFLAGS := -I$(IH264D_DIR)/common/armv8
ZARCHIVE_SOURCES_C := $(DEP_DIR)/ZArchive/src/sha_256.c
ZARCHIVE_SOURCES_CXX := $(DEP_DIR)/ZArchive/src/zarchivereader.cpp $(DEP_DIR)/ZArchive/src/zarchivewriter.cpp
ZARCHIVE_FLAGS := -I$(DEP_DIR)/ZArchive/include -I$(DEP_DIR)/zstd/lib -w
IMGUI_SOURCES_CXX := $(addprefix $(DEP_DIR)/imgui/,imgui.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp)
IMGUI_FLAGS := -I$(DEP_DIR)/imgui -w
GLSLANG_SOURCES_CXX := $(addprefix $(GLSLANG_DIR)/, \
glslang/GenericCodeGen/CodeGen.cpp glslang/GenericCodeGen/Link.cpp glslang/MachineIndependent/glslang_tab.cpp glslang/MachineIndependent/attribute.cpp glslang/MachineIndependent/Constant.cpp glslang/MachineIndependent/iomapper.cpp glslang/MachineIndependent/InfoSink.cpp glslang/MachineIndependent/Initialize.cpp glslang/MachineIndependent/IntermTraverse.cpp glslang/MachineIndependent/Intermediate.cpp glslang/MachineIndependent/ParseContextBase.cpp glslang/MachineIndependent/ParseHelper.cpp glslang/MachineIndependent/PoolAlloc.cpp glslang/MachineIndependent/RemoveTree.cpp glslang/MachineIndependent/Scan.cpp glslang/MachineIndependent/ShaderLang.cpp glslang/MachineIndependent/SpirvIntrinsics.cpp glslang/MachineIndependent/SymbolTable.cpp glslang/MachineIndependent/Versions.cpp glslang/MachineIndependent/intermOut.cpp glslang/MachineIndependent/limits.cpp glslang/MachineIndependent/linkValidate.cpp glslang/MachineIndependent/parseConst.cpp glslang/MachineIndependent/reflection.cpp glslang/MachineIndependent/preprocessor/Pp.cpp glslang/MachineIndependent/preprocessor/PpAtom.cpp glslang/MachineIndependent/preprocessor/PpContext.cpp glslang/MachineIndependent/preprocessor/PpScanner.cpp glslang/MachineIndependent/preprocessor/PpTokens.cpp glslang/MachineIndependent/propagateNoContraction.cpp glslang/ResourceLimits/ResourceLimits.cpp SPIRV/GlslangToSpv.cpp SPIRV/InReadableOrder.cpp SPIRV/Logger.cpp SPIRV/SpvBuilder.cpp SPIRV/SpvPostProcess.cpp SPIRV/doc.cpp SPIRV/SpvTools.cpp SPIRV/disassemble.cpp)
ifeq ($(OS_WIN),1)
GLSLANG_SOURCES_CXX += $(GLSLANG_DIR)/glslang/OSDependent/Windows/ossource.cpp
GLSLANG_FLAGS := -DGLSLANG_OSINCLUDE_WIN32
else
GLSLANG_SOURCES_CXX += $(GLSLANG_DIR)/glslang/OSDependent/Unix/ossource.cpp
GLSLANG_FLAGS := -DGLSLANG_OSINCLUDE_UNIX
endif
GLSLANG_FLAGS += -DENABLE_SPIRV -DENABLE_OPT=0 -I$(GLSLANG_DIR) -I$(GLSLANG_GEN_DIR) -w
# fmt is header-only except on Android (precompiled.h): format.cc there only,
# as its definitions and the inline ones every other TU has would clash where
# the linker does not merge them (PE/COFF)
FMT_SOURCES_CXX := $(DEP_DIR)/fmt/src/os.cc
FMT_FLAGS := -I$(DEP_DIR)/fmt/include -w
ifeq ($(OS_ANDROID),1)
FMT_SOURCES_CXX += $(DEP_DIR)/fmt/src/format.cc
else
FMT_FLAGS += -DFMT_HEADER_ONLY
endif
PUGIXML_SOURCES_CXX := $(DEP_DIR)/pugixml/src/pugixml.cpp
PUGIXML_FLAGS := -w
ZLIB_SOURCES_C := $(addprefix $(DEP_DIR)/zlib/,adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c)
ZLIB_FLAGS := -w
ifneq ($(OS_WIN),1)
ZLIB_FLAGS += -DHAVE_UNISTD_H
endif
ZSTD_SOURCES_C := $(addprefix $(DEP_DIR)/zstd/lib/, \
common/debug.c common/entropy_common.c common/error_private.c common/fse_decompress.c common/pool.c common/threading.c common/xxhash.c common/zstd_common.c compress/fse_compress.c compress/hist.c compress/huf_compress.c compress/zstd_compress.c compress/zstd_compress_literals.c compress/zstd_compress_sequences.c compress/zstd_compress_superblock.c compress/zstd_double_fast.c compress/zstd_fast.c compress/zstd_lazy.c compress/zstd_ldm.c compress/zstd_opt.c compress/zstd_preSplit.c compress/zstdmt_compress.c decompress/huf_decompress.c decompress/zstd_ddict.c decompress/zstd_decompress.c decompress/zstd_decompress_block.c)
ZSTD_SOURCES_ASM :=
ZSTD_FLAGS := -I$(DEP_DIR)/zstd/lib -I$(DEP_DIR)/zstd/lib/common -DZSTD_MULTITHREAD -w
ifeq ($(ARCH_X86_64),1)
ifneq ($(OS_WIN),1)
ZSTD_SOURCES_ASM += $(DEP_DIR)/zstd/lib/decompress/huf_decompress_amd64.S
else
ZSTD_FLAGS += -DZSTD_DISABLE_ASM
endif
endif
LIBUSB_DIR := $(DEP_DIR)/libusb/libusb
LIBUSB_SOURCES_C := $(addprefix $(LIBUSB_DIR)/,core.c descriptor.c hotplug.c io.c strerror.c sync.c)
ifneq (,$(filter 1,$(OS_LINUX) $(OS_ANDROID)))
LIBUSB_SOURCES_C += $(LIBUSB_DIR)/os/linux_usbfs.c $(LIBUSB_DIR)/os/linux_netlink.c
endif
ifeq ($(OS_UNIX),1)
LIBUSB_SOURCES_C += $(LIBUSB_DIR)/os/events_posix.c $(LIBUSB_DIR)/os/threads_posix.c
endif
ifeq ($(OS_WIN),1)
LIBUSB_SOURCES_C += $(addprefix $(LIBUSB_DIR)/os/,threads_windows.c windows_common.c \
windows_usbdk.c windows_winusb.c events_windows.c)
endif
ifeq ($(OS_MACOS),1)
LIBUSB_SOURCES_C += $(LIBUSB_DIR)/os/darwin_usb.c
endif
LIBUSB_FLAGS := -I$(DEP_DIR)/libusb-config -I$(LIBUSB_DIR) -w
XBYAK_SOURCES_CXX := $(DEP_DIR)/xbyak_aarch64/src/xbyak_aarch64_impl.cpp $(DEP_DIR)/xbyak_aarch64/src/util_impl.cpp
XBYAK_FLAGS := -I$(DEP_DIR)/xbyak_aarch64 -I$(DEP_DIR)/xbyak_aarch64/xbyak_aarch64 -w
LIB_GROUPS := CORE CURLCOMPAT LRC IH264D ZARCHIVE IMGUI GLSLANG FMT PUGIXML ZLIB ZSTD
ifeq ($(HAVE_LIBUSB),1)
LIB_GROUPS += LIBUSB
endif
ifeq ($(ARCH_ARM64),1)
LIB_GROUPS += XBYAK
endif
+39 -33
View File
@@ -1,48 +1,48 @@
# Cemu Libretro # ParaLLEl GX2
Cemu (Wii U emulator) as a libretro core for RetroArch. Cemu (Wii U emulator) as a libretro core for RetroArch, `parallel_gx2_libretro`.
Version: **2.6-372-g29f2f1fa**, upstream Cemu as merged (`29f2f1fa`, 15 Oct 2026). Version: **2.6-372-g29f2f1fa**, upstream Cemu as merged (`29f2f1fa`, 15 Oct 2026).
## Getting the core ## Getting the core
Through RetroArch: **Online Updater → Core Downloader → Cemu**. The libretro buildbot builds the `libretro` branch nightly for Windows x64, Linux x64 and arm64, macOS x64 and arm64, and Android arm64-v8a and x86_64, so the downloadable core is always the current tree. The CI also builds iOS, tvOS and webOS. There are no releases here on purpose: one more place to download from is one more place to be out of date. Through RetroArch: **Online Updater → Core Downloader → Nintendo - Wii U (ParaLLEl GX2)**. The libretro buildbot builds the `libretro` branch nightly for Windows x64, Linux x64 and arm64, macOS x64 and arm64, and Android arm64-v8a and x86_64, so the downloadable core is always the current tree. The CI also builds iOS, tvOS and webOS. There are no releases here on purpose: one more place to download from is one more place to be out of date.
Build it yourself for anything else, or to test a change. Build it yourself for anything else, or to test a change.
## Build ## Build
A static Makefile builds the core and everything it uses: the libraries are
submodules built with it, and the network layer is libretro-common's (HTTP
and TLS, with no libcurl, OpenSSL or mbedTLS), so nothing but a C++20
compiler and, on Linux, the system's GL and X11 headers is needed.
```bash ```bash
# Install dependencies (Ubuntu/Debian) # Ubuntu/Debian: the headers the OpenGL and Vulkan surfaces are declared with
sudo apt install -y cmake gcc g++ ninja-build nasm libpulse-dev \ sudo apt install -y g++ make libgl-dev libegl-dev libx11-dev libxrender-dev libx11-xcb-dev
libsecret-1-dev libgcrypt20-dev libsystemd-dev freeglut3-dev
# Clone with submodules - vcpkg and the community graphic packs live in them, # Clone with submodules - the libraries and the community graphic packs live
# and configure fails with "Could not find toolchain file # in them. On an existing clone: git submodule update --init --recursive
# .../dependencies/vcpkg/..." without them. On an existing clone: git clone --recursive https://github.com/libretro/parallel-gx2.git
# git submodule update --init --recursive cd parallel-gx2
git clone --recursive https://github.com/WizzardSK/cemu-libretro.git
cd cemu-libretro
# Configure (vcpkg handles dependencies automatically) make -j$(nproc)
cmake -S . -B build -DCMAKE_BUILD_TYPE=release \ # Result: parallel_gx2_libretro.so
-DCMAKE_C_COMPILER=/usr/bin/gcc -DCMAKE_CXX_COMPILER=/usr/bin/g++ \
-G Ninja
# On arm64, vcpkg needs VCPKG_FORCE_SYSTEM_BINARIES=1, and
# VCPKG_MAX_CONCURRENCY is worth capping on low-memory boards.
# Build
cmake --build build --target cemu_libretro
# Result: bin/cemu_libretro.so
``` ```
The same Makefile builds the other targets: `make` under MSYS2 MinGW64 (or
with `CC`/`CXX` set to a MinGW-w64 cross compiler) for Windows,
`platform=osx` with `arch=arm64` or `arch=x86_64` for macOS,
`platform=ios-arm64` and `platform=tvos-arm64` (Metal only),
`platform=android-arm64` and `platform=android-x86_64` with `NDK_ROOT` set,
and a webOS SDK compiler as `CC`/`CXX` for webOS. `DEBUG=1` builds without
optimisation, `SANITIZER=address` with a sanitizer.
## Install ## Install
```bash ```bash
cp bin/cemu_libretro.so ~/.config/retroarch/cores/ cp parallel_gx2_libretro.so ~/.config/retroarch/cores/
cp cemu_libretro.info ~/.config/retroarch/info/ cp parallel_gx2_libretro.info ~/.config/retroarch/info/
``` ```
## Setup ## Setup
@@ -117,17 +117,23 @@ git fetch cemuup main
git merge cemuup/main git merge cemuup/main
``` ```
Last sync: upstream `29f2f1fa` (merged 23 Oct 2026). The core reports its version as `git describe --tags --match v2.6 cemuup/main` (`2.6-372-g29f2f1fa`), set in `retro_get_system_info` and `cemu_libretro.info`; update both, and the version at the top of this README, on every sync. Last sync: upstream `29f2f1fa` (merged 23 Oct 2026). The core reports its version as `git describe --tags --match v2.6 cemuup/main` (`2.6-372-g29f2f1fa`), set in `retro_get_system_info` and `parallel_gx2_libretro.info`; update both, and the version at the top of this README, on every sync.
The fork has no `main` of its own: upstream is merged straight into `libretro`, The fork has no `main` of its own: upstream is merged straight into `libretro`,
the only long-lived branch here. the only long-lived branch here.
What tends to break on a sync, none of which the conflict resolution shows: What tends to break on a sync, none of which the conflict resolution shows:
- **Precompiled headers.** Upstream force-includes `precompiled.h` per target - **Source lists.** The core builds from the Makefile, not upstream's CMake
via `cemu_use_precompiled_header()`. Targets this fork adds (`cemu_libretro`, (which `.upstream-excluded` removes on every merge): a source file upstream
`CemuHeadlessGui`) have to call it too, or they lose `uint32`, `std::span` adds or removes has to be added to or removed from `Makefile.common` by
and the `_mm_pause` shim. hand. Every unit of the core is compiled with `precompiled.h` force-included,
as upstream's `cemu_use_precompiled_header()` does.
- **Boost and the network libraries.** The core has no Boost: the parts the
emulator uses are small headers in `dependencies/boost-compat`, and
`dependencies/curl-compat` carries libcurl's API over libretro-common's
HTTP and TLS. Upstream code that reaches for another Boost header, or a
libcurl or OpenSSL call these do not have yet, fails to build until they do.
- **Copied functions drifting.** The libretro `VulkanRenderer` constructor is a - **Copied functions drifting.** The libretro `VulkanRenderer` constructor is a
copy of upstream's adapted for the shared device; upstream keeps changing the copy of upstream's adapted for the shared device; upstream keeps changing the
original underneath it. original underneath it.
@@ -140,5 +146,5 @@ What tends to break on a sync, none of which the conflict resolution shows:
Debugging a crash on aarch64: `backtrace()` cannot unwind past the signal frame, Debugging a crash on aarch64: `backtrace()` cannot unwind past the signal frame,
so the posix handler also records `fault/pc/lr/sp` into `<system>/Cemu/log.txt`. so the posix handler also records `fault/pc/lr/sp` into `<system>/Cemu/log.txt`.
Convert `lr` with the module base from that log's `cemu_libretro.so(+0xOFFSET) Convert `lr` with the module base from that log's `parallel_gx2_libretro.so(+0xOFFSET)
[0xABSOLUTE]` line and run `addr2line -Cfie bin/cemu_libretro.so <offset>`. [0xABSOLUTE]` line and run `addr2line -Cfie parallel_gx2_libretro.so <offset>`.
-279
View File
@@ -1,279 +0,0 @@
# SPDX-FileCopyrightText: 2014 Alex Merry <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMFindModuleHelpers
--------------------
Helper macros for find modules: ``ecm_find_package_version_check()``,
``ecm_find_package_parse_components()`` and
``ecm_find_package_handle_library_components()``.
::
ecm_find_package_version_check(<name>)
Prints warnings if the CMake version or the project's required CMake version
is older than that required by extra-cmake-modules.
::
ecm_find_package_parse_components(<name>
RESULT_VAR <variable>
KNOWN_COMPONENTS <component1> [<component2> [...]]
[SKIP_DEPENDENCY_HANDLING])
This macro will populate <variable> with a list of components found in
<name>_FIND_COMPONENTS, after checking that all those components are in the
list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print
an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
``return()``.
The order of components in <variable> is guaranteed to match the order they
are listed in the ``KNOWN_COMPONENTS`` argument.
If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable
<name>_<component>_component_deps will be checked for dependent components.
If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
dependencies will also be added to <variable>.
::
ecm_find_package_handle_library_components(<name>
COMPONENTS <component> [<component> [...]]
[SKIP_DEPENDENCY_HANDLING])
[SKIP_PKG_CONFIG])
Creates an imported library target for each component. The operation of this
macro depends on the presence of a number of CMake variables.
The <name>_<component>_lib variable should contain the name of this library,
and <name>_<component>_header variable should contain the name of a header
file associated with it (whatever relative path is normally passed to
'#include'). <name>_<component>_header_subdir variable can be used to specify
which subdirectory of the include path the headers will be found in.
``ecm_find_package_components()`` will then search for the library
and include directory (creating appropriate cache variables) and create an
imported library target named <name>::<component>.
Additional variables can be used to provide additional information:
If ``SKIP_PKG_CONFIG``, the <name>_<component>_pkg_config variable is set, and
pkg-config is found, the pkg-config module given by
<name>_<component>_pkg_config will be searched for and used to help locate the
library and header file. It will also be used to set
<name>_<component>_VERSION.
Note that if version information is found via pkg-config,
<name>_<component>_FIND_VERSION can be set to require a particular version
for each component.
If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property
of the imported target for <component> will be set to contain the imported
targets for the components listed in <name>_<component>_component_deps.
<component>_FOUND will also be set to ``FALSE`` if any of the components in
<name>_<component>_component_deps are not found. This requires the components
in <name>_<component>_component_deps to be listed before <component> in the
``COMPONENTS`` argument.
The following variables will be set:
``<name>_TARGETS``
the imported targets
``<name>_LIBRARIES``
the found libraries
``<name>_INCLUDE_DIRS``
the combined required include directories for the components
``<name>_DEFINITIONS``
the "other" CFLAGS provided by pkg-config, if any
``<name>_VERSION``
the value of ``<name>_<component>_VERSION`` for the first component that
has this variable set (note that components are searched for in the order
they are passed to the macro), although if it is already set, it will not
be altered
.. note::
These variables are never cleared, so if
``ecm_find_package_handle_library_components()`` is called multiple times with
different components (typically because of multiple ``find_package()`` calls) then
``<name>_TARGETS``, for example, will contain all the targets found in any
call (although no duplicates).
Since pre-1.0.0.
#]=======================================================================]
include(CMakeParseArguments)
macro(ecm_find_package_version_check module_name)
if(CMAKE_VERSION VERSION_LESS 3.16.0)
message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake")
endif()
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0)
message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake")
endif()
endmacro()
macro(ecm_find_package_parse_components module_name)
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
set(ecm_fppc_oneValueArgs RESULT_VAR)
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
if(ECM_FPPC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPPC_RESULT_VAR)
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
endif()
if(${module_name}_FIND_COMPONENTS)
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
# Make sure deps are included
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
if("${ecm_fppc_index}" STREQUAL "-1")
if(NOT ${module_name}_FIND_QUIETLY)
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
endif()
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
endif()
endforeach()
endforeach()
else()
message(STATUS "Skipping dependency handling for ${module_name}")
endif()
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
# This makes sure components are listed in the same order as
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
set(${ECM_FPPC_RESULT_VAR})
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
endif()
endforeach()
# if there are any left, they are unknown components
if(ecm_fppc_requestedComps)
set(ecm_fppc_msgType STATUS)
if(${module_name}_FIND_REQUIRED)
set(ecm_fppc_msgType FATAL_ERROR)
endif()
if(NOT ${module_name}_FIND_QUIETLY)
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
endif()
return()
endif()
else()
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
endif()
endmacro()
macro(ecm_find_package_handle_library_components module_name)
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
set(ecm_fpwc_oneValueArgs)
set(ecm_fpwc_multiValueArgs COMPONENTS)
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
if(ECM_FPWC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPWC_COMPONENTS)
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
endif()
include(FindPackageHandleStandardArgs)
find_package(PkgConfig QUIET)
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
set(ecm_fpwc_dep_vars)
set(ecm_fpwc_dep_targets)
if(NOT SKIP_DEPENDENCY_HANDLING)
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
endforeach()
endif()
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
${${module_name}_${ecm_fpwc_comp}_pkg_config})
endif()
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
)
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
)
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
if(NOT ${module_name}_VERSION)
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
endif()
set(FPHSA_NAME_MISMATCHED 1)
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
FOUND_VAR
${module_name}_${ecm_fpwc_comp}_FOUND
REQUIRED_VARS
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
${ecm_fpwc_dep_vars}
VERSION_VAR
${module_name}_${ecm_fpwc_comp}_VERSION
)
unset(FPHSA_NAME_MISMATCHED)
mark_as_advanced(
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
)
if(${module_name}_${ecm_fpwc_comp}_FOUND)
list(APPEND ${module_name}_LIBRARIES
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
list(APPEND ${module_name}_INCLUDE_DIRS
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
set(${module_name}_DEFINITIONS
${${module_name}_DEFINITIONS}
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
)
endif()
list(APPEND ${module_name}_TARGETS
"${module_name}::${ecm_fpwc_comp}")
endif()
endforeach()
if(${module_name}_LIBRARIES)
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
endif()
if(${module_name}_INCLUDE_DIRS)
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
endif()
if(${module_name}_DEFINITIONS)
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
endif()
if(${module_name}_TARGETS)
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
endif()
endmacro()
-1
View File
@@ -1 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)
-16
View File
@@ -1,16 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
include(FindPackageHandleStandardArgs)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(GTK3 IMPORTED_TARGET gtk+-3.0)
if (GTK3_FOUND)
add_library(GTK3::gtk ALIAS PkgConfig::GTK3)
endif()
find_package_handle_standard_args(GTK3
REQUIRED_VARS GTK3_LINK_LIBRARIES
VERSION_VAR GTK3_VERSION
)
endif()
-137
View File
@@ -1,137 +0,0 @@
# SPDX-FileCopyrightText: 2014 Alex Merry <[email protected]>
# SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
FindWayland
-----------
Try to find Wayland.
This is a component-based find module, which makes use of the COMPONENTS
and OPTIONAL_COMPONENTS arguments to find_module. The following components
are available::
Client Server Cursor Egl
If no components are specified, this module will act as though all components
were passed to OPTIONAL_COMPONENTS.
This module will define the following variables, independently of the
components searched for or found:
``Wayland_FOUND``
TRUE if (the requested version of) Wayland is available
``Wayland_VERSION``
Found Wayland version
``Wayland_TARGETS``
A list of all targets imported by this module (note that there may be more
than the components that were requested)
``Wayland_LIBRARIES``
This can be passed to target_link_libraries() instead of the imported
targets
``Wayland_INCLUDE_DIRS``
This should be passed to target_include_directories() if the targets are
not used for linking
``Wayland_DEFINITIONS``
This should be passed to target_compile_options() if the targets are not
used for linking
``Wayland_DATADIR``
The core wayland protocols data directory
Since 5.73.0
For each searched-for components, ``Wayland_<component>_FOUND`` will be set to
TRUE if the corresponding Wayland library was found, and FALSE otherwise. If
``Wayland_<component>_FOUND`` is TRUE, the imported target
``Wayland::<component>`` will be defined. This module will also attempt to
determine ``Wayland_*_VERSION`` variables for each imported target, although
``Wayland_VERSION`` should normally be sufficient.
In general we recommend using the imported targets, as they are easier to use
and provide more control. Bear in mind, however, that if any target is in the
link interface of an exported library, it must be made available by the
package config file.
Since pre-1.0.0.
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
ecm_find_package_version_check(Wayland)
set(Wayland_known_components
Client
Server
Cursor
Egl
)
foreach(_comp ${Wayland_known_components})
string(TOLOWER "${_comp}" _lc_comp)
set(Wayland_${_comp}_component_deps)
set(Wayland_${_comp}_pkg_config "wayland-${_lc_comp}")
set(Wayland_${_comp}_lib "wayland-${_lc_comp}")
set(Wayland_${_comp}_header "wayland-${_lc_comp}.h")
endforeach()
set(Wayland_Egl_component_deps Client)
ecm_find_package_parse_components(Wayland
RESULT_VAR Wayland_components
KNOWN_COMPONENTS ${Wayland_known_components}
)
ecm_find_package_handle_library_components(Wayland
COMPONENTS ${Wayland_components}
)
# If pkg-config didn't provide us with version information,
# try to extract it from wayland-version.h
# (Note that the version from wayland-egl.pc will probably be
# the Mesa version, rather than the Wayland version, but that
# version will be ignored as we always find wayland-client.pc
# first).
if(NOT Wayland_VERSION)
find_file(Wayland_VERSION_HEADER
NAMES wayland-version.h
HINTS ${Wayland_INCLUDE_DIRS}
)
mark_as_advanced(Wayland_VERSION_HEADER)
if(Wayland_VERSION_HEADER)
file(READ ${Wayland_VERSION_HEADER} _wayland_version_header_contents)
string(REGEX REPLACE
"^.*[ \t]+WAYLAND_VERSION[ \t]+\"([0-9.]*)\".*$"
"\\1"
Wayland_VERSION
"${_wayland_version_header_contents}"
)
unset(_wayland_version_header_contents)
endif()
endif()
find_package_handle_standard_args(Wayland
FOUND_VAR
Wayland_FOUND
REQUIRED_VARS
Wayland_LIBRARIES
VERSION_VAR
Wayland_VERSION
HANDLE_COMPONENTS
)
pkg_get_variable(Wayland_DATADIR wayland-scanner pkgdatadir)
if (CMAKE_CROSSCOMPILING AND (NOT EXISTS "${Wayland_DATADIR}/wayland.xml"))
# PKG_CONFIG_SYSROOT_DIR only applies to -I and -L flags, so pkg-config
# does not prepend CMAKE_SYSROOT when cross-compiling unless you pass
# --define-prefix explicitly. Therefore we have to manually do prepend
# it here when cross-compiling.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/16647#note_844761
set(Wayland_DATADIR ${CMAKE_SYSROOT}${Wayland_DATADIR})
endif()
if (NOT EXISTS "${Wayland_DATADIR}/wayland.xml")
message(WARNING "Could not find wayland.xml in ${Wayland_DATADIR}")
endif()
include(FeatureSummary)
set_package_properties(Wayland PROPERTIES
URL "https://wayland.freedesktop.org/"
DESCRIPTION "C library implementation of the Wayland protocol: a protocol for a compositor to talk to its clients"
)
-38
View File
@@ -1,38 +0,0 @@
# SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
FindWaylandProtocols
--------------------
Try to find wayland-protocols on a Unix system.
This will define the following variables:
``WaylandProtocols_FOUND``
True if (the requested version of) wayland-protocols is available
``WaylandProtocols_VERSION``
The version of wayland-protocols
``WaylandProtocols_DATADIR``
The wayland protocols data directory
#]=======================================================================]
find_package(PkgConfig QUIET)
pkg_check_modules(PKG_wayland_protocols QUIET wayland-protocols)
set(WaylandProtocols_VERSION ${PKG_wayland_protocols_VERSION})
pkg_get_variable(WaylandProtocols_DATADIR wayland-protocols pkgdatadir)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WaylandProtocols
FOUND_VAR WaylandProtocols_FOUND
REQUIRED_VARS WaylandProtocols_DATADIR
VERSION_VAR WaylandProtocols_VERSION
)
include(FeatureSummary)
set_package_properties(WaylandProtocols PROPERTIES
DESCRIPTION "Specifications of extended Wayland protocols"
URL "https://wayland.freedesktop.org/"
)
-162
View File
@@ -1,162 +0,0 @@
# SPDX-FileCopyrightText: 2012-2014 Pier Luigi Fiorini <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
FindWaylandScanner
------------------
Try to find wayland-scanner.
If the wayland-scanner executable is not in your PATH, you can provide
an alternative name or full path location with the ``WaylandScanner_EXECUTABLE``
variable.
This will define the following variables:
``WaylandScanner_FOUND``
True if wayland-scanner is available.
``WaylandScanner_EXECUTABLE``
The wayland-scanner executable.
If ``WaylandScanner_FOUND`` is TRUE, it will also define the following imported
target:
``Wayland::Scanner``
The wayland-scanner executable.
This module provides the following functions to generate C protocol
implementations:
- ``ecm_add_wayland_client_protocol``
- ``ecm_add_wayland_server_protocol``
::
ecm_add_wayland_client_protocol(<target>
PROTOCOL <xmlfile>
BASENAME <basename>)
ecm_add_wayland_client_protocol(<source_files_var>
PROTOCOL <xmlfile>
BASENAME <basename>)
Generate Wayland client protocol files from ``<xmlfile>`` XML
definition for the ``<basename>`` interface and append those files
to ``<source_files_var>`` or ``<target>``.
::
ecm_add_wayland_server_protocol(<target>
PROTOCOL <xmlfile>
BASENAME <basename>)
ecm_add_wayland_server_protocol(<source_files_var>
PROTOCOL <xmlfile>
BASENAME <basename>)
Generate Wayland server protocol files from ``<xmlfile>`` XML
definition for the ``<basename>`` interface and append those files
to ``<source_files_var>`` or ``<target>``.
Since 1.4.0.
#]=======================================================================]
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
ecm_find_package_version_check(WaylandScanner)
# Find wayland-scanner
find_program(WaylandScanner_EXECUTABLE NAMES wayland-scanner)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WaylandScanner
FOUND_VAR
WaylandScanner_FOUND
REQUIRED_VARS
WaylandScanner_EXECUTABLE
)
mark_as_advanced(WaylandScanner_EXECUTABLE)
if(NOT TARGET Wayland::Scanner AND WaylandScanner_FOUND)
add_executable(Wayland::Scanner IMPORTED)
set_target_properties(Wayland::Scanner PROPERTIES
IMPORTED_LOCATION "${WaylandScanner_EXECUTABLE}"
)
endif()
include(FeatureSummary)
set_package_properties(WaylandScanner PROPERTIES
URL "https://wayland.freedesktop.org/"
DESCRIPTION "Executable that converts XML protocol files to C code"
)
include(CMakeParseArguments)
function(ecm_add_wayland_client_protocol target_or_sources_var)
# Parse arguments
set(oneValueArgs PROTOCOL BASENAME)
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN})
if(ARGS_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_client_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
endif()
get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE)
set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-client-protocol.h")
set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c")
set_source_files_properties(${_client_header} GENERATED)
set_source_files_properties(${_code} GENERATED)
set_property(SOURCE ${_client_header} ${_code} PROPERTY SKIP_AUTOMOC ON)
add_custom_command(OUTPUT "${_client_header}"
COMMAND ${WaylandScanner_EXECUTABLE} client-header ${_infile} ${_client_header}
DEPENDS ${_infile} VERBATIM)
add_custom_command(OUTPUT "${_code}"
COMMAND ${WaylandScanner_EXECUTABLE} public-code ${_infile} ${_code}
DEPENDS ${_infile} ${_client_header} VERBATIM)
if (TARGET ${target_or_sources_var})
target_sources(${target_or_sources_var} PRIVATE "${_client_header}" "${_code}")
else()
list(APPEND ${target_or_sources_var} "${_client_header}" "${_code}")
set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE)
endif()
endfunction()
function(ecm_add_wayland_server_protocol target_or_sources_var)
# Parse arguments
set(oneValueArgs PROTOCOL BASENAME)
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN})
if(ARGS_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_server_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"")
endif()
ecm_add_wayland_client_protocol(${target_or_sources_var}
PROTOCOL ${ARGS_PROTOCOL}
BASENAME ${ARGS_BASENAME})
get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE)
set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-server-protocol.h")
set(_server_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c")
set_property(SOURCE ${_server_header} ${_server_code} PROPERTY SKIP_AUTOMOC ON)
set_source_files_properties(${_server_header} GENERATED)
add_custom_command(OUTPUT "${_server_header}"
COMMAND ${WaylandScanner_EXECUTABLE} server-header ${_infile} ${_server_header}
DEPENDS ${_infile} VERBATIM)
if (TARGET ${target_or_sources_var})
target_sources(${target_or_sources_var} PRIVATE "${_server_header}")
else()
list(APPEND ${target_or_sources_var} "${_server_header}")
set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE)
endif()
endfunction()
-20
View File
@@ -1,20 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(zarchive IMPORTED_TARGET GLOBAL zarchive)
if (zarchive_FOUND)
add_library(ZArchive::zarchive ALIAS PkgConfig::zarchive)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ZArchive
REQUIRED_VARS
zarchive_LINK_LIBRARIES
zarchive_FOUND
VERSION_VAR
zarchive_VERSION
)
-20
View File
@@ -1,20 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
find_package(bluez CONFIG)
if (NOT bluez_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(bluez IMPORTED_TARGET GLOBAL bluez-1.0 bluez)
if (bluez_FOUND)
add_library(bluez::bluez ALIAS PkgConfig::bluez)
endif ()
endif ()
endif ()
find_package_handle_standard_args(bluez
REQUIRED_VARS
bluez_LINK_LIBRARIES
bluez_FOUND
VERSION_VAR bluez_VERSION
)
-27
View File
@@ -1,27 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
include(FindPackageHandleStandardArgs)
find_package(imgui CONFIG)
if (imgui_FOUND)
# Use upstream imguiConfig.cmake if possible
find_package_handle_standard_args(imgui CONFIG_MODE)
else()
# Fallback to pkg-config otherwise
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(imgui IMPORTED_TARGET GLOBAL imgui)
if (imgui_FOUND)
add_library(imgui::imgui ALIAS PkgConfig::imgui)
endif()
endif()
find_package_handle_standard_args(imgui
REQUIRED_VARS
imgui_LINK_LIBRARIES
imgui_FOUND
VERSION_VAR
imgui_VERSION
)
endif()
-20
View File
@@ -1,20 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
find_package(libusb CONFIG)
if (NOT libusb_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb)
if (libusb_FOUND)
add_library(libusb::libusb ALIAS PkgConfig::libusb)
endif ()
endif ()
endif ()
find_package_handle_standard_args(libusb
REQUIRED_VARS
libusb_LINK_LIBRARIES
libusb_FOUND
VERSION_VAR libusb_VERSION
)
-49
View File
@@ -1,49 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
include(FindPackageHandleStandardArgs)
find_package(wxWidgets CONFIG COMPONENTS ${wxWidgets_FIND_COMPONENTS})
if (wxWidgets_FOUND)
# Use upstream wxWidgetsConfig.cmake if possible
find_package_handle_standard_args(wxWidgets CONFIG_MODE)
else()
# Fall back to CMake's FindwxWidgets
# Temporarily unset CMAKE_MODULE_PATH to avoid calling the current find
# module recursively
set(_tmp_module_path "${CMAKE_MODULE_PATH}")
set(CMAKE_MODULE_PATH "")
find_package(wxWidgets MODULE QUIET COMPONENTS ${wxWidgets_FIND_COMPONENTS})
set(CMAKE_MODULE_PATH "${_tmp_module_path}")
unset(_tmp_module_path)
if (wxWidgets_FOUND)
add_library(wx::base IMPORTED INTERFACE)
target_include_directories(wx::base INTERFACE ${wxWidgets_INCLUDE_DIRS})
target_link_libraries(wx::base INTERFACE ${wxWidgets_LIBRARIES})
target_link_directories(wx::base INTERFACE ${wxWidgets_LIBRARY_DIRS})
target_compile_definitions(wx::base INTERFACE ${wxWidgets_DEFINITIONS})
target_compile_options(wx::base INTERFACE ${wxWidgets_CXX_FLAGS})
# FindwxWidgets sets everything into a single set of variables, so it is
# impossible to tell what libraries are required for what component.
# To be compatible with wxWidgetsConfig, we create an alias for each
# component so that the user can still use target_link_libraries(wx::gl)
foreach(component ${wxWidgets_FIND_COMPONENTS})
if (NOT component STREQUAL "base")
# don't alias base to itself
add_library(wx::${component} ALIAS wx::base)
endif()
endforeach()
endif()
find_package_handle_standard_args(wxWidgets
REQUIRED_VARS
wxWidgets_LIBRARIES
wxWidgets_FOUND
VERSION_VAR
wxWidgets_VERSION_STRING
)
endif()
-33
View File
@@ -1,33 +0,0 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
include(FindPackageHandleStandardArgs)
find_package(zstd CONFIG)
if (zstd_FOUND)
# Use upstream zstdConfig.cmake if possible
if (NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_static)
add_library(zstd::zstd ALIAS zstd::libzstd_static)
elseif (TARGET zstd::libzstd_shared)
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
endif()
endif()
find_package_handle_standard_args(zstd CONFIG_MODE)
else()
# Fallback to pkg-config otherwise
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libzstd IMPORTED_TARGET GLOBAL libzstd)
if (libzstd_FOUND)
add_library(zstd::zstd ALIAS PkgConfig::libzstd)
endif()
endif()
find_package_handle_standard_args(zstd
REQUIRED_VARS
libzstd_LINK_LIBRARIES
libzstd_FOUND
VERSION_VAR libzstd_VERSION
)
endif()
+175
View File
@@ -0,0 +1,175 @@
// The Boost.StringAlgo predicates and case helpers the emulator uses. Case is
// folded per character with the C library's tolower/towlower, which is what
// Boost does under the default ("C") locale.
#pragma once
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cwctype>
#include <iterator>
#include <string>
#include <string_view>
#include <type_traits>
namespace boost { namespace algorithm {
namespace detail {
template<class C>
inline C fold(C c)
{
if constexpr (sizeof(C) == 1)
return (C)std::tolower((unsigned char)c);
else
return (C)std::towlower((std::wint_t)c);
}
template<class C>
inline C fold_upper(C c)
{
if constexpr (sizeof(C) == 1)
return (C)std::toupper((unsigned char)c);
else
return (C)std::towupper((std::wint_t)c);
}
template<class T>
struct char_of
{
using type = std::remove_cv_t<std::remove_reference_t<decltype(*std::begin(std::declval<const T&>()))>>;
};
template<class C> struct char_of<C*> { using type = std::remove_cv_t<C>; };
template<class C> struct char_of<const C*> { using type = std::remove_cv_t<C>; };
template<class C, std::size_t N> struct char_of<C[N]> { using type = std::remove_cv_t<C>; };
// Every argument as a view: strings, views, string literals and char pointers.
template<class T>
inline auto view(const T& s)
{
using C = typename char_of<std::decay_t<T>>::type;
if constexpr (std::is_pointer_v<std::decay_t<T>>)
return std::basic_string_view<C>(s);
else
return std::basic_string_view<C>(std::data(s), std::size(s));
}
// The two sides may differ in character type (a std::string_view against a
// wide literal); characters are compared by value, as Boost does.
template<class C1, class C2, class Eq>
inline bool equal_with(std::basic_string_view<C1> a, std::basic_string_view<C2> b, Eq eq)
{
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), eq);
}
template<class C1, class C2, class Eq>
inline bool find_with(std::basic_string_view<C1> haystack, std::basic_string_view<C2> needle, Eq eq)
{
return std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), eq) != haystack.end();
}
struct is_equal { template<class A, class B> bool operator()(A a, B b) const { return (wchar_t)a == (wchar_t)b; } };
struct is_iequal { template<class A, class B> bool operator()(A a, B b) const { return (wchar_t)fold(a) == (wchar_t)fold(b); } };
} // namespace detail
template<class A, class B>
inline bool equals(const A& a, const B& b)
{
return detail::equal_with(detail::view(a), detail::view(b), detail::is_equal());
}
template<class A, class B>
inline bool iequals(const A& a, const B& b)
{
return detail::equal_with(detail::view(a), detail::view(b), detail::is_iequal());
}
template<class A, class B>
inline bool starts_with(const A& s, const B& prefix)
{
auto v = detail::view(s);
auto p = detail::view(prefix);
return v.size() >= p.size() && detail::equal_with(v.substr(0, p.size()), p, detail::is_equal());
}
template<class A, class B>
inline bool istarts_with(const A& s, const B& prefix)
{
auto v = detail::view(s);
auto p = detail::view(prefix);
return v.size() >= p.size() && detail::equal_with(v.substr(0, p.size()), p, detail::is_iequal());
}
template<class A, class B>
inline bool ends_with(const A& s, const B& suffix)
{
auto v = detail::view(s);
auto p = detail::view(suffix);
return v.size() >= p.size() && detail::equal_with(v.substr(v.size() - p.size()), p, detail::is_equal());
}
template<class A, class B>
inline bool iends_with(const A& s, const B& suffix)
{
auto v = detail::view(s);
auto p = detail::view(suffix);
return v.size() >= p.size() && detail::equal_with(v.substr(v.size() - p.size()), p, detail::is_iequal());
}
template<class A, class B>
inline bool contains(const A& s, const B& needle)
{
return detail::find_with(detail::view(s), detail::view(needle), detail::is_equal());
}
template<class A, class B>
inline bool icontains(const A& s, const B& needle)
{
return detail::find_with(detail::view(s), detail::view(needle), detail::is_iequal());
}
template<class S>
inline void to_lower(S& s)
{
for (auto& c : s)
c = detail::fold(c);
}
template<class S>
inline void to_upper(S& s)
{
for (auto& c : s)
c = detail::fold_upper(c);
}
template<class S>
inline S to_lower_copy(S s)
{
to_lower(s);
return s;
}
template<class S>
inline S to_upper_copy(S s)
{
to_upper(s);
return s;
}
} // namespace algorithm
using algorithm::contains;
using algorithm::ends_with;
using algorithm::equals;
using algorithm::icontains;
using algorithm::iends_with;
using algorithm::iequals;
using algorithm::istarts_with;
using algorithm::starts_with;
using algorithm::to_lower;
using algorithm::to_lower_copy;
using algorithm::to_upper;
using algorithm::to_upper_copy;
} // namespace boost
+21
View File
@@ -0,0 +1,21 @@
// boost::bind and its placeholders: the standard library's.
#pragma once
#include <functional>
namespace boost {
namespace placeholders {
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
} // namespace placeholders
template<class F, class... Args>
inline auto bind(F&& f, Args&&... args)
{
return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}
} // namespace boost
@@ -0,0 +1,365 @@
// The storage behind boost::container::small_vector and static_vector in this
// tree: up to N elements live inside the object, and a small_vector moves to
// the heap past that while a static_vector stops there (as Boost does, with an
// exception). Only the subset of the Boost interface the emulator uses, with
// the same semantics.
#pragma once
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost { namespace container { namespace detail {
template<class T, std::size_t N, bool CanGrow>
class inline_vector
{
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = T*;
using const_iterator = const T*;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
inline_vector() noexcept : m_data(inline_data()), m_size(0), m_capacity(N) {}
explicit inline_vector(size_type count) : inline_vector()
{
resize(count);
}
inline_vector(size_type count, const T& value) : inline_vector()
{
assign(count, value);
}
template<class It, class = typename std::iterator_traits<It>::iterator_category>
inline_vector(It first, It last) : inline_vector()
{
assign(first, last);
}
inline_vector(std::initializer_list<T> list) : inline_vector()
{
assign(list.begin(), list.end());
}
inline_vector(const inline_vector& other) : inline_vector()
{
assign(other.begin(), other.end());
}
inline_vector(inline_vector&& other) noexcept(std::is_nothrow_move_constructible_v<T>) : inline_vector()
{
take(std::move(other));
}
~inline_vector()
{
clear();
release_heap();
}
inline_vector& operator=(const inline_vector& other)
{
if (this != &other)
assign(other.begin(), other.end());
return *this;
}
inline_vector& operator=(inline_vector&& other) noexcept(std::is_nothrow_move_constructible_v<T>)
{
if (this != &other)
{
clear();
take(std::move(other));
}
return *this;
}
inline_vector& operator=(std::initializer_list<T> list)
{
assign(list.begin(), list.end());
return *this;
}
void assign(size_type count, const T& value)
{
clear();
reserve(count);
for (size_type i = 0; i < count; i++)
::new ((void*)(m_data + i)) T(value);
m_size = count;
}
template<class It, class = typename std::iterator_traits<It>::iterator_category>
void assign(It first, It last)
{
clear();
if constexpr (std::is_base_of_v<std::forward_iterator_tag, typename std::iterator_traits<It>::iterator_category>)
reserve((size_type)std::distance(first, last));
for (; first != last; ++first)
emplace_back(*first);
}
void assign(std::initializer_list<T> list) { assign(list.begin(), list.end()); }
reference at(size_type pos)
{
if (pos >= m_size)
throw std::out_of_range("inline_vector::at");
return m_data[pos];
}
const_reference at(size_type pos) const
{
if (pos >= m_size)
throw std::out_of_range("inline_vector::at");
return m_data[pos];
}
reference operator[](size_type pos) noexcept { return m_data[pos]; }
const_reference operator[](size_type pos) const noexcept { return m_data[pos]; }
reference front() noexcept { return m_data[0]; }
const_reference front() const noexcept { return m_data[0]; }
reference back() noexcept { return m_data[m_size - 1]; }
const_reference back() const noexcept { return m_data[m_size - 1]; }
T* data() noexcept { return m_data; }
const T* data() const noexcept { return m_data; }
iterator begin() noexcept { return m_data; }
const_iterator begin() const noexcept { return m_data; }
const_iterator cbegin() const noexcept { return m_data; }
iterator end() noexcept { return m_data + m_size; }
const_iterator end() const noexcept { return m_data + m_size; }
const_iterator cend() const noexcept { return m_data + m_size; }
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
bool empty() const noexcept { return m_size == 0; }
size_type size() const noexcept { return m_size; }
size_type capacity() const noexcept { return m_capacity; }
size_type max_size() const noexcept { return CanGrow ? (size_type)-1 / sizeof(T) : N; }
static constexpr size_type static_capacity = N;
void reserve(size_type count)
{
if (count > m_capacity)
grow_to(count);
}
void shrink_to_fit() noexcept {}
void clear() noexcept
{
std::destroy(m_data, m_data + m_size);
m_size = 0;
}
template<class... Args>
reference emplace_back(Args&&... args)
{
if (m_size == m_capacity)
grow_to(next_capacity(m_size + 1));
T* p = ::new ((void*)(m_data + m_size)) T(std::forward<Args>(args)...);
m_size++;
return *p;
}
void push_back(const T& value) { emplace_back(value); }
void push_back(T&& value) { emplace_back(std::move(value)); }
void pop_back() noexcept
{
m_size--;
std::destroy_at(m_data + m_size);
}
template<class... Args>
iterator emplace(const_iterator pos, Args&&... args)
{
const size_type index = (size_type)(pos - m_data);
if (index == m_size)
{
emplace_back(std::forward<Args>(args)...);
return m_data + index;
}
// built before the move, as the argument may refer into this vector
T value(std::forward<Args>(args)...);
emplace_back(std::move(m_data[m_size - 1]));
std::move_backward(m_data + index, m_data + m_size - 2, m_data + m_size - 1);
m_data[index] = std::move(value);
return m_data + index;
}
iterator insert(const_iterator pos, const T& value) { return emplace(pos, value); }
iterator insert(const_iterator pos, T&& value) { return emplace(pos, std::move(value)); }
iterator insert(const_iterator pos, size_type count, const T& value)
{
const size_type index = (size_type)(pos - m_data);
const T copy(value);
for (size_type i = 0; i < count; i++)
emplace(m_data + index + i, copy);
return m_data + index;
}
template<class It, class = typename std::iterator_traits<It>::iterator_category>
iterator insert(const_iterator pos, It first, It last)
{
const size_type index = (size_type)(pos - m_data);
const size_type oldSize = m_size;
for (; first != last; ++first)
emplace_back(*first);
std::rotate(m_data + index, m_data + oldSize, m_data + m_size);
return m_data + index;
}
iterator insert(const_iterator pos, std::initializer_list<T> list)
{
return insert(pos, list.begin(), list.end());
}
iterator erase(const_iterator pos)
{
return erase(pos, pos + 1);
}
iterator erase(const_iterator first, const_iterator last)
{
T* f = m_data + (first - m_data);
T* l = m_data + (last - m_data);
if (f != l)
{
T* newEnd = std::move(l, m_data + m_size, f);
std::destroy(newEnd, m_data + m_size);
m_size = (size_type)(newEnd - m_data);
}
return f;
}
void resize(size_type count)
{
if (count < m_size)
{
std::destroy(m_data + count, m_data + m_size);
m_size = count;
return;
}
reserve(count);
for (; m_size < count; m_size++)
::new ((void*)(m_data + m_size)) T();
}
void resize(size_type count, const T& value)
{
if (count < m_size)
{
std::destroy(m_data + count, m_data + m_size);
m_size = count;
return;
}
reserve(count);
for (; m_size < count; m_size++)
::new ((void*)(m_data + m_size)) T(value);
}
void swap(inline_vector& other)
{
inline_vector tmp(std::move(other));
other = std::move(*this);
*this = std::move(tmp);
}
friend bool operator==(const inline_vector& a, const inline_vector& b)
{
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
}
friend bool operator!=(const inline_vector& a, const inline_vector& b) { return !(a == b); }
friend bool operator<(const inline_vector& a, const inline_vector& b)
{
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
private:
static constexpr std::size_t kInline = N ? N : 1;
T* inline_data() noexcept { return std::launder(reinterpret_cast<T*>(m_inline)); }
bool is_inline() const noexcept { return m_data == reinterpret_cast<const T*>(m_inline); }
size_type next_capacity(size_type needed) const
{
size_type c = m_capacity ? m_capacity * 2 : 4;
return c < needed ? needed : c;
}
void grow_to(size_type count)
{
if constexpr (!CanGrow)
{
throw std::bad_alloc();
}
else
{
T* p = static_cast<T*>(::operator new(count * sizeof(T), std::align_val_t(alignof(T))));
std::uninitialized_move(m_data, m_data + m_size, p);
std::destroy(m_data, m_data + m_size);
release_heap();
m_data = p;
m_capacity = count;
}
}
void release_heap() noexcept
{
if (!is_inline())
::operator delete(m_data, std::align_val_t(alignof(T)));
m_data = inline_data();
m_capacity = N;
}
// other is left empty; this is empty on entry
void take(inline_vector&& other)
{
if (!other.is_inline())
{
release_heap();
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_data = other.inline_data();
other.m_size = 0;
other.m_capacity = N;
return;
}
reserve(other.m_size);
std::uninitialized_move(other.m_data, other.m_data + other.m_size, m_data);
m_size = other.m_size;
other.clear();
}
T* m_data;
size_type m_size;
size_type m_capacity;
alignas(T) unsigned char m_inline[kInline * sizeof(T)];
};
}}} // namespace boost::container::detail
@@ -0,0 +1,26 @@
// boost::container::small_vector, on the storage in detail/inline_vector.hpp.
#pragma once
#include "detail/inline_vector.hpp"
namespace boost { namespace container {
template<class T, std::size_t N, class Allocator = void, class Options = void>
class small_vector : public detail::inline_vector<T, N, true>
{
using base = detail::inline_vector<T, N, true>;
public:
using base::base;
small_vector() = default;
small_vector(const small_vector&) = default;
small_vector(small_vector&&) = default;
small_vector& operator=(const small_vector&) = default;
small_vector& operator=(small_vector&&) = default;
small_vector& operator=(std::initializer_list<T> list)
{
base::assign(list.begin(), list.end());
return *this;
}
};
}} // namespace boost::container
@@ -0,0 +1,27 @@
// boost::container::static_vector, on the storage in detail/inline_vector.hpp:
// never more than Capacity elements, and no heap.
#pragma once
#include "detail/inline_vector.hpp"
namespace boost { namespace container {
template<class T, std::size_t Capacity, class Options = void>
class static_vector : public detail::inline_vector<T, Capacity, false>
{
using base = detail::inline_vector<T, Capacity, false>;
public:
using base::base;
static_vector() = default;
static_vector(const static_vector&) = default;
static_vector(static_vector&&) = default;
static_vector& operator=(const static_vector&) = default;
static_vector& operator=(static_vector&&) = default;
static_vector& operator=(std::initializer_list<T> list)
{
base::assign(list.begin(), list.end());
return *this;
}
};
}} // namespace boost::container
+29
View File
@@ -0,0 +1,29 @@
// boost::core::demangle: the C++ ABI's demangler where there is one, the name
// as it is elsewhere.
#pragma once
#include <cstdlib>
#include <string>
#if defined(__GNUC__) || defined(__clang__)
#include <cxxabi.h>
#endif
namespace boost { namespace core {
inline std::string demangle(const char* name)
{
#if defined(__GNUC__) || defined(__clang__)
int status = 0;
char* d = abi::__cxa_demangle(name, nullptr, nullptr, &status);
if (d)
{
std::string out(d);
std::free(d);
return out;
}
#endif
return name;
}
}} // namespace boost::core
+112
View File
@@ -0,0 +1,112 @@
// boost::dynamic_bitset: a bit set sized at run time, with the operations the
// recompiler uses. Bits past size() are kept clear.
#pragma once
#include <cstddef>
#include <vector>
namespace boost {
template<class Block = unsigned long>
class dynamic_bitset
{
static constexpr std::size_t kBits = sizeof(Block) * 8;
public:
using size_type = std::size_t;
dynamic_bitset() = default;
explicit dynamic_bitset(size_type n, unsigned long value = 0) { resize(n); if (n && value) m_blocks[0] = (Block)value; trim(); }
size_type size() const noexcept { return m_size; }
bool empty() const noexcept { return m_size == 0; }
void resize(size_type n, bool value = false)
{
const size_type old = m_size;
m_blocks.resize((n + kBits - 1) / kBits, value ? ~(Block)0 : (Block)0);
m_size = n;
if (value)
for (size_type i = old; i < n && i < ((old + kBits - 1) / kBits) * kBits; i++)
set(i);
trim();
}
bool test(size_type i) const { return (m_blocks[i / kBits] >> (i % kBits)) & 1; }
bool operator[](size_type i) const { return test(i); }
dynamic_bitset& set(size_type i, bool value = true)
{
if (value)
m_blocks[i / kBits] |= (Block)1 << (i % kBits);
else
m_blocks[i / kBits] &= ~((Block)1 << (i % kBits));
return *this;
}
dynamic_bitset& set()
{
for (auto& b : m_blocks)
b = ~(Block)0;
trim();
return *this;
}
dynamic_bitset& reset(size_type i) { return set(i, false); }
dynamic_bitset& reset()
{
for (auto& b : m_blocks)
b = 0;
return *this;
}
bool any() const
{
for (auto b : m_blocks)
if (b)
return true;
return false;
}
bool none() const { return !any(); }
dynamic_bitset& operator&=(const dynamic_bitset& o)
{
for (size_type i = 0; i < m_blocks.size() && i < o.m_blocks.size(); i++)
m_blocks[i] &= o.m_blocks[i];
for (size_type i = o.m_blocks.size(); i < m_blocks.size(); i++)
m_blocks[i] = 0;
return *this;
}
dynamic_bitset& operator|=(const dynamic_bitset& o)
{
for (size_type i = 0; i < m_blocks.size() && i < o.m_blocks.size(); i++)
m_blocks[i] |= o.m_blocks[i];
return *this;
}
bool operator==(const dynamic_bitset& o) const { return m_size == o.m_size && m_blocks == o.m_blocks; }
bool operator!=(const dynamic_bitset& o) const { return !(*this == o); }
size_type count() const
{
size_type n = 0;
for (auto b : m_blocks)
for (; b; b &= b - 1)
n++;
return n;
}
private:
void trim()
{
if (m_size % kBits && !m_blocks.empty())
m_blocks.back() &= ((Block)1 << (m_size % kBits)) - 1;
}
std::vector<Block> m_blocks;
size_type m_size = 0;
};
} // namespace boost
+15
View File
@@ -0,0 +1,15 @@
// boost::hash_combine, with Boost's mixing.
#pragma once
#include <cstddef>
#include <functional>
namespace boost {
template<class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
} // namespace boost
@@ -0,0 +1,41 @@
// boost::make_transform_iterator: an iterator that hands out f(*it).
#pragma once
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost {
template<class F, class It>
class transform_iterator
{
public:
using iterator_category = std::forward_iterator_tag;
using reference = decltype(std::declval<const F&>()(*std::declval<It>()));
using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
using difference_type = typename std::iterator_traits<It>::difference_type;
using pointer = void;
transform_iterator() = default;
transform_iterator(It it, F f) : m_it(std::move(it)), m_f(std::move(f)) {}
reference operator*() const { return m_f(*m_it); }
transform_iterator& operator++() { ++m_it; return *this; }
transform_iterator operator++(int) { transform_iterator t = *this; ++m_it; return t; }
bool operator==(const transform_iterator& o) const { return m_it == o.m_it; }
bool operator!=(const transform_iterator& o) const { return m_it != o.m_it; }
const It& base() const { return m_it; }
private:
It m_it{};
F m_f{};
};
template<class It, class F>
inline transform_iterator<F, It> make_transform_iterator(It it, F f)
{
return transform_iterator<F, It>(std::move(it), std::move(f));
}
} // namespace boost
+154
View File
@@ -0,0 +1,154 @@
// boost::nowide::narrow and widen: UTF-8 to and from wchar_t text (UTF-16
// where wchar_t is 16 bits, UTF-32 elsewhere). As in Boost.Nowide, a malformed
// sequence becomes U+FFFD rather than an error.
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <cwchar>
#include <string>
#include <string_view>
#include <type_traits>
namespace boost { namespace nowide {
namespace detail {
constexpr char32_t kReplacement = 0xFFFD;
// Decodes one code point at s[i], moving i past it.
inline char32_t decode_utf8(const char* s, std::size_t n, std::size_t& i)
{
const unsigned char c = (unsigned char)s[i++];
if (c < 0x80)
return c;
int extra;
char32_t cp;
char32_t min;
if ((c & 0xE0) == 0xC0) { extra = 1; cp = c & 0x1F; min = 0x80; }
else if ((c & 0xF0) == 0xE0) { extra = 2; cp = c & 0x0F; min = 0x800; }
else if ((c & 0xF8) == 0xF0) { extra = 3; cp = c & 0x07; min = 0x10000; }
else return kReplacement;
for (int k = 0; k < extra; k++)
{
if (i >= n || ((unsigned char)s[i] & 0xC0) != 0x80)
return kReplacement;
cp = (cp << 6) | ((unsigned char)s[i++] & 0x3F);
}
if (cp < min || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
return kReplacement;
return cp;
}
inline void encode_utf8(std::string& out, char32_t cp)
{
if (cp < 0x80)
out.push_back((char)cp);
else if (cp < 0x800)
{
out.push_back((char)(0xC0 | (cp >> 6)));
out.push_back((char)(0x80 | (cp & 0x3F)));
}
else if (cp < 0x10000)
{
out.push_back((char)(0xE0 | (cp >> 12)));
out.push_back((char)(0x80 | ((cp >> 6) & 0x3F)));
out.push_back((char)(0x80 | (cp & 0x3F)));
}
else
{
out.push_back((char)(0xF0 | (cp >> 18)));
out.push_back((char)(0x80 | ((cp >> 12) & 0x3F)));
out.push_back((char)(0x80 | ((cp >> 6) & 0x3F)));
out.push_back((char)(0x80 | (cp & 0x3F)));
}
}
inline std::wstring widen(const char* s, std::size_t n)
{
std::wstring out;
out.reserve(n);
std::size_t i = 0;
while (i < n)
{
const char32_t cp = decode_utf8(s, n, i);
if constexpr (sizeof(wchar_t) == 2)
{
if (cp >= 0x10000)
{
const char32_t v = cp - 0x10000;
out.push_back((wchar_t)(0xD800 + (v >> 10)));
out.push_back((wchar_t)(0xDC00 + (v & 0x3FF)));
continue;
}
}
out.push_back((wchar_t)cp);
}
return out;
}
inline std::string narrow(const wchar_t* s, std::size_t n)
{
std::string out;
out.reserve(n);
for (std::size_t i = 0; i < n; i++)
{
char32_t cp = (char32_t)(std::make_unsigned_t<wchar_t>)s[i];
if constexpr (sizeof(wchar_t) == 2)
{
if (cp >= 0xD800 && cp <= 0xDBFF)
{
if (i + 1 < n && (char32_t)s[i + 1] >= 0xDC00 && (char32_t)s[i + 1] <= 0xDFFF)
{
cp = 0x10000 + ((cp - 0xD800) << 10) + ((char32_t)s[i + 1] - 0xDC00);
i++;
}
else
cp = kReplacement;
}
else if (cp >= 0xDC00 && cp <= 0xDFFF)
cp = kReplacement;
}
else if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
cp = kReplacement;
encode_utf8(out, cp);
}
return out;
}
} // namespace detail
inline std::wstring widen(const char* s) { return detail::widen(s, std::strlen(s)); }
inline std::wstring widen(const char* s, std::size_t n) { return detail::widen(s, n); }
inline std::wstring widen(const char* begin, const char* end) { return detail::widen(begin, (std::size_t)(end - begin)); }
inline std::wstring widen(std::string_view s) { return detail::widen(s.data(), s.size()); }
inline std::wstring widen(const std::string& s) { return detail::widen(s.data(), s.size()); }
// Into a buffer the caller owns, terminator included: the buffer, or null if
// it is too small.
inline wchar_t* widen(wchar_t* output, std::size_t output_size, const char* source)
{
const std::wstring w = widen(source);
if (w.size() + 1 > output_size)
return nullptr;
std::memcpy(output, w.c_str(), (w.size() + 1) * sizeof(wchar_t));
return output;
}
inline std::string narrow(const wchar_t* s) { return detail::narrow(s, std::wcslen(s)); }
inline std::string narrow(const wchar_t* s, std::size_t n) { return detail::narrow(s, n); }
inline std::string narrow(const wchar_t* begin, const wchar_t* end) { return detail::narrow(begin, (std::size_t)(end - begin)); }
inline std::string narrow(std::wstring_view s) { return detail::narrow(s.data(), s.size()); }
inline std::string narrow(const std::wstring& s) { return detail::narrow(s.data(), s.size()); }
inline char* narrow(char* output, std::size_t output_size, const wchar_t* source)
{
const std::string n = narrow(source);
if (n.size() + 1 > output_size)
return nullptr;
std::memcpy(output, n.c_str(), n.size() + 1);
return output;
}
}} // namespace boost::nowide
+4
View File
@@ -0,0 +1,4 @@
// Only the operating system tests of Boost.Predef are used here.
#pragma once
#include "predef/os.h"
+43
View File
@@ -0,0 +1,43 @@
// The BOOST_OS_* tests of Boost.Predef the emulator uses, detected the way
// Boost.Predef does (same macros, same order, so an earlier match hides a
// later one: iOS before macOS, Linux before Unix-only).
#pragma once
#if defined(__APPLE__) && defined(__MACH__) && defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
#define BOOST_OS_IOS 1
#else
#define BOOST_OS_IOS 0
#endif
#if !BOOST_OS_IOS && (defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__))
#define BOOST_OS_LINUX 1
#else
#define BOOST_OS_LINUX 0
#endif
#if !BOOST_OS_IOS && !BOOST_OS_LINUX && (defined(macintosh) || defined(Macintosh) || (defined(__APPLE__) && defined(__MACH__)))
#define BOOST_OS_MACOS 1
#else
#define BOOST_OS_MACOS 0
#endif
#if !BOOST_OS_IOS && !BOOST_OS_LINUX && !BOOST_OS_MACOS && \
(defined(BSD) || defined(_SYSTYPE_BSD) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__) || defined(__bsdi__))
#define BOOST_OS_BSD 1
#else
#define BOOST_OS_BSD 0
#endif
#if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)
#define BOOST_OS_UNIX 1
#else
#define BOOST_OS_UNIX 0
#endif
#if !BOOST_OS_IOS && !BOOST_OS_LINUX && !BOOST_OS_MACOS && !BOOST_OS_BSD && \
(defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__))
#define BOOST_OS_WINDOWS 1
#else
#define BOOST_OS_WINDOWS 0
#endif
@@ -0,0 +1,131 @@
// boost::property_tree::ptree and read_ini, for flat INI files: a key is
// "Section.name" (or "name" before the first section). get<std::string> throws
// when the key is missing and get_optional returns nothing, as in Boost.
#pragma once
#include <istream>
#include <map>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
namespace boost { namespace property_tree {
class ptree_bad_path : public std::runtime_error
{
public:
explicit ptree_bad_path(const std::string& path) : std::runtime_error("No such node (" + path + ")") {}
};
namespace ini_parser {
class ini_parser_error : public std::runtime_error
{
public:
ini_parser_error(const std::string& message, unsigned long line)
: std::runtime_error("<unspecified file>(" + std::to_string(line) + "): " + message) {}
};
} // namespace ini_parser
class ptree
{
public:
template<class T>
T get(const std::string& path) const
{
auto it = m_values.find(path);
if (it == m_values.end())
throw ptree_bad_path(path);
return convert<T>(it->second, path);
}
template<class T>
T get(const std::string& path, const T& fallback) const
{
auto it = m_values.find(path);
if (it == m_values.end())
return fallback;
return convert<T>(it->second, path);
}
template<class T>
std::optional<T> get_optional(const std::string& path) const
{
auto it = m_values.find(path);
if (it == m_values.end())
return std::nullopt;
return convert<T>(it->second, path);
}
void put(const std::string& path, const std::string& value) { m_values[path] = value; }
bool empty() const { return m_values.empty(); }
private:
template<class T>
static T convert(const std::string& s, const std::string& path)
{
if constexpr (std::is_same_v<T, std::string>)
return s;
else
{
std::istringstream in(s);
T v{};
in >> v;
if (in.fail())
throw ptree_bad_path(path);
return v;
}
}
std::map<std::string, std::string> m_values;
};
namespace ini_parser {
inline void read_ini(std::istream& in, ptree& pt)
{
auto trim = [](std::string s) {
const char* ws = " \t\r\n";
const auto b = s.find_first_not_of(ws);
if (b == std::string::npos)
return std::string();
const auto e = s.find_last_not_of(ws);
return s.substr(b, e - b + 1);
};
ptree result;
std::string section;
std::string line;
unsigned long lineNo = 0;
while (std::getline(in, line))
{
lineNo++;
line = trim(line);
if (line.empty() || line[0] == ';' || line[0] == '#')
continue;
if (line[0] == '[')
{
const auto end = line.find(']');
if (end == std::string::npos)
throw ini_parser_error("unmatched '['", lineNo);
section = trim(line.substr(1, end - 1));
continue;
}
const auto eq = line.find('=');
if (eq == std::string::npos)
throw ini_parser_error("'=' character not found in line", lineNo);
const std::string key = trim(line.substr(0, eq));
if (key.empty())
throw ini_parser_error("key expected", lineNo);
result.put(section.empty() ? key : section + "." + key, trim(line.substr(eq + 1)));
}
pt = std::move(result);
}
} // namespace ini_parser
using ini_parser::read_ini;
}} // namespace boost::property_tree
+11
View File
@@ -0,0 +1,11 @@
// boost::random::uniform_int_distribution: the standard library's.
#pragma once
#include <random>
namespace boost { namespace random {
template<class T = int>
using uniform_int_distribution = std::uniform_int_distribution<T>;
}} // namespace boost::random
@@ -0,0 +1,30 @@
// range | boost::adaptors::reversed: the range walked back to front.
#pragma once
#include <iterator>
namespace boost { namespace adaptors {
namespace detail {
struct reversed_tag {};
template<class R>
struct reversed_range
{
R& r;
auto begin() const { return std::rbegin(r); }
auto end() const { return std::rend(r); }
};
template<class R>
inline reversed_range<R> operator|(R& r, reversed_tag) { return reversed_range<R>{r}; }
template<class R>
inline reversed_range<const R> operator|(const R& r, reversed_tag) { return reversed_range<const R>{r}; }
} // namespace detail
inline constexpr detail::reversed_tag reversed{};
}} // namespace boost::adaptors
+120
View File
@@ -0,0 +1,120 @@
// boost::signals2::signal and connection, reduced to what EventService uses:
// connect, disconnect and emission, from any thread. Slots run in the order
// they were connected, as in Boost; a slot may disconnect itself while it runs.
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
namespace boost { namespace signals2 {
namespace detail {
struct slot_list_base
{
virtual ~slot_list_base() = default;
virtual void remove(uint64_t id) = 0;
};
} // namespace detail
class connection
{
public:
connection() = default;
connection(std::weak_ptr<detail::slot_list_base> list, uint64_t id) : m_list(std::move(list)), m_id(id) {}
void disconnect() const
{
if (auto list = m_list.lock())
list->remove(m_id);
}
bool connected() const { return !m_list.expired() && m_id != 0; }
uint64_t id() const { return m_id; }
private:
std::weak_ptr<detail::slot_list_base> m_list;
uint64_t m_id = 0;
};
template<class Signature>
class signal;
template<class R, class... Args>
class signal<R(Args...)>
{
struct slot_list : detail::slot_list_base
{
std::mutex mutex;
std::vector<std::pair<uint64_t, std::shared_ptr<std::function<R(Args...)>>>> slots;
uint64_t nextId = 1;
void remove(uint64_t id) override
{
std::lock_guard<std::mutex> lock(mutex);
for (auto it = slots.begin(); it != slots.end(); ++it)
{
if (it->first == id)
{
slots.erase(it);
return;
}
}
}
};
public:
signal() : m_list(std::make_shared<slot_list>()) {}
signal(const signal&) = delete;
signal& operator=(const signal&) = delete;
template<class F>
connection connect(F&& f)
{
std::lock_guard<std::mutex> lock(m_list->mutex);
const uint64_t id = m_list->nextId++;
m_list->slots.emplace_back(id, std::make_shared<std::function<R(Args...)>>(std::forward<F>(f)));
return connection(m_list, id);
}
void disconnect(const connection& c) { m_list->remove(c.id()); }
void disconnect_all_slots()
{
std::lock_guard<std::mutex> lock(m_list->mutex);
m_list->slots.clear();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m_list->mutex);
return m_list->slots.empty();
}
// The slots are called outside the lock, on a snapshot, so one may connect
// or disconnect without deadlocking.
template<class... CallArgs>
void operator()(CallArgs&&... args)
{
std::vector<std::shared_ptr<std::function<R(Args...)>>> snapshot;
{
std::lock_guard<std::mutex> lock(m_list->mutex);
snapshot.reserve(m_list->slots.size());
for (auto& s : m_list->slots)
snapshot.push_back(s.second);
}
for (auto& s : snapshot)
(*s)(args...);
}
private:
std::shared_ptr<slot_list> m_list;
};
}} // namespace boost::signals2
@@ -0,0 +1,82 @@
// boost::static_string<N>: a string of at most N characters stored inline.
#pragma once
#include <cstddef>
#include <cstring>
#include <stdexcept>
#include <string_view>
namespace boost {
template<std::size_t N, class CharT = char>
class basic_static_string
{
public:
using size_type = std::size_t;
basic_static_string() noexcept { m_data[0] = 0; }
basic_static_string(const CharT* s) { assign(s); }
size_type size() const noexcept { return m_size; }
size_type length() const noexcept { return m_size; }
static constexpr size_type capacity() noexcept { return N; }
static constexpr size_type max_size() noexcept { return N; }
bool empty() const noexcept { return m_size == 0; }
const CharT* c_str() const noexcept { return m_data; }
const CharT* data() const noexcept { return m_data; }
CharT* data() noexcept { return m_data; }
CharT& back() noexcept { return m_data[m_size - 1]; }
const CharT& back() const noexcept { return m_data[m_size - 1]; }
CharT& front() noexcept { return m_data[0]; }
CharT& operator[](size_type i) noexcept { return m_data[i]; }
const CharT& operator[](size_type i) const noexcept { return m_data[i]; }
const CharT* begin() const noexcept { return m_data; }
const CharT* end() const noexcept { return m_data + m_size; }
operator std::basic_string_view<CharT>() const noexcept { return {m_data, m_size}; }
void clear() noexcept { m_size = 0; m_data[0] = 0; }
void push_back(CharT c)
{
if (m_size >= N)
throw std::length_error("static_string::push_back");
m_data[m_size++] = c;
m_data[m_size] = 0;
}
void pop_back() noexcept
{
m_data[--m_size] = 0;
}
basic_static_string& assign(std::basic_string_view<CharT> s)
{
if (s.size() > N)
throw std::length_error("static_string::assign");
std::memcpy(m_data, s.data(), s.size() * sizeof(CharT));
m_size = s.size();
m_data[m_size] = 0;
return *this;
}
basic_static_string& assign(const CharT* s) { return assign(std::basic_string_view<CharT>(s)); }
int compare(std::basic_string_view<CharT> s) const noexcept
{
return std::basic_string_view<CharT>(m_data, m_size).compare(s);
}
int compare(const CharT* s) const noexcept { return compare(std::basic_string_view<CharT>(s)); }
friend bool operator==(const basic_static_string& a, std::basic_string_view<CharT> b) { return a.compare(b) == 0; }
friend bool operator!=(const basic_static_string& a, std::basic_string_view<CharT> b) { return a.compare(b) != 0; }
private:
CharT m_data[N + 1];
size_type m_size = 0;
};
template<std::size_t N>
using static_string = basic_static_string<N, char>;
} // namespace boost
File diff suppressed because it is too large. Load diff
+365
View File
@@ -0,0 +1,365 @@
/* The part of libcurl's API the core uses, carried by libretro-common's
* net_http and its built-in TLS client (curl_compat.cpp), so that no
* libcurl, OpenSSL or mbedTLS is linked.
*
* Option, info and error numbers are libcurl's own: nlibcurl hands the
* guest's numbers straight through. Options this layer has no use for
* are accepted and ignored, as libcurl does with ones it was built
* without.
*
* Differences from libcurl:
* - CURLOPT_SSL_CTX_FUNCTION receives a struct curl_tls_settings *
* where libcurl passes the TLS library's context; fill it with the
* curl_tls_* functions below.
* - CURLOPT_CLOSESOCKETFUNCTION and CURLOPT_SOCKOPTFUNCTION are never
* called: the sockets belong to net_http.
* - A request body from CURLOPT_READFUNCTION is read whole before the
* request goes out.
* - curl_multi_fdset() reports no descriptors; curl_multi_timeout()
* says when to call curl_multi_perform() again.
* - Redirects are followed whatever CURLOPT_FOLLOWLOCATION says, and a
* proxy is not used.
* - curl_formadd() takes names and contents (with their lengths) and a
* content type; files and buffers are refused. */
#ifndef CURL_COMPAT_CURL_H
#define CURL_COMPAT_CURL_H
#include <stddef.h>
#include <stdint.h>
#ifdef _WIN32
#include <winsock2.h>
typedef SOCKET curl_socket_t;
#else
typedef int curl_socket_t;
#endif
typedef void CURL;
typedef void CURLSH;
typedef void CURLM;
typedef int64_t curl_off_t;
struct curl_slist
{
char *data;
struct curl_slist *next;
};
#define CURLOPTTYPE_LONG 0
#define CURLOPTTYPE_OBJECTPOINT 10000
#define CURLOPTTYPE_FUNCTIONPOINT 20000
#define CURLOPTTYPE_OFF_T 30000
#define CURLOPTTYPE_BLOB 40000
typedef enum
{
CURLOPT_WRITEDATA = 10001,
CURLOPT_URL = 10002,
CURLOPT_PROXY = 10004,
CURLOPT_READDATA = 10009,
CURLOPT_TIMEOUT = 13,
CURLOPT_INFILESIZE = 14,
CURLOPT_POSTFIELDS = 10015,
CURLOPT_USERAGENT = 10018,
CURLOPT_LOW_SPEED_LIMIT = 19,
CURLOPT_LOW_SPEED_TIME = 20,
CURLOPT_HTTPHEADER = 10023,
CURLOPT_HTTPPOST = 10024,
CURLOPT_HEADERDATA = 10029,
CURLOPT_SSLVERSION = 32,
CURLOPT_CUSTOMREQUEST = 10036,
CURLOPT_VERBOSE = 41,
CURLOPT_NOPROGRESS = 43,
CURLOPT_NOBODY = 44,
CURLOPT_POST = 47,
CURLOPT_FOLLOWLOCATION = 52,
CURLOPT_PROGRESSDATA = 10057,
CURLOPT_POSTFIELDSIZE = 60,
CURLOPT_SSL_VERIFYPEER = 64,
CURLOPT_MAXREDIRS = 68,
CURLOPT_CONNECTTIMEOUT = 78,
CURLOPT_HTTPGET = 80,
CURLOPT_SSL_VERIFYHOST = 81,
CURLOPT_HTTP_VERSION = 84,
CURLOPT_BUFFERSIZE = 98,
CURLOPT_NOSIGNAL = 99,
CURLOPT_SHARE = 10100,
CURLOPT_SSL_CTX_DATA = 10109,
CURLOPT_SOCKOPTDATA = 10149,
CURLOPT_TIMEOUT_MS = 155,
CURLOPT_CONNECTTIMEOUT_MS = 156,
CURLOPT_CLOSESOCKETDATA = 10209,
CURLOPT_WRITEFUNCTION = 20011,
CURLOPT_READFUNCTION = 20012,
CURLOPT_PROGRESSFUNCTION = 20056,
CURLOPT_HEADERFUNCTION = 20079,
CURLOPT_SSL_CTX_FUNCTION = 20108,
CURLOPT_SOCKOPTFUNCTION = 20148,
CURLOPT_CLOSESOCKETFUNCTION = 20208,
CURLOPT_INFILESIZE_LARGE = 30115,
CURLOPT_POSTFIELDSIZE_LARGE = 30120,
CURLOPT_MAX_SEND_SPEED_LARGE = 30145,
CURLOPT_MAX_RECV_SPEED_LARGE = 30146
} CURLoption;
#define CURLOPT_FILE CURLOPT_WRITEDATA
typedef enum
{
CURLE_OK = 0,
CURLE_UNSUPPORTED_PROTOCOL = 1,
CURLE_FAILED_INIT = 2,
CURLE_URL_MALFORMAT = 3,
CURLE_COULDNT_RESOLVE_HOST = 6,
CURLE_COULDNT_CONNECT = 7,
CURLE_WRITE_ERROR = 23,
CURLE_READ_ERROR = 26,
CURLE_OUT_OF_MEMORY = 27,
CURLE_OPERATION_TIMEDOUT = 28,
CURLE_SSL_CONNECT_ERROR = 35,
CURLE_ABORTED_BY_CALLBACK = 42,
CURLE_BAD_FUNCTION_ARGUMENT = 43,
CURLE_TOO_MANY_REDIRECTS = 47,
CURLE_UNKNOWN_OPTION = 48,
CURLE_GOT_NOTHING = 52,
CURLE_SEND_ERROR = 55,
CURLE_RECV_ERROR = 56,
CURLE_SSL_CERTPROBLEM = 58,
CURLE_PEER_FAILED_VERIFICATION = 60
} CURLcode;
#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION
#define CURLINFO_STRING 0x100000
#define CURLINFO_LONG 0x200000
#define CURLINFO_DOUBLE 0x300000
#define CURLINFO_OFF_T 0x600000
#define CURLINFO_TYPEMASK 0xf00000
typedef enum
{
CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1,
CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7,
CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8,
CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9,
CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10,
CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13,
CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15,
CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18,
CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31
} CURLINFO;
typedef enum
{
CURLM_OK = 0,
CURLM_BAD_HANDLE = 1,
CURLM_BAD_EASY_HANDLE = 2,
CURLM_OUT_OF_MEMORY = 3,
CURLM_BAD_FUNCTION_ARGUMENT = 10
} CURLMcode;
typedef enum
{
CURLMOPT_MAXCONNECTS = 6
} CURLMoption;
typedef enum
{
CURLMSG_NONE,
CURLMSG_DONE
} CURLMSG;
typedef struct CURLMsg
{
CURLMSG msg;
CURL *easy_handle;
union
{
void *whatever;
CURLcode result;
} data;
} CURLMsg;
typedef enum
{
CURLSHE_OK,
CURLSHE_BAD_OPTION,
CURLSHE_IN_USE,
CURLSHE_INVALID,
CURLSHE_NOMEM
} CURLSHcode;
typedef enum
{
CURLSHOPT_NONE,
CURLSHOPT_SHARE,
CURLSHOPT_UNSHARE,
CURLSHOPT_LOCKFUNC,
CURLSHOPT_UNLOCKFUNC,
CURLSHOPT_USERDATA
} CURLSHoption;
typedef enum
{
CURL_LOCK_DATA_NONE,
CURL_LOCK_DATA_SHARE,
CURL_LOCK_DATA_COOKIE,
CURL_LOCK_DATA_DNS,
CURL_LOCK_DATA_SSL_SESSION,
CURL_LOCK_DATA_CONNECT
} curl_lock_data;
typedef enum
{
CURL_LOCK_ACCESS_NONE,
CURL_LOCK_ACCESS_SHARED,
CURL_LOCK_ACCESS_SINGLE
} curl_lock_access;
typedef enum
{
CURLSOCKTYPE_IPCXN,
CURLSOCKTYPE_ACCEPT
} curlsocktype;
#define CURL_GLOBAL_SSL (1 << 0)
#define CURL_GLOBAL_WIN32 (1 << 1)
#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32)
#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL
#define CURL_HTTP_VERSION_1_1 2L
#define CURL_SSLVERSION_DEFAULT 0
#define CURL_SSLVERSION_SSLv3 3
#define CURL_SSLVERSION_TLSv1_2 6
#define CURL_WRITEFUNC_PAUSE 0x10000001
#define CURL_READFUNC_ABORT 0x10000000
#define CURL_READFUNC_PAUSE 0x10000001
#define CURLPAUSE_RECV (1 << 0)
#define CURLPAUSE_SEND (1 << 2)
#define CURLPAUSE_ALL (CURLPAUSE_RECV | CURLPAUSE_SEND)
#define CURLPAUSE_CONT 0
typedef size_t (*curl_write_callback)(char *buffer, size_t size,
size_t nitems, void *userdata);
typedef size_t (*curl_read_callback)(char *buffer, size_t size,
size_t nitems, void *instream);
typedef int (*curl_progress_callback)(void *clientp,
double dltotal, double dlnow, double ultotal, double ulnow);
typedef int (*curl_sockopt_callback)(void *clientp,
curl_socket_t curlfd, curlsocktype purpose);
typedef int (*curl_closesocket_callback)(void *clientp,
curl_socket_t item);
typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, void *ssl_ctx,
void *userptr);
typedef void (*curl_lock_function)(CURL *handle, curl_lock_data data,
curl_lock_access locktype, void *userptr);
typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data,
void *userptr);
typedef void *(*curl_malloc_callback)(size_t size);
typedef void (*curl_free_callback)(void *ptr);
typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
typedef char *(*curl_strdup_callback)(const char *str);
typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
CURLcode curl_global_init(long flags);
/* Stops the transfer thread; call when nothing is transferring. */
void curl_global_cleanup(void);
CURL *curl_easy_init(void);
void curl_easy_cleanup(CURL *curl);
void curl_easy_reset(CURL *curl);
CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
CURLcode curl_easy_perform(CURL *curl);
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);
CURLcode curl_easy_pause(CURL *curl, int bitmask);
const char *curl_easy_strerror(CURLcode code);
struct curl_slist *curl_slist_append(struct curl_slist *list,
const char *data);
void curl_slist_free_all(struct curl_slist *list);
CURLM *curl_multi_init(void);
CURLMcode curl_multi_cleanup(CURLM *multi);
CURLMcode curl_multi_add_handle(CURLM *multi, CURL *curl);
CURLMcode curl_multi_remove_handle(CURLM *multi, CURL *curl);
CURLMcode curl_multi_perform(CURLM *multi, int *running_handles);
CURLMcode curl_multi_timeout(CURLM *multi, long *milliseconds);
CURLMcode curl_multi_setopt(CURLM *multi, CURLMoption option, ...);
CURLMcode curl_multi_fdset(CURLM *multi, void *read_fd_set,
void *write_fd_set, void *exc_fd_set, int *max_fd);
CURLMsg *curl_multi_info_read(CURLM *multi, int *msgs_in_queue);
/* multipart/form-data, for CURLOPT_HTTPPOST */
struct curl_httppost
{
struct curl_httppost *next;
char *name;
long namelength;
char *contents;
long contentslength;
char *contenttype;
};
typedef enum
{
CURLFORM_NOTHING,
CURLFORM_COPYNAME,
CURLFORM_PTRNAME,
CURLFORM_NAMELENGTH,
CURLFORM_COPYCONTENTS,
CURLFORM_PTRCONTENTS,
CURLFORM_CONTENTSLENGTH,
CURLFORM_FILECONTENT,
CURLFORM_ARRAY,
CURLFORM_OBSOLETE,
CURLFORM_FILE,
CURLFORM_BUFFER,
CURLFORM_BUFFERPTR,
CURLFORM_BUFFERLENGTH,
CURLFORM_CONTENTTYPE,
CURLFORM_CONTENTHEADER,
CURLFORM_FILENAME,
CURLFORM_END
} CURLformoption;
typedef enum
{
CURL_FORMADD_OK,
CURL_FORMADD_MEMORY,
CURL_FORMADD_OPTION_TWICE,
CURL_FORMADD_NULL,
CURL_FORMADD_UNKNOWN_OPTION,
CURL_FORMADD_INCOMPLETE,
CURL_FORMADD_ILLEGAL_ARRAY,
CURL_FORMADD_DISABLED
} CURLFORMcode;
CURLFORMcode curl_formadd(struct curl_httppost **httppost,
struct curl_httppost **last_post, ...);
void curl_formfree(struct curl_httppost *form);
CURLSH *curl_share_init(void);
CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, ...);
CURLSHcode curl_share_cleanup(CURLSH *share);
/* TLS settings for one transfer, filled in by its
* CURLOPT_SSL_CTX_FUNCTION. Certificates and keys are DER and copied. */
struct curl_tls_settings;
/* A trust anchor in place of the built-in bundle. */
bool curl_tls_add_trust(struct curl_tls_settings *tls,
const void *der, size_t len);
/* The client certificate and its RSA key (PKCS#1 or PKCS#8). */
bool curl_tls_set_client(struct curl_tls_settings *tls,
const void *cert_der, size_t cert_len,
const void *key_der, size_t key_len);
/* Suites to offer, by IANA number, most preferred first. */
void curl_tls_set_suites(struct curl_tls_settings *tls,
const unsigned *suites, size_t count);
/* Whether the peer's certificate and name are checked. */
void curl_tls_set_verify(struct curl_tls_settings *tls,
bool peer, bool host);
#endif
+3
View File
@@ -0,0 +1,3 @@
curl_compat_test
curl_compat_test.exe
*.o
+29
View File
@@ -0,0 +1,29 @@
# The curl_compat test (run_tests.sh); SANITIZER=address,undefined or
# SANITIZER=thread for the sanitizer runs.
LRC := ../../libretro-common
CXX ?= g++
CC ?= gcc
FLAGS := -O1 -g -I../include -I$(LRC)/include -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_CRYPTO -DHAVE_THREADS
ifneq ($(SANITIZER),)
FLAGS += -fsanitize=$(SANITIZER) -fno-omit-frame-pointer
endif
LRC_SOURCES := $(addprefix $(LRC)/,net/net_http.c net/net_socket.c net/net_compat.c \
net/net_socket_ssl_retro.c crypto/crypto.c crypto/kdf.c crypto/pk.c crypto/x509.c \
hash/lrc_hash.c utils/md5.c encodings/encoding_base64.c streams/file_stream.c \
vfs/vfs_implementation.c file/file_path.c file/file_path_io.c string/rstrtod.c \
string/stdstring.c encodings/encoding_utf.c features/features_cpu.c compat/compat_strl.c \
compat/fopen_utf8.c compat/compat_strcasestr.c compat/compat_posix_string.c time/rtime.c \
lists/string_list.c file/retro_dirent.c rthreads/rthreads.c rthreads/retro_eventcount.c)
LIBS := -lpthread
ifneq (,$(findstring mingw,$(CC)))
LIBS += -lws2_32 -liphlpapi -lbcrypt -static
EXE := .exe
endif
curl_compat_test$(EXE): curl_compat_test.cpp ../curl_compat.cpp $(LRC_SOURCES)
$(CC) $(FLAGS) -std=gnu11 -c $(LRC_SOURCES)
$(CXX) $(FLAGS) -std=gnu++20 curl_compat_test.cpp ../curl_compat.cpp *.o $(LIBS) -o $@
rm -f *.o
clean:
rm -f curl_compat_test curl_compat_test.exe *.o
+338
View File
@@ -0,0 +1,338 @@
/* curl_compat against local servers (run_tests.sh starts them):
* curl_compat_test <case> <http port> <https port> <dir>
* <dir> holds body.bin and its SHA-256 in body.sha256, and for the TLS
* cases ca.der, cli.der and cli.key.der. Exit 0 on success. */
#include <curl/curl.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <thread>
#include <vector>
#include <lrc_hash.h>
static std::string g_dir;
static int g_http, g_https;
static std::vector<uint8_t> slurp(const std::string &path)
{
std::vector<uint8_t> v;
FILE *f = fopen(path.c_str(), "rb");
if (!f)
return v;
char buf[65536];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), f)) > 0)
v.insert(v.end(), buf, buf + n);
fclose(f);
return v;
}
static std::string sha256hex(const std::vector<uint8_t> &v)
{
char hex[65];
sha256_hash(hex, v.data(), v.size());
return hex;
}
static std::string wantSha()
{
std::vector<uint8_t> s = slurp(g_dir + "/body.sha256");
return std::string(s.begin(), s.begin() + 64);
}
static std::string url(bool tls, const char *path)
{
return std::string(tls ? "https://localhost:" : "http://127.0.0.1:")
+ std::to_string(tls ? g_https : g_http) + path;
}
struct Sink
{
std::vector<uint8_t> body;
std::string headers;
int pauses = 0; /* pause this many times before taking data */
int paused = 0;
};
static size_t onWrite(char *p, size_t s, size_t n, void *u)
{
Sink *k = (Sink*)u;
if (k->paused < k->pauses)
{
k->paused++;
return CURL_WRITEFUNC_PAUSE;
}
k->body.insert(k->body.end(), p, p + s * n);
return s * n;
}
static size_t onHeader(char *p, size_t s, size_t n, void *u)
{
((Sink*)u)->headers.append(p, s * n);
return s * n;
}
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #c); return 1; } } while (0)
static int getBody(bool tls, curl_ssl_ctx_callback ctxFn = nullptr)
{
Sink k;
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, url(tls, "/body.bin").c_str());
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, onWrite);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &k);
curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, onHeader);
curl_easy_setopt(c, CURLOPT_HEADERDATA, &k);
if (ctxFn)
{
curl_easy_setopt(c, CURLOPT_SSL_CTX_FUNCTION, ctxFn);
curl_easy_setopt(c, CURLOPT_SSL_CTX_DATA, nullptr);
}
const CURLcode r = curl_easy_perform(c);
long status = 0;
double size = 0;
curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &status);
curl_easy_getinfo(c, CURLINFO_SIZE_DOWNLOAD, &size);
curl_easy_cleanup(c);
if (r != CURLE_OK)
fprintf(stderr, "perform: %s\n", curl_easy_strerror(r));
CHECK(r == CURLE_OK);
CHECK(status == 200);
CHECK(k.headers.compare(0, 13, "HTTP/1.1 200 ") == 0 || k.headers.compare(0, 13, "HTTP/1.1 200\r") == 0);
CHECK(k.headers.size() >= 4 && k.headers.compare(k.headers.size() - 4, 4, "\r\n\r\n") == 0);
CHECK((size_t)size == k.body.size());
CHECK(sha256hex(k.body) == wantSha());
return 0;
}
static int postEcho(bool useRead)
{
/* the server answers a POST with the SHA-256 of what it got */
std::vector<uint8_t> body = slurp(g_dir + "/body.bin");
body.resize(300001);
struct Reader { const std::vector<uint8_t> *v; size_t off; } rd{&body, 0};
Sink k;
CURL *c = curl_easy_init();
struct curl_slist *h = curl_slist_append(nullptr, "Content-Type: application/octet-stream");
h = curl_slist_append(h, "Content-Length: 1"); /* replaced, as nlibcurl relies on */
h = curl_slist_append(h, "X-Test: yes");
curl_easy_setopt(c, CURLOPT_URL, url(false, "/echo").c_str());
curl_easy_setopt(c, CURLOPT_HTTPHEADER, h);
curl_easy_setopt(c, CURLOPT_POST, 1L);
if (useRead)
{
curl_easy_setopt(c, CURLOPT_READFUNCTION, (curl_read_callback)[](char *p, size_t s, size_t n, void *u) -> size_t {
Reader *r = (Reader*)u;
size_t take = std::min(s * n, r->v->size() - r->off);
memcpy(p, r->v->data() + r->off, take);
r->off += take;
return take;
});
curl_easy_setopt(c, CURLOPT_READDATA, &rd);
curl_easy_setopt(c, CURLOPT_INFILESIZE_LARGE, (curl_off_t)body.size());
}
else
{
curl_easy_setopt(c, CURLOPT_POSTFIELDS, body.data());
curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)body.size());
}
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, onWrite);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &k);
const CURLcode r = curl_easy_perform(c);
curl_easy_cleanup(c);
curl_slist_free_all(h);
CHECK(r == CURLE_OK);
CHECK(std::string(k.body.begin(), k.body.end()) == sha256hex(body) + " yes application/octet-stream");
return 0;
}
static int form()
{
/* multipart/form-data from curl_formadd(), as nn_olv uploads */
struct curl_httppost *post = nullptr, *last = nullptr;
std::string desc = "a description";
std::string bin("\0\1\2--------------------------curlcompat", 40);
CHECK(curl_formadd(&post, &last, CURLFORM_COPYNAME, "description", CURLFORM_PTRCONTENTS, desc.data(),
CURLFORM_CONTENTSLENGTH, (long)desc.size(), CURLFORM_END) == CURL_FORMADD_OK);
CHECK(curl_formadd(&post, &last, CURLFORM_COPYNAME, "app_data", CURLFORM_PTRCONTENTS, bin.data(),
CURLFORM_CONTENTSLENGTH, (long)bin.size(), CURLFORM_END) == CURL_FORMADD_OK);
CHECK(curl_formadd(&post, &last, CURLFORM_FILE, "x", CURLFORM_END) == CURL_FORMADD_UNKNOWN_OPTION);
desc[0] = 'X'; /* the form kept its own copy */
Sink k;
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, url(false, "/echo").c_str());
curl_easy_setopt(c, CURLOPT_HTTPPOST, post);
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, onWrite);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &k);
const CURLcode r = curl_easy_perform(c);
curl_easy_cleanup(c);
curl_formfree(post);
CHECK(r == CURLE_OK);
/* the boundary grew past the one the binary part contains */
const std::string b = "------------------------curlcompatx";
const std::string want = "--" + b + "\r\nContent-Disposition: form-data; name=\"description\"\r\n\r\na description\r\n"
+ "--" + b + "\r\nContent-Disposition: form-data; name=\"app_data\"\r\n\r\n" + bin + "\r\n--" + b + "--\r\n";
CHECK(std::string(k.body.begin(), k.body.end())
== sha256hex(std::vector<uint8_t>(want.begin(), want.end())) + " None multipart/form-data; boundary=" + b);
return 0;
}
static int notFound()
{
Sink k;
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, url(false, "/missing").c_str());
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, onWrite);
curl_easy_setopt(c, CURLOPT_WRITEDATA, &k);
const CURLcode r = curl_easy_perform(c);
long status = 0;
curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &status);
curl_easy_cleanup(c);
CHECK(r == CURLE_OK);
CHECK(status == 404);
return 0;
}
static int timeout()
{
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, url(false, "/stall").c_str());
curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 700L);
const CURLcode r = curl_easy_perform(c);
curl_easy_cleanup(c);
CHECK(r == CURLE_OPERATION_TIMEDOUT);
return 0;
}
static int multi(int pauses)
{
const int N = 4;
Sink k[N];
CURL *c[N];
CURLM *m = curl_multi_init();
for (int i = 0; i < N; i++)
{
k[i].pauses = pauses;
c[i] = curl_easy_init();
curl_easy_setopt(c[i], CURLOPT_URL, url(false, "/body.bin").c_str());
curl_easy_setopt(c[i], CURLOPT_WRITEFUNCTION, onWrite);
curl_easy_setopt(c[i], CURLOPT_WRITEDATA, &k[i]);
CHECK(curl_multi_add_handle(m, c[i]) == CURLM_OK);
}
int running = N, done = 0;
while (done < N)
{
curl_multi_perform(m, &running);
CURLMsg *msg;
int left;
while ((msg = curl_multi_info_read(m, &left)))
{
CHECK(msg->msg == CURLMSG_DONE);
CHECK(msg->data.result == CURLE_OK);
done++;
}
/* a paused transfer resumes when asked, as a game would */
for (int i = 0; i < N; i++)
if (k[i].paused && k[i].paused <= k[i].pauses)
curl_easy_pause(c[i], CURLPAUSE_CONT);
long t = 0;
curl_multi_timeout(m, &t);
if (t > 0)
std::this_thread::sleep_for(std::chrono::milliseconds(t));
}
CHECK(running == 0);
for (int i = 0; i < N; i++)
{
CHECK(sha256hex(k[i].body) == wantSha());
CHECK(k[i].paused == pauses);
curl_multi_remove_handle(m, c[i]);
curl_easy_cleanup(c[i]);
}
curl_multi_cleanup(m);
return 0;
}
static CURLcode tlsCtx(CURL *, void *tls, void *)
{
std::vector<uint8_t> ca = slurp(g_dir + "/ca.der");
std::vector<uint8_t> cert = slurp(g_dir + "/cli.der");
std::vector<uint8_t> key = slurp(g_dir + "/cli.key.der");
static const unsigned suite = 0x0035; /* TLS_RSA_WITH_AES_256_CBC_SHA */
if (!curl_tls_add_trust((curl_tls_settings*)tls, ca.data(), ca.size())
|| !curl_tls_set_client((curl_tls_settings*)tls, cert.data(), cert.size(), key.data(), key.size()))
return CURLE_SSL_CERTPROBLEM;
curl_tls_set_suites((curl_tls_settings*)tls, &suite, 1);
return CURLE_OK;
}
static CURLcode tlsWrongCa(CURL *, void *tls, void *)
{
std::vector<uint8_t> other = slurp(g_dir + "/cli.der"); /* signed nothing the server shows */
curl_tls_add_trust((curl_tls_settings*)tls, other.data(), other.size());
return CURLE_OK;
}
static int tlsRefused()
{
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, url(true, "/body.bin").c_str());
curl_easy_setopt(c, CURLOPT_SSL_CTX_FUNCTION, tlsWrongCa);
const CURLcode r = curl_easy_perform(c);
curl_easy_cleanup(c);
CHECK(r == CURLE_SSL_CONNECT_ERROR);
return 0;
}
static int threads()
{
/* several threads at once, each with its own handle, as the IOSU
* services and the guest's nlibcurl workers do */
int fails = 0;
std::vector<std::thread> t;
for (int i = 0; i < 6; i++)
t.emplace_back([&fails, i] { if ((i & 1) ? postEcho(true) : getBody(false)) fails++; });
for (auto &x : t)
x.join();
CHECK(fails == 0);
return 0;
}
static int restart()
{
/* the transfer thread stops at curl_global_cleanup() and comes back */
CHECK(getBody(false) == 0);
curl_global_cleanup();
CHECK(getBody(false) == 0);
curl_global_cleanup();
curl_global_cleanup();
return 0;
}
int main(int argc, char **argv)
{
if (argc != 5)
return 2;
g_http = atoi(argv[2]);
g_https = atoi(argv[3]);
g_dir = argv[4];
const std::string t = argv[1];
int r = 2;
curl_global_init(CURL_GLOBAL_DEFAULT); /* as retro_init() does */
if (t == "get") r = getBody(false);
else if (t == "post") r = postEcho(false);
else if (t == "post-read") r = postEcho(true);
else if (t == "404") r = notFound();
else if (t == "form") r = form();
else if (t == "timeout") r = timeout();
else if (t == "multi") r = multi(0);
else if (t == "pause") r = multi(2);
else if (t == "tls") r = getBody(true, tlsCtx);
else if (t == "tls-refused") r = tlsRefused();
else if (t == "threads") r = threads();
else if (t == "restart") r = restart();
curl_global_cleanup();
return r;
}
+42
View File
@@ -0,0 +1,42 @@
#!/bin/sh
# curl_compat against local servers: GET, POST from a buffer, from a
# read callback and as a multipart form, a 404, a timeout, the multi interface with and without
# paused transfers, several threads at once, the transfer thread stopped
# and started again, and TLS with the client certificate and the
# RSA/AES-256-CBC suite nlibcurl asks for, against a server that requires
# both - and refused with a trust anchor that signed nothing. Needs
# python3 and openssl. RUNNER prefixes the test (wine for a MinGW build).
set -e
cd "$(dirname "$0")"
D=$(mktemp -d)
trap 'kill $HTTP $TLS 2>/dev/null || true; rm -rf $D' EXIT
make -s
RUN=${RUNNER:-}
# a case that hangs fails rather than holding the job
command -v timeout >/dev/null 2>&1 && RUN="timeout 120 $RUN"
EXE=${EXE:-}
head -c 1048579 /dev/urandom > $D/body.bin
python3 -c "import hashlib,sys; print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" $D/body.bin > $D/body.sha256
openssl req -x509 -newkey rsa:2048 -nodes -keyout $D/ca.key -out $D/ca.pem -days 2 -subj "/CN=test CA" 2>/dev/null
openssl req -newkey rsa:2048 -nodes -keyout $D/srv.key -out $D/srv.csr -subj "/CN=localhost" 2>/dev/null
printf 'subjectAltName=DNS:localhost\nbasicConstraints=CA:FALSE\n' > $D/srv.ext
openssl x509 -req -in $D/srv.csr -CA $D/ca.pem -CAkey $D/ca.key -CAcreateserial -out $D/srv.pem -days 2 -extfile $D/srv.ext 2>/dev/null
openssl x509 -in $D/ca.pem -outform DER -out $D/ca.der
openssl req -x509 -newkey rsa:2048 -nodes -keyout $D/cca.key -out $D/cca.pem -days 2 -subj "/CN=client CA" 2>/dev/null
openssl req -newkey rsa:2048 -nodes -keyout $D/cli.key -out $D/cli.csr -subj "/CN=client" 2>/dev/null
openssl x509 -req -in $D/cli.csr -CA $D/cca.pem -CAkey $D/cca.key -CAcreateserial -out $D/cli.pem -days 2 2>/dev/null
openssl x509 -in $D/cli.pem -outform DER -out $D/cli.der
openssl rsa -in $D/cli.key -traditional -outform DER -out $D/cli.key.der 2>/dev/null
HP=${HTTP_PORT:-45180}
TP=${TLS_PORT:-45443}
python3 server.py $HP $D & HTTP=$!
(cd $D && exec openssl s_server -accept $TP -cert srv.pem -key srv.key -tls1_2 -cipher AES256-SHA \
-Verify 1 -CAfile cca.pem -WWW) >/dev/null 2>&1 & TLS=$!
sleep 1
fail=0
for t in get post post-read form 404 timeout multi pause threads restart tls tls-refused; do
if $RUN ./curl_compat_test$EXE $t $HP $TP $D > $D/out 2>&1; then echo "ok: $t"
else echo "FAIL: $t: $(cat $D/out)"; fail=1; fi
done
[ $fail -eq 0 ] && echo "[pass] curl_compat"
exit $fail
+30
View File
@@ -0,0 +1,30 @@
# The test server: a file, an echo of a POST's hash, a 404 and a request
# that is never answered.
import hashlib, http.server, os, sys, time
port, root = int(sys.argv[1]), sys.argv[2]
class H(http.server.BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, *a): pass
def reply(self, code, body, ctype="application/octet-stream"):
self.send_response(code)
self.send_header("Content-Type", ctype)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
if self.path == "/body.bin":
with open(os.path.join(root, "body.bin"), "rb") as f:
self.reply(200, f.read())
elif self.path == "/stall":
time.sleep(30)
else:
self.reply(404, b"not here", "text/plain")
def do_POST(self):
n = int(self.headers.get("Content-Length", "0"))
data = self.rfile.read(n)
out = "%s %s %s" % (hashlib.sha256(data).hexdigest(), self.headers.get("X-Test"), self.headers.get("Content-Type"))
self.reply(200, out.encode(), "text/plain")
class S(http.server.ThreadingHTTPServer):
allow_reuse_address = True
daemon_threads = True
S(("127.0.0.1", port), H).serve_forever()
Vendored Submodule
+1
Submodule dependencies/fmt added at 407c905e45.
Vendored Submodule
+1
Submodule dependencies/glm added at 0af55ccecd.
Vendored Submodule
+1
Submodule dependencies/glslang added at 1062752a89.
-204
View File
@@ -1,204 +0,0 @@
cmake_minimum_required (VERSION 3.8)
project ("ih264d")
add_library (ih264d
"common/ih264_buf_mgr.c"
"common/ih264_buf_mgr.h"
"common/ih264_cabac_tables.c"
"common/ih264_cabac_tables.h"
"common/ih264_cavlc_tables.c"
"common/ih264_cavlc_tables.h"
"common/ih264_chroma_intra_pred_filters.c"
"common/ih264_common_tables.c"
"common/ih264_common_tables.h"
"common/ih264_deblk_edge_filters.c"
"common/ih264_deblk_edge_filters.h"
"common/ih264_deblk_tables.c"
"common/ih264_deblk_tables.h"
"common/ih264_debug.h"
"common/ih264_defs.h"
"common/ih264_disp_mgr.c"
"common/ih264_disp_mgr.h"
"common/ih264_dpb_mgr.c"
"common/ih264_dpb_mgr.h"
"common/ih264_error.h"
"common/ih264_ihadamard_scaling.c"
"common/ih264_inter_pred_filters.c"
"common/ih264_inter_pred_filters.h"
"common/ih264_intra_pred_filters.h"
"common/ih264_iquant_itrans_recon.c"
"common/ih264_list.c"
"common/ih264_list.h"
"common/ih264_luma_intra_pred_filters.c"
"common/ih264_macros.h"
"common/ih264_mem_fns.c"
"common/ih264_mem_fns.h"
"common/ih264_padding.c"
"common/ih264_padding.h"
"common/ih264_resi_trans.h"
"common/ih264_resi_trans_quant.c"
"common/ih264_size_defs.h"
"common/ih264_structs.h"
"common/ih264_trans_data.c"
"common/ih264_trans_data.h"
"common/ih264_trans_macros.h"
"common/ih264_trans_quant_itrans_iquant.h"
"common/ih264_typedefs.h"
"common/ih264_weighted_pred.c"
"common/ih264_weighted_pred.h"
"common/ithread.c"
"common/ithread.h"
"decoder/ih264d.h"
"decoder/ih264d_api.c"
"decoder/ih264d_bitstrm.c"
"decoder/ih264d_bitstrm.h"
"decoder/ih264d_cabac.c"
"decoder/ih264d_cabac.h"
"decoder/ih264d_cabac_init_tables.c"
"decoder/ih264d_compute_bs.c"
"decoder/ih264d_deblocking.c"
"decoder/ih264d_deblocking.h"
"decoder/ih264d_debug.h"
"decoder/ih264d_defs.h"
"decoder/ih264d_dpb_manager.h"
"decoder/ih264d_dpb_mgr.c"
"decoder/ih264d_error_handler.h"
"decoder/ih264d_format_conv.c"
"decoder/ih264d_format_conv.h"
"decoder/ih264d_function_selector.h"
"decoder/ih264d_function_selector_generic.c"
"decoder/ih264d_inter_pred.c"
"decoder/ih264d_inter_pred.h"
"decoder/ih264d_mb_utils.c"
"decoder/ih264d_mb_utils.h"
"decoder/ih264d_mem_request.h"
"decoder/ih264d_mvpred.c"
"decoder/ih264d_mvpred.h"
"decoder/ih264d_nal.c"
"decoder/ih264d_nal.h"
"decoder/ih264d_parse_bslice.c"
"decoder/ih264d_parse_cabac.c"
"decoder/ih264d_parse_cabac.h"
"decoder/ih264d_parse_cavlc.c"
"decoder/ih264d_parse_cavlc.h"
"decoder/ih264d_parse_headers.c"
"decoder/ih264d_parse_headers.h"
"decoder/ih264d_parse_islice.c"
"decoder/ih264d_parse_islice.h"
"decoder/ih264d_parse_mb_header.c"
"decoder/ih264d_parse_mb_header.h"
"decoder/ih264d_parse_pslice.c"
"decoder/ih264d_parse_slice.c"
"decoder/ih264d_parse_slice.h"
"decoder/ih264d_process_bslice.c"
"decoder/ih264d_process_bslice.h"
"decoder/ih264d_process_intra_mb.c"
"decoder/ih264d_process_intra_mb.h"
"decoder/ih264d_process_pslice.c"
"decoder/ih264d_process_pslice.h"
"decoder/ih264d_quant_scaling.c"
"decoder/ih264d_quant_scaling.h"
"decoder/ih264d_sei.c"
"decoder/ih264d_sei.h"
"decoder/ih264d_structs.h"
"decoder/ih264d_tables.c"
"decoder/ih264d_tables.h"
"decoder/ih264d_thread_compute_bs.c"
"decoder/ih264d_thread_compute_bs.h"
"decoder/ih264d_thread_parse_decode.c"
"decoder/ih264d_thread_parse_decode.h"
"decoder/ih264d_transfer_address.h"
"decoder/ih264d_utils.c"
"decoder/ih264d_utils.h"
"decoder/ih264d_vui.c"
"decoder/ih264d_vui.h"
"decoder/iv.h"
"decoder/ivd.h"
)
if (CMAKE_OSX_ARCHITECTURES)
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE
"common/x86/ih264_chroma_intra_pred_filters_ssse3.c"
"common/x86/ih264_deblk_chroma_ssse3.c"
"common/x86/ih264_deblk_luma_ssse3.c"
"common/x86/ih264_ihadamard_scaling_sse42.c"
"common/x86/ih264_ihadamard_scaling_ssse3.c"
"common/x86/ih264_inter_pred_filters_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_dc_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_sse42.c"
"common/x86/ih264_iquant_itrans_recon_ssse3.c"
"common/x86/ih264_luma_intra_pred_filters_ssse3.c"
"common/x86/ih264_mem_fns_ssse3.c"
"common/x86/ih264_padding_ssse3.c"
"common/x86/ih264_platform_macros.h"
"common/x86/ih264_resi_trans_quant_sse42.c"
"common/x86/ih264_weighted_pred_sse42.c"
"decoder/x86/ih264d_function_selector.c"
"decoder/x86/ih264d_function_selector_sse42.c"
"decoder/x86/ih264d_function_selector_ssse3.c"
)
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
enable_language( C CXX ASM )
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
target_sources(ih264d PRIVATE
"common/armv8/ih264_deblk_chroma_av8.s"
"common/armv8/ih264_deblk_luma_av8.s"
"common/armv8/ih264_default_weighted_pred_av8.s"
"common/armv8/ih264_ihadamard_scaling_av8.s"
"common/armv8/ih264_inter_pred_chroma_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_horz_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_vert_av8.s"
"common/armv8/ih264_inter_pred_luma_copy_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_vert_qpel_av8.s"
"common/armv8/ih264_intra_pred_chroma_av8.s"
"common/armv8/ih264_intra_pred_luma_16x16_av8.s"
"common/armv8/ih264_intra_pred_luma_4x4_av8.s"
"common/armv8/ih264_intra_pred_luma_8x8_av8.s"
"common/armv8/ih264_iquant_itrans_recon_av8.s"
"common/armv8/ih264_iquant_itrans_recon_dc_av8.s"
"common/armv8/ih264_mem_fns_neon_av8.s"
"common/armv8/ih264_neon_macros.s"
"common/armv8/ih264_padding_neon_av8.s"
"common/armv8/ih264_platform_macros.h"
"common/armv8/ih264_resi_trans_quant_av8.s"
"common/armv8/ih264_weighted_bi_pred_av8.s"
"common/armv8/ih264_weighted_pred_av8.s"
"decoder/arm/ih264d_function_selector_a9q.c"
"decoder/arm/ih264d_function_selector_av8.c"
"decoder/arm/ih264d_function_selector.c"
)
target_compile_options(ih264d PRIVATE -DARMV8 $<$<COMPILE_LANGUAGE:ASM,Clang>:-Wno-unused-command-line-argument>)
if(NOT MSVC)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
endif()
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
else()
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
endif()
if(MSVC)
set_property(TARGET ih264d PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# tune settings for slightly better performance
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Oi>) # enable intrinsic functions
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Ot>) # favor speed
target_compile_options(ih264d PRIVATE "/GS-") # disable runtime checks
endif()
-26
View File
@@ -1,26 +0,0 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ]
}
]
}
@@ -0,0 +1,117 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_posix_string.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/posix_string.h>
#ifdef _WIN32
#undef strcasecmp
#undef strdup
#undef isblank
#undef strtok_r
#include <ctype.h>
#include <stdlib.h>
#include <stddef.h>
#include <compat/strl.h>
#include <string.h>
/* ASCII case folding, not tolower.
*
* tolower takes an int that must be representable as unsigned char or
* EOF. A plain char sign-extends any byte above 0x7F into a negative
* value, so passing one straight in is undefined - and these are
* compared on file paths and user text, where such bytes are ordinary.
* glibc happens to tolerate it because its table is offset for exactly
* this mistake; nothing guarantees the next libc will.
*
* Folding only A-Z is also what the callers mean. Anything wider makes
* a comparison depend on the user's locale, which for extensions,
* driver names and identifiers is not wanted. */
#define COMPAT_LOWER(c) \
(((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c))
int retro_strcasecmp__(const char *a, const char *b)
{
while (*a && *b)
{
int a_ = COMPAT_LOWER((unsigned char)*a);
int b_ = COMPAT_LOWER((unsigned char)*b);
if (a_ != b_)
return a_ - b_;
a++;
b++;
}
return COMPAT_LOWER((unsigned char)*a) - COMPAT_LOWER((unsigned char)*b);
}
char *retro_strdup__(const char *orig)
{
size_t _len = strlen(orig) + 1;
char *ret = (char*)malloc(_len);
if (!ret)
return NULL;
memcpy(ret, orig, _len);
return ret;
}
int retro_isblank__(int c)
{
return (c == ' ') || (c == '\t');
}
char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
{
char *first = NULL;
if (!saveptr || !delim)
return NULL;
if (str)
*saveptr = str;
do
{
char *ptr = NULL;
first = *saveptr;
while (*first && strchr(delim, *first))
*first++ = '\0';
if (*first == '\0')
return NULL;
ptr = first + 1;
while (*ptr && !strchr(delim, *ptr))
ptr++;
*saveptr = ptr + (*ptr ? 1 : 0);
*ptr = '\0';
} while (strlen(first) == 0);
return first;
}
#endif
+99
View File
@@ -0,0 +1,99 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strcasestr.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <ctype.h>
#include <compat/strcasestr.h>
/* ASCII case folding, done here rather than through tolower.
*
* tolower takes an int whose value must be representable as unsigned
* char or EOF; passing a plain char sign-extends anything above 0x7F
* into a negative value, which is undefined. This is called on
* arbitrary bytes - file paths, HTTP responses, config text - so that
* was reachable. Folding only A-Z is also what every caller here
* means: extensions, window manager names, config keys and header
* fields are all ASCII, and locale-dependent folding of anything else
* would make the result depend on the user's locale. */
#define STRCASESTR_LOWER(c) \
(((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c))
/* Pretty much strncasecmp. */
static int casencmp(const char *a, const char *b, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
{
int a_lower = STRCASESTR_LOWER((unsigned char)a[i]);
int b_lower = STRCASESTR_LOWER((unsigned char)b[i]);
if (a_lower != b_lower)
return a_lower - b_lower;
}
return 0;
}
/* Let memchr find the candidate positions.
*
* Comparing at every offset means folding every byte of the haystack,
* which measured some sixty times slower than strstr on the same
* inputs - enough to matter where this is called per file over a
* directory. Only the first character of the needle decides whether
* an offset is worth examining, and memchr finds those far faster than
* a byte loop can, so it does the skipping and the fold only runs
* where a match is actually possible. A needle whose first character
* has no case of its own - a digit, a punctuation mark - costs one
* memchr rather than two. */
char *compat_strcasestr(const char *haystack, const char *needle)
{
size_t nlen = strlen(needle);
size_t hlen = strlen(haystack);
const char *end = haystack + hlen;
int lo;
int up;
if (!nlen)
return (char*)haystack;
if (nlen > hlen)
return NULL;
lo = STRCASESTR_LOWER((unsigned char)needle[0]);
up = (lo >= 'a' && lo <= 'z') ? (lo - ('a' - 'A')) : lo;
while ((size_t)(end - haystack) >= nlen)
{
size_t left = (size_t)(end - haystack);
const char *a = (const char*)memchr(haystack, lo, left);
const char *b = (up != lo)
? (const char*)memchr(haystack, up, left) : NULL;
const char *p = !a ? b : (!b ? a : (a < b ? a : b));
if (!p || (size_t)(end - p) < nlen)
return NULL;
if (!casencmp(p + 1, needle + 1, nlen - 1))
return (char*)p;
haystack = p + 1;
}
return NULL;
}
+71
View File
@@ -0,0 +1,71 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strl.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <compat/strl.h>
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
#if !(defined(__MACH__) && defined(__APPLE__))
size_t strlcpy(char *s, const char *in, size_t len)
{
size_t src_len = strlen(in);
#ifdef LIBRETRO_STRL_CHECK_OVERLAP
/* Test builds: fail the way macOS's fortified libc does on an
* overlapping copy (__chk_fail_overlap), so the Linux test suites
* catch what only Apple's runtime would otherwise catch. */
if (len && src_len)
{
const char *s_end = s + (src_len < len - 1 ? src_len : len - 1) + 1;
const char *in_end = in + src_len + 1;
if (!(s_end <= in || in_end <= s))
{
fprintf(stderr, "strlcpy: overlapping copy (dst %p, src %p, %u bytes)\n",
(void*)s, (const void*)in, (unsigned)src_len);
abort();
}
}
#endif
if (len)
{
size_t cpy_len = src_len < len - 1 ? src_len : len - 1;
memcpy(s, in, cpy_len);
s[cpy_len] = '\0';
}
return src_len;
}
/* The destination scan is bounded by 'len': 's' is not required to
* contain a NUL within the first 'len' bytes. When it does not, no
* bytes are written and the return value is len + strlen(source),
* matching OpenBSD, Darwin and glibc. */
size_t strlcat(char *s, const char *source, size_t len)
{
const char *nul = (const char*)memchr(s, 0, len);
size_t dst_len = nul ? (size_t)(nul - s) : len;
if (dst_len == len)
return len + strlen(source);
return dst_len + strlcpy(s + dst_len, source, len - dst_len);
}
#endif
+149
View File
@@ -0,0 +1,149 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fopen_utf8.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <compat/fopen_utf8.h>
#include <encodings/utf.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <boolean.h>
#if defined(__WINRT__)
#include <stdint.h>
#include <windows.h>
#include <fileapifromapp.h>
#include <io.h>
#include <fcntl.h>
#endif
#ifdef _WIN32
#undef fopen
#if defined(LEGACY_WIN32) || defined(LEGACY_WIN32_RUNTIME)
static void *fopen_utf8_ansi(const char * filename, const char * mode)
{
char * filename_local = utf8_to_local_string_alloc(filename);
if (filename_local)
{
FILE *ret = fopen(filename_local, mode);
free(filename_local);
return ret;
}
return NULL;
}
#endif
#if !defined(LEGACY_WIN32) || defined(LEGACY_WIN32_RUNTIME)
static void *fopen_utf8_wide(const char * filename, const char * mode)
{
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
if (filename_w)
{
FILE *ret = NULL;
#if defined(__WINRT__)
HANDLE file_handle = INVALID_HANDLE_VALUE;
DWORD desired_access = 0;
DWORD creation_disposition = OPEN_EXISTING;
int open_flags = O_BINARY;
int fd = -1;
bool append = mode && strchr(mode, 'a');
bool write = mode && strchr(mode, 'w');
bool plus = mode && strchr(mode, '+');
wchar_t *path = filename_w;
while (*path)
{
if (*path == L'/')
*path = L'\\';
path++;
}
if (mode && strchr(mode, 'r'))
desired_access |= GENERIC_READ;
if (write || append || plus)
desired_access |= GENERIC_WRITE;
if (plus)
desired_access |= GENERIC_READ;
if (append)
creation_disposition = OPEN_ALWAYS;
else if (write)
creation_disposition = CREATE_ALWAYS;
if (plus)
open_flags |= O_RDWR;
else if (append || write)
open_flags |= O_WRONLY;
else
open_flags |= O_RDONLY;
if (append)
open_flags |= O_APPEND;
if (write || append)
open_flags |= O_CREAT;
if (write)
open_flags |= O_TRUNC;
file_handle = CreateFile2FromAppW(filename_w, desired_access,
FILE_SHARE_READ | FILE_SHARE_WRITE,
creation_disposition, NULL);
if (file_handle != INVALID_HANDLE_VALUE)
{
fd = _open_osfhandle((intptr_t)file_handle, open_flags);
if (fd != -1)
ret = _fdopen(fd, mode);
if (!ret)
{
if (fd != -1)
_close(fd);
else
CloseHandle(file_handle);
}
}
#else
wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
if (mode_w)
{
ret = _wfopen(filename_w, mode_w);
free(mode_w);
}
#endif
free(filename_w);
return ret;
}
return NULL;
}
#endif
void *fopen_utf8(const char * filename, const char * mode)
{
#if defined(LEGACY_WIN32_RUNTIME)
if (win32_needs_local_encoding())
return fopen_utf8_ansi(filename, mode);
return fopen_utf8_wide(filename, mode);
#elif defined(LEGACY_WIN32)
return fopen_utf8_ansi(filename, mode);
#else
return fopen_utf8_wide(filename, mode);
#endif
}
#endif
File diff suppressed because it is too large. Load diff
+914
View File
@@ -0,0 +1,914 @@
/* Copyright (C) 2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (kdf.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <crypto/kdf.h>
#include <crypto/crypto.h>
#include <retro_miscellaneous.h>
#include <encodings/utf.h>
void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
const uint8_t *key, size_t key_len)
{
unsigned i;
uint8_t k[64];
memset(k, 0, sizeof(k));
if (key_len > 64)
{
struct sha256_state s;
sha256_stream_init(&s, 0);
sha256_stream_update(&s, key, key_len);
sha256_stream_final(&s, k);
crypto_memzero(&s, sizeof(s));
}
else
memcpy(k, key, key_len);
for (i = 0; i < 64; i++)
k[i] ^= 0x36;
sha256_stream_init(&ctx->inner, 0);
sha256_stream_update(&ctx->inner, k, 64);
for (i = 0; i < 64; i++)
k[i] ^= 0x36 ^ 0x5c;
sha256_stream_init(&ctx->outer, 0);
sha256_stream_update(&ctx->outer, k, 64);
crypto_memzero(k, sizeof(k));
}
void hmac_sha256_update(struct hmac_sha256_ctx *ctx,
const uint8_t *data, size_t len)
{
sha256_stream_update(&ctx->inner, data, len);
}
void hmac_sha256_final(struct hmac_sha256_ctx *ctx, uint8_t *mac)
{
uint8_t ih[32];
sha256_stream_final(&ctx->inner, ih);
sha256_stream_update(&ctx->outer, ih, 32);
sha256_stream_final(&ctx->outer, mac);
crypto_memzero(ih, sizeof(ih));
crypto_memzero(ctx, sizeof(*ctx));
}
void hmac_sha256(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac)
{
struct hmac_sha256_ctx ctx;
hmac_sha256_init(&ctx, key, key_len);
hmac_sha256_update(&ctx, data, len);
hmac_sha256_final(&ctx, mac);
}
/* HMAC-SHA1: what Kerberos aes*-cts-hmac-sha1-96 (RFC 3962) keys its
* checksums and its string-to-key PBKDF2 with. Same shape as the
* SHA-256 one above. */
void hmac_sha1_init(struct hmac_sha1_ctx *ctx,
const uint8_t *key, size_t key_len)
{
unsigned i;
uint8_t k[64];
memset(k, 0, sizeof(k));
if (key_len > 64)
SHA1Digest(key, key_len, k);
else
memcpy(k, key, key_len);
for (i = 0; i < 64; i++)
k[i] ^= 0x36;
sha1_stream_init(&ctx->inner);
sha1_stream_update(&ctx->inner, k, 64);
for (i = 0; i < 64; i++)
k[i] ^= 0x36 ^ 0x5c;
sha1_stream_init(&ctx->outer);
sha1_stream_update(&ctx->outer, k, 64);
crypto_memzero(k, sizeof(k));
}
void hmac_sha1_update(struct hmac_sha1_ctx *ctx,
const uint8_t *data, size_t len)
{
sha1_stream_update(&ctx->inner, data, len);
}
void hmac_sha1_final(struct hmac_sha1_ctx *ctx, uint8_t *mac)
{
uint8_t ih[20];
sha1_stream_final(&ctx->inner, ih);
sha1_stream_update(&ctx->outer, ih, 20);
sha1_stream_final(&ctx->outer, mac);
crypto_memzero(ih, sizeof(ih));
crypto_memzero(ctx, sizeof(*ctx));
}
void hmac_sha1(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac)
{
struct hmac_sha1_ctx ctx;
hmac_sha1_init(&ctx, key, key_len);
hmac_sha1_update(&ctx, data, len);
hmac_sha1_final(&ctx, mac);
}
int pbkdf2_hmac_sha1(const uint8_t *password, size_t password_len,
const uint8_t *salt, size_t salt_len, uint32_t iterations,
uint8_t *out, size_t out_len)
{
/* as pbkdf2_hmac_sha256: keyed once, state copied per block */
struct pbkdf2_sha1_work
{
struct hmac_sha1_ctx base;
struct hmac_sha1_ctx ctx;
} *w;
uint8_t u[20];
uint8_t t[20];
uint8_t ibe[4];
uint32_t block = 0;
size_t done = 0;
if (!iterations)
return -1;
if (!(w = (struct pbkdf2_sha1_work*)malloc(sizeof(*w))))
return -1;
hmac_sha1_init(&w->base, password, password_len);
while (done < out_len)
{
uint32_t i;
unsigned j;
size_t take;
block++;
crypto_store32_be(ibe, block);
memcpy(&w->ctx, &w->base, sizeof(w->ctx));
hmac_sha1_update(&w->ctx, salt, salt_len);
hmac_sha1_update(&w->ctx, ibe, 4);
hmac_sha1_final(&w->ctx, u);
memcpy(t, u, 20);
for (i = 1; i < iterations; i++)
{
memcpy(&w->ctx, &w->base, sizeof(w->ctx));
hmac_sha1_update(&w->ctx, u, 20);
hmac_sha1_final(&w->ctx, u);
for (j = 0; j < 20; j++)
t[j] ^= u[j];
}
take = out_len - done;
if (take > 20)
take = 20;
memcpy(out + done, t, take);
done += take;
}
crypto_memzero(w, sizeof(*w));
free(w);
crypto_memzero(u, sizeof(u));
crypto_memzero(t, sizeof(t));
return 0;
}
void hmac_sha384_init(struct hmac_sha384_ctx *ctx,
const uint8_t *key, size_t key_len)
{
unsigned i;
uint8_t k[128];
memset(k, 0, sizeof(k));
if (key_len > 128)
{
struct sha512_state s;
sha512_stream_init(&s, 1);
sha512_stream_update(&s, key, key_len);
sha512_stream_final(&s, k);
}
else
memcpy(k, key, key_len);
for (i = 0; i < 128; i++)
k[i] ^= 0x36;
sha512_stream_init(&ctx->inner, 1);
sha512_stream_update(&ctx->inner, k, 128);
for (i = 0; i < 128; i++)
k[i] ^= 0x36 ^ 0x5c;
sha512_stream_init(&ctx->outer, 1);
sha512_stream_update(&ctx->outer, k, 128);
crypto_memzero(k, sizeof(k));
}
void hmac_sha384_update(struct hmac_sha384_ctx *ctx,
const uint8_t *data, size_t len)
{
sha512_stream_update(&ctx->inner, data, len);
}
void hmac_sha384_final(struct hmac_sha384_ctx *ctx, uint8_t *mac)
{
uint8_t ih[48];
sha512_stream_final(&ctx->inner, ih);
sha512_stream_update(&ctx->outer, ih, 48);
sha512_stream_final(&ctx->outer, mac);
crypto_memzero(ih, sizeof(ih));
crypto_memzero(ctx, sizeof(*ctx));
}
void hkdf_sha256_extract(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len, uint8_t *prk)
{
static const uint8_t zero[32] = { 0 };
if (!salt || !salt_len)
{
salt = zero;
salt_len = sizeof(zero);
}
hmac_sha256(salt, salt_len, ikm, ikm_len, prk);
}
int hkdf_sha256_expand(const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len)
{
struct hmac_sha256_ctx ctx;
uint8_t t[32];
size_t tlen = 0;
size_t done = 0;
uint8_t n = 0;
if (okm_len > 255 * 32)
return -1;
while (done < okm_len)
{
size_t take;
n++;
hmac_sha256_init(&ctx, prk, prk_len);
hmac_sha256_update(&ctx, t, tlen);
hmac_sha256_update(&ctx, info, info_len);
hmac_sha256_update(&ctx, &n, 1);
hmac_sha256_final(&ctx, t);
tlen = 32;
take = okm_len - done;
if (take > 32)
take = 32;
memcpy(okm + done, t, take);
done += take;
}
crypto_memzero(t, sizeof(t));
return 0;
}
void hmac_sha384(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac)
{
struct hmac_sha384_ctx ctx;
hmac_sha384_init(&ctx, key, key_len);
hmac_sha384_update(&ctx, data, len);
hmac_sha384_final(&ctx, mac);
}
/* HKDF over SHA-384, for the TLS 1.3 SHA-384 suite. */
void hkdf_sha384_extract(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len, uint8_t *prk)
{
static const uint8_t zero[48] = { 0 };
if (!salt || !salt_len)
{
salt = zero;
salt_len = sizeof(zero);
}
hmac_sha384(salt, salt_len, ikm, ikm_len, prk);
}
int hkdf_sha384_expand(const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len)
{
struct hmac_sha384_ctx ctx;
uint8_t t[48];
size_t tlen = 0;
size_t done = 0;
uint8_t n = 0;
if (okm_len > 255 * 48)
return -1;
while (done < okm_len)
{
size_t take;
n++;
hmac_sha384_init(&ctx, prk, prk_len);
hmac_sha384_update(&ctx, t, tlen);
hmac_sha384_update(&ctx, info, info_len);
hmac_sha384_update(&ctx, &n, 1);
hmac_sha384_final(&ctx, t);
tlen = 48;
take = okm_len - done;
if (take > 48)
take = 48;
memcpy(okm + done, t, take);
done += take;
}
crypto_memzero(t, sizeof(t));
return 0;
}
int hkdf_sha256(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len)
{
uint8_t prk[32];
int ret;
hkdf_sha256_extract(salt, salt_len, ikm, ikm_len, prk);
ret = hkdf_sha256_expand(prk, sizeof(prk), info, info_len, okm, okm_len);
crypto_memzero(prk, sizeof(prk));
return ret;
}
int pbkdf2_hmac_sha256(const uint8_t *password, size_t password_len,
const uint8_t *salt, size_t salt_len, uint32_t iterations,
uint8_t *out, size_t out_len)
{
/* The password-keyed HMAC is the same for every block and every
* iteration; key it once and copy the keyed state. Two HMAC
* contexts are 1.5 KiB, most of a small console thread stack, so
* they live on the heap for the duration - a single allocation
* against thousands of compression rounds. */
struct pbkdf2_work
{
struct hmac_sha256_ctx base;
struct hmac_sha256_ctx ctx;
} *w;
uint8_t u[32];
uint8_t t[32];
uint8_t ibe[4];
uint32_t block = 0;
size_t done = 0;
if (!iterations)
return -1;
if (!(w = (struct pbkdf2_work*)malloc(sizeof(*w))))
return -1;
hmac_sha256_init(&w->base, password, password_len);
while (done < out_len)
{
uint32_t i;
unsigned j;
size_t take;
block++;
crypto_store32_be(ibe, block);
memcpy(&w->ctx, &w->base, sizeof(w->ctx));
hmac_sha256_update(&w->ctx, salt, salt_len);
hmac_sha256_update(&w->ctx, ibe, 4);
hmac_sha256_final(&w->ctx, u);
memcpy(t, u, 32);
for (i = 1; i < iterations; i++)
{
memcpy(&w->ctx, &w->base, sizeof(w->ctx));
hmac_sha256_update(&w->ctx, u, 32);
hmac_sha256_final(&w->ctx, u);
for (j = 0; j < 32; j++)
t[j] ^= u[j];
}
take = out_len - done;
if (take > 32)
take = 32;
memcpy(out + done, t, take);
done += take;
}
crypto_memzero(w, sizeof(*w));
free(w);
crypto_memzero(u, sizeof(u));
crypto_memzero(t, sizeof(t));
return 0;
}
#if defined(_XBOX)
#include <xtl.h>
#elif defined(_WIN32) && !defined(__WINRT__)
#include <windows.h>
#elif defined(__WINRT__)
#include <windows.h>
#include <bcrypt.h>
#elif defined(_3DS)
#include <3ds/types.h>
#include <3ds/services/ps.h>
#elif defined(VITA)
#include <psp2/kernel/rng.h>
#elif defined(HAVE_LIBNX)
#include <switch.h>
#elif defined(PSP) || defined(WIIU) || defined(PS2) || defined(GEKKO) || defined(__PS3__) || defined(__PSL1GHT__)
#include <features/features_cpu.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#if defined(__linux__)
#include <sys/syscall.h>
/* Hidden by unistd.h under -ansi; same declaration features_cpu.c
* carries for the same reason. */
extern long syscall(long number, ...);
#endif
#endif
#if defined(_WIN32) && !defined(__WINRT__) && !defined(_XBOX)
/* Both are advapi32 exports; resolved at run time so that neither the
* link line nor the oldest SDK the tree targets (_WIN32_WINNT=0x0400)
* has to know about them. RtlGenRandom (XP and later) first, the
* CryptoAPI provider as the fallback for NT4 / 9x era hosts. */
typedef BOOLEAN (WINAPI *rtl_gen_random_t)(PVOID, ULONG);
typedef BOOL (WINAPI *crypt_acquire_context_t)(void**, const char*, const char*, DWORD, DWORD);
typedef BOOL (WINAPI *crypt_gen_random_t)(void*, DWORD, BYTE*);
typedef BOOL (WINAPI *crypt_release_context_t)(void*, DWORD);
#define CRYPTO_PROV_RSA_FULL 1
#define CRYPTO_CRYPT_VERIFYCONTEXT 0xF0000000
#define CRYPTO_CRYPT_SILENT 0x00000040
static int crypto_random_win32(uint8_t *buf, size_t len)
{
HMODULE advapi = GetModuleHandleA("advapi32.dll");
rtl_gen_random_t gen;
crypt_acquire_context_t acquire;
crypt_gen_random_t cgen;
crypt_release_context_t release;
void *prov = NULL;
int ret = -1;
if (!advapi)
advapi = LoadLibraryA("advapi32.dll");
if (!advapi)
return -1;
gen = (rtl_gen_random_t)GetProcAddress(advapi, "SystemFunction036");
if (gen)
{
/* ULONG per call; loop for the pathological caller. */
while (len)
{
ULONG chunk = (len > 0x7fffffff) ? 0x7fffffff : (ULONG)len;
if (!gen(buf, chunk))
break;
buf += chunk;
len -= chunk;
}
if (!len)
return 0;
}
acquire = (crypt_acquire_context_t)GetProcAddress(advapi, "CryptAcquireContextA");
cgen = (crypt_gen_random_t)GetProcAddress(advapi, "CryptGenRandom");
release = (crypt_release_context_t)GetProcAddress(advapi, "CryptReleaseContext");
if (!acquire || !cgen || !release)
return -1;
if (!acquire(&prov, NULL, NULL, CRYPTO_PROV_RSA_FULL,
CRYPTO_CRYPT_VERIFYCONTEXT | CRYPTO_CRYPT_SILENT))
return -1;
if (cgen(prov, (DWORD)len, buf))
ret = 0;
release(prov, 0);
return ret;
}
#endif
int crypto_random_bytes(uint8_t *buf, size_t len)
{
if (!len)
return 0;
#if defined(_WIN32) && !defined(__WINRT__) && !defined(_XBOX)
return crypto_random_win32(buf, len);
#elif defined(__WINRT__)
return (BCryptGenRandom(NULL, buf, (ULONG)len,
BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0) ? 0 : -1;
#elif defined(_3DS)
PS_GenerateRandomBytes(buf, len);
return 0;
#elif defined(VITA)
while (len)
{
size_t chunk = (len > 64) ? 64 : len;
if (sceKernelGetRandomNumber(buf, chunk) < 0)
return -1;
buf += chunk;
len -= chunk;
}
return 0;
#elif defined(HAVE_LIBNX)
randomGet(buf, len);
return 0;
#elif defined(PSP) || defined(WIIU) || defined(PS2) || defined(GEKKO) || defined(__PS3__) || defined(__PSL1GHT__) || defined(_XBOX)
/* No kernel RNG on these; stir the clock, a call counter and two
* stack/heap addresses through HKDF. Weak, and the only thing
* available. The keychain does not rest on it: its nonces only
* have to be unique per install, which a counter-mixed clock is. */
{
static uint32_t calls = 0;
uint8_t seed[32];
uint64_t t;
#if defined(_XBOX)
t = (uint64_t)GetTickCount();
#else
t = (uint64_t)cpu_features_get_time_usec();
#endif
calls++;
crypto_store64_le(seed, t);
crypto_store32_le(seed + 8, calls);
crypto_store64_le(seed + 12, (uint64_t)(uintptr_t)buf);
crypto_store64_le(seed + 20, (uint64_t)(uintptr_t)&t);
crypto_store32_le(seed + 28, (uint32_t)len);
return hkdf_sha256(NULL, 0, seed, sizeof(seed),
(const uint8_t*)"rng", 3, buf, len);
}
#else
{
size_t off = 0;
#if defined(__linux__) && defined(SYS_getrandom)
/* getrandom() first: works in a chroot or sandbox with no
* /dev. EINTR is retried, anything else falls through. */
while (off < len)
{
long r = syscall(SYS_getrandom, buf + off, len - off, 0);
if (r < 0)
{
if (errno == EINTR)
continue;
break;
}
off += (size_t)r;
}
if (off == len)
return 0;
off = 0;
#endif
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return -1;
while (off < len)
{
ssize_t r = read(fd, buf + off, len - off);
if (r < 0)
{
if (errno == EINTR)
continue;
break;
}
if (r == 0)
break;
off += (size_t)r;
}
close(fd);
return (off == len) ? 0 : -1;
}
}
#endif
}
static int drbg_mix(struct drbg_ctx *ctx, const uint8_t *fresh, size_t fresh_len,
const uint8_t *extra, size_t extra_len)
{
/* new key || nonce = HKDF(salt = old key, ikm = fresh || extra) */
uint8_t prk[32];
uint8_t okm[DRBG_KEY_SIZE + DRBG_NONCE_SIZE];
struct hmac_sha256_ctx h;
hmac_sha256_init(&h, ctx->key, sizeof(ctx->key));
hmac_sha256_update(&h, fresh, fresh_len);
if (extra && extra_len)
hmac_sha256_update(&h, extra, extra_len);
hmac_sha256_final(&h, prk);
if (hkdf_sha256_expand(prk, sizeof(prk),
(const uint8_t*)"drbg", 4, okm, sizeof(okm)) != 0)
return -1;
memcpy(ctx->key, okm, DRBG_KEY_SIZE);
memcpy(ctx->nonce, okm + DRBG_KEY_SIZE, DRBG_NONCE_SIZE);
ctx->counter = 0;
ctx->since_reseed = 0;
ctx->seeded = 1;
crypto_memzero(prk, sizeof(prk));
crypto_memzero(okm, sizeof(okm));
return 0;
}
int drbg_reseed(struct drbg_ctx *ctx, const uint8_t *extra, size_t extra_len)
{
uint8_t fresh[48];
int ret;
if (crypto_random_bytes(fresh, sizeof(fresh)) != 0)
return -1;
ret = drbg_mix(ctx, fresh, sizeof(fresh), extra, extra_len);
crypto_memzero(fresh, sizeof(fresh));
return ret;
}
int drbg_init(struct drbg_ctx *ctx, const uint8_t *seed, size_t seed_len)
{
memset(ctx, 0, sizeof(*ctx));
if (seed)
return drbg_mix(ctx, seed, seed_len, NULL, 0);
return drbg_reseed(ctx, NULL, 0);
}
int drbg_generate(struct drbg_ctx *ctx, uint8_t *out, size_t len)
{
uint8_t block[CHACHA20_BLOCK_SIZE];
if (!ctx->seeded)
return -1;
if (ctx->since_reseed + len > DRBG_RESEED_INTERVAL)
if (drbg_reseed(ctx, NULL, 0) != 0)
return -1;
ctx->since_reseed += len;
/* First block of every call is the next key (fast key erasure),
* output starts at the block after it. */
chacha20_block(ctx->key, ctx->nonce, ctx->counter, block);
ctx->counter++;
memcpy(ctx->key, block, DRBG_KEY_SIZE);
while (len)
{
size_t n = (len < CHACHA20_BLOCK_SIZE) ? len : CHACHA20_BLOCK_SIZE;
chacha20_block(ctx->key, ctx->nonce, ctx->counter, block);
ctx->counter++;
memcpy(out, block, n);
out += n;
len -= n;
}
/* Ratchet again so the key that produced this output is gone. */
chacha20_block(ctx->key, ctx->nonce, ctx->counter, block);
ctx->counter++;
memcpy(ctx->key, block, DRBG_KEY_SIZE);
crypto_memzero(block, sizeof(block));
return 0;
}
void drbg_free(struct drbg_ctx *ctx)
{
crypto_memzero(ctx, sizeof(*ctx));
}
/* ---- MD4 (RFC 1320) ---------------------------------------------- */
#define MD4_F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define MD4_G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define MD4_H(x, y, z) ((x) ^ (y) ^ (z))
static void md4_block(uint32_t *st, const uint8_t *p)
{
uint32_t x[16];
uint32_t a = st[0], b = st[1], c = st[2], d = st[3];
unsigned i;
for (i = 0; i < 16; i++)
x[i] = crypto_load32_le(p + 4 * i);
#define R1(a, b, c, d, k, s) a = crypto_rotl32(a + MD4_F(b, c, d) + x[k], s)
#define R2(a, b, c, d, k, s) a = crypto_rotl32(a + MD4_G(b, c, d) + x[k] + 0x5a827999u, s)
#define R3(a, b, c, d, k, s) a = crypto_rotl32(a + MD4_H(b, c, d) + x[k] + 0x6ed9eba1u, s)
R1(a,b,c,d, 0,3); R1(d,a,b,c, 1,7); R1(c,d,a,b, 2,11); R1(b,c,d,a, 3,19);
R1(a,b,c,d, 4,3); R1(d,a,b,c, 5,7); R1(c,d,a,b, 6,11); R1(b,c,d,a, 7,19);
R1(a,b,c,d, 8,3); R1(d,a,b,c, 9,7); R1(c,d,a,b,10,11); R1(b,c,d,a,11,19);
R1(a,b,c,d,12,3); R1(d,a,b,c,13,7); R1(c,d,a,b,14,11); R1(b,c,d,a,15,19);
R2(a,b,c,d, 0,3); R2(d,a,b,c, 4,5); R2(c,d,a,b, 8,9); R2(b,c,d,a,12,13);
R2(a,b,c,d, 1,3); R2(d,a,b,c, 5,5); R2(c,d,a,b, 9,9); R2(b,c,d,a,13,13);
R2(a,b,c,d, 2,3); R2(d,a,b,c, 6,5); R2(c,d,a,b,10,9); R2(b,c,d,a,14,13);
R2(a,b,c,d, 3,3); R2(d,a,b,c, 7,5); R2(c,d,a,b,11,9); R2(b,c,d,a,15,13);
R3(a,b,c,d, 0,3); R3(d,a,b,c, 8,9); R3(c,d,a,b, 4,11); R3(b,c,d,a,12,15);
R3(a,b,c,d, 2,3); R3(d,a,b,c,10,9); R3(c,d,a,b, 6,11); R3(b,c,d,a,14,15);
R3(a,b,c,d, 1,3); R3(d,a,b,c, 9,9); R3(c,d,a,b, 5,11); R3(b,c,d,a,13,15);
R3(a,b,c,d, 3,3); R3(d,a,b,c,11,9); R3(c,d,a,b, 7,11); R3(b,c,d,a,15,15);
#undef R1
#undef R2
#undef R3
st[0] += a; st[1] += b; st[2] += c; st[3] += d;
crypto_memzero(x, sizeof(x));
}
void md4(const uint8_t *data, size_t len, uint8_t *digest)
{
uint32_t st[4];
uint8_t tail[128];
size_t rem, off = 0, i;
uint64_t bits = (uint64_t)len * 8;
st[0] = 0x67452301u; st[1] = 0xefcdab89u;
st[2] = 0x98badcfeu; st[3] = 0x10325476u;
while (len - off >= 64)
{
md4_block(st, data + off);
off += 64;
}
rem = len - off;
memset(tail, 0, sizeof(tail));
memcpy(tail, data + off, rem);
tail[rem] = 0x80;
i = (rem < 56) ? 56 : 120;
crypto_store64_le(tail + i, bits);
md4_block(st, tail);
if (i == 120)
md4_block(st, tail + 64);
for (i = 0; i < 4; i++)
crypto_store32_le(digest + 4 * i, st[i]);
crypto_memzero(tail, sizeof(tail));
crypto_memzero(st, sizeof(st));
}
/* ---- HMAC-MD5 ----------------------------------------------------- */
void hmac_md5(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac)
{
MD5_CTX ctx;
uint8_t k[64];
uint8_t ih[16];
unsigned i;
memset(k, 0, sizeof(k));
if (key_len > 64)
{
MD5_Init(&ctx);
MD5_Update(&ctx, key, (unsigned long)key_len);
MD5_Final(k, &ctx);
}
else
memcpy(k, key, key_len);
for (i = 0; i < 64; i++)
k[i] ^= 0x36;
MD5_Init(&ctx);
MD5_Update(&ctx, k, 64);
MD5_Update(&ctx, data, (unsigned long)len);
MD5_Final(ih, &ctx);
for (i = 0; i < 64; i++)
k[i] ^= 0x36 ^ 0x5c;
MD5_Init(&ctx);
MD5_Update(&ctx, k, 64);
MD5_Update(&ctx, ih, 16);
MD5_Final(mac, &ctx);
crypto_memzero(k, sizeof(k));
crypto_memzero(ih, sizeof(ih));
crypto_memzero(&ctx, sizeof(ctx));
}
/* ---- SP 800-108 counter mode ------------------------------------- */
void kbkdf_hmac_sha256(const uint8_t *ki, size_t ki_len,
const uint8_t *label, size_t label_len,
const uint8_t *context, size_t context_len,
uint8_t *out, size_t out_len)
{
struct hmac_sha256_ctx h;
uint8_t be[4];
uint8_t block[32];
uint32_t i = 0;
size_t done = 0;
const uint8_t zero = 0;
while (done < out_len)
{
size_t take = out_len - done < 32 ? out_len - done : 32;
i++;
hmac_sha256_init(&h, ki, ki_len);
crypto_store32_be(be, i);
hmac_sha256_update(&h, be, 4);
hmac_sha256_update(&h, label, label_len);
hmac_sha256_update(&h, &zero, 1);
hmac_sha256_update(&h, context, context_len);
crypto_store32_be(be, (uint32_t)(out_len * 8));
hmac_sha256_update(&h, be, 4);
hmac_sha256_final(&h, block);
memcpy(out + done, block, take);
done += take;
}
crypto_memzero(block, sizeof(block));
}
/* ---- NTOWFv2 ------------------------------------------------------ */
/* UTF-8 to UTF-16LE into a caller buffer of @cap code units;
* returns the octet count or 0 when it does not fit / is not UTF-8. */
static void crypto_store16_le_pair(uint8_t *p, uint16_t v)
{
p[0] = (uint8_t)v;
p[1] = (uint8_t)(v >> 8);
}
static size_t ntlm_utf16le(const char *s, uint8_t *out, size_t cap,
int upper)
{
size_t n = 0;
while (*s)
{
uint32_t cp = utf8_walk(&s);
if (cp == 0 || cp > 0x10ffff)
return 0;
if (upper && cp >= 'a' && cp <= 'z')
cp -= 32;
if (cp >= 0x10000)
{
if (n + 2 > cap)
return 0;
cp -= 0x10000;
crypto_store16_le_pair(out + 2 * n, (uint16_t)(0xd800 | (cp >> 10)));
crypto_store16_le_pair(out + 2 * n + 2, (uint16_t)(0xdc00 | (cp & 0x3ff)));
n += 2;
}
else
{
if (n + 1 > cap)
return 0;
crypto_store16_le_pair(out + 2 * n, (uint16_t)cp);
n += 1;
}
}
return n * 2;
}
int ntlm_ntowf_v2(const char *password, const char *user,
const char *domain, uint8_t *out)
{
/* 255 code points each, as UTF-16 with surrogates: 1020 octets. */
uint8_t *buf = (uint8_t*)malloc(2 * 1020);
uint8_t nt[16];
size_t plen, ulen, dlen;
if (!buf)
return -1;
plen = ntlm_utf16le(password, buf, 510, 0);
if (!plen && *password)
{
free(buf);
return -1;
}
md4(buf, plen, nt);
ulen = ntlm_utf16le(user, buf, 510, 1);
dlen = ntlm_utf16le(domain, buf + ulen, 510, 0);
if ((!ulen && *user) || (!dlen && *domain))
{
crypto_memzero(buf, 2 * 1020);
free(buf);
return -1;
}
hmac_md5(nt, 16, buf, ulen + dlen, out);
crypto_memzero(nt, sizeof(nt));
crypto_memzero(buf, 2 * 1020);
free(buf);
return 0;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+171
View File
@@ -0,0 +1,171 @@
/*
https://github.com/superwills/NibbleAndAHalf
base64.h -- Fast base64 encoding and decoding.
version 1.0.0, April 17, 2013 143a
Copyright (C) 2013 William Sherif
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
William Sherif
[email protected]
YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
Modified for RetroArch formatting, logging, and header files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <encodings/base64.h>
static const char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* maps A=>0,B=>1.. */
static const unsigned char unb64[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 0, 0,
0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 0, 0, 0, 0, 0, 0, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
}; /* This array has 256 elements */
/*
Converts binary data of length=len to base64 characters.
Length of the resultant string is stored in flen
(you must pass pointer flen).
*/
char* base64(const void* binaryData, int len, int *flen)
{
char* res;
int byteNo; /* I need this after the loop */
const unsigned char* bin = (const unsigned char*) binaryData;
int rc = 0; /* result counter */
int modulusLen = len % 3 ;
/* 2 gives 1 and 1 gives 2, but 0 gives 0. */
int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1);
*flen = 4*(len + pad)/3;
if (!(res = (char*) malloc(*flen + 1))) /* and one for the NULL */
return 0;
for (byteNo=0; byteNo <= len-3; byteNo+=3)
{
unsigned char BYTE0 = bin[byteNo];
unsigned char BYTE1 = bin[byteNo+1];
unsigned char BYTE2 = bin[byteNo+2];
res[rc++] = b64[BYTE0 >> 2];
res[rc++] = b64[((0x3&BYTE0)<<4) + (BYTE1 >> 4)];
res[rc++] = b64[((0x0f&BYTE1)<<2) + (BYTE2>>6)];
res[rc++] = b64[0x3f&BYTE2];
}
if (pad==2)
{
res[rc++] = b64[bin[byteNo] >> 2];
res[rc++] = b64[(0x3&bin[byteNo])<<4];
res[rc++] = '=';
res[rc++] = '=';
}
else if (pad==1)
{
res[rc++] = b64[bin[byteNo] >> 2];
res[rc++] = b64[((0x3&bin[byteNo])<<4) + (bin[byteNo+1] >> 4)];
res[rc++] = b64[(0x0f&bin[byteNo+1])<<2];
res[rc++] = '=';
}
res[rc]=0; /* NULL TERMINATOR! ;) */
return res;
}
unsigned char* unbase64(const char* ascii, int len, int *flen)
{
int charNo;
unsigned char *bin;
const unsigned char *safeAsciiPtr = (const unsigned char*) ascii;
int cb = 0;
int pad = 0;
/* Valid base64 has length that is a non-zero multiple of 4.
* Shorter or misaligned inputs are malformed and have historically
* caused 1-byte heap overflows when pad > 0 (e.g. "AB="). */
if (len < 4 || (len & 3) != 0)
{
*flen = 0;
return 0;
}
if (safeAsciiPtr[len-1]=='=')
++pad;
if (safeAsciiPtr[len-2]=='=')
++pad;
*flen = 3*len/4 - pad;
if (!(bin = (unsigned char*)malloc(*flen)))
return 0;
for (charNo=0; charNo <= len-4-pad; charNo+=4)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
int C = unb64[safeAsciiPtr[charNo+2]];
int D = unb64[safeAsciiPtr[charNo+3]];
bin[cb++] = (A<<2) | (B>>4);
bin[cb++] = (B<<4) | (C>>2);
bin[cb++] = (C<<6) | (D);
}
if (pad==1)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
int C = unb64[safeAsciiPtr[charNo+2]];
bin[cb++] = (A<<2) | (B>>4);
bin[cb++] = (B<<4) | (C>>2);
}
else if (pad==2)
{
int A = unb64[safeAsciiPtr[charNo]];
int B = unb64[safeAsciiPtr[charNo+1]];
bin[cb++] = (A<<2) | (B>>4);
}
return bin;
}
File diff suppressed because it is too large. Load diff
+271
View File
@@ -0,0 +1,271 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (cpu_class.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* Per-processor performance class: the one source of truth shared by
* cpu_features_get_processor_order() (features_cpu.c) and
* sthread_prefer_fast_cores() / sthread_get_core_topology()
* (rthreads.c), so a thread pinned "to the fast cores" and a thread
* pinned "to the strongest processor" land on the same silicon.
*
* This is a private implementation header: every function is static
* and the file is included by exactly the two .c files above, because
* neither may link the other (rthreads.c is built on its own by the
* sample harnesses, features_cpu.c by the cores).
*
* cpu_class_read() fills cls[id] for id < len with a class where a
* higher value is a faster core, and returns the number of ids
* covered, or 0 when the platform publishes no class at all - a
* homogeneous part, or one this code cannot see into - in which case
* every processor is to be taken as the same class.
*
* Linux, in order of trust:
* 1. The kernel's own hybrid classification: on Intel hybrid parts
* the perf driver publishes the P-cores as cpu_core/cpus and the
* E-cores as cpu_atom/cpus.
* 2. cpu_capacity, the scheduler's per-CPU throughput figure on ARM
* big.LITTLE, where the biggest cluster is 1024 and a little one a
* few hundred. A three-tier part (one prime, some big, some
* little) keeps its big cores in the fast class by taking
* everything within CPU_CLASS_CAPACITY_PCT of the best.
* 3. cpuinfo_max_freq, within CPU_CLASS_FREQ_PCT of the highest. The
* band is what makes this usable on x86: favoured cores (Intel
* Turbo Boost Max 3.0, AMD CPPC preferred cores) and the two CCDs
* of an X3D part differ from their siblings by a few percent,
* while a real slow cluster sits 20-40% lower.
* Windows 10 1607+: CPU Sets EfficiencyClass, which rises with
* performance (P = 1, E = 0 on Intel hybrid). Resolved at run time so
* older targets still link and simply report nothing.
* Everything else: nothing.
*
* A builder may point CPU_CLASS_SYSFS at a fixture tree in place of
* /sys/devices/system/cpu. */
#ifndef __LIBRETRO_SDK_CPU_CLASS_H
#define __LIBRETRO_SDK_CPU_CLASS_H
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <retro_inline.h>
#ifndef CPU_CLASS_SYSFS
#define CPU_CLASS_SYSFS "/sys/devices/system/cpu"
#endif
#define CPU_CLASS_CAPACITY_PCT 70
#define CPU_CLASS_FREQ_PCT 85
#define CPU_CLASS_MAX_IDS 256
#if defined(__linux__)
/* Reads one unsigned long from a sysfs file; 0 when absent or empty. */
static unsigned long cpu_class_sysfs_ulong(const char *path)
{
unsigned long v = 0;
FILE *f = fopen(path, "r");
if (!f)
return 0;
if (fscanf(f, "%lu", &v) != 1)
v = 0;
fclose(f);
return v;
}
/* Parses a sysfs cpulist ("0-7,16-23") at path, setting mark[id] for
* each id < len it names; returns whether the file existed and named
* at least one processor. */
static bool cpu_class_sysfs_cpulist(const char *path,
unsigned char *mark, size_t len)
{
char line[512];
char *p;
bool any = false;
FILE *f = fopen(path, "r");
if (!f)
return false;
line[0] = '\0';
if (!fgets(line, sizeof(line), f))
line[0] = '\0';
fclose(f);
for (p = line; *p; )
{
char *next;
unsigned long lo, hi, i;
if (*p < '0' || *p > '9')
{
p++;
continue;
}
lo = hi = strtoul(p, &next, 10);
if (*next == '-')
hi = strtoul(next + 1, &next, 10);
for (i = lo; i <= hi && i < len; i++)
{
mark[i] = 1;
any = true;
}
p = next;
}
return any;
}
/* Reads "<cpu>/<leaf>" for every id < len; sets cls[id] to 1 where the
* value is within pct percent of the highest, 0 otherwise (including
* ids with no value). Returns the highest id with a value plus one, or
* 0 when none had one. */
static size_t cpu_class_by_value(const char *leaf, unsigned pct,
unsigned char *cls, size_t len)
{
unsigned long best = 0;
size_t i, n = 0;
/* 32-bit is plenty: kHz clocks top out below 10^7 and capacities
* at 1024. On the heap, so the callers' frames stay within the
* 2 KiB thread-stack budget (PSP, GX). */
unsigned *vals;
if (len > CPU_CLASS_MAX_IDS)
len = CPU_CLASS_MAX_IDS;
if (!(vals = (unsigned*)malloc(len * sizeof(*vals) + 1)))
{
memset(cls, 0, len);
return 0;
}
for (i = 0; i < len; i++)
{
char path[256];
snprintf(path, sizeof(path), CPU_CLASS_SYSFS "/cpu%u/%s",
(unsigned)i, leaf);
vals[i] = (unsigned)cpu_class_sysfs_ulong(path);
if (vals[i])
{
n = i + 1;
if (vals[i] > best)
best = vals[i];
}
}
for (i = 0; i < len; i++)
cls[i] = (vals[i] && vals[i] * 100 >= best * pct) ? 1 : 0;
free(vals);
return n;
}
static size_t cpu_class_read(unsigned char *cls, size_t len)
{
unsigned char atom[CPU_CLASS_MAX_IDS];
size_t i, n;
if (len > CPU_CLASS_MAX_IDS)
len = CPU_CLASS_MAX_IDS;
memset(cls, 0, len);
memset(atom, 0, sizeof(atom));
/* 1. Intel hybrid, as the kernel sees it (/sys/devices/cpu_core and
* cpu_atom, beside /sys/devices/system). Only meaningful when
* both kinds exist: cpu_core alone is what a homogeneous Intel
* part publishes. */
if ( cpu_class_sysfs_cpulist(CPU_CLASS_SYSFS "/../../cpu_core/cpus", cls, len)
&& cpu_class_sysfs_cpulist(CPU_CLASS_SYSFS "/../../cpu_atom/cpus", atom, len))
{
n = 0;
for (i = 0; i < len; i++)
if (cls[i] || atom[i])
n = i + 1;
return n;
}
memset(cls, 0, len);
/* 2. Scheduler capacity (ARM), then 3. maximum clock. */
if ((n = cpu_class_by_value("cpu_capacity", CPU_CLASS_CAPACITY_PCT, cls, len)))
return n;
return cpu_class_by_value("cpufreq/cpuinfo_max_freq", CPU_CLASS_FREQ_PCT,
cls, len);
}
#elif defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
#include <windows.h>
typedef BOOL (WINAPI *cpu_class_get_cpusets_t)(void*, ULONG, ULONG*,
HANDLE, ULONG);
/* SYSTEM_CPU_SET_INFORMATION, read by offset since older SDKs lack the
* type: Size at 0, Type at 4 (0 = CpuSet), Id at 8, Group at 12,
* LogicalProcessorIndex at 14, CoreIndex at 15, EfficiencyClass at 18.
* Ids are the processor's index within group 0, the numbering an
* affinity mask is built from; other groups are beyond a 64-bit mask
* and are left out. */
static size_t cpu_class_read(unsigned char *cls, size_t len)
{
HMODULE k32 = GetModuleHandleA("kernel32.dll");
cpu_class_get_cpusets_t getinfo = NULL;
unsigned char *buf = NULL;
ULONG len_needed = 0;
ULONG off;
size_t n = 0;
memset(cls, 0, len);
if (k32)
getinfo = (cpu_class_get_cpusets_t)(void (*)(void))
GetProcAddress(k32, "GetSystemCpuSetInformation");
if (!getinfo)
return 0;
getinfo(NULL, 0, &len_needed, GetCurrentProcess(), 0);
if (!len_needed || !(buf = (unsigned char*)malloc(len_needed)))
return 0;
if (!getinfo(buf, len_needed, &len_needed, GetCurrentProcess(), 0))
{
free(buf);
return 0;
}
for (off = 0; off + 20 <= len_needed; )
{
DWORD size = *(DWORD*)(buf + off);
if (size < 20)
break;
if (*(DWORD*)(buf + off + 4) == 0 && *(WORD*)(buf + off + 12) == 0)
{
unsigned id = buf[off + 14];
if (id < len)
{
cls[id] = buf[off + 18];
if ((size_t)id + 1 > n)
n = id + 1;
}
}
off += size;
}
free(buf);
return n;
}
#else
/* No class source: inline so a platform whose callers are compiled
* out of the ranking does not carry an unreferenced function. */
static INLINE size_t cpu_class_read(unsigned char *cls, size_t len)
{
memset(cls, 0, len);
return 0;
}
#endif
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+321
View File
@@ -0,0 +1,321 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path_io.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <retro_posix_source.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
/* MinGW's <sys/stat.h> defines stat (and in some configs mkdir) as
* function-like macros that map to _stat64/_stati64/_mkdir. This must
* be undone BEFORE including libretro.h below: otherwise the macro
* rewrites the struct member name in the retro_vfs_interface definition
* (retro_vfs_stat_t stat -> _stat64), and then the use sites
* vfs_iface->stat / vfs_iface->mkdir no longer match it, breaking the
* Windows build. The POSIX functions are still reachable via their
* real names where this TU needs them (it doesn't call stat()/mkdir()
* directly; all I/O goes through the VFS *_impl callbacks). */
#ifdef stat
#undef stat
#endif
#ifdef mkdir
#undef mkdir
#endif
#include <boolean.h>
#include <file/file_path.h>
#include <compat/strl.h>
#include <compat/posix_string.h>
#include <retro_miscellaneous.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h> /* stat() is defined here */
#endif
/* <direct.h> (MinGW, included just above) can re-establish the mkdir
* macro after the earlier #undef, so drop it again here before the use
* site below. Same rationale for stat, defensively. */
#ifdef stat
#undef stat
#endif
#ifdef mkdir
#undef mkdir
#endif
/* TODO/FIXME - globals */
static retro_vfs_stat_t path_stat32_cb = retro_vfs_stat_impl;
static retro_vfs_stat_64_t path_stat64_cb = retro_vfs_stat_64_impl;
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
/* VFS API v5. NULL when a frontend older than v5 is in use, so the
* wrappers report failure instead of touching the local file system
* behind a foreign frontend's back. */
static retro_vfs_set_readonly_t path_set_readonly_cb = retro_vfs_set_readonly_impl;
static retro_vfs_get_mtime_t path_get_mtime_cb = retro_vfs_get_mtime_impl;
static retro_vfs_set_mtime_t path_set_mtime_cb = retro_vfs_set_mtime_impl;
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
const struct retro_vfs_interface*
vfs_iface = vfs_info->iface;
path_stat32_cb = retro_vfs_stat_impl;
path_stat64_cb = retro_vfs_stat_64_impl;
path_mkdir_cb = retro_vfs_mkdir_impl;
path_set_readonly_cb = retro_vfs_set_readonly_impl;
path_get_mtime_cb = retro_vfs_get_mtime_impl;
path_set_mtime_cb = retro_vfs_set_mtime_impl;
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
return;
path_stat32_cb = vfs_iface->stat;
path_mkdir_cb = vfs_iface->mkdir;
if (vfs_info->required_interface_version >= STAT64_REQUIRED_VFS_VERSION)
path_stat64_cb = vfs_iface->stat_64;
else
path_stat64_cb = NULL;
/* Members a v5 frontend left NULL stay NULL: the wrappers then
* report "unavailable" rather than dereferencing them. */
path_set_readonly_cb = NULL;
path_get_mtime_cb = NULL;
path_set_mtime_cb = NULL;
if (vfs_info->required_interface_version >= METADATA_REQUIRED_VFS_VERSION)
{
path_set_readonly_cb = vfs_iface->set_readonly;
path_get_mtime_cb = vfs_iface->get_mtime;
path_set_mtime_cb = vfs_iface->set_mtime;
}
}
bool path_is_readonly(const char *path)
{
if (path_stat64_cb)
return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_READONLY) != 0;
return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_READONLY) != 0;
}
bool path_set_readonly(const char *path, bool readonly)
{
if (!path_set_readonly_cb)
return false;
return path_set_readonly_cb(path, readonly ? 1 : 0) == 0;
}
bool path_get_mtime(const char *path, int64_t *mtime)
{
if (!path_get_mtime_cb || !mtime)
return false;
return path_get_mtime_cb(path, mtime) == 0;
}
bool path_set_mtime(const char *path, int64_t mtime)
{
if (!path_set_mtime_cb)
return false;
return path_set_mtime_cb(path, mtime) == 0;
}
int path_stat(const char *path)
{
/* Use 64‑bit stat if available, else fallback */
return path_stat64_cb ? path_stat64_cb(path, NULL) : path_stat32_cb(path, NULL);
}
/**
* path_is_directory:
* @path : path
*
* Checks if path is a directory.
*
* @return true if path is a directory, otherwise false.
*/
bool path_is_directory(const char *path)
{
if (path_stat64_cb)
return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
}
bool path_is_character_special(const char *path)
{
if (path_stat64_cb)
return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
}
bool path_is_valid(const char *path)
{
if (path_stat64_cb)
return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
}
bool path_set_private(const char *path)
{
return retro_vfs_restrict_permissions_impl(path) == 0;
}
int64_t path_get_size(const char *path)
{
int64_t filesize = 0;
int32_t filesize32 = 0;
if (path_stat64_cb && path_stat64_cb(path, &filesize) != 0)
return filesize;
/* Fallback: 32-bit stat */
if (path_stat32_cb && path_stat32_cb(path, &filesize32) != 0)
return (int64_t)filesize32;
return -1;
}
/**
* path_rmdir:
* @dir : directory path.
*
* Removes the empty directory @dir. There is no libretro VFS entry
* for this, so it always goes through the built-in implementation.
*
* @return true if the directory was removed, otherwise false -
* including a directory that is not empty, and platforms with no
* directory removal, where an empty directory is simply left.
**/
bool path_rmdir(const char *dir)
{
return retro_vfs_rmdir_impl(dir) == 0;
}
/**
* path_mkdir:
* @dir : directory
*
* Create directory on filesystem.
*
* Recursive function.
*
* @return true if directory could be created, otherwise false.
**/
/* True for "dev:name", "dev:/name" and "dev:\name" (a trailing
* separator allowed): a directory whose parent is a device root. The
* root cannot be probed everywhere - the PSP firmware refuses a stat of
* "ms0:/" - and path_parent_dir() turns it into a relative "./", but a
* mounted device's root always exists. */
static bool path_parent_is_device_root(const char *p)
{
const char *colon = strchr(p, ':');
const char *s;
if (!colon || colon == p)
return false;
for (s = p; s < colon; s++)
if (*s == '/' || *s == '\\')
return false;
s = colon + 1;
if (*s == '/' || *s == '\\')
s++;
if (!*s || *s == '/' || *s == '\\')
return false;
while (*s && *s != '/' && *s != '\\')
s++;
if (*s)
s++;
return *s == '\0';
}
bool path_mkdir(const char *dir)
{
bool norecurse = false;
char *basedir = NULL;
if (!(dir && *dir))
return false;
/* Nothing to do if it is already there.
*
* Without this the common case - which for archive extraction is
* every member after the first in a given directory - still costs
* a strdup, a path_parent_dir, a strcmp, a stat of the parent, a
* mkdir that is guaranteed to fail with EEXIST, and then a stat of
* the leaf to interpret that failure. Two directory probes and a
* doomed syscall to answer a question one probe answers. On Win32
* each of those probes is a UTF-16 conversion allocation plus both
* GetFileAttributesW and _wstat64, so the saving there is larger
* than the syscall count suggests.
*
* This is not a shortcut around the slow path's result: the slow
* path already returns true for an existing directory, by way of
* the mkdir -> -2 -> path_is_directory sequence at the bottom. The
* one behavioural difference is at a filesystem root ("/", "C:\"),
* where path_parent_dir empties the string and the !*basedir guard
* below returns false today; such a directory does exist, so
* reporting true for it is the correction, not a regression. */
if (path_is_directory(dir))
return true;
if (path_parent_is_device_root(dir))
norecurse = true;
else
{
/* Use heap. Real chance of stack
* overflow if we recurse too hard. */
if (!(basedir = strdup(dir)))
return false;
path_parent_dir(basedir, strlen(basedir));
if (!*basedir || !strcmp(basedir, dir))
{
free(basedir);
return false;
}
if ( path_is_directory(basedir)
|| path_mkdir(basedir))
norecurse = true;
free(basedir);
}
if (norecurse)
{
int ret = path_mkdir_cb(dir);
/* Don't treat this as an error. */
if (ret == -2 && path_is_directory(dir))
return true;
else if (ret == 0)
return true;
}
return false;
}
+148
View File
@@ -0,0 +1,148 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_dirent.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <retro_common.h>
#include <boolean.h>
#include <retro_dirent.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
/* TODO/FIXME - static globals */
static retro_vfs_opendir_t dirent_opendir_cb = NULL;
static retro_vfs_readdir_t dirent_readdir_cb = NULL;
static retro_vfs_dirent_get_name_t dirent_dirent_get_name_cb = NULL;
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
static retro_vfs_dirent_stat_t dirent_dirent_stat_cb = NULL;
/* Set when a frontend older than VFS API v5 owns the directory
* handles: the local _impl cannot be used on a foreign handle, so
* retro_dirent_stat() reports "unavailable" instead. */
static bool dirent_stat_unavailable = false;
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
const struct retro_vfs_interface* vfs_iface;
dirent_opendir_cb = NULL;
dirent_readdir_cb = NULL;
dirent_dirent_get_name_cb = NULL;
dirent_dirent_is_dir_cb = NULL;
dirent_closedir_cb = NULL;
dirent_dirent_stat_cb = NULL;
dirent_stat_unavailable = false;
vfs_iface = vfs_info->iface;
if (
vfs_info->required_interface_version < DIRENT_REQUIRED_VFS_VERSION
|| !vfs_iface)
return;
dirent_opendir_cb = vfs_iface->opendir;
dirent_readdir_cb = vfs_iface->readdir;
dirent_dirent_get_name_cb = vfs_iface->dirent_get_name;
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
dirent_closedir_cb = vfs_iface->closedir;
/* A frontend that negotiated v5 but left the member NULL is treated
* like an older one: the local _impl must not touch its handles. */
if (vfs_info->required_interface_version >= DIRENT_STAT_REQUIRED_VFS_VERSION
&& vfs_iface->dirent_stat)
dirent_dirent_stat_cb = vfs_iface->dirent_stat;
else
dirent_stat_unavailable = true;
}
struct RDIR *retro_opendir_include_hidden(
const char *name, bool include_hidden)
{
if (dirent_opendir_cb)
return (struct RDIR *)dirent_opendir_cb(name, include_hidden);
return (struct RDIR *)retro_vfs_opendir_impl(name, include_hidden);
}
struct RDIR *retro_opendir(const char *name)
{
return retro_opendir_include_hidden(name, false);
}
bool retro_dirent_error(struct RDIR *rdir)
{
/* Left for compatibility */
return false;
}
int retro_readdir(struct RDIR *rdir)
{
if (dirent_readdir_cb)
return dirent_readdir_cb((struct retro_vfs_dir_handle *)rdir);
return retro_vfs_readdir_impl((struct retro_vfs_dir_handle *)rdir);
}
const char *retro_dirent_get_name(struct RDIR *rdir)
{
if (dirent_dirent_get_name_cb)
return dirent_dirent_get_name_cb((struct retro_vfs_dir_handle *)rdir);
return retro_vfs_dirent_get_name_impl((struct retro_vfs_dir_handle *)rdir);
}
/**
*
* retro_dirent_is_dir:
* @rdir : pointer to the directory entry.
* @unused : deprecated, included for compatibility reasons, pass NULL
*
* Is the directory listing entry a directory?
*
* Returns: true if directory listing entry is
* a directory, false if not.
*/
bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused)
{
if (dirent_dirent_is_dir_cb)
return dirent_dirent_is_dir_cb((struct retro_vfs_dir_handle *)rdir);
return retro_vfs_dirent_is_dir_impl((struct retro_vfs_dir_handle *)rdir);
}
int retro_dirent_stat(struct RDIR *rdir, int64_t *size, int64_t *mtime)
{
if (!rdir)
return 0;
if (dirent_dirent_stat_cb)
return dirent_dirent_stat_cb((struct retro_vfs_dir_handle *)rdir, size, mtime);
if (dirent_stat_unavailable)
return 0;
return retro_vfs_dirent_stat_impl((struct retro_vfs_dir_handle *)rdir, size, mtime);
}
void retro_closedir(struct RDIR *rdir)
{
if (dirent_closedir_cb)
dirent_closedir_cb((struct retro_vfs_dir_handle *)rdir);
else
retro_vfs_closedir_impl((struct retro_vfs_dir_handle *)rdir);
}
File diff suppressed because it is too large. Load diff
+39
View File
@@ -0,0 +1,39 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (boolean.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_BOOLEAN_H
#define __LIBRETRO_SDK_BOOLEAN_H
#ifndef __cplusplus
#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(SN_TARGET_PS3)
/* Hack applied for MSVC when compiling in C89 mode as it isn't C99 compliant. */
#define bool unsigned char
#define true 1
#define false 0
#else
#include <stdbool.h>
#endif
#endif
#endif
@@ -0,0 +1,34 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fopen_utf8.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H
#define __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H
#ifdef _WIN32
/* Defined to error rather than fopen_utf8, to make it clear to everyone reading the code that not worrying about utf16 is fine */
/* TODO: enable */
/* #define fopen (use fopen_utf8 instead) */
void *fopen_utf8(const char * filename, const char * mode);
#else
#define fopen_utf8 fopen
#endif
#endif
+148
View File
@@ -0,0 +1,148 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (msvc.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_COMPAT_MSVC_H
#define __LIBRETRO_SDK_COMPAT_MSVC_H
#ifdef _MSC_VER
#ifdef __cplusplus
extern "C" {
#endif
/* Pre-MSVC 2015 compilers don't implement snprintf, vsnprintf in a cross-platform manner. */
#if _MSC_VER < 1900
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#ifndef snprintf
#define snprintf c99_snprintf_retro__
#endif
int c99_snprintf_retro__(char *s, size_t len, const char *format, ...);
#ifndef vsnprintf
#define vsnprintf c99_vsnprintf_retro__
#endif
int c99_vsnprintf_retro__(char *s, size_t len, const char *format, va_list ap);
#endif
/* strtoll/strtoull are C99. The CRT grew _strtoi64/_strtoui64 in Visual
* Studio 2005, and those take the same three arguments with the same
* meaning, so for 2005 and later the mapping is exact. Visual C++ 6.0,
* 2002 and 2003 have neither; _atoi64 is not a stand-in for them because
* it accepts no base and reports no end pointer, so compat_strtoll.c
* supplies a real implementation instead. */
#if _MSC_VER >= 1900
/* The UCRT has the real thing; do not shadow it with a macro,
* which would also catch std::strtoll in C++ translation units. */
#elif _MSC_VER >= 1400
#ifndef strtoll
#define strtoll _strtoi64
#endif
#ifndef strtoull
#define strtoull _strtoui64
#endif
#else
__int64 strtoll_retro__(const char *nptr, char **endptr, int base);
unsigned __int64 strtoull_retro__(const char *nptr, char **endptr, int base);
#ifndef strtoll
#define strtoll strtoll_retro__
#endif
#ifndef strtoull
#define strtoull strtoull_retro__
#endif
#endif
#ifdef __cplusplus
}
#endif
#undef UNICODE /* Do not bother with UNICODE at this time. */
#include <direct.h>
#include <stddef.h>
#define _USE_MATH_DEFINES
#include <math.h>
/* Python headers defines ssize_t and sets HAVE_SSIZE_T.
* Cannot duplicate these efforts.
*/
#ifndef HAVE_SSIZE_T
#if defined(_WIN64)
typedef __int64 ssize_t;
#elif defined(_WIN32)
typedef int ssize_t;
#endif
#endif
#define mkdir(dirname, unused) _mkdir(dirname)
#undef strcasecmp
#define strcasecmp _stricmp
#undef strncasecmp
#define strncasecmp _strnicmp
/* Disable some of the annoying warnings. */
#pragma warning(disable : 4800)
#pragma warning(disable : 4805)
#pragma warning(disable : 4244)
#pragma warning(disable : 4305)
#pragma warning(disable : 4146)
#pragma warning(disable : 4267)
#pragma warning(disable : 4723)
#pragma warning(disable : 4996)
/* roundf and va_copy is available since MSVC 2013 */
#if _MSC_VER < 1800
#define roundf(in) (in >= 0.0f ? floorf(in + 0.5f) : ceilf(in - 0.5f))
#define va_copy(x, y) ((x) = (y))
#endif
#if _MSC_VER <= 1310
#ifndef __cplusplus
/* VC6 math.h doesn't define some functions when in C mode.
* Trying to define a prototype gives "undefined reference".
* But providing an implementation then gives "function already has body".
* So the equivalent of the implementations from math.h are used as
* defines here instead, and it seems to work.
*/
#define cosf(x) ((float)cos((double)x))
#define powf(x, y) ((float)pow((double)x, (double)y))
#define sinf(x) ((float)sin((double)x))
#define ceilf(x) ((float)ceil((double)x))
#define floorf(x) ((float)floor((double)x))
#define sqrtf(x) ((float)sqrt((double)x))
#define fabsf(x) ((float)fabs((double)(x)))
#endif
#endif
#ifndef PATH_MAX
#define PATH_MAX _MAX_PATH
#endif
#ifndef SIZE_MAX
#define SIZE_MAX _UI32_MAX
#endif
#endif
#endif
@@ -0,0 +1,93 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (posix_string.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_COMPAT_POSIX_STRING_H
#define __LIBRETRO_SDK_COMPAT_POSIX_STRING_H
#include <retro_common_api.h>
/**
* @file posix_string.h
*
* Portable reimplementations of various string functions
* that are normally provided by libc or POSIX.
*/
#ifdef _MSC_VER
#include <compat/msvc.h>
#endif
RETRO_BEGIN_DECLS
#if defined(_WIN32) || defined(DOXYGEN)
#undef strtok_r
#define strtok_r(str, delim, saveptr) retro_strtok_r__(str, delim, saveptr)
/**
* Portable reimplementation of \c strtok_r().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strtok.3.html
*/
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
#if defined(_MSC_VER) || defined(DOXYGEN)
#undef strcasecmp
#undef strdup
#define strcasecmp(a, b) retro_strcasecmp__(a, b)
#define strdup(orig) retro_strdup__(orig)
/**
* Portable reimplementation of \c strcasecmp().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strcasecmp.3.html
*/
int strcasecmp(const char *a, const char *b);
/**
* Portable reimplementation of \c strdup().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strdup.3.html
*/
char *strdup(const char *orig);
/* isblank is available since MSVC 2013 */
#if _MSC_VER < 1800
#undef isblank
#define isblank(c) retro_isblank__(c)
/**
* Portable reimplementation of \c isblank().
* The original function will be used if it's available.
*
* @see https://en.cppreference.com/w/c/string/byte/isblank
*/
int isblank(int c);
#endif
#endif
RETRO_END_DECLS
#endif
@@ -0,0 +1,61 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (strcasestr.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_COMPAT_STRCASESTR_H
#define __LIBRETRO_SDK_COMPAT_STRCASESTR_H
#include <string.h>
#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H)
#include "../../../config.h"
#endif
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/**
* Case-insensitive substring search.
*
* Called by its own name rather than shadowing strcasestr, and used
* unconditionally rather than only where the C library lacks one. The
* library's version is not reliably the better choice: glibc's measured
* some fifty times slower than this on a representative search, and the
* handheld targets that set HAVE_STRCASESTR are the slowest machines
* RetroArch runs on and the least likely to have a good implementation.
* Using one function everywhere also means one set of semantics, rather
* than case folding that varies with the platform's locale.
*
* Folds ASCII only, which is what the callers mean - extensions, driver
* names, search terms - and matches the behaviour of the C library
* versions it replaces for every input that is not undefined.
*
* @param haystack String to search in.
* @param needle String to search for.
* @return Pointer to the first occurrence within \c haystack,
* or NULL if there is none.
*/
char *compat_strcasestr(const char *haystack, const char *needle);
RETRO_END_DECLS
#endif
+130
View File
@@ -0,0 +1,130 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (strl.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_COMPAT_STRL_H
#define __LIBRETRO_SDK_COMPAT_STRL_H
/**
* @file strl.h
*
* Portable implementation of \c strlcpy(3) and \c strlcat(3).
* If these functions are available on the target platform,
* then the originals should be imported instead.
*
* @see https://linux.die.net/man/3/strlcpy
*/
#include <string.h>
#include <stddef.h>
#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H)
#include "../../../config.h"
#endif
#include <retro_common_api.h>
#include <retro_inline.h>
RETRO_BEGIN_DECLS
#if defined(__MACH__) && defined(__APPLE__)
#ifndef HAVE_STRL
#define HAVE_STRL
#endif
#endif
#ifndef HAVE_STRL
/* Avoid possible naming collisions during link since
* we prefer to use the actual name. */
#define strlcpy(dst, src, size) strlcpy_retro__(dst, src, size)
#define strlcat(dst, src, size) strlcat_retro__(dst, src, size)
/**
* @brief Portable implementation of \c strlcpy(3).
* @see https://linux.die.net/man/3/strlcpy
*/
size_t strlcpy(char *s, const char *source, size_t len);
/**
* @brief Portable implementation of \c strlcat(3).
* @see https://linux.die.net/man/3/strlcpy
*/
size_t strlcat(char *s, const char *source, size_t len);
#endif
/**
* @brief Copy a string \b literal, without scanning it at runtime.
*
* \c strlcpy() begins with \c strlen() of its source, and because it
* lives in another translation unit that scan happens at runtime even
* when the source is a literal whose length the compiler already knows.
* The scan is roughly half the cost of a \c strlcpy() of a short string
* -- there is a fixed setup cost before a single byte is compared -- so
* for a literal it is pure overhead.
*
* @param dst Destination buffer.
* @param lit A string \b literal. Anything else is a compile error:
* the \c "" concatenation below only parses for a literal,
* which is what keeps \c sizeof from silently measuring a
* pointer instead of the characters.
* @param size Size of @p dst, exactly as for \c strlcpy().
*
* @return The length of @p lit, matching \c strlcpy().
*
* The destination bound is not dropped: when the literal (with its
* terminator) does not fit, this defers to \c strlcpy() so the copy
* truncates rather than overruns. With a compile-time @p size the test
* folds away and only one arm survives; with a runtime @p size it costs
* a comparison that predicts perfectly.
*/
static INLINE size_t strlcpy_lit_(char *s, const char *lit,
size_t lit_size, size_t len)
{
/* lit_size is sizeof() of a literal, so it is a constant at every
* call site and this branch folds away; when @len is a constant too
* -- sizeof(buf), the common case -- the whole call becomes the
* stores memcpy() compiles to. */
if (lit_size <= len)
{
memcpy(s, lit, lit_size);
return lit_size - 1;
}
return strlcpy(s, lit, len);
}
#define strlcpy_lit(dst, lit, size) \
strlcpy_lit_((dst), "" lit, sizeof("" lit), (size))
/**
* A version of \c strndup(3) that guarantees the result will be null-terminated.
*
* @param s The string to duplicate.
* @param n The maximum number of characters to copy from \c s.
* The result will allocate one more byte than this value.
* @return Pointer to the cloned string.
* Must be freed with \c free().
*/
char *strldup(const char *s, size_t n);
RETRO_END_DECLS
#endif
+468
View File
@@ -0,0 +1,468 @@
/* Copyright (C) 2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (crypto.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_CRYPTO_H
#define _LIBRETRO_CRYPTO_H
#include <stdint.h>
#include <stddef.h>
#include <retro_inline.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* Built only under HAVE_CRYPTO (Makefile.common, griffin); the small
* consoles leave it out and anything optional on top of it - the
* keychain, the cleanroom TLS, SMB and NFS clients - is gated on it.
*
* Symmetric primitives: constant-time helpers, AES (block, CTR, GCM,
* CMAC), ChaCha20, Poly1305 and the AEAD front over them. Hashes are
* in lrc_hash.h, key derivation in kdf.h, public key in pk.h. */
/* Byte-order helpers and the two operations every primitive here
* needs to get right on secret data: comparing without an early exit,
* and clearing a buffer in a way the optimiser may not drop. */
static INLINE uint32_t crypto_load32_be(const uint8_t *p)
{
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16)
| ((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
static INLINE void crypto_store32_be(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v >> 24);
p[1] = (uint8_t)(v >> 16);
p[2] = (uint8_t)(v >> 8);
p[3] = (uint8_t)(v);
}
static INLINE uint64_t crypto_load64_be(const uint8_t *p)
{
return ((uint64_t)crypto_load32_be(p) << 32) | crypto_load32_be(p + 4);
}
static INLINE void crypto_store64_be(uint8_t *p, uint64_t v)
{
crypto_store32_be(p, (uint32_t)(v >> 32));
crypto_store32_be(p + 4, (uint32_t)(v));
}
static INLINE uint32_t crypto_load32_le(const uint8_t *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static INLINE void crypto_store32_le(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v);
p[1] = (uint8_t)(v >> 8);
p[2] = (uint8_t)(v >> 16);
p[3] = (uint8_t)(v >> 24);
}
static INLINE void crypto_store64_le(uint8_t *p, uint64_t v)
{
crypto_store32_le(p, (uint32_t)(v));
crypto_store32_le(p + 4, (uint32_t)(v >> 32));
}
static INLINE uint32_t crypto_rotl32(uint32_t v, unsigned n)
{
return (v << n) | (v >> (32 - n));
}
/**
* crypto_memeq_ct:
*
* Returns: 1 when the two buffers are equal, 0 otherwise. Runs in
* time depending only on @len.
**/
int crypto_memeq_ct(const void *a, const void *b, size_t len);
/**
* crypto_memzero:
*
* Clears @len octets through a volatile pointer so that a dead-store
* elimination pass cannot remove it.
**/
void crypto_memzero(void *p, size_t len);
/* AES (FIPS 197), encryption direction only. Every mode this tree
* needs - CTR, GCM, CMAC, and the CCM SMB3 will want - runs the
* forward cipher, so no inverse cipher is carried. */
#define AES_BLOCK_SIZE 16
struct aes_ctx
{
uint32_t rk[60];
unsigned rounds;
};
struct aes_gcm_ctx
{
struct aes_ctx aes;
/* H = E_K(0^128), as two big-endian 64-bit halves, and the 4-bit
* multiplication tables the scalar GHASH walks. */
uint64_t h_hi;
uint64_t h_lo;
uint64_t hh[16];
uint64_t hl[16];
};
/**
* aes_init:
* @key_len : 16, 24 or 32 octets.
*
* Returns: 0 on success, -1 on an unsupported key length.
**/
int aes_init(struct aes_ctx *ctx, const uint8_t *key, size_t key_len);
void aes_encrypt_block(const struct aes_ctx *ctx,
const uint8_t *in, uint8_t *out);
/**
* aes_decrypt_block:
*
* Inverse cipher on one block with the same context aes_init() made;
* no separate decryption schedule is needed. Serves CBC and CTS
* (Kerberos); the AEAD modes above never decrypt with the cipher.
**/
void aes_decrypt_block(const struct aes_ctx *ctx,
const uint8_t *in, uint8_t *out);
/**
* aes_ctr_crypt:
* @counter : 16-octet counter block. The low 32 bits (big
* endian, octets 12..15) are incremented per
* block, as GCM does; the block is updated in
* place so a caller can continue a stream.
*
* Encrypts or decrypts @len octets (the two are the same operation).
* @in and @out may alias. Partial trailing blocks are allowed but a
* continuation must start on a block boundary.
**/
/**
* crypto_aes_hw:
*
* Returns: 1 when AES runs on instructions made for it here (AES-NI
* with PCLMULQDQ, or the ARMv8 AES and PMULL), 0 when it runs in
* software - where ChaCha20-Poly1305 is several times cheaper.
**/
int crypto_aes_hw(void);
void aes_ctr_crypt(const struct aes_ctx *ctx, uint8_t *counter,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_encrypt:
* @iv : 16 octets; left holding the last ciphertext
* block, so a caller can continue the chain.
* @len : a multiple of 16.
*
* CBC encryption. @in and @out may alias.
**/
void aes_cbc_encrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_decrypt:
* @iv : 16 octets; left holding the last ciphertext
* block, so a caller can continue the chain.
* @len : a multiple of 16.
*
* CBC decryption, several blocks at once where the cipher runs on
* instructions made for it. @in and @out may alias.
**/
void aes_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_encrypt_lanes:
* @n : independent chains, each @len octets (a
* multiple of 16) from in[i] to out[i] under
* iv[i], which is left as aes_cbc_encrypt()
* leaves it.
*
* A CBC chain waits on the cipher block by block; where the cipher
* runs on instructions made for it, independent chains are interleaved
* so their waits overlap.
**/
void aes_cbc_encrypt_lanes(const struct aes_ctx *ctx, unsigned n,
uint8_t *const *iv, const uint8_t *const *in, uint8_t *const *out,
size_t len);
/**
* aes_cmac:
*
* CMAC (RFC 4493 / SP 800-38B) of @len octets under @ctx, 16-octet
* tag to @mac.
**/
void aes_cmac(const struct aes_ctx *ctx,
const uint8_t *msg, size_t len, uint8_t *mac);
/* CMAC over a message given in pieces, which need not be blocks:
* init, update per piece, final for the tag. aes_cmac() is these
* three over one piece. */
struct aes_cmac_ctx
{
const struct aes_ctx *aes;
size_t n; /* octets held in buf */
uint8_t x[16];
uint8_t buf[16];
};
void aes_cmac_init(struct aes_cmac_ctx *s, const struct aes_ctx *aes);
void aes_cmac_update(struct aes_cmac_ctx *s, const uint8_t *msg, size_t len);
void aes_cmac_final(struct aes_cmac_ctx *s, uint8_t *mac);
/**
* aes_ccm_encrypt:
* @nonce_len : 7 to 13 octets (SMB3 uses 11).
* @tag_len : 4 to 16, even.
*
* CCM (RFC 3610) under a plain AES context. @ct may alias @pt.
*
* Returns: 0 on success, -1 on a bad nonce or tag length or a message
* too long for the nonce.
**/
int aes_ccm_encrypt(const struct aes_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *pt, size_t pt_len,
uint8_t *ct, uint8_t *tag, size_t tag_len);
/**
* aes_ccm_decrypt:
*
* Returns: 0 on success, -1 on a bad parameter or authentication
* failure (the plaintext is cleared in that case: CCM cannot check
* the tag before decrypting).
**/
int aes_ccm_decrypt(const struct aes_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, size_t tag_len, uint8_t *pt);
/* CCM over a message given in pieces, in order, which need not be
* blocks: init with the whole message length, then encrypt or
* decrypt each piece (in place or not), then the tag. The one-shot
* calls above are these over one piece. */
struct aes_ccm_stream
{
const struct aes_ctx *aes;
size_t len; /* message length given at init */
size_t off; /* octets processed */
size_t L;
size_t tag_len;
uint8_t x[16]; /* CBC-MAC chain */
uint8_t ctr[16];
uint8_t ks[16]; /* keystream of the current block */
uint8_t s0[16];
};
/**
* aes_ccm_stream_init:
*
* Returns: 0, or -1 on a bad nonce or tag length or a @msg_len too
* long for the nonce.
**/
int aes_ccm_stream_init(struct aes_ccm_stream *s, const struct aes_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
size_t msg_len, size_t tag_len);
void aes_ccm_stream_encrypt(struct aes_ccm_stream *s,
const uint8_t *pt, uint8_t *ct, size_t len);
void aes_ccm_stream_decrypt(struct aes_ccm_stream *s,
const uint8_t *ct, uint8_t *pt, size_t len);
/* @tag_len octets, once every octet of the message has been given */
void aes_ccm_stream_tag(struct aes_ccm_stream *s, uint8_t *tag);
/**
* aes_gcm_init:
*
* Returns: 0 on success, -1 on an unsupported key length.
**/
int aes_gcm_init(struct aes_gcm_ctx *ctx,
const uint8_t *key, size_t key_len);
/**
* aes_gcm_encrypt:
* @nonce : Any length; 12 octets is the fast path.
* @tag : 16 octets out.
*
* @ct may alias @pt. Returns 0.
**/
int aes_gcm_encrypt(const struct aes_gcm_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *pt, size_t pt_len,
uint8_t *ct, uint8_t *tag);
/**
* aes_gcm_decrypt:
* @tag : 16 octets in.
*
* The tag is checked in constant time before anything is written to
* @pt; @pt may alias @ct.
*
* Returns: 0 on success, -1 on authentication failure (nothing is
* written to @pt).
**/
int aes_gcm_decrypt(const struct aes_gcm_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, uint8_t *pt);
/* GCM with its two halves apart, over pieces that need not be blocks:
* the AAD and then the ciphertext are hashed piece by piece, in order,
* for the tag; aes_gcm_stream_crypt() runs the counter mode at any
* octet offset of the ciphertext, so pieces can be decrypted where
* they lie and in any order - after the tag is checked, as
* aes_gcm_decrypt() does. GMAC is the AAD alone. */
struct aes_gcm_stream
{
const struct aes_gcm_ctx *g;
uint64_t y_hi;
uint64_t y_lo;
uint64_t aad_len;
uint64_t ct_len;
size_t part_len; /* octets held in part */
uint8_t j0[16];
uint8_t part[16];
};
void aes_gcm_stream_init(struct aes_gcm_stream *s,
const struct aes_gcm_ctx *ctx, const uint8_t *nonce, size_t nonce_len);
/* all of the AAD before any ciphertext */
void aes_gcm_stream_aad(struct aes_gcm_stream *s,
const uint8_t *aad, size_t len);
void aes_gcm_stream_hash_ct(struct aes_gcm_stream *s,
const uint8_t *ct, size_t len);
void aes_gcm_stream_tag(struct aes_gcm_stream *s, uint8_t *tag);
/* @len octets at octet @off of the ciphertext; @in and @out may alias */
void aes_gcm_stream_crypt(const struct aes_gcm_stream *s, uint64_t off,
const uint8_t *in, uint8_t *out, size_t len);
/* ChaCha20 (RFC 8439): 256-bit key, 96-bit nonce, 32-bit block counter. */
#define CHACHA20_KEY_SIZE 32
#define CHACHA20_NONCE_SIZE 12
#define CHACHA20_BLOCK_SIZE 64
/**
* chacha20_block:
*
* Writes the 64-octet keystream block for @counter to @out.
**/
void chacha20_block(const uint8_t *key, const uint8_t *nonce,
uint32_t counter, uint8_t *out);
/**
* chacha20_xor:
*
* XORs @len octets of keystream starting at block @counter into
* @in -> @out (encrypt and decrypt are the same operation; the two
* may alias). A continuation must start on a 64-octet boundary.
**/
void chacha20_xor(const uint8_t *key, const uint8_t *nonce,
uint32_t counter, const uint8_t *in, uint8_t *out, size_t len);
/* Poly1305 (RFC 8439) one-time authenticator, 32-octet key, 16-octet
* tag. Arithmetic is 26-bit limbs on 64-bit products, with no
* data-dependent branches. */
#define POLY1305_KEY_SIZE 32
#define POLY1305_TAG_SIZE 16
struct poly1305_ctx
{
uint32_t r[5];
uint32_t h[5];
uint32_t pad[4];
size_t leftover;
uint8_t buffer[16];
uint8_t final;
};
void poly1305_init(struct poly1305_ctx *ctx, const uint8_t *key);
void poly1305_update(struct poly1305_ctx *ctx,
const uint8_t *m, size_t len);
void poly1305_final(struct poly1305_ctx *ctx, uint8_t *tag);
/* One-shot. */
void poly1305_auth(uint8_t *tag, const uint8_t *m, size_t len,
const uint8_t *key);
/* Authenticated encryption with associated data: ChaCha20-Poly1305
* (RFC 8439) and AES-256-GCM behind one call. Both take a 32-octet key,
* a 12-octet nonce and produce a 16-octet tag. */
enum aead_alg
{
AEAD_CHACHA20_POLY1305 = 0,
AEAD_AES256_GCM = 1
};
#define AEAD_KEY_SIZE 32
#define AEAD_NONCE_SIZE 12
#define AEAD_TAG_SIZE 16
/**
* aead_encrypt:
*
* @ct may alias @pt.
*
* Returns: 0 on success, -1 on a bad algorithm, key or nonce length.
**/
int aead_encrypt(enum aead_alg alg,
const uint8_t *key, size_t key_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *pt, size_t pt_len,
uint8_t *ct, uint8_t *tag, size_t tag_len);
/**
* aead_decrypt:
*
* Verifies @tag in constant time before writing any plaintext;
* @pt may alias @ct.
*
* Returns: 0 on success, -1 on a bad parameter, -2 on authentication
* failure (nothing written to @pt).
**/
int aead_decrypt(enum aead_alg alg,
const uint8_t *key, size_t key_len,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, size_t tag_len,
uint8_t *pt);
RETRO_END_DECLS
#endif
+243
View File
@@ -0,0 +1,243 @@
/* Copyright (C) 2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (kdf.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_CRYPTO_KDF_H
#define _LIBRETRO_CRYPTO_KDF_H
#include <stdint.h>
#include <stddef.h>
#include <lrc_hash.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* Keyed hashing and key derivation over SHA-256, plus the DRBG and
* the platform entropy source it seeds from. */
/* HMAC-SHA256 (RFC 2104) over the streaming SHA-256 in lrc_hash. */
#define HMAC_SHA256_SIZE 32
struct hmac_sha256_ctx
{
struct sha256_state inner;
struct sha256_state outer;
};
void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
const uint8_t *key, size_t key_len);
void hmac_sha256_update(struct hmac_sha256_ctx *ctx,
const uint8_t *data, size_t len);
/**
* hmac_sha256_final:
* @mac : 32 octets out. The context is cleared.
**/
void hmac_sha256_final(struct hmac_sha256_ctx *ctx, uint8_t *mac);
/* One-shot. */
void hmac_sha256(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac);
/* HMAC-SHA384, for the TLS PRF of the SHA-384 cipher suites. */
#define HMAC_SHA384_SIZE 48
/* HMAC-SHA1 and PBKDF2-HMAC-SHA1 (RFC 2898), for the Kerberos AES
* enctypes of RFC 3962, whose checksum and string-to-key are SHA-1
* based. Not for new designs of our own. */
struct hmac_sha1_ctx
{
struct sha1_state inner;
struct sha1_state outer;
};
void hmac_sha1_init(struct hmac_sha1_ctx *ctx,
const uint8_t *key, size_t key_len);
void hmac_sha1_update(struct hmac_sha1_ctx *ctx,
const uint8_t *data, size_t len);
/* @mac is 20 octets; the context is wiped. */
void hmac_sha1_final(struct hmac_sha1_ctx *ctx, uint8_t *mac);
void hmac_sha1(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac);
int pbkdf2_hmac_sha1(const uint8_t *password, size_t password_len,
const uint8_t *salt, size_t salt_len, uint32_t iterations,
uint8_t *out, size_t out_len);
struct hmac_sha384_ctx
{
struct sha512_state inner;
struct sha512_state outer;
};
void hmac_sha384_init(struct hmac_sha384_ctx *ctx,
const uint8_t *key, size_t key_len);
void hmac_sha384_update(struct hmac_sha384_ctx *ctx,
const uint8_t *data, size_t len);
void hmac_sha384_final(struct hmac_sha384_ctx *ctx, uint8_t *mac);
void hmac_sha384(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac);
/* HKDF-SHA256 (RFC 5869). */
/**
* hkdf_sha256_extract:
* @salt : May be NULL/0 (a zero key is used).
* @prk : 32 octets out.
**/
void hkdf_sha256_extract(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len, uint8_t *prk);
/**
* hkdf_sha256_expand:
* @okm_len : At most 255 * 32.
*
* Returns: 0 on success, -1 when @okm_len is too large.
**/
int hkdf_sha256_expand(const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len);
/* The same over SHA-384 (48-octet PRK, 48 octets per block). */
void hkdf_sha384_extract(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len, uint8_t *prk);
int hkdf_sha384_expand(const uint8_t *prk, size_t prk_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len);
/* Extract then expand. */
int hkdf_sha256(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len,
const uint8_t *info, size_t info_len,
uint8_t *okm, size_t okm_len);
/* PBKDF2-HMAC-SHA256 (RFC 8018). */
/**
* pbkdf2_hmac_sha256:
* @iterations : At least 1.
*
* Returns: 0 on success, -1 on a zero iteration count or out of memory.
**/
int pbkdf2_hmac_sha256(const uint8_t *password, size_t password_len,
const uint8_t *salt, size_t salt_len, uint32_t iterations,
uint8_t *out, size_t out_len);
/**
* crypto_random_bytes:
*
* Fills @buf from the platform entropy source (BCrypt / RtlGenRandom
* / CryptGenRandom on Windows, getentropy or /dev/urandom on POSIX,
* the kernel RNG on 3DS, Vita, Switch, PSP, Wii U and PS2).
*
* Returns: 0 on success, -1 when no source produced @len octets.
**/
int crypto_random_bytes(uint8_t *buf, size_t len);
/* Deterministic random bit generator on ChaCha20 with fast key
* erasure: every generate call takes its next key from the keystream
* it produces, so a state captured later cannot reproduce output
* handed out earlier. Reseeds from crypto_random_bytes() after
* DRBG_RESEED_INTERVAL octets. Not thread-safe; one per thread or
* caller-locked. */
#define DRBG_KEY_SIZE 32
#define DRBG_NONCE_SIZE 12
struct drbg_ctx
{
uint64_t since_reseed;
uint32_t counter;
uint8_t key[DRBG_KEY_SIZE];
uint8_t nonce[DRBG_NONCE_SIZE];
uint8_t seeded;
};
#define DRBG_RESEED_INTERVAL ((uint64_t)1 << 30)
/**
* drbg_init:
* @seed : Optional entropy; NULL seeds from
* crypto_random_bytes(). Any length is mixed
* through HKDF, so a caller with a nonce or
* personalisation string can append it.
*
* Returns: 0 on success, -1 when no entropy was available.
**/
int drbg_init(struct drbg_ctx *ctx, const uint8_t *seed, size_t seed_len);
/**
* drbg_reseed:
*
* Mixes fresh platform entropy (and @extra, if any) into the key.
**/
int drbg_reseed(struct drbg_ctx *ctx, const uint8_t *extra, size_t extra_len);
/**
* drbg_generate:
*
* Returns: 0 on success, -1 when the context is unseeded or a
* scheduled reseed found no entropy.
**/
int drbg_generate(struct drbg_ctx *ctx, uint8_t *out, size_t len);
void drbg_free(struct drbg_ctx *ctx);
/* NTLMv2 needs two things nothing else here does: MD4 (RFC 1320),
* the hash NT password hashes are still made of, and HMAC-MD5 over
* the MD5 in lrc_hash. They exist for that one protocol. */
#define MD4_DIGEST_SIZE 16
void md4(const uint8_t *data, size_t len, uint8_t *digest);
#define HMAC_MD5_SIZE 16
void hmac_md5(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t len, uint8_t *mac);
/**
* kbkdf_hmac_sha256:
*
* SP 800-108 KDF in counter mode with HMAC-SHA256: K(i) = HMAC(Ki,
* [i]_32 || label || 0x00 || context || [L]_32), the form SMB 3.x
* derives its signing, encryption and application keys with. The
* separator octet is included; @label and @context are passed as
* SMB spells them (including their trailing NUL).
**/
void kbkdf_hmac_sha256(const uint8_t *ki, size_t ki_len,
const uint8_t *label, size_t label_len,
const uint8_t *context, size_t context_len,
uint8_t *out, size_t out_len);
/**
* ntlm_ntowf_v2:
* @password, @user, @domain: UTF-8. The user name is upper-cased
* (ASCII) as NTOWFv2 requires; the domain is not.
* @out : 16 octets: HMAC-MD5(MD4(UTF-16LE(password)),
* UTF-16LE(UPPER(user) || domain)).
*
* Returns: 0 on success, -1 on a string that is not valid UTF-8 or is
* longer than 255 code points.
**/
int ntlm_ntowf_v2(const char *password, const char *user,
const char *domain, uint8_t *out);
RETRO_END_DECLS
#endif
+296
View File
@@ -0,0 +1,296 @@
/* Copyright (C) 2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (pk.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_CRYPTO_PK_H
#define _LIBRETRO_CRYPTO_PK_H
#include <stdint.h>
#include <stddef.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* Public-key side of the cleanroom stack: fixed-width bignum, RSA
* PKCS#1 v1.5 signing and verification, P-256 / P-384 ECDH and ECDSA
* verification, X25519, and ECDH / ECDSA on sect233r1. */
/* Fixed-width multi-precision arithmetic for the public-key code:
* little-endian arrays of bn_word, every operand of one call the
* same length k, Montgomery multiplication with double-word products. There
* is no dynamic sizing and no allocation in here; callers pick k from
* the modulus and keep the working storage themselves (on the heap
* for RSA - see rsa.c - since a 4096-bit operand is 512 bytes). */
/* 64-bit limbs with 128-bit products wherever the compiler has them
* (GCC and Clang on 64-bit targets): a Montgomery multiplication is
* a quarter of the word products of the 32-bit form. Everywhere else,
* 32-bit limbs with 64-bit products, which is plain C89. */
#if defined(__SIZEOF_INT128__) && !defined(BN_WORD_32)
typedef uint64_t bn_word;
__extension__ typedef unsigned __int128 bn_dword;
#define BN_WORD_BITS 64
#else
typedef uint32_t bn_word;
typedef uint64_t bn_dword;
#define BN_WORD_BITS 32
#endif
#define BN_WORD_BYTES (BN_WORD_BITS / 8)
/* 4096-bit RSA is the largest key in any CA bundle this tree ships. */
#define BN_MAX_WORDS (4096 / BN_WORD_BITS)
/* Words of scratch bn_mont_mul() and bn_mont_sqr() need for length k. */
#define BN_MONT_TMP_WORDS(k) (2 * (k) + 2)
void bn_from_be(bn_word *r, unsigned k, const uint8_t *in, size_t len);
void bn_to_be(uint8_t *out, size_t len, const bn_word *a, unsigned k);
void bn_zero(bn_word *r, unsigned k);
void bn_copy(bn_word *r, const bn_word *a, unsigned k);
int bn_is_zero(const bn_word *a, unsigned k);
/* -1, 0, 1; constant time in k. */
int bn_cmp(const bn_word *a, const bn_word *b, unsigned k);
bn_word bn_add(bn_word *r, const bn_word *a, const bn_word *b, unsigned k);
bn_word bn_sub(bn_word *r, const bn_word *a, const bn_word *b, unsigned k);
/* r = a + b mod m and r = a - b mod m, for a, b < m. */
void bn_mod_add(bn_word *r, const bn_word *a, const bn_word *b,
const bn_word *m, unsigned k);
void bn_mod_sub(bn_word *r, const bn_word *a, const bn_word *b,
const bn_word *m, unsigned k);
/* r = bit ? a : b, without branching on bit. */
void bn_select(bn_word *r, bn_word bit, const bn_word *a,
const bn_word *b, unsigned k);
/* Highest set bit + 1, 0 for zero. */
unsigned bn_bit_length(const bn_word *a, unsigned k);
unsigned bn_get_bit(const bn_word *a, unsigned i);
/* Montgomery machinery, R = 2^(BN_WORD_BITS * k). */
bn_word bn_mont_n0(const bn_word *m);
/* r2 = R^2 mod m. Only needs m odd. */
void bn_mont_r2(bn_word *r2, const bn_word *m, unsigned k);
/* r = a * b * R^-1 mod m, for a, b < m; @tmp has BN_MONT_TMP_WORDS(k). */
void bn_mont_mul(bn_word *r, const bn_word *a, const bn_word *b,
const bn_word *m, bn_word n0, unsigned k, bn_word *tmp);
/* r = a * a * R^-1 mod m: the off-diagonal products once, doubled,
* which is what an exponentiation spends most of its time on. */
void bn_mont_sqr(bn_word *r, const bn_word *a,
const bn_word *m, bn_word n0, unsigned k, bn_word *tmp);
/* r = a * b mod m (plain domain), through @r2 = R^2 mod m. */
void bn_mod_mul(bn_word *r, const bn_word *a, const bn_word *b,
const bn_word *m, const bn_word *r2, bn_word n0, unsigned k,
bn_word *tmp);
/* r = base^exp mod m, plain domain. exp has ek words. Left-to-right
* binary; exponents here are public (RSA e, curve orders). @work has
* 3k + BN_MONT_TMP_WORDS(k) words. */
void bn_mod_exp(bn_word *r, const bn_word *base,
const bn_word *exp, unsigned ek,
const bn_word *m, const bn_word *r2, bn_word n0, unsigned k,
bn_word *work);
/* RSASSA-PKCS1-v1_5 signature verification (RFC 8017 8.2.2), the
* only RSA operation a TLS client with a CA bundle needs. Public-key
* operands only, so nothing here is secret. */
enum rsa_hash
{
RSA_HASH_SHA256 = 0,
RSA_HASH_SHA384 = 1,
RSA_HASH_SHA512 = 2,
RSA_HASH_SHA1 = 3,
/* MD5 || SHA-1, 36 octets, without a DigestInfo (TLS 1.0, 1.1) */
RSA_HASH_MD5_SHA1 = 4
};
/**
* rsa_pkcs1_verify:
* @n, @n_len : Modulus, big-endian, up to 512 octets (4096 bits).
* @e, @e_len : Public exponent, big-endian.
* @digest : The already computed hash of the signed data,
* 32/48/64 octets for @hash.
* @sig, @sig_len : Signature, big-endian, @sig_len == the modulus
* length after leading zeros.
*
* Returns: 0 when the signature is valid, -1 otherwise (bad key or
* parameter, or verification failure; the two are not distinguished
* on purpose).
**/
int rsa_pkcs1_verify(const uint8_t *n, size_t n_len,
const uint8_t *e, size_t e_len,
enum rsa_hash hash, const uint8_t *digest, size_t digest_len,
const uint8_t *sig, size_t sig_len);
/**
* rsa_pkcs1_encrypt:
* RSAES-PKCS1-v1_5 encryption (RFC 8017 7.2.1) under a public key, as
* a TLS RSA key exchange sends its premaster secret.
* @out, @out_len : Out, @out_len == the modulus length.
*
* Returns: 0 on success, -1 on a bad key, a message longer than the
* modulus allows, or no randomness.
**/
int rsa_pkcs1_encrypt(const uint8_t *n, size_t n_len,
const uint8_t *e, size_t e_len,
const uint8_t *msg, size_t msg_len, uint8_t *out, size_t out_len);
/* An RSA private key, every member a big-endian integer pointing into
* memory the caller keeps (x509_rsa_private_key_parse() fills it from
* DER). Leading zero octets are allowed. */
struct rsa_private_key
{
const uint8_t *n; size_t n_len;
const uint8_t *e; size_t e_len;
const uint8_t *d; size_t d_len;
const uint8_t *p; size_t p_len;
const uint8_t *q; size_t q_len;
const uint8_t *dp; size_t dp_len; /* d mod (p - 1) */
const uint8_t *dq; size_t dq_len; /* d mod (q - 1) */
const uint8_t *qi; size_t qi_len; /* q^-1 mod p */
};
/**
* rsa_pkcs1_sign:
* RSASSA-PKCS1-v1_5 signature (RFC 8017 8.2.1) over an already
* computed @digest, through the CRT with an exponentiation whose
* operations and memory accesses do not depend on the key. p and q
* must have the same number of words. The signature is checked with
* the public exponent before it is returned.
* @sig, @sig_len : Out, @sig_len == the modulus length.
*
* Returns: 0 on success, -1 on a bad or inconsistent key or a
* parameter out of range (@sig is then zeroed).
**/
int rsa_pkcs1_sign(const struct rsa_private_key *key,
enum rsa_hash hash, const uint8_t *digest, size_t digest_len,
uint8_t *sig, size_t sig_len);
/**
* rsa_pss_verify:
* RSASSA-PSS with MGF1 over the same hash and a salt of the hash
* length (TLS 1.3 rsa_pss_rsae_*). Arguments as rsa_pkcs1_verify().
**/
int rsa_pss_verify(const uint8_t *n, size_t n_len,
const uint8_t *e, size_t e_len,
enum rsa_hash hash, const uint8_t *digest, size_t digest_len,
const uint8_t *sig, size_t sig_len);
/**
* x25519:
* RFC 7748 X25519: @out = @scalar * @u_le, all 32 octets little-endian
* as on the wire; the scalar is clamped here. Returns -1 when the
* result is the all-zero point (a low-order input), 0 otherwise.
* x25519_base() uses the base point 9.
**/
int x25519(uint8_t *out, const uint8_t *scalar, const uint8_t *u_le);
int x25519_base(uint8_t *out, const uint8_t *scalar);
/* NIST P-256 (secp256r1): ECDH for the TLS key exchange and ECDSA
* signature verification for certificates. Points on the wire are
* uncompressed SEC 1 encodings, 0x04 || X || Y. Field and scalar
* arithmetic run through bn's Montgomery machinery at k = 8. */
#define P256_SCALAR_SIZE 32
#define P256_POINT_SIZE 65
/**
* p256_keygen:
* @priv : 32 random octets; rejected when 0 or >= n, so
* the caller draws again (probability ~2^-32).
* @pub : 65 octets out.
*
* Returns: 0 on success, -1 on an unusable scalar.
**/
int p256_keygen(const uint8_t *priv, uint8_t *pub);
/**
* p256_ecdh:
* @shared : 32 octets out, the x coordinate.
*
* Validates @peer (on the curve, coordinates in range) first.
*
* Returns: 0 on success, -1 on a bad key or a result at infinity.
**/
int p256_ecdh(const uint8_t *priv, const uint8_t *peer, uint8_t *shared);
/**
* p256_ecdsa_verify:
* @digest : Message hash; the leftmost 32 octets are used
* (FIPS 186-4 6.4), shorter digests are allowed.
* @r, @s : 32 octets each, big-endian.
*
* Returns: 0 when valid, -1 otherwise.
**/
int p256_ecdsa_verify(const uint8_t *pub,
const uint8_t *digest, size_t digest_len,
const uint8_t *r, const uint8_t *s);
/* NIST P-384 (secp384r1), the same operations on 48-octet scalars and
* 97-octet points. Needed for the CA roots that carry P-384 keys. */
#define P384_SCALAR_SIZE 48
#define P384_POINT_SIZE 97
int p384_keygen(const uint8_t *priv, uint8_t *pub);
int p384_ecdh(const uint8_t *priv, const uint8_t *peer, uint8_t *shared);
int p384_ecdsa_verify(const uint8_t *pub,
const uint8_t *digest, size_t digest_len,
const uint8_t *r, const uint8_t *s);
/* SECG sect233r1 (NIST B-233), the binary curve of the Nintendo DSi,
* Wii, Wii U and 3DS certificates and tickets. Scalars, coordinates and
* signature halves are 30 octets big-endian; points are 0x04 || X || Y.
* A private key may be any 30 octets: it is reduced mod n, and rejected
* only when that leaves 0. Every operation with a private key runs a
* fixed-length Montgomery ladder. */
#define B233_SCALAR_SIZE 30
#define B233_POINT_SIZE 61
int b233_keygen(const uint8_t *priv, uint8_t *pub);
/**
* b233_ecdh:
* @shared : 30 octets out, the x coordinate.
*
* Validates @peer (on the curve, in the subgroup of order n) first.
*
* Returns: 0 on success, -1 on a bad key.
**/
int b233_ecdh(const uint8_t *priv, const uint8_t *peer, uint8_t *shared);
/**
* b233_ecdsa_sign:
* @digest : Message hash; its leftmost 233 bits are used.
* @r, @s : 30 octets each, out.
*
* The nonce is deterministic, RFC 6979 with HMAC-SHA256.
*
* Returns: 0 on success, -1 on an unusable private key.
**/
int b233_ecdsa_sign(const uint8_t *priv,
const uint8_t *digest, size_t digest_len,
uint8_t *r, uint8_t *s);
int b233_ecdsa_verify(const uint8_t *pub,
const uint8_t *digest, size_t digest_len,
const uint8_t *r, const uint8_t *s);
RETRO_END_DECLS
#endif
+230
View File
@@ -0,0 +1,230 @@
/* Copyright (C) 2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (x509.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_CRYPTO_X509_H
#define _LIBRETRO_CRYPTO_X509_H
#include <stdint.h>
#include <stddef.h>
#include <time.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* X.509 for a TLS client: parse a DER certificate, load the PEM trust
* bundle, and verify a server chain against it. Keys are RSA (up to
* 4096 bits), P-256 and P-384; signatures are RSA PKCS#1 v1.5 and ECDSA over
* SHA-256/384/512. Names are compared as DER octet strings. */
/* extendedKeyUsage as far as a TLS client cares */
#define X509_EKU_ABSENT 0 /* no extension: any use */
#define X509_EKU_SERVER_AUTH 1 /* serverAuth or anyExtendedKeyUsage listed */
#define X509_EKU_OTHER 2 /* present, without either */
enum x509_key_type
{
X509_KEY_NONE = 0,
X509_KEY_RSA,
X509_KEY_P256,
X509_KEY_P384
};
enum x509_sig_alg
{
X509_SIG_NONE = 0,
X509_SIG_RSA_SHA256,
X509_SIG_RSA_SHA384,
X509_SIG_RSA_SHA512,
X509_SIG_ECDSA_SHA256,
X509_SIG_ECDSA_SHA384,
X509_SIG_ECDSA_SHA512
};
/* Every pointer below points into the DER the certificate was parsed
* from, which the caller keeps alive for as long as the struct. */
struct x509_cert
{
const uint8_t *der; size_t der_len;
const uint8_t *tbs; size_t tbs_len; /* signed part */
const uint8_t *subject; size_t subject_len;
const uint8_t *issuer; size_t issuer_len;
const uint8_t *sig; size_t sig_len;
const uint8_t *rsa_n; size_t rsa_n_len;
const uint8_t *rsa_e; size_t rsa_e_len;
const uint8_t *san; size_t san_len; /* SubjectAltName value */
const uint8_t *cn; size_t cn_len; /* subject commonName */
const uint8_t *ec_point; /* 65 or 97 octets */
time_t not_before;
time_t not_after;
enum x509_key_type key_type;
enum x509_sig_alg sig_alg;
int is_ca; /* basicConstraints cA; -1 when absent */
int path_len; /* basicConstraints pathLenConstraint; -1 none */
int key_usage; /* keyUsage bits, -1 when absent */
int eku; /* X509_EKU_*: may this key authenticate a server? */
};
#define X509_KU_DIGITAL_SIGNATURE 0x80
#define X509_KU_KEY_ENCIPHERMENT 0x20
#define X509_KU_KEY_CERT_SIGN 0x04
/**
* x509_parse:
*
* Returns: 0 on success, -1 on malformed DER. An unsupported key or
* signature algorithm parses (type NONE) so a chain can still be
* walked past it and reported.
**/
int x509_parse(struct x509_cert *c, const uint8_t *der, size_t len);
struct rsa_private_key;
/**
* x509_rsa_private_key_parse:
* An RSAPrivateKey (PKCS#1) or a PrivateKeyInfo with one (PKCS#8),
* unencrypted DER. @key points into @der afterwards.
*
* Returns: 0 on success, -1 on anything else.
**/
int x509_rsa_private_key_parse(struct rsa_private_key *key,
const uint8_t *der, size_t len);
/**
* x509_verify_signature:
*
* Checks @c's signature under @issuer's key.
*
* Returns: 0 when valid, -1 otherwise.
**/
int x509_verify_signature(const struct x509_cert *c,
const struct x509_cert *issuer);
/**
* x509_verify_ecdsa_digest:
*
* Checks a DER ECDSA-Sig-Value @sig over an already computed @digest
* under @key's P-256 or P-384 key. What x509_verify_signature() uses
* for ECDSA, exposed for the TLS ServerKeyExchange signature.
*
* Returns: 0 when valid, -1 otherwise.
**/
int x509_verify_ecdsa_digest(const struct x509_cert *key,
const uint8_t *digest, size_t digest_len,
const uint8_t *sig, size_t sig_len);
/**
* x509_match_hostname:
*
* dNSName entries of the subjectAltName, with a wildcard allowed as
* the whole leftmost label; the subject commonName only when there is
* no subjectAltName at all. An IP address (see x509_parse_ip()) matches
* only an iPAddress entry, never a dNSName or the commonName.
*
* Returns: 0 on a match, -1 otherwise.
**/
int x509_match_hostname(const struct x509_cert *c, const char *host);
/**
* x509_parse_ip:
*
* @host as an IP address: dotted IPv4 (4 bytes, no leading zeros), or
* IPv6 with "::" compression and an optional trailing dotted IPv4,
* brackets and a "%zone" allowed (16 bytes). Returns 4 or 16 with the
* address in @out, 0 when @host is not an IP literal - a host name.
**/
int x509_parse_ip(const char *host, uint8_t out[16]);
/**
* x509_trust_load_pem:
* @pem, @len : Concatenated PEM certificates (cacert.h).
*
* Replaces the trust store with every certificate in @pem whose key
* this code can use. Idempotent per bundle: a second call with the
* same pointer and length is a no-op.
*
* Returns: number of anchors loaded, -1 out of memory.
**/
int x509_trust_load_pem(const char *pem, size_t len);
/**
* x509_trust_load_pem_parts:
*
* As x509_trust_load_pem() for a bundle kept in @count parts, each a
* run of whole certificates (net/cacert.h, split for compilers that
* refuse long string literals): all of them are loaded into the one
* trust store, the table standing for the source.
*
* Returns: the number of anchors loaded, or -1 on failure.
**/
int x509_trust_load_pem_parts(const char *const *parts, const size_t *lens,
unsigned count);
/**
* x509_trust_load_der_parts:
*
* As x509_trust_load_pem_parts() for a bundle kept as DER (net/cacert.h):
* each part is a run of whole DER certificates, read in place for the
* life of the store rather than decoded and copied.
*
* Returns: the number of anchors loaded, or -1 on failure.
**/
int x509_trust_load_der_parts(const unsigned char *const *parts,
const size_t *lens, unsigned count);
void x509_trust_free(void);
/**
* x509_verify_chain:
* @ders, @lens, @n : The server's chain, leaf first, as sent.
* @host : Expected hostname, or NULL to skip the check.
* @now : Current time for the validity check.
* @info, @info_len : On failure, a short reason for logging.
*
* Builds the path leaf -> ... -> anchor using the chain's own
* certificates and the trust store, checking signatures, validity,
* basicConstraints and keyUsage on the way.
*
* Returns: 0 when the leaf chains to a trust anchor and matches
* @host, -1 otherwise.
**/
int x509_verify_chain(const uint8_t **ders, const size_t *lens, unsigned n,
const char *host, time_t now, char *info, size_t info_len);
/* The leaf's key encrypts a premaster secret (a TLS RSA key exchange)
* rather than signing: its keyUsage must allow keyEncipherment. */
#define X509_VERIFY_KEY_ENCIPHERMENT 1
/**
* x509_verify_chain_ex:
* As x509_verify_chain(), against @trust_count DER certificates in
* place of the trust store when @trust is not NULL, and with
* X509_VERIFY_* @flags.
**/
int x509_verify_chain_ex(const uint8_t **ders, const size_t *lens, unsigned n,
const char *host, time_t now,
const uint8_t *const *trust, const size_t *trust_lens, unsigned trust_count,
unsigned flags, char *info, size_t info_len);
RETRO_END_DECLS
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef _LIBRETRO_ENCODINGS_BASE64_H
#define _LIBRETRO_ENCODINGS_BASE64_H
#include <stdint.h>
#include <stddef.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/**
* Encodes binary data into a \c NULL-terminated base64 string.
*
* @param binaryData The data to encode.
* Behavior is undefined if \c NULL.
* @param len The length of the data to encode.
* @param flen Pointer to the length of the returned string.
* @return Pointer to the base64-encoded string, or \c NULL on failure.
* The returned string is owned by the caller and must be released with \c free().
* @see unbase64
*/
char* base64(const void* binaryData, int len, int *flen);
/**
* Decodes a base64-encoded string into binary data.
*
* @param ascii The base64 string to decode, in ASCII format.
* @param len Length of the string to decode.
* @param flen Pointer to the length of the returned data.
* @return The decoded binary data, or \c NULL on failure.
* The returned buffer is owned by the caller and must be released with \c free().
* @see base64
*/
unsigned char* unbase64(const char* ascii, int len, int *flen);
RETRO_END_DECLS
#endif
+328
View File
@@ -0,0 +1,328 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (utf.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_ENCODINGS_UTF_H
#define _LIBRETRO_ENCODINGS_UTF_H
#include <stdint.h>
#include <stddef.h>
#include <boolean.h>
#include <retro_common_api.h>
#include <retro_inline.h>
RETRO_BEGIN_DECLS
enum CodePage
{
CODEPAGE_LOCAL = 0, /* CP_ACP */
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
};
/**
* utf8_conv_utf32:
*
* Simple implementation. Assumes the sequence is
* properly synchronized and terminated.
**/
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size);
/**
* utf16_conv_utf8:
*
* Leaf function.
**/
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size);
/**
* utf8len:
*
* Leaf function.
**/
size_t utf8len(const char *string);
/**
* utf8cpy:
*
* Acts mostly like strlcpy.
*
* Copies the given number of UTF-8 characters,
* but at most @d_len bytes.
*
* Always NULL terminates. Does not copy half a character.
* @s is assumed valid UTF-8.
* Use only if @chars is considerably less than @d_len.
*
* Hidden non-leaf function cost:
* - Calls memcpy
*
* @return Number of bytes.
**/
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
/**
* utf8skip:
*
* Leaf function
**/
const char *utf8skip(const char *str, size_t chars);
/**
* utf8_walk:
*
* Does not validate the input.
*
* Leaf function.
*
* @return Returns garbage if it's not UTF-8.
**/
uint32_t utf8_walk(const char **string);
/* utf8_walk with its ASCII case inline, for loops that walk text a
* codepoint at a time every frame: a call per character is most of
* what such a loop costs when the text is ASCII. */
static INLINE uint32_t utf8_walk_inline(const char **string)
{
const uint8_t *s = (const uint8_t*)*string;
if (*s < 0x80)
{
*string = (const char*)(s + 1);
return *s;
}
return utf8_walk(string);
}
/**
* utf16_to_char_string:
**/
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
/**
* utf8_to_local_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *utf8_to_local_string_alloc(const char *str);
/**
* local_to_utf8_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *local_to_utf8_string_alloc(const char *str);
/**
* local_to_utf8_string:
*
* Same conversion as local_to_utf8_string_alloc(), into a caller buffer.
* Targets where the local codepage needs no conversion - Xbox, and any
* UNICODE build - copy rather than allocate. Truncates like strlcpy().
*
* @return false only if the conversion itself failed.
**/
bool local_to_utf8_string(const char *in, char *s, size_t len);
/**
* utf8_to_utf16_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
wchar_t *utf8_to_utf16_string_alloc(const char *str);
/**
* utf16_to_utf8_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *utf16_to_utf8_string_alloc(const wchar_t *str);
/* Which of the two conversion families above a Win32 call needs.
*
* The A entry points take a string in the local codepage
* (utf8_to_local_string_alloc); the W ones take UTF-16
* (utf8_to_utf16_string_alloc). Which is correct is a property of the
* target, not a preference, and it lives here because choosing between
* those two families is the whole of the question.
*
* LEGACY_WIN32 selects the ANSI entry points, and there are exactly
* three ways to earn it.
*
* 1. Xbox, where the W entry points are not stubs - they are absent.
* The 2002 XDK's WinBase.h declares CreateFileA and #defines
* CreateFile to it; there is no CreateFileW anywhere in the header,
* nor GetFileAttributesW, FindFirstFileW, DeleteFileW, MoveFileW,
* CreateDirectoryW or RemoveDirectoryW. The only W entry points it
* has are string helpers: lstrlenW, lstrcpyW, lstrcatW, lstrcmpW,
* lstrcmpiW, lstrcpynW, wsprintfW, wvsprintfW, CharUpperW,
* CharLowerW, IsBadStringPtrW, OutputDebugStringW. A W filesystem
* call there does not fail at runtime, it fails to link.
*
* (The CRT is the odd one out: _wopen, _wsopen, _wcreat, _wfopen
* and _wstat64 are all declared, and _wopen really does ship in
* libcmt.lib. But the Win32 layer underneath is ANSI-only, so the
* branch has to go ANSI as a whole.)
*
* 2. Windows 95/98/Me, where the W entry points exist but are stubs
* that fail with ERROR_CALL_NOT_IMPLEMENTED - the reason Microsoft
* shipped MSLU/unicows.dll at all. Only a small documented set
* (the lstr* family, MultiByteToWideChar, WideCharToMultiByte, the
* text-out calls) actually works.
*
* Detected by _WIN32_WINDOWS, which is the macro that means "this
* build targets 9x" - w32api.h spells out the scale: Windows95
* 0x0400, Windows98 0x0410, WindowsME 0x0500 - or by HAVE_WIN9X,
* for build systems that would rather say it in one place.
*
* 3. Anything a build forces with -DLEGACY_WIN32.
*
* WinRT/UWP is never legacy, Xbox or not: the ANSI entry points are
* outside the app API partition there, so it needs the W path.
*
* What this deliberately does NOT key off is _WIN32_WINNT, which is
* what six separate copies of this test used to do:
*
* #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
*
* That is the NT scale, and reading it as a 9x test was wrong in both
* directions. It caught NT 3.51 and NT 4.0 targets, which are
* Unicode-native - W is the real entry point on NT and A is the
* wrapper - so it forced ANSI on the one family that never needed it,
* discarding every path not representable in the ANSI codepage to do
* it. And it missed 9x unless someone happened to set an NT macro
* low, which a 9x build has no reason to do: mingw-w64 does not
* predefine _WIN32_WINNT, and sdkddkver.h defaults it to 0x0A00 when
* the build is silent, so the default configuration took the W path on
* the exact platform the test existed to protect.
*
* The two scales even collide: _WIN32_WINDOWS 0x0500 is Windows Me,
* while _WIN32_WINNT 0x0500 is Windows 2000.
*/
#if !defined(LEGACY_WIN32) && defined(_WIN32) && !defined(__WINRT__)
#if defined(_XBOX) || defined(_WIN32_WINDOWS) || defined(HAVE_WIN9X)
#define LEGACY_WIN32
#endif
#endif
/* Runtime selection, for the one binary that has to be both.
*
* Everything above is compile-time: a build is ANSI or it is wide, and
* a 9x-capable executable gives up Unicode paths on NT to get there.
* HAVE_WIN9X_RUNTIME compiles both branches and picks per call from a
* cached predicate instead.
*
* Opt-in, because it is not free: both branches in the binary, and a
* branch the other configurations do not execute. A build that does
* not ask keeps exactly the compile-time selection above and pays
* nothing - not the code, not the test.
*
* Never available where one of the two branches cannot exist:
*
* _XBOX - the W entry points are absent, so the wide branch
* would not link, never mind not run.
* __WINRT__ - the ANSI entry points are outside the app API
* partition, so the legacy branch would not compile.
* LEGACY_WIN32 forced - the build has already said what it wants.
*
* The predicate is inline in this header on purpose: a new .c file in
* libretro-common is a line every consuming repo has to add to its own
* build, for a feature almost none of them want. One cached int per
* translation unit costs a probe each rather than one, which is 16 us
* of startup in the only configuration that compiles it at all.
*/
#if defined(HAVE_WIN9X_RUNTIME) && defined(_WIN32) \
&& !defined(_XBOX) && !defined(__WINRT__) && !defined(LEGACY_WIN32)
#define LEGACY_WIN32_RUNTIME 1
#endif
#ifdef LEGACY_WIN32_RUNTIME
/* _WINDOWS_ is the include guard both the Microsoft and the mingw-w64
* windows.h define, so this pulls it in only where a translation unit
* has not already. */
#ifndef _WINDOWS_
#ifndef NOMINMAX
#define NOMINMAX /* MSVC defines min/max as macros in C++ too */
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
/* Non-zero when the W entry points are not usable, i.e. on 95/98/Me.
*
* A feature probe rather than a version check. GetVersionEx would
* work - dwPlatformId is VER_PLATFORM_WIN32_WINDOWS only on 9x, and
* that field is not touched by the version lie a manifest-less process
* gets on 8.1 and up - but it answers the question next to the one
* being asked. What matters is not which Windows this is, it is
* whether the W entry points do anything, and that is directly
* observable: on 9x they are stubs that set ERROR_CALL_NOT_IMPLEMENTED.
* Asking the thing itself also credits a wrapper that implements them
* - MSLU/unicows, wine, anything later - rather than talking past it.
*
* GetFileAttributesW on "." is the cheapest probe there is: no handle,
* no allocation, a path that exists everywhere. Its return value is
* discarded, since INVALID_FILE_ATTRIBUTES is a perfectly good answer
* from a real implementation in some sandbox; only the error code
* carries the signal.
*
* The cache is a plain int with no interlock. Two threads racing both
* call the same stub, compute the same value and store it; losing the
* race costs a second probe and nothing else. */
static INLINE int win32_needs_local_encoding(void)
{
static int cached = -1;
if (cached < 0)
{
SetLastError(NO_ERROR);
GetFileAttributesW(L".");
cached = (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) ? 1 : 0;
}
return cached;
}
#endif
/* Migration aid for out-of-tree builds that expressed "old Windows"
* the only way this used to understand. Silent for anything that has
* already said what it means, and it never changes the outcome - the
* decision above is already made. */
#if !defined(LEGACY_WIN32) && !defined(LEGACY_WIN32_NO_MIGRATION_NOTE)
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 && !defined(_XBOX) && !defined(__WINRT__)
#if defined(_MSC_VER)
#pragma message("libretro-common: a low _WIN32_WINNT no longer implies the ANSI Win32 path. NT is Unicode-native; if this target runs on Windows 95/98/Me, define _WIN32_WINDOWS (or HAVE_WIN9X).")
#elif defined(__GNUC__)
#warning "libretro-common: a low _WIN32_WINNT no longer implies the ANSI Win32 path. NT is Unicode-native; if this target runs on Windows 95/98/Me, define _WIN32_WINDOWS (or HAVE_WIN9X)."
#endif
#endif
#endif
RETRO_END_DECLS
#endif
@@ -0,0 +1,154 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (features_cpu.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_SDK_CPU_INFO_H
#define _LIBRETRO_SDK_CPU_INFO_H
#include <retro_common_api.h>
#include <stdint.h>
#include <libretro.h>
RETRO_BEGIN_DECLS
/**
* Gets the time in ticks since some unspecified epoch.
* The notion of a "tick" varies per platform.
*
* The epoch may change between devices or across reboots.
*
* Suitable for use as a default implementation of \c retro_perf_callback::get_perf_counter,
* (or as a fallback by the core),
* although a frontend may provide its own implementation.
*
* @return The current time, in ticks.
* @see retro_perf_callback::get_perf_counter
*/
retro_perf_tick_t cpu_features_get_perf_counter(void);
/**
* Gets the time in microseconds since some unspecified epoch.
*
* The epoch may change between devices or across reboots.
*
* Suitable for use as a default implementation of \c retro_perf_callback::get_time_usec,
* (or as a fallback by the core),
* although a frontend may provide its own implementation.
*
* @return The current time, in microseconds.
* @see retro_perf_callback::get_time_usec
*/
retro_time_t cpu_features_get_time_usec(void);
/**
* retro_sleep_until_us:
* @deadline : an instant on cpu_features_get_time_usec()'s clock.
*
* Sleeps until that clock reads at least @deadline. Absolute where the
* platform offers it (clock_nanosleep TIMER_ABSTIME on Linux and
* Android, mach_wait_until on Darwin), a re-armed high-resolution
* relative wait elsewhere. Never early against that clock; may be
* late, as every sleep may. Not available to the salamander launchers,
* which do not link features_cpu.c.
**/
void retro_sleep_until_us(retro_time_t deadline);
/**
* Returns the available features (mostly SIMD extensions)
* supported by this CPU.
*
* Suitable for use as a default implementation of \c retro_perf_callback::get_time_usec,
* (or as a fallback by the core),
* although a frontend may provide its own implementation.
*
* @return Bitmask of all CPU features available.
* @see RETRO_SIMD
* @see retro_perf_callback::get_cpu_features
*/
uint64_t cpu_features_get(void);
#if (defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310)) && !defined(__MACH__)
/**
* Executes the x86 CPUID instruction for the requested leaf, writing the
* EAX, EBX, ECX and EDX result registers into flags[0], flags[1], flags[2]
* and flags[3] respectively. Available on x86/x86-64 targets only.
*
* @param func The CPUID leaf (function number) to query.
* @param[out] flags Array of four 32-bit words receiving EAX/EBX/ECX/EDX.
*/
void x86_cpuid(uint32_t func, int32_t flags[4]);
#endif
/**
* @return The number of logical processors available -- hardware
* threads rather than physical cores, so an SMT part reports both of
* each core -- or 1 if it could not be determined.
*
* @see cpu_features_get_core_amount_physical
*/
unsigned cpu_features_get_core_amount(void);
/**
* The count to size work by where a thread wants a core to itself,
* rather than the thread count cpu_features_get_core_amount() returns.
*
* @return The number of physical cores available, or the value
* cpu_features_get_core_amount() gives where the distinction cannot be
* drawn, which is never larger and so stays a safe answer.
*/
unsigned cpu_features_get_core_amount_physical(void);
/**
* Order the processors best-first, for a caller pinning a thread that
* wants the strongest processor still free.
*
* Entries are operating system processor identifiers, the numbering an
* affinity mask is built from, ordered by descending core performance
* (the fast class first, then the larger last-level cache, then the
* higher clock) and with an SMT sibling placed after the processor it
* shares a core with. Processors outside the calling thread's affinity
* mask are left out. Where the platform publishes no topology the order is simply
* ascending, which names every processor exactly once and so remains
* usable, just unranked.
*
* @param s Receives the identifiers.
* @param len Number of entries @s has room for.
* @return The number of entries written, which is 0 where no
* identifiers could be established and the caller should leave
* affinity alone.
*/
size_t cpu_features_get_processor_order(unsigned *s, size_t len);
/**
* Returns the name of the CPU model.
*
* @param[out] name Pointer to a buffer to store the name.
* Will be \c NULL-terminated.
* If \c NULL, this value will not be modified.
* @param len The amount of space available in \c name.
*/
void cpu_features_get_model_name(char *name, int len);
RETRO_END_DECLS
#endif
+760
View File
@@ -0,0 +1,760 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_FILE_PATH_H
#define __LIBRETRO_SDK_FILE_PATH_H
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#include <libretro.h>
#include <retro_common_api.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
#define PATH_REQUIRED_VFS_VERSION 3
#define STAT64_REQUIRED_VFS_VERSION 4
#define METADATA_REQUIRED_VFS_VERSION 5
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info);
/* Order in this enum is equivalent to negative sort order in filelist
* (i.e. DIRECTORY is on top of PLAIN_FILE) */
enum
{
RARCH_FILETYPE_UNSET,
RARCH_PLAIN_FILE,
RARCH_COMPRESSED_FILE_IN_ARCHIVE,
RARCH_COMPRESSED_ARCHIVE,
RARCH_DIRECTORY,
RARCH_FILE_UNSUPPORTED
};
struct path_linked_list
{
char *path;
struct path_linked_list *next;
};
/**
* Create a new linked list with one item in it
* The path on this item will be set to NULL
**/
struct path_linked_list* path_linked_list_new(void);
/* Free the entire linked list */
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
/**
* Add a node to the linked list with this path
* If the first node's path if it's not yet set,
* set this instead
**/
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
/**
* path_is_compressed_file:
* @path : path
*
* Checks if path is a compressed file.
*
* Returns: true (1) if path is a compressed file, otherwise false (0).
**/
bool path_is_compressed_file(const char *path);
/**
* path_contains_compressed_file:
* @path : path
*
* Checks if path contains a compressed file.
*
* Currently we only check for hash symbol (#) inside the pathname.
* If path is ever expanded to a general URI, we should check for that here.
*
* Example: Somewhere in the path there might be a compressed file
* E.g.: /path/to/file.7z#mygame.img
*
* Returns: true (1) if path contains compressed file, otherwise false (0).
**/
#define path_contains_compressed_file(path) (path_get_archive_delim((path)) != NULL)
/**
* path_get_archive_delim:
* @path : path
*
* Find delimiter of an archive file. Only the first '#'
* after a compression extension is considered.
*
* @return pointer to the delimiter in the path if it contains
* a path inside a compressed file, otherwise NULL.
**/
const char *path_get_archive_delim(const char *path);
/**
* path_get_extension:
* @path : path
*
* Gets extension of file. Only '.'s
* after the last slash are considered.
*
* Hidden non-leaf function cost:
* - calls strrchr
*
* @return extension part from the path.
**/
const char *path_get_extension(const char *path);
/**
* path_get_extension_mutable:
* @path : path
*
* Specialized version of path_get_extension(). Return
* value is mutable.
*
* Gets extension of file. Only '.'s
* after the last slash are considered.
*
* @return extension part from the path.
**/
char *path_get_extension_mutable(const char *path);
/**
* path_remove_extension:
* @path : path
*
* Mutates path by removing its extension. Removes all
* text after and including the last '.'.
* Only '.'s after the last slash are considered.
*
* Hidden non-leaf function cost:
* - calls strrchr
*
* @return
* 1) If path has an extension, returns path with the
* extension removed.
* 2) If there is no extension, returns NULL.
* 3) If path is empty or NULL, returns NULL
*/
char *path_remove_extension(char *path);
/**
* path_basename:
* @path : path
*
* Get basename from @path.
*
* Hidden non-leaf function cost:
* - Calls path_get_archive_delim()
*
* @return basename from path.
**/
const char *path_basename(const char *path);
/**
* path_basename_nocompression:
* @path : path
*
* Specialized version of path_basename().
* Get basename from @path.
*
* @return basename from path.
**/
const char *path_basename_nocompression(const char *path);
/**
* path_basedir:
* @path : path
*
* Extracts base directory by mutating path.
* Keeps trailing '/'.
**/
size_t path_basedir(char *path);
/**
* path_parent_dir:
* @s : path
* @len : size of buffer
*
* Extracts parent directory by mutating path.
* Assumes that path is a directory. Keeps trailing '/'.
* If the path was already at the root directory, returns empty string
**/
size_t path_parent_dir(char *s, size_t len);
/**
* path_resolve_realpath:
* @s : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
* Resolves use of ".", "..", multiple slashes etc in absolute paths.
*
* Relative paths are rebased on the current working dir.
*
* @return @s if successful, NULL otherwise.
* Note: Not implemented on consoles
* Note: Symlinks are only resolved on Unix-likes
* Note: The current working dir might not be what you expect,
* e.g. on Android it is "/"
* Use of fill_pathname_resolve_relative() should be preferred
**/
char *path_resolve_realpath(char *s, size_t len, bool resolve_symlinks);
/**
* path_relative_to:
* @s : buffer to write the relative path to
* @path : path to be expressed relatively
* @base : relative to this
* @len : size of output buffer
*
* Turns @path into a path relative to @base and writes it to @s.
*
* @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
* Both @path and @base are assumed to be absolute paths without "." or "..".
*
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
*
* @return Length of the string copied into @s
**/
size_t path_relative_to(char *s, const char *path, const char *base,
size_t len);
/**
* path_is_absolute:
* @path : path
*
* Checks if @path is an absolute path or a relative path.
*
* @return true if path is absolute, false if path is relative.
**/
bool path_is_absolute(const char *path);
/**
* fill_pathname:
* @s : output path
* @in_path : input path
* @replace : what to replace
* @len : buffer size of output path
*
* FIXME: Verify
*
* Replaces filename extension with 'replace' and outputs result to s.
* The extension here is considered to be the string from the last '.'
* to the end.
*
* Only '.'s after the last slash are considered as extensions.
* If no '.' is present, in_path and replace will simply be concatenated.
* 'len' is buffer size of 's'.
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
* s = "/foo/bar/baz/boo.asm"
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
* s = "/foo/bar/baz/boo"
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
* - calls strrchr
* - calls strlcat
*
* @return Length of the string copied into @s
*/
size_t fill_pathname(char *s, const char *in_path,
const char *replace, size_t len);
/**
* fill_dated_filename:
* @s : output filename
* @ext : extension of output filename
* @len : buffer size of output filename
*
* Creates a 'dated' filename prefixed by 'RetroArch', and
* concatenates extension (@ext) to it.
*
* E.g.:
* s = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls rtime_localtime()
* - Calls strftime
* - Calls strlcpy
*
**/
size_t fill_dated_filename(char *s, const char *ext, size_t len);
/**
* fill_str_dated_filename:
* @s : output filename
* @in_str : input string
* @ext : extension of output filename
* @len : buffer size of output filename
*
* Creates a 'dated' filename prefixed by the string @in_str, and
* concatenates extension (@ext) to it.
*
* E.g.:
* s = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls time
* - Calls rtime_localtime()
* - Calls strlcpy (at least once)
* - Calls strftime
*
* @return Length of the string copied into @s
**/
size_t fill_str_dated_filename(char *s, const char *in_str, const char *ext, size_t len);
/**
* find_last_slash:
* @str : path
*
* Find last slash in path. Tries to find
* a backslash on Windows too which takes precedence
* over regular slash.
*
* Leaf function.
*
* @return pointer to last slash/backslash found in @str.
**/
char *find_last_slash(const char *str);
/**
* fill_pathname_dir:
* @s : input directory path
* @in_basename : input basename to be appended to @s
* @replace : replacement to be appended to @in_basename
* @len : size of buffer
*
* Appends basename of 'in_basename', to 's', along with 'replace'.
* Basename of in_basename is the string after the last '/' or '\\',
* i.e the filename without directories.
*
* If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
* 'len' is buffer size of 's'.
*
* E.g..: s = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => s = "/tmp/some_dir/foo.c.asm"
*
* Hidden non-leaf function cost:
* - Calls fill_pathname_slash()
* - Calls path_basename()
* - Calls strlcpy 2x
**/
size_t fill_pathname_dir(char *s, const char *in_basename,
const char *replace, size_t len);
/**
* fill_pathname_base:
* @s : output path
* @in_path : input path
* @len : size of output path
*
* Copies basename of @in_path into @s.
*
* Hidden non-leaf function cost:
* - Calls path_basename()
* - Calls strlcpy
*
* @return length of the string copied into @s
**/
size_t fill_pathname_base(char *s, const char *in_path, size_t len);
/**
* fill_pathname_basedir:
* @s : output directory
* @in_path : input path
* @len : size of output directory
*
* Copies base directory of @in_path into @s.
* If in_path is a path without any slashes (relative current directory),
* @s will get path "./".
*
* Hidden non-leaf function cost:
* - Calls strlcpy
* - Calls path_basedir()
**/
size_t fill_pathname_basedir(char *s, const char *in_path, size_t len);
/**
* fill_pathname_parent_dir_name:
* @s : output string
* @in_dir : input directory
* @len : size of @s
*
* Copies only the parent directory name of @in_dir into @s.
* The two buffers must not overlap. Removes trailing '/'.
*
* Hidden non-leaf function cost:
* - Can call strlcpy
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_parent_dir_name(char *s,
const char *in_dir, size_t len);
/**
* fill_pathname_parent_dir:
* @s : output directory
* @in_dir : input directory
* @len : size of output directory
*
* Copies parent directory of @in_dir into @s.
* Assumes @in_dir is a directory. Keeps trailing '/'.
* If the path was already at the root directory, @s will be an empty string.
*
* Hidden non-leaf function cost:
* - Can call strlcpy if (@s!= @in_dir)
* - Calls strlen if (@s == @in_dir)
* - Calls path_parent_dir()
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_parent_dir(char *s,
const char *in_dir, size_t len);
/**
* fill_pathname_resolve_relative:
* @s : output path
* @in_refpath : input reference path
* @in_path : input path
* @len : size of @s
*
* Joins basedir of @in_refpath together with @in_path.
* If @in_path is an absolute path, s = in_path.
* E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
* s = "/foo/bar/foobar.cg".
**/
void fill_pathname_resolve_relative(char *s, const char *in_refpath,
const char *in_path, size_t len);
/**
* fill_pathname_join:
* @s : output path
* @dir : directory
* @path : path
* @len : size of output path
*
* Joins a directory (@dir) and path (@path) together.
* Makes sure not to get two consecutive slashes
* between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy at least once
* - calls fill_pathname_slash()
*
* Deprecated. Use fill_pathname_join_special() instead
* if you can ensure @dir != @s
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_join(char *s, const char *dir,
const char *path, size_t len);
/**
* fill_pathname_join_special:
* @s : output path
* @dir : directory
* @path : path
* @len : size of output path
*
* Macro alias of fill_pathname_join(); the two are identical.
* Kept for source compatibility with existing callers in
* RetroArch and downstream cores. Each argument expands exactly
* once, so arguments with side effects are safe. The distinct
* identifier fill_pathname_join_special_ext() is unaffected.
*
* @dir and @s may be identical (the join is then performed in
* place); buffers that overlap without being identical are
* undefined.
*
* Joins a directory (@dir) and path (@path) together.
* Makes sure not to get two consecutive slashes
* between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
*
* @return Length of the string copied into @s
**/
#define fill_pathname_join_special(s, dir, path, len) \
fill_pathname_join((s), (dir), (path), (len))
size_t fill_pathname_join_special_ext(char *s,
const char *dir, const char *path,
const char *last, const char *ext,
size_t len);
/**
* fill_pathname_join_delim:
* @s : output path
* @dir : directory
* @path : path
* @delim : delimiter
* @len : size of output path
*
* Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim).
*
* Hidden non-leaf function cost:
* - can call strlen
* - can call strlcpy
* - can call strlcat
**/
size_t fill_pathname_join_delim(char *s, const char *dir,
const char *path, const char delim, size_t len);
size_t fill_pathname_expand_special(char *s,
const char *in_path, size_t len);
size_t fill_pathname_abbreviate_special(char *s,
const char *in_path, size_t len);
/**
* fill_pathname_abbreviated_or_relative:
*
* Fills the supplied path with either the abbreviated path or
* the relative path, which ever one has less depth / number of slashes
*
* If lengths of abbreviated and relative paths are the same,
* the relative path will be used
* @in_path can be an absolute, relative or abbreviated path
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_abbreviated_or_relative(char *s,
const char *in_refpath, const char *in_path, size_t len);
/**
* sanitize_path_part:
*
* @path_part : directory or filename
* @len : length of path_part
*
* Takes single part of a path eg. single filename
* or directory, and removes any special chars that are
* unavailable.
*
* @returns newly allocated string that has been sanitized.
* Caller is responsible for freeing the returned string.
**/
char *sanitize_path_part(const char *path_part, size_t len);
/**
* pathname_conform_slashes_to_os:
*
* @path : path
*
* Leaf function.
*
* Changes the slashes to the correct kind for the os
* So forward slash on linux and backslash on Windows
**/
void pathname_conform_slashes_to_os(char *s);
/**
* pathname_make_slashes_portable:
* @path : path
*
* Leaf function.
*
* Change all slashes to forward so they are more
* portable between Windows and Linux
**/
void pathname_make_slashes_portable(char *s);
/**
* path_basedir:
* @path : path
*
* Extracts base directory by mutating path.
* Keeps trailing '/'.
**/
void path_basedir_wrapper(char *s);
/**
* path_char_is_slash:
* @c : character
*
* Checks if character (@c) is a slash.
*
* @return true if character is a slash, otherwise false.
**/
#ifdef _WIN32
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
#else
#define PATH_CHAR_IS_SLASH(c) ((c) == '/')
#endif
/**
* path_default_slash and path_default_slash_c:
*
* Gets the default slash separator.
*
* @return default slash separator.
**/
#ifdef _WIN32
#define PATH_DEFAULT_SLASH() "\\"
#define PATH_DEFAULT_SLASH_C() '\\'
#else
#define PATH_DEFAULT_SLASH() "/"
#define PATH_DEFAULT_SLASH_C() '/'
#endif
/**
* fill_pathname_slash:
* @s : path
* @len : size of path
*
* Assumes path is a directory. Appends a slash
* if not already there.
* Hidden non-leaf function cost:
* - can call strlcat once if it returns false
* - calls strlen
**/
size_t fill_pathname_slash(char *s, size_t len);
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
size_t fill_pathname_application_path(char *s, size_t len);
size_t fill_pathname_application_dir(char *s, size_t len);
size_t fill_pathname_home_dir(char *s, size_t len);
#endif
/**
* path_mkdir:
* @dir : directory
*
* Create directory on filesystem.
*
* Recursive function.
*
* Hidden non-leaf function cost:
* - Calls strdup
* - Calls path_parent_dir()
* - Calls strcmp
* - Calls path_is_directory()
* - Calls path_mkdir()
*
* @return true if directory could be created, otherwise false.
**/
bool path_mkdir(const char *dir);
/**
* path_rmdir:
* @dir : directory path.
*
* Removes the empty directory @dir.
*
* @return true if the directory was removed, otherwise false -
* including a directory that is not empty, and platforms with no
* directory removal, where an empty directory is simply left.
**/
bool path_rmdir(const char *dir);
/**
* path_is_directory:
* @path : path
*
* Checks if path is a directory.
*
* @return true if path is a directory, otherwise false.
*/
bool path_is_directory(const char *path);
/* Time format strings with AM-PM designation require special
* handling due to platform dependence
* @return Length of the string written to @s
*/
size_t strftime_am_pm(char *s, size_t len, const char* format,
const void* timeptr);
bool path_is_character_special(const char *path);
int path_stat(const char *path);
bool path_is_valid(const char *path);
/**
* path_set_private:
* @path : file
*
* Restricts @path to the owning user (0600 on POSIX). Use for files
* that hold secrets. A no-op that returns true on platforms where
* the location is already private or has no permission model.
*
* @return true if the permissions are restricted (or nothing needed
* doing), false if the platform supports it and it failed.
**/
bool path_set_private(const char *path);
/**
* path_is_readonly:
* @path : path
*
* Whether the current user cannot write to @path. Reads the
* RETRO_VFS_STAT_IS_READONLY flag, which frontends older than
* VFS API v5 never set, so the answer there is always false.
*
* @return true if @path exists and is read-only.
**/
bool path_is_readonly(const char *path);
/**
* path_set_readonly:
* @path : path
* @readonly : true to make read-only, false to make writable
*
* POSIX: toggles the write bits. Windows: FILE_ATTRIBUTE_READONLY.
*
* @return true on success, false if unsupported on this platform,
* the file system, or the negotiated VFS version (< 5).
**/
bool path_set_readonly(const char *path, bool readonly);
/**
* path_get_mtime:
* @path : path
* @mtime : receives seconds since 1970-01-01T00:00:00Z
*
* @return true on success, false if unavailable.
**/
bool path_get_mtime(const char *path, int64_t *mtime);
/**
* path_set_mtime:
* @path : path
* @mtime : seconds since 1970-01-01T00:00:00Z
*
* @return true on success, false if unsupported or it failed.
**/
bool path_set_mtime(const char *path, int64_t mtime);
int64_t path_get_size(const char *path);
bool is_path_accessible_using_standard_io(const char *path);
RETRO_END_DECLS
#endif
+83
View File
@@ -0,0 +1,83 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (libco.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBCO_H
#define LIBCO_H
#include <retro_common_api.h>
#ifdef LIBCO_C
#ifdef LIBCO_MP
#ifdef _MSC_VER
#define thread_local __declspec(thread)
#else
#define thread_local __thread
#endif
#else
#define thread_local
#endif
#endif
RETRO_BEGIN_DECLS
typedef void* cothread_t;
/**
* co_active:
*
* Gets the currently active context.
*
* Returns: active context.
**/
cothread_t co_active(void);
/**
* co_create:
* @int : stack size
* @funcptr : thread entry function callback
*
* Create a co_thread.
*
* Returns: cothread if successful, otherwise NULL.
*/
cothread_t co_create(unsigned int, void (*)(void));
/**
* co_delete:
* @cothread : cothread object
*
* Frees a co_thread.
*/
void co_delete(cothread_t cothread);
/**
* co_switch:
* @cothread : cothread object to switch to
*
* Do a context switch to @cothread.
*/
void co_switch(cothread_t cothread);
RETRO_END_DECLS
/* ifndef LIBCO_H */
#endif
File diff suppressed because it is too large. Load diff
+211
View File
@@ -0,0 +1,211 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (string_list.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_STRING_LIST_H
#define __LIBRETRO_SDK_STRING_LIST_H
#include <retro_common_api.h>
#include <boolean.h>
#include <stdlib.h>
#include <stddef.h>
RETRO_BEGIN_DECLS
union string_list_elem_attr
{
bool b;
int i;
void *p;
};
struct string_list_elem
{
char *data;
void *userdata;
union string_list_elem_attr attr;
};
struct string_list
{
struct string_list_elem *elems;
size_t size;
size_t cap;
};
/**
* string_list_find_elem:
* @list : pointer to string list
* @elem : element to find inside the string list.
*
* Searches for an element (@elem) inside the string list.
*
* @return 1-based index if element could be found, otherwise 0.
*/
int string_list_find_elem(const struct string_list *list, const char *elem);
/**
* string_list_find_elem_prefix:
* @list : pointer to string list
* @prefix : prefix to append to @elem
* @elem : element to find inside the string list.
*
* Searches for an element (@elem) inside the string list. Will
* also search for the same element prefixed by @prefix.
*
* Returns: true (1) if element could be found, otherwise false (0).
*/
bool string_list_find_elem_prefix(const struct string_list *list,
const char *prefix, const char *elem);
/**
* string_split:
* @str : string to turn into a string list
* @delim : delimiter character to use for splitting the string.
*
* Creates a new string list based on string @str, delimited by @delim.
*
* Returns: new string list if successful, otherwise NULL.
*/
struct string_list *string_split(const char *str, const char *delim);
/**
* string_split_packed:
* @str : string to split
* @delim : delimiter characters
*
* Like string_split(), in one allocation holding the list, its
* elements and the tokens. The list is read-only: it cannot grow,
* and string_list_deinitialize() refuses it. Free it with
* string_list_free(). A packed list has cap 0 and non-NULL elems.
*
* @return the list, or NULL on allocation failure.
*/
struct string_list *string_split_packed(const char *str, const char *delim);
bool string_split_noalloc(struct string_list *list,
const char *str, const char *delim);
bool string_list_deinitialize(struct string_list *list);
bool string_list_initialize(struct string_list *list);
/**
* string_list_new:
*
* Creates a new string list. Has to be freed manually.
*
* @return New string list if successful, otherwise NULL.
**/
struct string_list *string_list_new(void);
/**
* string_list_append:
* @list : pointer to string list
* @elem : element to add to the string list
* @attr : attributes of new element.
*
* Appends a new element to the string list.
*
* Hidden non-leaf function cost:
* - Calls string_list_capacity()
* - Calls strdup
*
* @return true if successful, otherwise false.
**/
bool string_list_append(struct string_list *list, const char *elem,
union string_list_elem_attr attr);
/**
* string_list_append_n:
* @list : pointer to string list
* @elem : element to add to the string list
* @len : length of @elem (caller already knows it)
* @attr : attributes of new element.
*
* Appends a new element when the caller already has its length,
* avoiding a redundant strlen inside strdup.
*
* @return true if successful, otherwise false.
**/
bool string_list_append_n(struct string_list *list, const char *elem,
size_t len, union string_list_elem_attr attr);
/**
* string_list_free
* @list : pointer to string list object
*
* Frees a string list.
**/
void string_list_free(struct string_list *list);
/**
* string_list_join_concat:
* @s : buffer that @list will be joined to.
* @len : length of @s.
* @list : pointer to string list.
* @delim : delimiter character for @list.
*
* A string list will be joined/concatenated as a
* string to @s, delimited by @delim.
*
* NOTE: @s must be NULL-terminated.
*
* Hidden non-leaf function cost:
* - Calls strlen()
* - Calls strlcpy x times in a loop
**/
void string_list_join_concat(char *s, size_t len,
const struct string_list *list, const char *sep);
/**
* string_list_join_concat_special:
* @s : buffer that @list will be joined to.
* @len : length of @s.
* @list : pointer to string list.
* @delim : delimiter character for @list.
*
* Specialized version of string_list_join_concat
* without the bounds check.
*
* A string list will be joined/concatenated as a
* string to @s, delimited by @delim.
**/
void string_list_join_concat_special(char *s, size_t len,
const struct string_list *list, const char *delim);
/**
* string_list_capacity:
* @list : pointer to string list
* @cap : new capacity for string list.
*
* Change maximum capacity of string list's size.
*
* @return true if successful, otherwise false.
**/
bool string_list_capacity(struct string_list *list, size_t cap);
struct string_list *string_list_clone(const struct string_list *src);
RETRO_END_DECLS
#endif
+227
View File
@@ -0,0 +1,227 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (lrc_hash.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_HASH_H
#define __LIBRETRO_SDK_HASH_H
#include <stdint.h>
#include <stddef.h>
#include <compat/msvc.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <retro_inline.h>
#include <retro_common_api.h>
#include <boolean.h>
RETRO_BEGIN_DECLS
/**
* sha256_hash:
* @s : Output.
* @in : Input.
* @len : Size of @out.
*
* Hashes SHA256 and outputs a human readable string.
**/
/**
* Streaming SHA-256 / SHA-224.
*
* The one-shot sha256_hash() above stays the short way to a hex
* string; these are for a caller that feeds a digest in pieces, or
* that has to copy a digest mid-stream. Members are laid out for the
* implementation to work on, not to be read by a caller.
*/
struct sha256_state
{
uint64_t len;
union
{
uint8_t u8[64];
uint32_t u32[16];
} in;
uint32_t w[64];
uint32_t h[8];
unsigned inlen;
unsigned is224;
};
/**
* sha256_stream_init:
* @p : State to start.
* @is224 : Non-zero selects SHA-224 rather than SHA-256.
**/
void sha256_stream_init(struct sha256_state *p, unsigned is224);
void sha256_stream_update(struct sha256_state *p,
const uint8_t *data, size_t len);
/**
* sha256_stream_final:
* @digest : 32 octets for SHA-256, 28 for SHA-224.
**/
void sha256_stream_final(struct sha256_state *p, uint8_t *digest);
/**
* sha256_stream_block:
*
* Compresses one 64-octet block straight into the state, leaving the
* length count alone. For a caller driving the padding itself.
**/
void sha256_stream_block(struct sha256_state *p, const uint8_t *data);
void sha256_hash(char *s, const uint8_t *in, size_t len);
/* SHA-512 and SHA-384 (FIPS 180-4), for the signature digests CA
* bundles use. */
#define SHA512_DIGEST_SIZE 64
#define SHA384_DIGEST_SIZE 48
struct sha512_state
{
uint64_t h[8];
uint64_t len;
uint8_t buf[128];
unsigned buflen;
unsigned is384;
};
void sha512_stream_init(struct sha512_state *p, unsigned is384);
void sha512_stream_update(struct sha512_state *p,
const uint8_t *data, size_t len);
/* 64 octets for SHA-512, 48 for SHA-384. */
void sha512_stream_final(struct sha512_state *p, uint8_t *digest);
/**
* SHA1Digest:
* @data : Input.
* @len : Size of @data.
* @digest : Output.
*
* Hashes SHA1
**/
void SHA1Digest(const uint8_t* data, size_t len, uint8_t digest[20]);
/**
* SHA1DigestMany:
* @data : the first message
* @stride : from each message to the next
* @len : each message's length
* @n : the messages
* @digests : 20 octets for each, one after another
*
* SHA1Digest() of @n messages of the same length, the i-th at
* @data + i * @stride. Where the hardware does the rounds, two run
* side by side, which is close to twice as fast as one after the
* other.
**/
void SHA1DigestMany(const uint8_t *data, size_t stride, size_t len,
unsigned n, uint8_t *digests);
/**
* Streaming SHA-1, alongside the one-shot SHA1Digest() above.
*/
struct sha1_state
{
uint32_t digest[5];
uint32_t length_low;
uint32_t length_high;
unsigned char block[64];
int block_index;
int computed;
int corrupted;
};
void sha1_stream_init(struct sha1_state *p);
void sha1_stream_update(struct sha1_state *p,
const uint8_t *data, size_t len);
/**
* sha1_stream_final:
* @digest : 20 octets.
*
* Returns: true on success, false where the stream was marked bad.
**/
bool sha1_stream_final(struct sha1_state *p, uint8_t *digest);
/**
* sha1_stream_block:
*
* Compresses one 64-octet block straight into the state, leaving the
* length count alone.
**/
void sha1_stream_block(struct sha1_state *p, const uint8_t *data);
int sha1_calculate(const char *path, char *result);
uint32_t djb2_calculate(const char *str);
/* One portable MD5 for every platform. This used to alias the CC_MD5_*
* family from CommonCrypto on Apple, but those have been deprecated
* since macOS 10.15, and the first code to actually call the aliases
* (libsmb2's hmac-md5) promptly failed the Apple builds on it. The
* implementation in utils/md5.c has no platform dependencies at all. */
typedef struct {
uint32_t lo, hi;
uint32_t a, b, c, d;
unsigned char buffer[64];
uint32_t block[16];
} MD5_CTX;
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See md5.c for more information.
*/
void MD5_Init(MD5_CTX *ctx);
void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
void MD5_Final(unsigned char *result, MD5_CTX *ctx);
RETRO_END_DECLS
#endif
+522
View File
@@ -0,0 +1,522 @@
/* Copyright (C) 2010-2022 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_compat.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_SDK_NET_COMPAT_H
#define _LIBRETRO_SDK_NET_COMPAT_H
#include <stdint.h>
#include <boolean.h>
#include <retro_inline.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#endif
#include <retro_common_api.h>
#if defined(_WIN32) && !defined(_XBOX)
#define WIN32_LEAN_AND_MEAN
#ifndef NOMINMAX
#define NOMINMAX /* MSVC defines min/max as macros in C++ too */
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
/* socket_poll() is provided on every Win32 target. The implementation
* uses select(), which exists on 9x through current Windows. Do not
* call WSAPoll; that export is Vista+ only. */
#define NETWORK_HAVE_POLL 1
/* Vista winsock2.h already has these. 9x/XP SDKs do not.
* Values must match Microsoft WSAPOLLFD bits, not POSIX poll.h. */
#ifndef POLLRDNORM
#define POLLRDNORM 0x0100
#endif
#ifndef POLLRDBAND
#define POLLRDBAND 0x0200
#endif
#ifndef POLLIN
#define POLLIN (POLLRDNORM | POLLRDBAND)
#endif
#ifndef POLLPRI
#define POLLPRI 0x0400
#endif
#ifndef POLLWRNORM
#define POLLWRNORM 0x0010
#endif
#ifndef POLLOUT
#define POLLOUT POLLWRNORM
#endif
#ifndef POLLWRBAND
#define POLLWRBAND 0x0020
#endif
#ifndef POLLERR
#define POLLERR 0x0001
#endif
#ifndef POLLHUP
#define POLLHUP 0x0002
#endif
#ifndef POLLNVAL
#define POLLNVAL 0x0004
#endif
/* winsock2.h declares struct pollfd only when the target is Vista or
* newer. 9x/2000/XP targets get it here, with the WSAPOLLFD layout. */
#ifndef HAVE_POLLFD
#define HAVE_POLLFD 1
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)
struct pollfd
{
SOCKET fd;
short events;
short revents;
};
#endif
#endif
#elif defined(_XBOX)
#define NOD3D
#include <xtl.h>
#include <io.h>
#ifndef SO_KEEPALIVE
#define SO_KEEPALIVE 0 /* verify if correct */
#endif
#define socklen_t unsigned int
#define NETWORK_HAVE_POLL 1
#ifndef POLLRDNORM
#define POLLRDNORM 0x0100
#endif
#ifndef POLLRDBAND
#define POLLRDBAND 0x0200
#endif
#ifndef POLLIN
#define POLLIN (POLLRDNORM | POLLRDBAND)
#endif
#ifndef POLLPRI
#define POLLPRI 0x0400
#endif
#ifndef POLLWRNORM
#define POLLWRNORM 0x0010
#endif
#ifndef POLLOUT
#define POLLOUT POLLWRNORM
#endif
#ifndef POLLWRBAND
#define POLLWRBAND 0x0020
#endif
#ifndef POLLERR
#define POLLERR 0x0001
#endif
#ifndef POLLHUP
#define POLLHUP 0x0002
#endif
#ifndef POLLNVAL
#define POLLNVAL 0x0004
#endif
#ifndef HAVE_POLLFD
#define HAVE_POLLFD 1
struct pollfd
{
SOCKET fd;
short events;
short revents;
};
#endif
#elif defined(VITA)
#include <psp2/net/net.h>
#include <psp2/net/netctl.h>
#define NETWORK_HAVE_POLL 1
#define AF_UNSPEC 0
#define AF_INET SCE_NET_AF_INET
#define SOCK_STREAM SCE_NET_SOCK_STREAM
#define SOCK_DGRAM SCE_NET_SOCK_DGRAM
#define INADDR_ANY SCE_NET_INADDR_ANY
#define INADDR_NONE 0xFFFFFFFF
#define SOL_SOCKET SCE_NET_SOL_SOCKET
#define SO_REUSEADDR SCE_NET_SO_REUSEADDR
#define SO_KEEPALIVE SCE_NET_SO_KEEPALIVE
#define SO_BROADCAST SCE_NET_SO_BROADCAST
#define SO_SNDBUF SCE_NET_SO_SNDBUF
#define SO_RCVBUF SCE_NET_SO_RCVBUF
#define SO_SNDTIMEO SCE_NET_SO_SNDTIMEO
#define SO_RCVTIMEO SCE_NET_SO_RCVTIMEO
#define SO_ERROR SCE_NET_SO_ERROR
#define SO_NBIO SCE_NET_SO_NBIO
#define IPPROTO_IP SCE_NET_IPPROTO_IP
#define IP_MULTICAST_TTL SCE_NET_IP_MULTICAST_TTL
#define IPPROTO_TCP SCE_NET_IPPROTO_TCP
#define TCP_NODELAY SCE_NET_TCP_NODELAY
#define IPPROTO_UDP SCE_NET_IPPROTO_UDP
#define MSG_DONTWAIT SCE_NET_MSG_DONTWAIT
#define POLLIN SCE_NET_EPOLLIN
#define POLLOUT SCE_NET_EPOLLOUT
#define POLLERR SCE_NET_EPOLLERR
#define POLLHUP SCE_NET_EPOLLHUP
#define POLLNVAL 0
#define sockaddr SceNetSockaddr
#define sockaddr_in SceNetSockaddrIn
#define in_addr SceNetInAddr
#define socklen_t unsigned int
#define socket(a,b,c) sceNetSocket("unknown",a,b,c)
#define getsockname sceNetGetsockname
#define getsockopt sceNetGetsockopt
#define setsockopt sceNetSetsockopt
#define bind sceNetBind
#define listen sceNetListen
#define accept sceNetAccept
#define connect sceNetConnect
#define send sceNetSend
#define sendto sceNetSendto
#define recv sceNetRecv
#define recvfrom sceNetRecvfrom
#define htonl sceNetHtonl
#define ntohl sceNetNtohl
#define htons sceNetHtons
#define ntohs sceNetNtohs
#define inet_ntop sceNetInetNtop
#define inet_pton sceNetInetPton
#elif defined(GEKKO) && !defined(GEKKO_NATIVE)
#include <network.h>
#include <ogc/libversion.h>
/* libogc 3 declares struct addrinfo in its own netdb.h, has its own
* inet_ntop and inet_pton, and keeps the poll flags in poll.h. */
#if _V_MAJOR_ >= 3
#include <poll.h>
#define NET_HAVE_SYS_ADDRINFO 1
#define NET_HAVE_SYS_INET_NTOP 1
#endif
#define NETWORK_HAVE_POLL 1
#define pollfd pollsd
#define socket(a,b,c) net_socket(a,b,c)
#define getsockopt(a,b,c,d,e) net_getsockopt(a,b,c,d,e)
#define setsockopt(a,b,c,d,e) net_setsockopt(a,b,c,d,e)
#define bind(a,b,c) net_bind(a,b,c)
#define listen(a,b) net_listen(a,b)
#define accept(a,b,c) net_accept(a,b,c)
#define connect(a,b,c) net_connect(a,b,c)
#define send(a,b,c,d) net_send(a,b,c,d)
#define sendto(a,b,c,d,e,f) net_sendto(a,b,c,d,e,f)
#define recv(a,b,c,d) net_recv(a,b,c,d)
#define recvfrom(a,b,c,d,e,f) net_recvfrom(a,b,c,d,e,f)
#define select(a,b,c,d,e) net_select(a,b,c,d,e)
#define gethostbyname(a) net_gethostbyname(a)
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#ifndef __PSL1GHT__
#include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#if !defined(__PSL1GHT__) && defined(__PS3__)
#include <netex/libnetctl.h>
#include <netex/errno.h>
#else
#include <signal.h>
#endif
#if defined(__PSL1GHT__)
#include <net/poll.h>
#define NETWORK_HAVE_POLL 1
#elif defined(WIIU)
#define WIIU_RCVBUF 0x40000
#define WIIU_SNDBUF 0x40000
#elif !defined(__PS3__)
#include <poll.h>
#define NETWORK_HAVE_POLL 1
#endif
#endif
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) && !defined(_3DS)
#define HAVE_INET6 1
#endif
#ifdef NETWORK_HAVE_POLL
#if defined(GEKKO) && !defined(GEKKO_NATIVE)
#define NET_POLL_FD(sockfd, sockfds) (sockfds)->socket = (sockfd)
#else
#define NET_POLL_FD(sockfd, sockfds) (sockfds)->fd = (sockfd)
#endif
#define NET_POLL_EVENT(sockev, sockfds) (sockfds)->events |= (sockev)
#define NET_POLL_HAS_EVENT(sockev, sockfds) ((sockfds)->revents & (sockev))
#endif
RETRO_BEGIN_DECLS
/* Compatibility layer for legacy or incomplete BSD socket implementations.
* Only for IPv4. Mostly useful for the consoles which do not support
* anything reasonably modern on the socket API side of things. */
#ifdef HAVE_SOCKET_LEGACY
#define sockaddr_storage sockaddr_in
#ifdef AI_PASSIVE
#undef AI_PASSIVE
#endif
#ifdef AI_CANONNAME
#undef AI_CANONNAME
#endif
#ifdef AI_NUMERICHOST
#undef AI_NUMERICHOST
#endif
#ifdef AI_NUMERICSERV
#undef AI_NUMERICSERV
#endif
#ifdef NI_NUMERICHOST
#undef NI_NUMERICHOST
#endif
#ifdef NI_NUMERICSERV
#undef NI_NUMERICSERV
#endif
#ifdef NI_NOFQDN
#undef NI_NOFQDN
#endif
#ifdef NI_NAMEREQD
#undef NI_NAMEREQD
#endif
#ifdef NI_DGRAM
#undef NI_DGRAM
#endif
#define AI_PASSIVE 1
#define AI_CANONNAME 2
#define AI_NUMERICHOST 4
#define AI_NUMERICSERV 8
#define NI_NUMERICHOST 1
#define NI_NUMERICSERV 2
#define NI_NOFQDN 4
#define NI_NAMEREQD 8
#define NI_DGRAM 16
#if !defined(__PS3__) && !defined(NET_HAVE_SYS_ADDRINFO)
struct addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
#endif
/* gai_strerror() not used, so we skip that. */
#else
/* Ensure that getaddrinfo and getnameinfo flags are always defined. */
#ifndef AI_PASSIVE
#define AI_PASSIVE 0
#endif
#ifndef AI_CANONNAME
#define AI_CANONNAME 0
#endif
#ifndef AI_NUMERICHOST
#define AI_NUMERICHOST 0
#endif
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif
#ifndef NI_NUMERICHOST
#define NI_NUMERICHOST 0
#endif
#ifndef NI_NUMERICSERV
#define NI_NUMERICSERV 0
#endif
#ifndef NI_NOFQDN
#define NI_NOFQDN 0
#endif
#ifndef NI_NAMEREQD
#define NI_NAMEREQD 0
#endif
#ifndef NI_DGRAM
#define NI_DGRAM 0
#endif
#endif
#if defined(_XBOX)
struct hostent
{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
char *h_addr;
char *h_end;
};
#elif defined(VITA)
struct pollfd
{
int fd;
unsigned events;
unsigned revents;
unsigned __pad; /* Align to 64-bits boundary */
};
struct hostent
{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
char *h_addr;
char *h_end;
};
#endif
static INLINE bool isagain(int val)
{
#if defined(_WIN32)
return (val == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK);
#elif !defined(__PSL1GHT__) && defined(__PS3__)
return (sys_net_errno == SYS_NET_EAGAIN) || (sys_net_errno == SYS_NET_EWOULDBLOCK);
#elif defined(VITA)
return (val == SCE_NET_ERROR_EAGAIN) || (val == SCE_NET_ERROR_EWOULDBLOCK);
#elif defined(WIIU)
return (val == -1) && (socketlasterr() == SO_SUCCESS || socketlasterr() == SO_EWOULDBLOCK);
#elif defined(GEKKO) && !defined(GEKKO_NATIVE)
return (-val == EAGAIN);
#else
return (val < 0) && (errno == EAGAIN || errno == EWOULDBLOCK);
#endif
}
static INLINE bool isinprogress(int val)
{
#if defined(_WIN32)
return (val == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK);
#elif !defined(__PSL1GHT__) && defined(__PS3__)
return (sys_net_errno == SYS_NET_EINPROGRESS);
#elif defined(VITA)
return (val == SCE_NET_ERROR_EINPROGRESS);
#elif defined(WIIU)
return (val == -1) && (socketlasterr() == SO_EINPROGRESS);
#elif defined(GEKKO) && !defined(GEKKO_NATIVE)
return (-val == EINPROGRESS);
#else
return (val < 0) && (errno == EINPROGRESS);
#endif
}
#if defined(_WIN32) && !defined(_XBOX)
#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
int inet_pton(int af, const char *src, void *dst);
#endif
#elif defined(_XBOX)
struct hostent *gethostbyname(const char *name);
#elif defined(VITA)
char *inet_ntoa(struct in_addr in);
int inet_aton(const char *cp, struct in_addr *inp);
uint32_t inet_addr(const char *cp);
struct hostent *gethostbyname(const char *name);
#elif defined(GEKKO) && !defined(GEKKO_NATIVE)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
int inet_pton(int af, const char *src, void *dst);
#endif
int getaddrinfo_retro(const char *node, const char *service,
struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo_retro(struct addrinfo *res);
int getnameinfo_retro(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
bool addr_6to4(struct sockaddr_storage *addr);
bool ipv4_is_lan_address(const struct sockaddr_in *addr);
bool ipv4_is_cgnat_address(const struct sockaddr_in *addr);
/**
* network_init:
*
* Platform specific socket library initialization.
*
* @return true if successful, otherwise false.
**/
bool network_init(void);
RETRO_END_DECLS
#endif
+412
View File
@@ -0,0 +1,412 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_http.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_SDK_NET_HTTP_H
#define _LIBRETRO_SDK_NET_HTTP_H
#include <stdint.h>
#include <boolean.h>
#include <string.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
struct http_t;
struct http_connection_t;
struct http_connection_t *net_http_connection_new(const char *url, const char *method, const char *data);
/**
* net_http_connection_iterate:
*
* Leaf function.
**/
bool net_http_connection_iterate(struct http_connection_t *conn);
bool net_http_connection_done(struct http_connection_t *conn);
void net_http_connection_free(struct http_connection_t *conn);
void net_http_connection_set_user_agent(struct http_connection_t *conn, const char *user_agent);
void net_http_connection_set_headers(struct http_connection_t *conn, const char *headers);
struct ssl_socket_retro_config;
/**
* net_http_connection_set_tls:
*
* TLS settings of the request's own (net_socket_ssl.h), for the
* built-in client: its trust anchors, a client certificate, its
* suites. @cfg is borrowed for as long as the transfer runs. Such a
* transfer runs on a connection of its own, neither taken from the
* pool nor left in it. With another TLS backend the request fails.
**/
void net_http_connection_set_tls(struct http_connection_t *conn,
const struct ssl_socket_retro_config *cfg);
/**
* net_http_sink_t:
*
* Called with each run of decoded body bytes as they arrive. Return
* false to abort the transfer (a failed write, a full disk); the
* handle then reports an error like any other transport failure.
*
* Runs on whichever thread drives net_http_update().
**/
/**
* net_http_init:
*
* Call once at startup, before the first transfer; it creates the
* eventcount a DNS wait parks on, and nothing creates it lazily.
* Idempotent, but not safe to call concurrently.
*
* Threading: net_http takes no locks. The DNS cache and connection
* pool belong to the thread that drives transfers - every
* net_http_new/update/wait/delete sharing them must come from one
* thread at a time, and net_http_init()/net_http_deinit() run on it or
* while it is stopped. Handing that role to another thread needs an
* ordering point in between (a thread join, a queue handoff). DNS
* lookups run on threads of their own and publish into the cache with
* an atomic store. Building connection objects
* (net_http_connection_*) touches no shared state and is fine from any
* thread.
**/
void net_http_init(void);
/**
* net_http_deinit:
*
* Closes pooled connections and frees the DNS cache and the locks.
* Call at shutdown, after the last transfer has finished.
**/
void net_http_deinit(void);
typedef bool (*net_http_sink_t)(void *userdata, const void *data, size_t len);
/**
* net_http_connection_set_sink:
*
* Stream the response body to @cb instead of accumulating it, so peak
* memory is the receive window rather than the whole payload. With a
* sink set, net_http_data() returns NULL/0; status and headers are
* unaffected.
**/
void net_http_connection_set_sink(struct http_connection_t *conn,
net_http_sink_t cb, void *userdata);
void net_http_connection_set_content(struct http_connection_t *conn, const char *content_type,
size_t content_length, const void *content);
/**
* net_http_source_t:
*
* Called for the next run of request-body bytes: fill up to @len bytes
* of @buf and return how many were filled. Return 0 at the end of the
* body and a negative value on error; either one short of the
* advertised Content-Length fails the request like a transport error.
* Runs on whichever thread drives net_http_update().
**/
typedef int64_t (*net_http_source_t)(void *userdata, void *buf, size_t len);
/**
* net_http_source_rewind_t:
*
* Asked to restart the body from its first byte when the request has
* to be replayed on a fresh connection (a pooled connection the peer
* closed while idle). Return false if that is not possible; the
* request then fails instead of being replayed.
**/
typedef bool (*net_http_source_rewind_t)(void *userdata);
/**
* net_http_connection_set_content_source:
*
* Send a request body of @content_length bytes pulled from @source as
* the socket takes them, so peak memory is one send buffer rather than
* the whole body. Replaces any body given by
* net_http_connection_set_content(). @rewind may be NULL, in which
* case the request is never replayed on a fresh connection.
**/
void net_http_connection_set_content_source(struct http_connection_t *conn,
const char *content_type, size_t content_length,
net_http_source_t source, net_http_source_rewind_t rewind,
void *userdata);
const char *net_http_connection_url(struct http_connection_t *conn);
const char* net_http_connection_method(struct http_connection_t* conn);
struct http_t *net_http_new(struct http_connection_t *conn);
/**
* net_http_fd:
*
* Leaf function.
*
* You can use this to call net_http_update
* only when something will happen; select() it for reading.
**/
int net_http_fd(struct http_t *state);
/**
* net_http_update:
*
* @return true if it's done, or if something broke.
* @total will be 0 if it's not known.
**/
bool net_http_update(struct http_t *state, size_t* progress, size_t* total);
/**
* net_http_update_budget:
*
* net_http_update(), with the socket drained while
* @within_budget(@budget, 0, 0) allows - one read, of at most
* 256 KiB, per work item - instead of up to a fixed 256 KiB per
* call. For a caller that paces its I/O with a time window (the
* frontend's shared per-frame I/O window). A NULL @within_budget is
* net_http_update().
**/
bool net_http_update_budget(struct http_t *state,
size_t* progress, size_t* total,
bool (*within_budget)(void *budget, size_t avail, size_t len),
void *budget);
/**
* net_http_wait:
*
* Waits until the transfer can make progress again, or until
* @timeout_ms elapses, for callers driving a transfer from their own
* thread rather than once per frame. Unlike net_http_fd(), which hands
* out the descriptor and leaves the caller to work out what to select
* for, this knows which way the transfer is waiting and knows when the
* last pass left bytes already buffered, in which case it returns at
* once.
*
* @return true if the transport is ready or no wait was needed,
* false if the timeout elapsed first.
**/
bool net_http_wait(struct http_t *state, int timeout_ms);
/**
* net_http_status:
*
* Report HTTP status. 200, 404, or whatever.
*
* Leaf function.
*
* @return HTTP status code.
**/
int net_http_status(struct http_t *state);
/**
* net_http_error:
*
* Leaf function
**/
bool net_http_error(struct http_t *state);
/**
* net_http_failure:
*
* Where a transfer that never produced a status failed: the transport
* stage, as a literal ("dns_lookup_failed", "ssl_connect_failed",
* ...), and through @code the library's own error for it when there
* is one - the TLS library's for the ssl stages (negative), the OS
* socket error (errno, or WSAGetLastError on Windows; positive) for
* socket_create/connect/send, 0 otherwise. NULL
* when the transport did not fail. For turning "HTTP -1" into a
* message that says what went wrong.
**/
const char *net_http_failure(struct http_t *state, int *code);
/**
* net_http_headers_take:
* @accept_err : also return them when the transfer failed
*
* The response headers as one block: each "Name: value" line
* NUL-terminated, the block ending in an empty line ("\0\0" at its
* end; a response with no header lines is a single "\0"). Walk it
* with net_http_header_next(), or look a field up with
* net_http_header_value().
*
* Ownership moves to the caller, who frees it with free(); repeated
* calls return the same pointer. NULL when no status line was parsed,
* and on a transport error unless @accept_err - headers are returned
* for any parsed response, HTTP error statuses such as 401 included
* (needed for auth challenges).
**/
char *net_http_headers_take(struct http_t *state, bool accept_err);
/**
* net_http_headers_compact:
* @raw : header text, one "Name: value" line per LF or CRLF, NUL-terminated,
* with one spare byte after the terminator
*
* Rewrites @raw in place into the block net_http_headers_take()
* returns: each line NUL-terminated, trailing CR and spaces trimmed,
* blank lines dropped, an empty line at the end. Output never outgrows
* input, except for that closing NUL, which is what the spare byte is
* for. For backends that get the headers as text from somewhere else
* (the browser, under Emscripten).
*
* Returns: the block's length, closing NUL excluded.
**/
size_t net_http_headers_compact(char *raw);
/**
* net_http_header_next:
*
* The line after @line in the block @headers (the first line when
* @line is NULL), or NULL at the end:
*
* for (h = net_http_header_next(hdrs, NULL); h;
* h = net_http_header_next(hdrs, h))
**/
const char *net_http_header_next(const char *headers, const char *line);
/**
* net_http_header_value:
*
* The value of the first field named @name (case-insensitive, whole
* name), leading whitespace skipped; NULL if there is none. Points
* into @headers. Fields that may repeat (WWW-Authenticate) want
* net_http_header_next() instead.
**/
const char *net_http_header_value(const char *headers, const char *name);
/**
* net_http_header:
*
* The value of response header @name, looked up without taking the
* block: usable mid-transfer, from a sink callback, once the status line
* and headers are in. Points into the handle's own storage, valid until
* the handle follows a redirect, is deleted, or its headers are taken.
* NULL before the headers arrive, after a transport failure, or when the
* field is absent.
**/
const char *net_http_header(struct http_t *state, const char *name);
/**
* net_http_headers:
*
* Legacy: the headers as a newly built string_list, owned by the
* caller. Costs an allocation per line; nothing in RetroArch uses it.
* Same NULL rules as net_http_headers_take().
**/
struct string_list *net_http_headers(struct http_t *state);
struct string_list *net_http_headers_ex(struct http_t *state, bool accept_error);
/**
* net_http_body_is_framed:
* @headers : header block as net_http_headers_take() returns it
*
* True when the response frames its body with Content-Length or
* "Transfer-Encoding: chunked", tested exactly as the receiver picks
* the body type. A transfer that ends short of either framing fails,
* but a body delimited only by the connection closing cannot be told
* apart from one cut off mid-transfer, so callers that must not act
* on a truncated body (writing a downloaded file over a local one)
* check this first.
**/
bool net_http_body_is_framed(const char *headers);
/**
* net_http_data:
*
* Leaf function.
*
* @return the downloaded data. Ownership of the returned buffer passes
* to the caller, who frees it with free(); net_http_delete() no longer
* does. A NULL return leaves the buffer with the handler.
* If the status is not 20x and accept_error is false, it returns NULL.
**/
uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error);
/**
* net_http_delete:
*
* Cleans up all memory.
**/
void net_http_delete(struct http_t *state);
/**
* net_http_urlencode:
*
* URL Encode a string
* caller is responsible for deleting the destination buffer
**/
void net_http_urlencode(char **dest, const char *source);
/**
* net_http_urlencode_full:
*
* Re-encode a full URL
**/
void net_http_urlencode_full(char *s, const char *source, size_t len);
/**
* net_http_url_join:
* @dst : output buffer
* @dst_size : size of @dst including the NUL
* @base : absolute http:// or https:// URL
* @ref : reference to resolve against @base (RFC 3986 section 5)
*
* An empty @ref gives @base; an absolute http(s) URL replaces it; a
* reference starting with "//" keeps only the scheme, "/" keeps scheme
* and authority, "?" keeps the path and "#" keeps path and query;
* anything else replaces the last segment of the base path. "." and
* ".." segments are then removed. No allocation.
*
* Returns: length of the result, or -1 when @base is not an http(s)
* URL, @ref has another scheme, or @dst is too small (@dst is then an
* empty string).
**/
int net_http_url_join(char *dst, size_t dst_size,
const char *base, const char *ref);
/**
* net_http_urldecode:
*
* Decode %XX escapes from @src into @dst. A '%' not followed by two
* hex digits is copied as is; '+' is left alone (it is a space only in
* form bodies). Returns the decoded length, or -1 when @dst is too
* small (@dst holds the truncated, terminated prefix).
*
* Not for a URL that is about to be requested: decoding "%20" puts a
* space in the request line. Use it on names meant for display or for
* the local filesystem.
**/
int net_http_urldecode(char *dst, size_t dst_size, const char *src);
/**
* net_http_urldecode_inplace:
*
* As net_http_urldecode(), in @s itself; decoded text is never longer
* than its source. Returns the decoded length.
**/
int net_http_urldecode_inplace(char *s);
RETRO_END_DECLS
#endif
+116
View File
@@ -0,0 +1,116 @@
/* Copyright (C) 2010-2022 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_socket.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_SDK_NET_SOCKET_H
#define _LIBRETRO_SDK_NET_SOCKET_H
#include <stddef.h>
#include <stdint.h>
#include <boolean.h>
#include <net/net_compat.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
enum socket_domain
{
SOCKET_DOMAIN_INET = 0
};
enum socket_type
{
SOCKET_TYPE_DATAGRAM = 0,
SOCKET_TYPE_STREAM,
SOCKET_TYPE_SEQPACKET
};
enum socket_protocol
{
SOCKET_PROTOCOL_NONE = 0,
SOCKET_PROTOCOL_TCP,
SOCKET_PROTOCOL_UDP
};
typedef struct socket_target
{
unsigned port;
const char *server;
enum socket_domain domain;
enum socket_protocol prot;
} socket_target_t;
int socket_init(void **address, uint16_t port, const char *server,
enum socket_type type, int family);
int socket_next(void **address);
int socket_close(int fd);
bool socket_set_block(int fd, bool block);
/* TODO: all callers should be converted to socket_set_block() */
bool socket_nonblock(int fd);
int socket_select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout);
#ifdef NETWORK_HAVE_POLL
int socket_poll(struct pollfd *fds, unsigned nfds, int timeout);
#endif
bool socket_wait(int fd, bool *rd, bool *wr, int timeout);
bool socket_send_all_blocking(int fd, const void *data_, size_t len, bool no_signal);
bool socket_send_all_blocking_with_timeout(int fd,
const void *data_, size_t len, int timeout, bool no_signal);
ssize_t socket_send_all_nonblocking(int fd, const void *data_, size_t len,
bool no_signal);
bool socket_receive_all_blocking(int fd, void *data_, size_t len);
bool socket_receive_all_blocking_with_timeout(int fd,
void *data_, size_t len, int timeout);
ssize_t socket_receive_all_nonblocking(int fd, bool *error,
void *data_, size_t len);
bool socket_bind(int fd, void *data);
int socket_connect(int fd, void *data);
bool socket_connect_with_timeout(int fd, void *data, int timeout);
int socket_create(
const char *name,
enum socket_domain domain_type,
enum socket_type socket_type,
enum socket_protocol protocol_type);
void socket_set_target(void *data, socket_target_t *in_addr);
RETRO_END_DECLS
#endif
@@ -0,0 +1,152 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_socket.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_SDK_NET_SOCKET_SSL_H
#define _LIBRETRO_SDK_NET_SOCKET_SSL_H
#include <stdint.h>
#include <stdlib.h>
#include <boolean.h>
#include <retro_common_api.h>
/* ssize_t, as net_socket.h gets it. */
#include <net/net_compat.h>
RETRO_BEGIN_DECLS
/* How long a TLS read, in the handshake or in a record, waits for the
* server's next bytes before it fails: a server that goes silent must
* not hold the caller's thread for good. */
#ifndef SSL_SOCKET_IO_TIMEOUT_MS
#define SSL_SOCKET_IO_TIMEOUT_MS 30000
#endif
void* ssl_socket_init(int fd, const char *domain);
int ssl_socket_connect(void *state_data, void *data, bool timeout_enable, bool nonblock);
/* The TLS library's own code for the most recent failure in
* ssl_socket_init()/ssl_socket_connect(), as the library reports it
* (mbedtls: negative, so -0x7780 style; BearSSL: its BR_ERR_* value),
* or 0 when the last failure was in the socket layer or there was
* none. For logging: it is what turns "connect failed" into a
* message the library's error table can decode. */
int ssl_socket_last_error(void *state_data);
int ssl_socket_send_all_blocking(void *state_data, const void *data_, size_t len, bool no_signal);
/* Takes as much of @data_ as the connection can accept without
* blocking and returns how much that was: 0 when the socket is full,
* -1 on error. Bytes taken may still sit inside the TLS layer; once
* the last of them is taken, call ssl_socket_flush_nonblocking() until
* it reports 1. After a 0, the next call must pass the same buffer,
* at least as long (mbedtls has to finish the record it began). */
ssize_t ssl_socket_send_all_nonblocking(void *state_data, const void *data_, size_t len, bool no_signal);
/* Moves bytes already taken by ssl_socket_send_all_nonblocking() on to
* the socket without blocking. 1: nothing left pending, 0: the socket
* is full (wait for it to be writable and call again), -1: error. */
int ssl_socket_flush_nonblocking(void *state_data);
int ssl_socket_receive_all_blocking(void *state_data, void *data_, size_t len);
ssize_t ssl_socket_receive_all_nonblocking(void *state_data, bool *error, void *data_, size_t len);
void ssl_socket_close(void *state_data);
void ssl_socket_free(void *state_data);
/* Certificate-verification policy hook. `mode` is a tls_verify_mode value
* (0 = required, 1 = optional, 2 = disabled, see network/tls_config.h).
* Called once at startup and whenever the setting changes; the active
* backend snapshots the value at the next ssl_socket_connect. */
void ssl_socket_set_verify_mode(unsigned mode);
/* Weak logging hooks. The active SSL backend ships no-op defaults so
* libretro-common still builds/links standalone; RetroArch overrides them
* in network/tls_log.c to route into RARCH_ERR/RARCH_WARN without dragging
* verbosity.h into vendored code. `mode_required` != 0 means a hard
* (REQUIRED) failure; == 0 means a soft (OPTIONAL) failure. */
void ssl_socket_log_verify_fail(int mode_required, const char *domain,
const char *verify_info);
void ssl_socket_log_verify_disabled(const char *domain);
/* net_socket_ssl_retro.c only: replace the built-in CA bundle with
* @pem (concatenated PEM certificates) for the process, NULL to
* restore. For tests against a local server. */
void ssl_socket_retro_set_trust_pem(const char *pem, size_t len);
/* net_socket_ssl_retro.c only: whether the last handshake on
* @state_data resumed a cached session rather than running in full. */
int ssl_socket_retro_was_resumed(void *state_data);
/* net_socket_ssl_retro.c only: 0x0304 for a TLS 1.3 connection,
* 0x0303 for 1.2, 0 before the handshake. */
unsigned ssl_socket_retro_version(void *state_data);
/* net_socket_ssl_retro.c only: the negotiated cipher suite's IANA
* number (0x1303 is TLS_CHACHA20_POLY1305_SHA256), 0 before the
* handshake. */
unsigned ssl_socket_retro_suite(void *state_data);
struct rsa_private_key;
/* net_socket_ssl_retro.c only: one connection's own settings, for
* clients that pin their own CAs or present a certificate. Every
* pointer is borrowed until ssl_socket_connect() returns. */
struct ssl_socket_retro_config
{
/* Trust anchors, DER, in place of the built-in bundle; NULL for
* the bundle. */
const uint8_t *const *trust;
const size_t *trust_lens;
/* Client certificate chain, DER, leaf first, and the leaf's RSA
* key: sent when the server asks for one. A connection presenting
* a certificate runs TLS 1.2. */
const uint8_t *const *chain;
const size_t *chain_lens;
const struct rsa_private_key *key;
/* TLS 1.2 and 1.3 suites to offer, by IANA number, most preferred
* first; NULL for the default ECDHE AEAD set. This may name the RSA
* key exchange and CBC suites, which the default never offers. */
const unsigned *suites;
unsigned trust_count;
unsigned chain_count;
unsigned suite_count;
unsigned flags; /* SSL_RETRO_* */
};
/* Accept any server certificate. */
#define SSL_RETRO_NO_VERIFY (1 << 0)
/* Check the chain but not that it names the host. */
#define SSL_RETRO_NO_HOSTNAME (1 << 1)
/**
* ssl_socket_retro_configure:
* Applies @cfg to a connection between ssl_socket_init() and
* ssl_socket_connect(). Such a connection neither resumes a cached
* session nor leaves one in the cache.
*
* Returns: 0 on success, -1 when @cfg names no suite this client has.
**/
int ssl_socket_retro_configure(void *state_data,
const struct ssl_socket_retro_config *cfg);
RETRO_END_DECLS
#endif
+144
View File
@@ -0,0 +1,144 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------
* The following license statement only applies to this file (mpsc_stack.h).
* ---------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_MPSC_STACK_H__
#define __LIBRETRO_SDK_MPSC_STACK_H__
/* A many-producer, single-consumer intrusive stack: any thread pushes,
* one thread takes everything at once. The shape a worker-to-main
* handoff wants - deferred messages, retire lists - where the consumer
* drains on its own cadence and order within the batch is recovered by
* reversing, since a push stacks newest-first.
*
* Producers never wait and never touch a lock; the consumer's drain is
* one exchange. There is no ABA hazard in this shape: nodes are only
* pushed, and the consumer takes the whole chain, so a producer's
* compare-exchange retries against a moved head and nothing else.
*
* The node's link field belongs to the stack from push until the
* consumer has taken the chain; everything else in the node is
* published by the push and safe for the consumer to read after
* mpsc_stack_drain() returned it.
*/
#include <boolean.h>
#include <retro_atomic.h>
#include <retro_inline.h>
RETRO_BEGIN_DECLS
typedef struct mpsc_stack_node
{
struct mpsc_stack_node *next;
} mpsc_stack_node_t;
#if defined(RETRO_ATOMIC_HAS_PTR)
typedef struct mpsc_stack
{
retro_atomic_ptr_t head;
} mpsc_stack_t;
static INLINE void mpsc_stack_init(mpsc_stack_t *stack)
{
retro_atomic_ptr_init(&stack->head, NULL);
}
/* Any thread. The node's payload must be written before the push;
* the compare-exchange publishes it. */
static INLINE void mpsc_stack_push(mpsc_stack_t *stack,
mpsc_stack_node_t *node)
{
do
{
node->next = (mpsc_stack_node_t *)
retro_atomic_load_acquire_ptr(&stack->head);
} while (!retro_atomic_cas_ptr(&stack->head, node->next, node));
}
/* True when a drain would return nothing: the consumer's cheap
* per-iteration probe. */
static INLINE bool mpsc_stack_empty(mpsc_stack_t *stack)
{
return !retro_atomic_load_acquire_ptr(&stack->head);
}
/* The consumer. Takes the whole chain, newest-first; the caller walks
* or reverses it. */
static INLINE mpsc_stack_node_t *mpsc_stack_drain(mpsc_stack_t *stack)
{
return (mpsc_stack_node_t *)
retro_atomic_exchange_ptr(&stack->head, NULL);
}
#else
/* The volatile fallback backend offers no pointer atomics by design:
* it serves single-context targets where plain access is all the
* platform has. The stack keeps its shape with plain operations, and
* its guarantees follow the backend's - none beyond what the target
* itself provides, exactly as with every retro_atomic op there. */
typedef struct mpsc_stack
{
mpsc_stack_node_t * volatile head;
} mpsc_stack_t;
static INLINE void mpsc_stack_init(mpsc_stack_t *stack)
{
stack->head = NULL;
}
static INLINE void mpsc_stack_push(mpsc_stack_t *stack,
mpsc_stack_node_t *node)
{
node->next = stack->head;
stack->head = node;
}
static INLINE bool mpsc_stack_empty(mpsc_stack_t *stack)
{
return !stack->head;
}
static INLINE mpsc_stack_node_t *mpsc_stack_drain(mpsc_stack_t *stack)
{
mpsc_stack_node_t *node = stack->head;
stack->head = NULL;
return node;
}
#endif
/* The drained chain, oldest-first. */
static INLINE mpsc_stack_node_t *mpsc_stack_reverse(mpsc_stack_node_t *node)
{
mpsc_stack_node_t *chain = NULL;
while (node)
{
mpsc_stack_node_t *next = node->next;
node->next = chain;
chain = node;
node = next;
}
return chain;
}
RETRO_END_DECLS
#endif
@@ -0,0 +1,127 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------
* The following license statement only applies to this file (retro_retire.h).
* ---------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_RETRO_RETIRE_H__
#define __LIBRETRO_SDK_RETRO_RETIRE_H__
/* retro_retire.h - read shared objects that another thread replaces,
* with no lock on either side.
*
* A reader enters, loads the published pointers it wants and uses what
* they point at, then leaves. A writer swaps a new object in and
* retires the old one; the old one is freed once no reader is in, by
* whichever thread is last out, so nothing waits on anything.
*
* A reader counts itself in and then reads an epoch that a writer bumps
* once its swap is made, both sequentially consistent: so the reader
* loads the new object, or the drain that would free the old one sees
* the reader counted. Readers that never all leave at once hold the
* retired objects back; they are freed at the next moment none is in.
*
* Only where the pointer atomics are lock-free (RETRO_RETIRE_LOCK_FREE);
* elsewhere the caller keeps its own serialisation. A zero-filled
* retro_retire_t is ready, so a static one needs no setup. */
#include <retro_atomic.h>
#include <retro_inline.h>
#include <queues/mpsc_stack.h>
RETRO_BEGIN_DECLS
#if defined(RETRO_ATOMIC_HAS_PTR) && defined(RETRO_ATOMIC_LOCK_FREE)
#define RETRO_RETIRE_LOCK_FREE 1
typedef struct retro_retire
{
mpsc_stack_t retired;
retro_atomic_int_t readers;
retro_atomic_int_t epoch;
} retro_retire_t;
/* For one not zero-filled already. */
static INLINE void retro_retire_init(retro_retire_t *r)
{
mpsc_stack_init(&r->retired);
retro_atomic_int_init(&r->readers, 0);
retro_atomic_int_init(&r->epoch, 0);
}
/* Frees one retired object, given its node. */
typedef void (*retro_retire_free_t)(mpsc_stack_node_t *node);
/* Frees what was retired while no reader is in. One that is gets it
* back and frees it on its way out. */
static INLINE void retro_retire_drain(retro_retire_t *r,
retro_retire_free_t fn)
{
for (;;)
{
mpsc_stack_node_t *list;
if (retro_atomic_load_seq_cst_int(&r->readers))
return;
if (!(list = mpsc_stack_drain(&r->retired)))
return;
if (retro_atomic_load_seq_cst_int(&r->readers))
{
while (list)
{
mpsc_stack_node_t *next = list->next;
mpsc_stack_push(&r->retired, list);
list = next;
}
continue;
}
while (list)
{
mpsc_stack_node_t *next = list->next;
fn(list);
list = next;
}
}
}
/* What is published may be loaded, and used, until the leave. */
static INLINE void retro_retire_enter(retro_retire_t *r)
{
retro_atomic_fetch_add_seq_cst_int(&r->readers, 1);
(void)retro_atomic_load_seq_cst_int(&r->epoch);
}
static INLINE void retro_retire_leave(retro_retire_t *r,
retro_retire_free_t fn)
{
if (retro_atomic_fetch_add_seq_cst_int(&r->readers, -1) == 1)
retro_retire_drain(r, fn);
}
/* After a swap: @old (NULL: nothing) is freed once no reader is in. */
static INLINE void retro_retire(retro_retire_t *r, mpsc_stack_node_t *old,
retro_retire_free_t fn)
{
retro_atomic_fetch_add_seq_cst_int(&r->epoch, 1);
if (!old)
return;
mpsc_stack_push(&r->retired, old);
retro_retire_drain(r, fn);
}
#endif
RETRO_END_DECLS
#endif
+35
View File
@@ -0,0 +1,35 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_assert.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __RETRO_ASSERT_H
#define __RETRO_ASSERT_H
#include <assert.h>
#ifdef RARCH_INTERNAL
#include <stdio.h>
#define retro_assert(cond) ((void)( (cond) || (printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__), abort(), 0) ))
#else
#define retro_assert(cond) assert(cond)
#endif
#endif
File diff suppressed because it is too large. Load diff
+36
View File
@@ -0,0 +1,36 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_common.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_COMMON_RETRO_COMMON_H
#define _LIBRETRO_COMMON_RETRO_COMMON_H
/*!
* @internal This file is designed to normalize the libretro-common compiling environment.
* It is not to be used in public API headers, as they should be designed as leanly as possible.
* Nonetheless.. in the meantime, if you do something like use ssize_t, which is not fully portable,
* in a public API, you may need this.
*/
/* conditional compilation is handled inside here */
#include <compat/msvc.h>
#endif
+153
View File
@@ -0,0 +1,153 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_common_api.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_COMMON_RETRO_COMMON_API_H
#define _LIBRETRO_COMMON_RETRO_COMMON_API_H
/*
This file is designed to normalize the libretro-common compiling environment
for public API headers. This should be leaner than a normal compiling environment,
since it gets #included into other project's sources.
*/
/* ------------------------------------ */
/*
Ordinarily we want to put #ifdef __cplusplus extern "C" in C library
headers to enable them to get used by c++ sources.
However, we want to support building this library as C++ as well, so a
special technique is called for.
*/
#define RETRO_BEGIN_DECLS
#define RETRO_END_DECLS
#define RETRO_BEGIN_DECLS_CXX
#define RETRO_END_DECLS_CXX
#ifdef __cplusplus
#ifdef CXX_BUILD
/* build wants everything to be built as c++, so no extern "C" */
#else
#undef RETRO_BEGIN_DECLS
#undef RETRO_END_DECLS
#define RETRO_BEGIN_DECLS extern "C" {
#define RETRO_END_DECLS }
/* Force C++ linkage for a region inside a header that may be included
* from within a caller's extern "C" { ... } block -- needed when the
* region pulls in a C++ standard library header (e.g. <atomic>). */
#undef RETRO_BEGIN_DECLS_CXX
#undef RETRO_END_DECLS_CXX
#define RETRO_BEGIN_DECLS_CXX extern "C++" {
#define RETRO_END_DECLS_CXX }
#endif
#else
/* header is included by a C source file, so no extern "C" */
#endif
/*
IMO, this non-standard ssize_t should not be used.
However, it's a good example of how to handle something like this.
*/
#ifdef _MSC_VER
#ifndef HAVE_SSIZE_T
#define HAVE_SSIZE_T
#if defined(_WIN64)
typedef __int64 ssize_t;
#elif defined(_WIN32)
typedef int ssize_t;
#endif
#endif
#elif defined(__unix__) || (defined(__MACH__) && defined(__APPLE__)) \
|| defined(__HAIKU__)
/* POSIX home of ssize_t; <stdio.h> carries it only for a profile asking
* for the BSD or POSIX.1-2008 set. Console toolchains keep the arms
* above, as they do not define __unix__. */
#include <sys/types.h>
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1800
#include <inttypes.h>
#else
#ifndef PRId64
#define PRId64 "I64d"
#define PRIu64 "I64u"
#define PRIx64 "I64x"
#define PRIuPTR "Iu"
#endif
#endif
#else
/* C++11 says this one isn't needed, but apparently (some versions of) mingw require it anyways */
/* https://stackoverflow.com/questions/8132399/how-to-printf-uint64-t-fails-with-spurious-trailing-in-format */
/* https://github.com/libretro/RetroArch/issues/6009 */
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#include <inttypes.h>
#endif
#ifndef PRId64
#error "inttypes.h is being screwy"
#endif
#define STRING_REP_INT64 "%" PRId64
#define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR
/* Wrap a declaration in RETRO_DEPRECATED() to produce a compiler warning when
it's used. This is intended for developer machines, so it won't work on ancient
or obscure compilers */
#if defined(_MSC_VER)
#if _MSC_VER >= 1400 /* Visual C 2005 or later */
#define RETRO_DEPRECATED(decl) __declspec(deprecated) decl
#endif
#elif defined(__GNUC__)
#if __GNUC__ >= 3 /* GCC 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#elif defined(__clang__)
#if __clang_major__ >= 3 /* clang 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#endif
#ifndef RETRO_DEPRECATED /* Unsupported compilers */
#define RETRO_DEPRECATED(decl) decl
#endif
/*
I would like to see retro_inline.h moved in here; possibly boolean too.
rationale: these are used in public APIs, and it is easier to find problems
and write code that works the first time portably when they are included uniformly
than to do the analysis from scratch each time you think you need it, for each feature.
Moreover it helps force you to make hard decisions: if you EVER bring in boolean.h,
then you should pay the price everywhere, so you can see how much grief it will cause.
Of course, another school of thought is that you should do as little damage as possible
in as few places as possible...
*/
/* _LIBRETRO_COMMON_RETRO_COMMON_API_H */
#endif
+187
View File
@@ -0,0 +1,187 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_dirent.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __RETRO_DIRENT_H
#define __RETRO_DIRENT_H
#include <libretro.h>
#include <retro_common_api.h>
#include <boolean.h>
/** @defgroup dirent Directory Entries
* @{
*/
RETRO_BEGIN_DECLS
/**
* The minimum VFS version (as defined in \c retro_vfs_interface_info::required_interface_version)
* required by the \c dirent functions.
* If no acceptable VFS interface is provided,
* all dirent functions will fall back to libretro-common's implementations.
* @see retro_vfs_interface_info
*/
#define DIRENT_REQUIRED_VFS_VERSION 3
#define DIRENT_STAT_REQUIRED_VFS_VERSION 5
/**
* Installs a frontend-provided VFS interface for the dirent functions to use
* instead of libretro-common's built-in implementations.
*
* @param vfs_info The VFS interface returned by the frontend.
* The dirent functions will fall back to libretro-common's implementations
* if \c vfs_info::required_interface_version is too low.
* @see retro_vfs_interface_info
* @see RETRO_ENVIRONMENT_GET_VFS_INTERFACE
*/
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info);
/**
* Opaque handle to a directory entry (aka "dirent").
* It may name a file, directory, or other filesystem object.
* @see retro_opendir
*/
typedef struct RDIR RDIR;
/**
* Opens a directory for reading.
*
* @param name Path to a directory to open.
* @return An \c RDIR representing the given directory if successful.
* Returns \c NULL if \c name is \c NULL, the empty string, or does not name a directory.
* @note The returned \c RDIR must be closed with \c retro_closedir.
* @see retro_opendir_include_hidden
* @see retro_closedir
*/
struct RDIR *retro_opendir(const char *name);
/**
* @copybrief retro_opendir
*
* @param name Path to the directory to open.
* @param include_hidden Whether to include hidden files and directories
* when iterating over this directory with \c retro_readdir.
* Platforms and filesystems have different notions of "hidden" files.
* Setting this to \c false will not prevent this function from opening \c name.
* @return An \c RDIR representing the given directory if successful.
* Returns \c NULL if \c name is \c NULL, the empty string, or does not name a directory.
* @note The returned \c RDIR must be closed with \c retro_closedir.
* @see retro_opendir
* @see retro_closedir
*/
struct RDIR *retro_opendir_include_hidden(const char *name, bool include_hidden);
/**
* Reads the next entry in the given directory.
*
* Here's a usage example that prints the names of all files in the current directory:
* @code
* struct RDIR *rdir = retro_opendir(".");
* if (rdir)
* {
* while (retro_readdir(rdir))
* {
* const char *name = retro_dirent_get_name(rdir);
* printf("%s\n", name);
* }
* retro_closedir(rdir);
* rdir = NULL;
* }
* @endcode
*
* @param rdir The directory to iterate over.
* Behavior is undefined if \c NULL.
* @return \c true if the next entry was read successfully,
* \c false if there are no more entries to read or if there was an error.
* @note This may include "." and ".." on Unix-like platforms.
* @see retro_dirent_get_name
* @see retro_dirent_is_dir
*/
int retro_readdir(struct RDIR *rdir);
/**
* @deprecated Left for compatibility.
* @param rdir Ignored.
* @return \c false.
*/
bool retro_dirent_error(struct RDIR *rdir);
/**
* Gets the name of the dirent's current file or directory.
*
* @param rdir The dirent to get the name of.
* Behavior is undefined if \c NULL.
* @return The name of the directory entry (file, directory, etc.) that the dirent points to.
* Will return \c NULL if there was an error,
* \c retro_readdir has not been called on \c rdir,
* or if there are no more entries to read.
* @note This returns only a name, not a full path.
* @warning The returned string is managed by the VFS implementation
* and must not be modified or freed by the caller.
* @warning The returned string is only valid until the next call to \c retro_readdir.
* @see retro_readdir
*/
const char *retro_dirent_get_name(struct RDIR *rdir);
/**
* Checks if the given \c RDIR's current dirent names a directory.
*
* @param rdir The directory entry to check.
* Behavior is undefined if \c NULL.
* @param unused Ignored for compatibility reasons. Pass \c NULL.
* @return \c true if \c rdir refers to a directory, otherwise \c false.
* @see retro_readdir
*/
bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused);
/**
* Gets size, modification time and stat flags for the current dirent
* without opening it or building its path. Free on Windows (already in
* the find data), one fstatat on POSIX.
*
* Only valid after a \c retro_readdir that returned \c true and before
* the next \c retro_readdir or \c retro_closedir on the same handle.
*
* @param rdir The directory being enumerated.
* @param size If non-NULL, receives the entry size in bytes (0 for directories).
* @param mtime If non-NULL, receives seconds since 1970-01-01T00:00:00Z.
* @return A bitmask of \c RETRO_VFS_STAT flags for the entry,
* or 0 if unavailable (including frontends older than VFS API v5).
* @see retro_readdir
*/
int retro_dirent_stat(struct RDIR *rdir, int64_t *size, int64_t *mtime);
/**
* Closes an opened \c RDIR that was returned by \c retro_opendir.
*
* @param rdir The directory entry to close.
* If \c NULL, this function does nothing.
* @see retro_vfs_closedir_t
*/
void retro_closedir(struct RDIR *rdir);
RETRO_END_DECLS
/** @} */
#endif
+595
View File
@@ -0,0 +1,595 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_endianness.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_ENDIANNESS_H
#define __LIBRETRO_SDK_ENDIANNESS_H
#include <retro_inline.h>
#include <stdint.h>
#include <stdlib.h>
#if defined(_MSC_VER) && _MSC_VER > 1200
#define SWAP16 _byteswap_ushort
#define SWAP32 _byteswap_ulong
#else
/**
* Swaps the byte order of a 16-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its two bytes swapped.
*/
static INLINE uint16_t SWAP16(uint16_t x)
{
return ((x & 0x00ff) << 8) |
((x & 0xff00) >> 8);
}
/**
* Swaps the byte order of a 32-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its bytes swapped.
*/
static INLINE uint32_t SWAP32(uint32_t x)
{
return ((x & 0x000000ff) << 24) |
((x & 0x0000ff00) << 8) |
((x & 0x00ff0000) >> 8) |
((x & 0xff000000) >> 24);
}
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1200
static INLINE uint64_t SWAP64(uint64_t val)
{
return
((val & 0x00000000000000ff) << 56)
| ((val & 0x000000000000ff00) << 40)
| ((val & 0x0000000000ff0000) << 24)
| ((val & 0x00000000ff000000) << 8)
| ((val & 0x000000ff00000000) >> 8)
| ((val & 0x0000ff0000000000) >> 24)
| ((val & 0x00ff000000000000) >> 40)
| ((val & 0xff00000000000000) >> 56);
}
#else
/**
* Swaps the byte order of a 64-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its bytes swapped.
*/
static INLINE uint64_t SWAP64(uint64_t val)
{
return ((val & 0x00000000000000ffULL) << 56)
| ((val & 0x000000000000ff00ULL) << 40)
| ((val & 0x0000000000ff0000ULL) << 24)
| ((val & 0x00000000ff000000ULL) << 8)
| ((val & 0x000000ff00000000ULL) >> 8)
| ((val & 0x0000ff0000000000ULL) >> 24)
| ((val & 0x00ff000000000000ULL) >> 40)
| ((val & 0xff00000000000000ULL) >> 56);
}
#endif
#ifdef _MSC_VER
/* MSVC pre-defines macros depending on target arch */
#if defined (_M_IX86) || defined (_M_AMD64) || defined (_M_ARM) || defined (_M_ARM64)
#ifndef LSB_FIRST
#define LSB_FIRST 1
#endif
#elif _M_PPC
#ifndef MSB_FIRST
#define MSB_FIRST 1
#endif
#else
/* MSVC can run on _M_ALPHA and _M_IA64 too, but they're both bi-endian; need to find what mode MSVC runs them at */
#error "unknown platform, can't determine endianness"
#endif
#else
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#ifndef MSB_FIRST
#define MSB_FIRST 1
#endif
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#ifndef LSB_FIRST
#define LSB_FIRST 1
#endif
#else
#error "Invalid endianness macros"
#endif
#endif
#if defined(MSB_FIRST) && defined(LSB_FIRST)
# error "Bug in LSB_FIRST/MSB_FIRST definition"
#endif
#if !defined(MSB_FIRST) && !defined(LSB_FIRST)
# error "Bug in LSB_FIRST/MSB_FIRST definition"
#endif
#ifdef MSB_FIRST
# define RETRO_IS_BIG_ENDIAN 1
# define RETRO_IS_LITTLE_ENDIAN 0
/* For compatibility */
# define WORDS_BIGENDIAN 1
#else
# define RETRO_IS_BIG_ENDIAN 0
# define RETRO_IS_LITTLE_ENDIAN 1
/* For compatibility */
# undef WORDS_BIGENDIAN
#endif
/**
* Checks if the current CPU is little-endian.
*
* @return \c true on little-endian CPUs,
* \c false on big-endian CPUs.
*/
#define is_little_endian() RETRO_IS_LITTLE_ENDIAN
/**
* Byte-swaps an unsigned 64-bit integer on big-endian CPUs.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian CPUs,
* \c val unchanged on little-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big64(val) (SWAP64(val))
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_big64(val) (val)
#endif
/**
* Byte-swaps an unsigned 32-bit integer on big-endian CPUs.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian CPUs,
* \c val unchanged on little-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big32(val) (SWAP32(val))
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_big32(val) (val)
#endif
/**
* Byte-swaps an unsigned 64-bit integer on little-endian CPUs.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian CPUs,
* \c val unchanged on big-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little64(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_little64(val) (SWAP64(val))
#endif
/**
* Byte-swaps an unsigned 32-bit integer on little-endian CPUs.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian CPUs,
* \c val unchanged on big-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little32(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_little32(val) (SWAP32(val))
#endif
/**
* Byte-swaps an unsigned 16-bit integer on big-endian systems.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian systems,
* \c val unchanged on little-endian systems.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big16(val) (SWAP16(val))
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_big16(val) (val)
#endif
/**
* Byte-swaps an unsigned 16-bit integer on little-endian systems.
*
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian systems,
* \c val unchanged on big-endian systems.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little16(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
#define swap_if_little16(val) (SWAP16(val))
#endif
/**
* Stores a 32-bit integer in at the given address, in big-endian order.
*
* @param addr The address to store the value at.
* Behavior is undefined if \c NULL or not aligned to a 32-bit boundary.
* @param data The value to store in \c addr.
* Will be byteswapped if on a little-endian CPU.
*/
static INLINE void store32be(uint32_t *addr, uint32_t data)
{
*addr = swap_if_little32(data);
}
/**
* Loads a 32-bit integer order from the given address, in big-endian order.
*
* @param addr The address to load the value from.
* Behavior is undefined if \c NULL or not aligned to a 32-bit boundary.
* @return The value at \c addr, byteswapped if on a little-endian CPU.
*/
static INLINE uint32_t load32be(const uint32_t *addr)
{
return swap_if_little32(*addr);
}
/**
* Converts the given unsigned 16-bit integer to little-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le16(val) swap_if_big16(val)
/**
* Converts the given unsigned 32-bit integer to little-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le32(val) swap_if_big32(val)
/**
* Converts the given unsigned 64-bit integer to little-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le64(val) swap_if_big64(val)
/**
* Converts the given unsigned 16-bit integer to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu16(val) swap_if_big16(val)
/**
* Converts the given unsigned 32-bit integer to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu32(val) swap_if_big32(val)
/**
* Converts the given unsigned 64-bit integer to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu64(val) swap_if_big64(val)
/**
* Converts the given unsigned 16-bit integer to big-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be16(val) swap_if_little16(val)
/**
* Converts the given unsigned 32-bit integer to big-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be32(val) swap_if_little32(val)
/**
* Converts the given unsigned 64-bit integer to big-endian order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be64(val) swap_if_little64(val)
/**
* Converts the given unsigned 16-bit integer from big-endian to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu16(val) swap_if_little16(val)
/**
* Converts the given unsigned 32-bit integer from big-endian to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu32(val) swap_if_little32(val)
/**
* Converts the given unsigned 64-bit integer from big-endian to host-native order if necessary.
*
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu64(val) swap_if_little64(val)
#ifdef __GNUC__
/**
* This attribute indicates that pointers to this type may alias
* to pointers of any other type, similar to \c void* or \c char*.
*/
#define MAY_ALIAS __attribute__((__may_alias__))
#else
#define MAY_ALIAS
#endif
#pragma pack(push, 1)
struct retro_unaligned_uint16_s
{
uint16_t val;
} MAY_ALIAS;
struct retro_unaligned_uint32_s
{
uint32_t val;
} MAY_ALIAS;
struct retro_unaligned_uint64_s
{
uint64_t val;
} MAY_ALIAS;
#pragma pack(pop)
/**
* A wrapper around a \c uint16_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint16_s retro_unaligned_uint16_t;
/**
* A wrapper around a \c uint32_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint32_s retro_unaligned_uint32_t;
/**
* A wrapper around a \c uint64_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint64_s retro_unaligned_uint64_t;
/* L-value references to unaligned pointers. */
#define retro_unaligned16(p) (((retro_unaligned_uint16_t *)(p))->val)
#define retro_unaligned32(p) (((retro_unaligned_uint32_t *)(p))->val)
#define retro_unaligned64(p) (((retro_unaligned_uint64_t *)(p))->val)
/**
* Reads a 16-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @return The first two bytes of \c addr as a 16-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint16_t retro_get_unaligned_16be(void *addr)
{
return retro_be_to_cpu16(retro_unaligned16(addr));
}
/**
* Reads a 32-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @return The first four bytes of \c addr as a 32-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint32_t retro_get_unaligned_32be(void *addr)
{
return retro_be_to_cpu32(retro_unaligned32(addr));
}
/**
* Reads a 64-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @return The first eight bytes of \c addr as a 64-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint64_t retro_get_unaligned_64be(void *addr)
{
return retro_be_to_cpu64(retro_unaligned64(addr));
}
/**
* Reads a 16-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @return The first two bytes of \c addr as a 16-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint16_t retro_get_unaligned_16le(void *addr)
{
return retro_le_to_cpu16(retro_unaligned16(addr));
}
/**
* Reads a 32-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @return The first four bytes of \c addr as a 32-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint32_t retro_get_unaligned_32le(void *addr)
{
return retro_le_to_cpu32(retro_unaligned32(addr));
}
/**
* Reads a 64-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address of the integer to read.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @return The first eight bytes of \c addr as a 64-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint64_t retro_get_unaligned_64le(void *addr)
{
return retro_le_to_cpu64(retro_unaligned64(addr));
}
/**
* Writes a 16-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_16le(void *addr, uint16_t v)
{
retro_unaligned16(addr) = retro_cpu_to_le16(v);
}
/**
* Writes a 32-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_32le(void *addr, uint32_t v)
{
retro_unaligned32(addr) = retro_cpu_to_le32(v);
}
/**
* Writes a 64-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_64le(void *addr, uint64_t v)
{
retro_unaligned64(addr) = retro_cpu_to_le64(v);
}
/**
* Writes a 16-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_16be(void *addr, uint16_t v)
{
retro_unaligned16(addr) = retro_cpu_to_be16(v);
}
/**
* Writes a 32-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_32be(void *addr, uint32_t v)
{
retro_unaligned32(addr) = retro_cpu_to_be32(v);
}
/**
* Writes a 64-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* @param addr The address to write the integer to.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_64be(void *addr, uint64_t v)
{
retro_unaligned64(addr) = retro_cpu_to_be64(v);
}
#endif
+120
View File
@@ -0,0 +1,120 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_environment.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_ENVIRONMENT_H
#define __LIBRETRO_SDK_ENVIRONMENT_H
/*
This file is designed to create a normalized environment for compiling
libretro-common's private implementations, or any other sources which might
enjoy use of it's environment (RetroArch for instance).
This should be an elaborately crafted environment so that sources don't
need to be full of platform-specific workarounds.
*/
#if defined (__cplusplus)
/* The expected values would be
* 199711L, for ISO/IEC 14882:1998 or 14882:2003
*/
#elif defined(__STDC__)
/* This is standard C. */
#if (__STDC__ == 1)
/* The implementation is ISO-conforming. */
#define __STDC_ISO__
#else
/* The implementation is not ISO-conforming. */
#endif
#if defined(__STDC_VERSION__)
#if (__STDC_VERSION__ >= 201112L)
/* This is C11. */
#define __STDC_C11__
#elif (__STDC_VERSION__ >= 199901L)
/* This is C99. */
#define __STDC_C99__
#elif (__STDC_VERSION__ >= 199409L)
/* This is C89 with amendment 1. */
#define __STDC_C89__
#define __STDC_C89_AMENDMENT_1__
#else
/* This is C89 without amendment 1. */
#define __STDC_C89__
#endif
#else /* !defined(__STDC_VERSION__) */
/* This is C89. __STDC_VERSION__ is not defined. */
#define __STDC_C89__
#endif
#else /* !defined(__STDC__) */
/* This is not standard C. __STDC__ is not defined. */
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
/* Try to find out if we're compiling for WinRT or non-WinRT */
#if defined(_MSC_VER) && defined(__has_include)
#if __has_include(<winapifamily.h>)
#define HAVE_WINAPIFAMILY_H 1
#else
#define HAVE_WINAPIFAMILY_H 0
#endif
/* If _USING_V110_SDK71_ is defined it means we are using the Windows XP toolset. */
#elif defined(_MSC_VER) && (_MSC_VER >= 1700 && !_USING_V110_SDK71_) /* _MSC_VER == 1700 for Visual Studio 2012 */
#define HAVE_WINAPIFAMILY_H 1
#else
#define HAVE_WINAPIFAMILY_H 0
#endif
#if HAVE_WINAPIFAMILY_H
#include <winapifamily.h>
#define WINAPI_FAMILY_WINRT (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP))
#else
#define WINAPI_FAMILY_WINRT 0
#endif /* HAVE_WINAPIFAMILY_H */
#if WINAPI_FAMILY_WINRT
#undef __WINRT__
#define __WINRT__ 1
#endif
/* GCC and clang define __SSE__/__SSE2__ themselves; MSVC reports the
* same information through _M_IX86_FP and its architecture macros, so
* translate those into the standard spellings. The inner guards let
* clang-cl, which defines both sets, through without redefining. */
#if defined(_MSC_VER)
#if (defined(_M_IX86_FP) && _M_IX86_FP >= 1) || defined(_M_AMD64) || defined(_M_X64)
#ifndef __SSE__
#define __SSE__ 1
#endif
#endif
#if (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(_M_AMD64) || defined(_M_X64)
#ifndef __SSE2__
#define __SSE2__ 1
#endif
#endif
#endif /* _MSC_VER */
#endif
#endif
+45
View File
@@ -0,0 +1,45 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_inline.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_INLINE_H
#define __LIBRETRO_SDK_INLINE_H
#ifndef INLINE
/**
* Cross-platform inline specifier.
*
* Expands to something like \c __inline or \c inline,
* depending on the compiler.
*/
#if defined(_WIN32) || defined(__INTEL_COMPILER)
#define INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
#define INLINE inline
#elif defined(__GNUC__)
#define INLINE __inline__
#else
#define INLINE
#endif
#endif
#endif
Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff. Show more