diff --git a/.github/actions/setup-ninja/action.yml b/.github/actions/setup-ninja/action.yml new file mode 100644 index 000000000..a1d3ad983 --- /dev/null +++ b/.github/actions/setup-ninja/action.yml @@ -0,0 +1,62 @@ +name: 'Setup ninja' +description: 'Download ninja and add it to the PATH environment variable' +inputs: + version: + description: 'Ninja version' + default: '1.12.1' +runs: + using: 'composite' + steps: + - name: 'Calculate variables' + id: calc + shell: sh + run: | + case "${{ runner.os }}-${{ runner.arch }}" in + "Linux-X86" | "Linux-X64") + archive="ninja-linux.zip" + ;; + "Linux-ARM64") + archive="ninja-linux-aarch64.zip" + ;; + "macOS-X86" | "macOS-X64" | "macOS-ARM64") + archive="ninja-mac.zip" + ;; + "Windows-X86" | "Windows-X64") + archive="ninja-win.zip" + ;; + "Windows-ARM64") + archive="ninja-winarm64.zip" + ;; + *) + echo "Unsupported ${{ runner.os }}-${{ runner.arch }}" + exit 1; + ;; + esac + echo "archive=${archive}" >> ${GITHUB_OUTPUT} + echo "cache-key=${archive}-${{ inputs.version }}-${{ runner.os }}-${{ runner.arch }}" >> ${GITHUB_OUTPUT} + - name: 'Restore cached ${{ steps.calc.outputs.archive }}' + id: cache-restore + uses: actions/cache/restore@v4 + with: + path: '${{ runner.temp }}/${{ steps.calc.outputs.archive }}' + key: ${{ steps.calc.outputs.cache-key }} + - name: 'Download ninja ${{ inputs.version }} for ${{ runner.os }} (${{ runner.arch }})' + if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} + shell: pwsh + run: | + Invoke-WebRequest "https://github.com/ninja-build/ninja/releases/download/v${{ inputs.version }}/${{ steps.calc.outputs.archive }}" -OutFile "${{ runner.temp }}/${{ steps.calc.outputs.archive }}" + - name: 'Cache ${{ steps.calc.outputs.archive }}' + if: ${{ !steps.cache-restore.outputs.cache-hit || steps.cache-restore.outputs.cache-hit == 'false' }} + uses: actions/cache/save@v4 + with: + path: '${{ runner.temp }}/${{ steps.calc.outputs.archive }}' + key: ${{ steps.calc.outputs.cache-key }} + - name: 'Extract ninja' + shell: pwsh + run: | + 7z "-o${{ runner.temp }}/ninja-${{ inputs.version }}-${{ runner.arch }}" x "${{ runner.temp }}/${{ steps.calc.outputs.archive }}" + - name: 'Set output variables' + id: final + shell: pwsh + run: | + echo "${{ runner.temp }}/ninja-${{ inputs.version }}-${{ runner.arch }}" >> $env:GITHUB_PATH diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 692f04f7a..4b82bd595 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,25 +9,27 @@ jobs: strategy: matrix: platform: - - { name: Windows, os: windows-latest } - - { name: Linux, os: ubuntu-20.04, flags: -GNinja } - - { name: MacOS, os: macos-latest } + - { name: Windows-MSVC-x86, os: windows-latest, flags: -A Win32 } + - { name: Windows-MSVC-x64, os: windows-latest, flags: -A x64 } + - { name: Linux-x86_64, os: ubuntu-24.04, flags: -GNinja } + - { name: Linux-arm64, os: ubuntu-24.04-arm, flags: -GNinja } + - { name: MacOS-arm64, os: macos-latest } steps: - name: Setup Linux dependencies - if: runner.os == 'Linux' + if: startsWith(runner.os, 'Linux') run: | - sudo apt-get update - sudo apt-get install cmake ninja-build + sudo apt update -q + sudo apt install -y cmake ninja-build libgl1-mesa-dev libglu1-mesa-dev - name: Get sdl12-compat sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Get SDL2 headers - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: libsdl-org/SDL - ref: release-2.0.14 - path: sdl12-compat/SDL2 + ref: SDL2 + path: SDL2 - name: Configure CMake run: cmake -DSDL2_INCLUDE_DIR="${{ github.workspace }}/SDL2/include" -B build ${{ matrix.platform.flags }} - name: Build - run: cmake --build build/ + run: cmake --build build/ --verbose diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..9841ee9e2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,594 @@ +name: 'release' +run-name: 'Create sdl12-compat release artifacts for ${{ inputs.commit }}' + +on: + workflow_dispatch: + inputs: + commit: + description: 'Commit of sdl12-compat' + required: true + +jobs: + + src: + runs-on: ubuntu-latest + outputs: + project: ${{ steps.releaser.outputs.project }} + version: ${{ steps.releaser.outputs.version }} + src-tar-gz: ${{ steps.releaser.outputs.src-tar-gz }} + src-tar-xz: ${{ steps.releaser.outputs.src-tar-xz }} + src-zip: ${{ steps.releaser.outputs.src-zip }} + steps: + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: 'Fetch build-release.py' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.commit }} + sparse-checkout: 'build-scripts/build-release.py' + - name: 'Set up SDL sources' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.commit }} + path: 'SDL' + fetch-depth: 0 + - name: 'Build Source archive' + id: releaser + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + python build-scripts/build-release.py \ + --actions source \ + --commit ${{ inputs.commit }} \ + --root "${{ github.workspace }}/SDL" \ + --github \ + --debug + - name: 'Store source archives' + uses: actions/upload-artifact@v4 + with: + name: sources + path: '${{ github.workspace}}/dist' + - name: 'Generate summary' + run: | + echo "Run the following commands to download all artifacts:" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "mkdir -p /tmp/${{ steps.releaser.outputs.project }}-${{ steps.releaser.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "cd /tmp/${{ steps.releaser.outputs.project }}-${{ steps.releaser.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "gh run -R ${{ github.repository }} download ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + + linux-verify: + needs: [src] + runs-on: ubuntu-latest + steps: + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: 'Download source archives' + uses: actions/download-artifact@v4 + with: + name: sources + path: '/tmp' + - name: 'Unzip ${{ needs.src.outputs.src-zip }}' + id: zip + run: | + set -e + mkdir /tmp/zipdir + cd /tmp/zipdir + unzip "/tmp/${{ needs.src.outputs.src-zip }}" + echo "path=/tmp/zipdir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT + - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' + id: tar + run: | + set -e + mkdir -p /tmp/tardir + tar -C /tmp/tardir -v -x -f "/tmp/${{ needs.src.outputs.src-tar-gz }}" + echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT + - name: 'Compare contents of ${{ needs.src.outputs.src-zip }} and ${{ needs.src.outputs.src-tar-gz }}' + run: | + set -e + diff "${{ steps.zip.outputs.path }}" "${{ steps.tar.outputs.path }}" + - name: 'Test versioning' + shell: bash + run: | + ${{ steps.tar.outputs.path }}/build-scripts/test-versioning.sh + - name: 'Fetch setup-ninja action' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.commit }} + sparse-checkout: | + .github/actions/setup-ninja/action.yml + - name: 'Setup sdl2-compat' + uses: libsdl-org/setup-sdl@main + with: + version: 3-latest + version-sdl2-compat: 2-latest + install-linux-dependencies: true + - name: Set up ninja + uses: ./.github/actions/setup-ninja + - name: Install GLU development headers + run: | + sudo apt update -q + sudo apt install -y libglu1-mesa-dev + - name: 'CMake (configure + build)' + run: | + cmake \ + -GNinja \ + -S ${{ steps.tar.outputs.path }} \ + -B /tmp/build + cmake --build /tmp/build --verbose + # ctest --test-dir /tmp/build --no-tests=error --output-on-failure + +# dmg: +# needs: [src] +# runs-on: macos-latest +# outputs: +# dmg: ${{ steps.releaser.outputs.dmg }} +# steps: +# - name: 'Set up Python' +# uses: actions/setup-python@v5 +# with: +# python-version: '3.11' +# - name: 'Fetch build-release.py' +# uses: actions/checkout@v5 +# with: +# ref: ${{ inputs.commit }} +# sparse-checkout: 'build-scripts/build-release.py' +# - name: 'Install nasm' +# run: | +# brew install nasm +# - name: 'Download source archives' +# uses: actions/download-artifact@v4 +# with: +# name: sources +# path: '${{ github.workspace }}' +# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' +# id: tar +# run: | +# mkdir -p "${{ github.workspace }}/tardir" +# tar -C "${{ github.workspace }}/tardir" -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" +# echo "path=${{ github.workspace }}/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT +# - name: 'Download external dependencies' +# run: | +# sh "${{ steps.tar.outputs.path }}/external/download.sh" --depth 1 +# - name: 'Build SDL3_ttf.dmg' +# id: releaser +# shell: bash +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# python build-scripts/build-release.py \ +# --actions dmg \ +# --commit ${{ inputs.commit }} \ +# --root "${{ steps.tar.outputs.path }}" \ +# --github \ +# --debug +# - name: 'Store DMG image file' +# uses: actions/upload-artifact@v4 +# with: +# name: dmg +# path: '${{ github.workspace }}/dist' +# +# dmg-verify: +# needs: [dmg, src] +# runs-on: macos-latest +# steps: +# - name: 'Set up Python' +# uses: actions/setup-python@v5 +# with: +# python-version: '3.11' +# - name: 'Fetch build-release.py' +# uses: actions/checkout@v5 +# with: +# ref: ${{ inputs.commit }} +# sparse-checkout: 'build-scripts/build-release.py' +# - name: 'Download source archives' +# uses: actions/download-artifact@v4 +# with: +# name: sources +# path: '${{ github.workspace }}' +# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' +# id: src +# run: | +# mkdir -p /tmp/tardir +# tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" +# echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT +# - name: 'Download dependencies' +# id: deps +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# python build-scripts/build-release.py \ +# --actions download \ +# --commit ${{ inputs.commit }} \ +# --root "${{ steps.src.outputs.path }}" \ +# --github \ +# --debug +# - name: 'Mount dependencies' +# id: deps-mount +# run: | +# hdiutil attach "${{ steps.deps.outputs.dep-path }}/SDL3-${{ steps.deps.outputs.dep-sdl-version }}.dmg" +# sdl_mount_pount="/Volumes/SDL3" +# if [ ! -d "$sdl_mount_pount/SDL3.xcframework" ]; then +# echo "Cannot find SDL3.xcframework!" +# exit 1 +# fi +# echo "path=${sdl_mount_pount}" >>$GITHUB_OUTPUT +# - name: 'Download ${{ needs.dmg.outputs.dmg }}' +# uses: actions/download-artifact@v4 +# with: +# name: dmg +# path: '${{ github.workspace }}' +# - name: 'Mount ${{ needs.dmg.outputs.dmg }}' +# id: mount +# run: | +# hdiutil attach '${{ github.workspace }}/${{ needs.dmg.outputs.dmg }}' +# mount_point="/Volumes/${{ needs.src.outputs.project }}" +# if [ ! -d "$mount_point/${{ needs.src.outputs.project }}.xcframework" ]; then +# echo "Cannot find ${{ needs.src.outputs.project }}.xcframework!" +# exit 1 +# fi +# echo "mount-point=${mount_point}">>$GITHUB_OUTPUT +# - name: 'Verify presence of optional frameworks' +# run: | +# OPTIONAL_FRAMEWORKS="gme opus wavpack xmp" +# rc=0 +# for opt in $OPTIONAL_FRAMEWORKS; do +# fw_path="${{ steps.mount.outputs.mount-point }}/optional/${opt}.xcframework" +# if [ -d "${fw_path}" ]; then +# echo "$fw_path OK" +# else +# echo "$fw_path MISSING" +# rc=1 +# fi +# done +# exit $rc +# - name: 'CMake (configure + build) Darwin' +# run: | +# set -e +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=FALSE \ +# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ +# -DCMAKE_SYSTEM_NAME=Darwin \ +# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ +# -Werror=dev \ +# -B build_darwin +# cmake --build build_darwin --config Release --verbose +# +# - name: 'CMake (configure + build) iOS' +# run: | +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=FALSE \ +# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ +# -DCMAKE_SYSTEM_NAME=iOS \ +# -DCMAKE_OSX_ARCHITECTURES="arm64" \ +# -Werror=dev \ +# -B build_ios +# cmake --build build_ios --config Release --verbose +# - name: 'CMake (configure + build) tvOS' +# run: | +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=FALSE \ +# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ +# -DCMAKE_SYSTEM_NAME=tvOS \ +# -DCMAKE_OSX_ARCHITECTURES="arm64" \ +# -Werror=dev \ +# -B build_tvos +# cmake --build build_tvos --config Release --verbose +# - name: 'CMake (configure + build) iOS simulator' +# run: | +# sysroot=$(xcodebuild -version -sdk iphonesimulator Path) +# echo "sysroot=$sysroot" +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=FALSE \ +# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ +# -DCMAKE_SYSTEM_NAME=iOS \ +# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ +# -DCMAKE_OSX_SYSROOT="${sysroot}" \ +# -Werror=dev \ +# -B build_ios_simulator +# cmake --build build_ios_simulator --config Release --verbose +# - name: 'CMake (configure + build) tvOS simulator' +# run: | +# sysroot=$(xcodebuild -version -sdk appletvsimulator Path) +# echo "sysroot=$sysroot" +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=FALSE \ +# -DCMAKE_PREFIX_PATH="${{ steps.mount.outputs.mount-point }};${{ steps.deps-mount.outputs.path }}" \ +# -DCMAKE_SYSTEM_NAME=tvOS \ +# -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ +# -DCMAKE_OSX_SYSROOT="${sysroot}" \ +# -Werror=dev \ +# -B build_tvos_simulator +# cmake --build build_tvos_simulator --config Release --verbose + msvc: + needs: [src] + runs-on: windows-2025 + outputs: + VC-x86: ${{ steps.releaser.outputs.VC-x86 }} + VC-x64: ${{ steps.releaser.outputs.VC-x64 }} + VC-devel: ${{ steps.releaser.outputs.VC-devel }} + steps: + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: 'Fetch build-release.py' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.commit }} + sparse-checkout: 'build-scripts/build-release.py' + - name: 'Download source archives' + uses: actions/download-artifact@v4 + with: + name: sources + path: '${{ github.workspace }}' + - name: 'Unzip ${{ needs.src.outputs.src-zip }}' + id: zip + run: | + New-Item C:\temp -ItemType Directory -ErrorAction SilentlyContinue + cd C:\temp + unzip "${{ github.workspace }}/${{ needs.src.outputs.src-zip }}" + echo "path=C:\temp\${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$Env:GITHUB_OUTPUT +# - name: 'Download external dependencies' +# run: | +# ${{ steps.zip.outputs.path }}/external/Get-GitModules.ps1 + - name: 'Build MSVC binary archives' + id: releaser + env: + GH_TOKEN: ${{ github.token }} + run: | + python build-scripts/build-release.py ` + --actions download msvc ` + --commit ${{ inputs.commit }} ` + --root "${{ steps.zip.outputs.path }}" ` + --github ` + --debug + - name: 'Store MSVC archives' + uses: actions/upload-artifact@v4 + with: + name: msvc + path: '${{ github.workspace }}/dist' + + msvc-verify: + needs: [msvc, src] + runs-on: windows-latest + steps: + - name: 'Fetch .github/actions/setup-ninja/action.yml' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.commit }} + sparse-checkout: | + .github/actions/setup-ninja/action.yml + build-scripts/build-release.py + - name: 'Set up Python' + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Set up ninja + uses: ./.github/actions/setup-ninja + - name: 'Download source archives' + uses: actions/download-artifact@v4 + with: + name: sources + path: '${{ github.workspace }}' + - name: 'Unzip ${{ needs.src.outputs.src-zip }}' + id: src + run: | + mkdir '${{ github.workspace }}/sources' + cd '${{ github.workspace }}/sources' + unzip "${{ github.workspace }}/${{ needs.src.outputs.src-zip }}" + echo "path=${{ github.workspace }}/sources/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$env:GITHUB_OUTPUT + - name: 'Download MSVC binaries' + uses: actions/download-artifact@v4 + with: + name: msvc + path: '${{ github.workspace }}' + - name: 'Unzip ${{ needs.msvc.outputs.VC-devel }}' + id: bin + run: | + mkdir '${{ github.workspace }}/vc' + cd '${{ github.workspace }}/vc' + unzip "${{ github.workspace }}/${{ needs.msvc.outputs.VC-devel }}" + echo "path=${{ github.workspace }}/vc/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$env:GITHUB_OUTPUT + - name: 'Configure vcvars x86' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64_x86 + - name: 'CMake (configure + build + tests) x86' + run: | + cmake -S "${{ steps.src.outputs.path }}/test" ` + -B build_x86 ` + -GNinja ` + -DCMAKE_BUILD_TYPE=Debug ` + -Werror=dev ` + -DTEST_SHARED=TRUE ` + -DTEST_STATIC=FALSE ` + -DCMAKE_SUPPRESS_REGENERATION=TRUE ` + -DCMAKE_PREFIX_PATH="${{ steps.bin.outputs.path }}" + Start-Sleep -Seconds 2 + cmake --build build_x86 --config Release --verbose + - name: 'Configure vcvars x64' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + - name: 'CMake (configure + build + tests) x64' + run: | + cmake -S "${{ steps.src.outputs.path }}/test" ` + -B build_x64 ` + -GNinja ` + -DCMAKE_BUILD_TYPE=Debug ` + -Werror=dev ` + -DTEST_SHARED=TRUE ` + -DTEST_STATIC=FALSE ` + -DCMAKE_SUPPRESS_REGENERATION=TRUE ` + -DCMAKE_PREFIX_PATH="${{ steps.bin.outputs.path }}" + Start-Sleep -Seconds 2 + cmake --build build_x64 --config Release --verbose +# - name: 'Configure vcvars arm64' +# uses: ilammy/msvc-dev-cmd@v1 +# with: +# arch: x64_arm64 +# - name: 'CMake (configure + build + tests) arm64' +# run: | +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" ` +# -B build_arm64 ` +# -GNinja ` +# -DCMAKE_BUILD_TYPE=Debug ` +# -Werror=dev ` +# -DTEST_SHARED=TRUE ` +# -DTEST_STATIC=FALSE ` +# -DCMAKE_SUPPRESS_REGENERATION=TRUE ` +# -DCMAKE_PREFIX_PATH="${{ steps.bin.outputs.path }};${{ steps.deps-extract.outputs.path }}" +# Start-Sleep -Seconds 2 +# cmake --build build_arm64 --config Release --verbose + +# mingw: +# needs: [src] +# runs-on: ubuntu-24.04 # FIXME: current ubuntu-latest ships an outdated mingw, replace with ubuntu-latest once 24.04 becomes the new default +# outputs: +# mingw-devel-tar-gz: ${{ steps.releaser.outputs.mingw-devel-tar-gz }} +# mingw-devel-tar-xz: ${{ steps.releaser.outputs.mingw-devel-tar-xz }} +# steps: +# - name: 'Set up Python' +# uses: actions/setup-python@v5 +# with: +# python-version: '3.11' +# - name: 'Fetch build-release.py' +# uses: actions/checkout@v5 +# with: +# ref: ${{ inputs.commit }} +# sparse-checkout: 'build-scripts/build-release.py' +# - name: 'Install Mingw toolchain' +# run: | +# sudo apt-get update -y +# sudo apt-get install -y gcc-mingw-w64 g++-mingw-w64 ninja-build +# - name: 'Download source archives' +# uses: actions/download-artifact@v4 +# with: +# name: sources +# path: '${{ github.workspace }}' +# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' +# id: tar +# run: | +# mkdir -p /tmp/tardir +# tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" +# echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT +# - name: 'Download external dependencies' +# run: | +# sh "${{ steps.tar.outputs.path }}/external/download.sh" --depth 1 +# - name: 'Build MinGW binary archives' +# id: releaser +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# python build-scripts/build-release.py \ +# --actions download mingw \ +# --commit ${{ inputs.commit }} \ +# --root "${{ steps.tar.outputs.path }}" \ +# --github \ +# --debug +# - name: 'Store MinGW archives' +# uses: actions/upload-artifact@v4 +# with: +# name: mingw +# path: '${{ github.workspace }}/dist' +# +# mingw-verify: +# needs: [mingw, src] +# runs-on: ubuntu-latest +# steps: +# - name: 'Set up Python' +# uses: actions/setup-python@v5 +# with: +# python-version: '3.11' +# - name: 'Fetch build-release.py' +# uses: actions/checkout@v5 +# with: +# ref: ${{ inputs.commit }} +# sparse-checkout: 'build-scripts/build-release.py' +# - name: 'Install Mingw toolchain' +# run: | +# sudo apt-get update -y +# sudo apt-get install -y gcc-mingw-w64 g++-mingw-w64 ninja-build +# - name: 'Download source archives' +# uses: actions/download-artifact@v4 +# with: +# name: sources +# path: '${{ github.workspace }}' +# - name: 'Untar ${{ needs.src.outputs.src-tar-gz }}' +# id: src +# run: | +# mkdir -p /tmp/tardir +# tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}" +# echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT +# - name: 'Download dependencies' +# id: deps +# env: +# GH_TOKEN: ${{ github.token }} +# run: | +# python build-scripts/build-release.py \ +# --actions download \ +# --commit ${{ inputs.commit }} \ +# --root "${{ steps.src.outputs.path }}" \ +# --github \ +# --debug +# - name: 'Untar and install dependencies' +# id: deps-extract +# run: | +# mkdir -p /tmp/deps-mingw/cmake +# mkdir -p /tmp/deps-mingw/i686-w64-mingw32 +# mkdir -p /tmp/deps-mingw/x86_64-w64-mingw32 +# +# mkdir -p /tmp/deps-mingw-extract/sdl3 +# tar -C /tmp/deps-mingw-extract/sdl3 -v -x -f "${{ steps.deps.outputs.dep-path }}/SDL3-devel-${{ steps.deps.outputs.dep-sdl-version }}-mingw.tar.gz" +# make -C /tmp/deps-mingw-extract/sdl3/SDL3-${{ steps.deps.outputs.dep-sdl-version }} install-all DESTDIR=/tmp/deps-mingw +# +# # FIXME: this should be fixed in SDL3 releases after 3.1.3 +# mkdir -p /tmp/deps-mingw/cmake +# cp -rv /tmp/deps-mingw-extract/sdl3/SDL3-${{ steps.deps.outputs.dep-sdl-version }}/cmake/* /tmp/deps-mingw/cmake +# - name: 'Download MinGW binaries' +# uses: actions/download-artifact@v4 +# with: +# name: mingw +# path: '${{ github.workspace }}' +# - name: 'Untar and install ${{ needs.mingw.outputs.mingw-devel-tar-gz }}' +# id: bin +# run: | +# mkdir -p /tmp/mingw-tardir +# tar -C /tmp/mingw-tardir -v -x -f "${{ github.workspace }}/${{ needs.mingw.outputs.mingw-devel-tar-gz }}" +# make -C /tmp/mingw-tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }} install-all DESTDIR=/tmp/deps-mingw +# - name: 'CMake (configure + build) i686' +# run: | +# set -e +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DCMAKE_BUILD_TYPE="Release" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=TRUE \ +# -DCMAKE_PREFIX_PATH="/tmp/deps-mingw" \ +# -DCMAKE_TOOLCHAIN_FILE="${{ steps.src.outputs.path }}/build-scripts/cmake-toolchain-mingw64-i686.cmake" \ +# -Werror=dev \ +# -B build_x86 +# cmake --build build_x86 --config Release --verbose +# - name: 'CMake (configure + build) x86_64' +# run: | +# set -e +# cmake -S "${{ steps.src.outputs.path }}/cmake/test" \ +# -DCMAKE_BUILD_TYPE="Release" \ +# -DTEST_SHARED=TRUE \ +# -DTEST_STATIC=TRUE \ +# -DCMAKE_PREFIX_PATH="/tmp/deps-mingw" \ +# -DCMAKE_TOOLCHAIN_FILE="${{ steps.src.outputs.path }}/build-scripts/cmake-toolchain-mingw64-x86_64.cmake" \ +# -Werror=dev \ +# -B build_x64 +# cmake --build build_x64 --config Release --verbose diff --git a/.github/workflows/watcom.yml b/.github/workflows/watcom.yml new file mode 100644 index 000000000..81b9e2283 --- /dev/null +++ b/.github/workflows/watcom.yml @@ -0,0 +1,39 @@ +name: Build (OpenWatcom) + +on: [push, pull_request] + +jobs: + os2: + name: ${{ matrix.platform.name }} + runs-on: windows-latest + + strategy: + matrix: + platform: + - { name: Windows, makefile: Makefile.w32 } + - { name: OS/2, makefile: Makefile.os2 } + + steps: + - uses: open-watcom/setup-watcom@v0 + - name: Get sdl12-compat sources + uses: actions/checkout@v4 + - name: Get SDL2 headers + uses: actions/checkout@v4 + with: + repository: libsdl-org/SDL + ref: SDL2 + path: SDL2 + - name: Build sdl12-compat + run: | + cd src && wmake -f ${{ matrix.platform.makefile }} SDL2INC="${{ github.workspace }}/SDL2/include" + cd .. + - name: Build tests + run: | + cd test && wmake -f ${{ matrix.platform.makefile }} + cd .. + - name: distclean + run: | + cd src && wmake -f ${{ matrix.platform.makefile }} distclean + cd .. + cd test && wmake -f ${{ matrix.platform.makefile }} distclean + cd .. diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 000000000..26159a8e8 --- /dev/null +++ b/BUGS.md @@ -0,0 +1,20 @@ + +# Reporting bugs + +Bugs are now managed in sdl12-compat's GitHub Issues tracker: + +https://github.com/libsdl-org/sdl12-compat/issues + +You may report bugs there, and search to see if a given issue has already +been reported, discussed, and maybe even fixed. + + +# Discussion with other humans + +You may also find help at the SDL forums: + +https://discourse.libsdl.org/ + +Bug reports are welcome here, but we really appreciate if you use the bug +tracker, as bugs discussed on the forums may be forgotten or missed. + diff --git a/BUGS.txt b/BUGS.txt deleted file mode 100644 index 2e31b0ba0..000000000 --- a/BUGS.txt +++ /dev/null @@ -1,18 +0,0 @@ - -Bugs are now managed in GitHub Issues, here: - - https://github.com/libsdl-org/sdl12-compat/issues - -You may report bugs there, and search to see if a given issue has already - been reported, discussed, and maybe even fixed. - - - -You may also find help at the SDL forums: - - https://discourse.libsdl.org/ - -Bug reports are welcome here, but we really appreciate if you use the bug - tracker, as bugs discussed on the forums may be forgotten or missed. - - diff --git a/CMakeLists.txt b/CMakeLists.txt index fe9e8ecd6..5cc8c0506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,26 +2,45 @@ # you can define SDL2_INCLUDE_DIR on the cmdline. For example: # cmake -DSDL2_INCLUDE_DIR=/opt/SDL2/include/SDL2 [other stuff] -cmake_minimum_required(VERSION 3.0.0) -project(sdl12_compat - VERSION 0.0.1 - LANGUAGES C) - -set(SDL12_COMPAT_VERSION_STR "1.2.50") +cmake_minimum_required(VERSION 3.0.0...4.1) +project(sdl12_compat VERSION 1.2.76 LANGUAGES C) option(SDL12TESTS "Enable to build SDL-1.2 test programs" ON) option(SDL12DEVEL "Enable installing SDL-1.2 development headers" ON) +option(STATICDEVEL "Enable installing static link library" OFF) + +if(STATICDEVEL AND NOT (CMAKE_SYSTEM_NAME MATCHES "Linux")) + message(FATAL_ERROR "Static builds are only supported on Linux.") +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_LIST_DIR}/cmake/modules" +) -set(CMAKE_SKIP_RPATH TRUE) +include(CheckCSourceCompiles) +include(CheckIncludeFile) +include(CheckCCompilerFlag) +include(GNUInstallDirs) if(APPLE) set(OSX_SRCS "src/SDL12_compat_objc.m") set_source_files_properties(${OSX_SRCS} PROPERTIES LANGUAGE C) - # the following matches SDL-1.2 Xcode project file - set(DYLIB_COMPAT_VERSION 1.0.0) - set(DYLIB_CURRENT_VERSION 12.50.0) + if(NOT CMAKE_VERSION VERSION_LESS "3.16") + enable_language(OBJC) + set_source_files_properties(${OSX_SRCS} PROPERTIES LANGUAGE OBJC) + endif() + # compatibility version set to match SDL-1.2 autotools build + # Xcode project file uses 1.0.0, but it's more compatible to use the + # higher version. + set(DYLIB_COMPAT_VERSION 12.0.0 CACHE STRING "library compatibility version") + set(DYLIB_CURRENT_VERSION 12.76.0 CACHE STRING "library current version") include_directories("/opt/X11/include") # hack. + if(CMAKE_VERSION VERSION_LESS 3.9) + else() + cmake_policy(SET CMP0068 NEW) # on macOS, don't let RPATH affect install_name. + endif() endif() + if(WIN32) set(WIN32_SRCS "src/version.rc") endif() @@ -32,20 +51,39 @@ set(SDL12COMPAT_SRCS ${WIN32_SRCS} ) add_library(SDL SHARED ${SDL12COMPAT_SRCS}) +add_library(SDL::SDL-shared ALIAS SDL) +add_library(SDL::SDL ALIAS SDL) -include(GNUInstallDirs) -include("cmake/modules/FindSDL2.cmake") +find_package(SDL2 CONFIG) +find_package(SDL2 MODULE) +if(NOT SDL2_INCLUDE_DIRS) + message(FATAL_ERROR "Could not find SDL2 headers") +endif() target_include_directories(SDL PRIVATE ${SDL2_INCLUDE_DIRS}) -# avoid DLL having 'lib' prefix with Windows MinGW builds -if(WIN32) - set(CMAKE_SHARED_LIBRARY_PREFIX "") - set_target_properties(SDL PROPERTIES COMPILE_DEFINITIONS "DLL_EXPORT") +set(EXTRA_CFLAGS ) +if (CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall") + check_c_compiler_flag(-Wdeclaration-after-statement HAVE_WDECLARATION_AFTER_STATEMENT) + if(HAVE_WDECLARATION_AFTER_STATEMENT) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wdeclaration-after-statement") + endif() + check_c_compiler_flag(-Werror=declaration-after-statement HAVE_WERROR_DECLARATION_AFTER_STATEMENT) + if(HAVE_WERROR_DECLARATION_AFTER_STATEMENT) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror=declaration-after-statement") + endif() endif() +# just in case: +check_include_file("immintrin.h" HAVE_IMMINTRIN_H) +if(NOT HAVE_IMMINTRIN_H) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -DSDL_DISABLE_IMMINTRIN_H") +endif() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +string(STRIP "${CMAKE_C_FLAGS}" CMAKE_C_FLAGS) if(UNIX AND NOT APPLE) set_target_properties(SDL PROPERTIES COMPILE_DEFINITIONS "_REENTRANT") - target_link_libraries(SDL PRIVATE dl) + target_link_libraries(SDL PRIVATE ${CMAKE_DL_LIBS}) endif() if(APPLE) set_target_properties(SDL PROPERTIES COMPILE_DEFINITIONS "_THREAD_SAFE") @@ -53,29 +91,53 @@ if(APPLE) "-Wl,-compatibility_version,${DYLIB_COMPAT_VERSION} -Wl,-current_version,${DYLIB_CURRENT_VERSION}") target_link_libraries(SDL PRIVATE "-framework AppKit") set_target_properties(SDL PROPERTIES - MACOSX_RPATH 1 OUTPUT_NAME "SDL-1.2.0" ) elseif(UNIX AND NOT ANDROID) set_target_properties(SDL PROPERTIES - VERSION "${SDL12_COMPAT_VERSION_STR}" + VERSION "${PROJECT_VERSION}" SOVERSION "0" OUTPUT_NAME "SDL-1.2") +elseif(WIN32) + # avoid DLL having 'lib' prefix with MinGW + set(CMAKE_SHARED_LIBRARY_PREFIX "") + set_target_properties(SDL PROPERTIES COMPILE_DEFINITIONS "DLL_EXPORT") + set_target_properties(SDL PROPERTIES + VERSION "${PROJECT_VERSION}" + SOVERSION "0" + OUTPUT_NAME "SDL") +elseif(OS2) + set_target_properties(SDL PROPERTIES COMPILE_DEFINITIONS "BUILD_SDL") # for DECLSPEC + set_target_properties(SDL PROPERTIES + VERSION "${PROJECT_VERSION}" + SOVERSION "0" + OUTPUT_NAME "SDL12") else() set_target_properties(SDL PROPERTIES - VERSION "${SDL12_COMPAT_VERSION_STR}" + VERSION "${PROJECT_VERSION}" SOVERSION "0" - OUTPUT_NAME "SDL") + OUTPUT_NAME "SDL") endif() if(MINGW) set_target_properties(SDL PROPERTIES LINK_FLAGS "-nostdlib") + target_link_libraries(SDL PRIVATE "-static-libgcc -lgcc") # libgcc is needed for 32 bit (x86) builds endif() if(MSVC) # Don't try to link with the default set of libraries. - set_target_properties(SDL PROPERTIES COMPILE_FLAGS "/GS-") + set(MSVC_FLAGS "/GS-") + check_c_source_compiles("int main(void) { +#ifndef _M_IX86 +#error not x86 +#endif +return 0; }" IS_X86) + if(IS_X86) # don't emit SSE2 in x86 builds + set(MSVC_FLAGS "${MSVC_FLAGS} /arch:SSE") + endif() + set_target_properties(SDL PROPERTIES COMPILE_FLAGS ${MSVC_FLAGS}) set_target_properties(SDL PROPERTIES LINK_FLAGS "/NODEFAULTLIB") # Make sure /RTC1 is disabled: (from SDL2 CMake) + set_property(TARGET SDL PROPERTY MSVC_RUNTIME_CHECKS "") foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO) @@ -83,98 +145,118 @@ if(MSVC) endforeach(flag_var) endif() - -if(SDL12TESTS) - if(NOT (WIN32 OR APPLE OR CYGWIN OR HAIKU OR BEOS)) - find_library(MATH_LIBRARY m) +# SDLmain library... +if(APPLE) + add_library(SDLmain STATIC src/SDLmain/macosx/SDLMain.m) + set_source_files_properties(src/SDLmain/macosx/SDLMain.m PROPERTIES LANGUAGE C) + if(NOT CMAKE_VERSION VERSION_LESS "3.16") + set_source_files_properties("src/SDLmain/macosx/SDLMain.m" PROPERTIES LANGUAGE OBJC) endif() - - macro(test_program _NAME _SRCS) - add_executable(${_NAME} ${_SRCS}) - target_include_directories(${_NAME} PRIVATE "include/SDL") - target_link_libraries(${_NAME} SDL) - set_target_properties(${_NAME} PROPERTIES COMPILE_DEFINITIONS "HAVE_OPENGL") - if(MATH_LIBRARY) - target_link_libraries(${_NAME} ${MATH_LIBRARY}) - endif() - endmacro() - - test_program(checkkeys "test/checkkeys.c") - test_program(graywin "test/graywin.c") - test_program(loopwave "test/loopwave.c") - test_program(testalpha "test/testalpha.c") - test_program(testbitmap "test/testbitmap.c") - test_program(testblitspeed "test/testblitspeed.c") - test_program(testcdrom "test/testcdrom.c") - test_program(testcursor "test/testcursor.c") - test_program(testdyngl "test/testdyngl.c") - test_program(testerror "test/testerror.c") - test_program(testfile "test/testfile.c") - test_program(testgamma "test/testgamma.c") - test_program(testgl "test/testgl.c") - test_program(testthread "test/testhread.c") - test_program(testiconv "test/testiconv.c") - test_program(testjoystick "test/testjoystick.c") - test_program(testkeys "test/testkeys.c") - test_program(testloadso "test/testloadso.c") - test_program(testlock "test/testlock.c") - test_program(testoverlay "test/testoverlay.c") - test_program(testoverlay2 "test/testoverlay2.c") - test_program(testpalette "test/testpalette.c") - test_program(testplatform "test/testplatform.c") - test_program(testsem "test/testsem.c") - test_program(testsprite "test/testsprite.c") - test_program(testtimer "test/testtimer.c") - test_program(testver "test/testver.c") - test_program(testvidinfo "test/testvidinfo.c") - test_program(testwin "test/testwin.c") - test_program(testwm "test/testwm.c") - test_program(threadwin "test/threadwin.c") - test_program(torturethread "test/torturethread.c") - - if(APPLE) - target_link_libraries(testgl "-framework OpenGL") - set_target_properties(testgl PROPERTIES COMPILE_DEFINITIONS "GL_SILENCE_DEPRECATION=1") +elseif(WIN32) + add_library(SDLmain STATIC src/SDLmain/win32/SDL_win32_main.c) + set_target_properties(SDLmain PROPERTIES COMPILE_DEFINITIONS "_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE") # !!! FIXME: don't use C runtime? We fixed this in SDL2. +else() + add_library(SDLmain STATIC src/SDLmain/dummy/SDL_dummy_main.c) +endif() +add_library(SDL::SDLmain ALIAS SDLmain) +target_include_directories(SDLmain PUBLIC "$") +target_include_directories(SDLmain PUBLIC "$") +if(MINGW OR CYGWIN) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_libraries(SDLmain PUBLIC "$<$,EXECUTABLE>:-Wl,--undefined=_WinMain@16>") else() - target_link_libraries(testgl "GL") + target_link_libraries(SDLmain PUBLIC "$<$,EXECUTABLE>:-Wl,--undefined=WinMain>") endif() +endif() - foreach(fname "icon.bmp" "moose.dat" "picture.xbm" "sail.bmp" "sample.bmp" "sample.wav" "utf8.txt") - file(COPY "${CMAKE_SOURCE_DIR}/test/${fname}" DESTINATION "${CMAKE_BINARY_DIR}") - endforeach(fname) +if(SDL12TESTS) + add_subdirectory(test) endif() -install(TARGETS SDL +install(TARGETS SDL SDLmain LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) if(SDL12DEVEL) - install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - ) - configure_file(sdl12_compat.pc.in sdl12_compat.pc @ONLY) - install(FILES ${CMAKE_BINARY_DIR}/sdl12_compat.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig - ) + install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + if(NOT MSVC) + if(WIN32) + set(SDL_CFLAGS "") + set(SDL_RLD_FLAGS "") + set(SDL_LIBS "-lmingw32 -lSDLmain -lSDL -mwindows") + set(SDL_STATIC_LIBS "") + elseif(APPLE) + set(SDL_CFLAGS "-D_THREAD_SAFE") + set(SDL_LIBS "-lSDLmain -lSDL -Wl,-framework,Cocoa") + set(SDL_STATIC_LIBS "") + set(SDL_RLD_FLAGS "") # !!! FIXME: this forces rpath, which we might want? + else() # unix + set(SDL_CFLAGS "-D_GNU_SOURCE=1 -D_REENTRANT") + set(SDL_RLD_FLAGS "") # !!! FIXME: this forces rpath, which we might want? + set(SDL_LIBS "-lSDL") + set(SDL_STATIC_LIBS "") + foreach(lib ${CMAKE_DL_LIBS}) + set(SDL_STATIC_LIBS "-l${lib}") + endforeach() + if(NOT STATICDEVEL) + set(SDL_STATIC_LIBS "") + endif() + endif() + + # !!! FIXME: do we _want_ static builds? + if(STATICDEVEL) + set(ENABLE_STATIC_TRUE "") + set(ENABLE_STATIC_FALSE "#") + else() + set(ENABLE_STATIC_TRUE "#") + set(ENABLE_STATIC_FALSE "") + endif() + set(ENABLE_SHARED_TRUE "") + set(ENABLE_SHARED_FALSE "#") + + configure_file(sdl12_compat.pc.in sdl12_compat.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sdl12_compat.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sdl-config.in" "${CMAKE_CURRENT_BINARY_DIR}/sdl-config" @ONLY) + install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/sdl-config" DESTINATION bin) + + # uninstall + if(NOT TARGET uninstall) + configure_file(cmake/cmake_uninstall.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + endif() + endif() + + set(SOEXT ${CMAKE_SHARED_LIBRARY_SUFFIX}) + get_target_property(SONAME SDL OUTPUT_NAME) + if(UNIX AND NOT ANDROID) + install(CODE " + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + \"lib${SONAME}${SOPOSTFIX}${SOEXT}\" \"libSDL${SOPOSTFIX}${SOEXT}\" + WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libSDL${SOPOSTFIX}${SOEXT} DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() - # !!! FIXME: lots of these sdl-config vars probably need more customization. - - # !!! FIXME: do we _want_ static builds? - set(ENABLE_STATIC_TRUE "") - set(ENABLE_STATIC_FALSE "#") - set(ENABLE_SHARED_TRUE "") - set(ENABLE_SHARED_FALSE "#") - - set(SDL_VERSION "${SDL12_COMPAT_VERSION_STR}") - set(SDL_CFLAGS "-D_GNU_SOURCE=1 -D_REENTRANT") - set(SDL_RLD_FLAGS "") # !!! FIXME: this forces rpath, which we might want? - set(SDL_LIBS "-lSDL") - set(SDL_STATIC_LIBS "-lm -ldl -lpthread") - set(prefix ${CMAKE_INSTALL_PREFIX}) - set(exec_prefix "\${prefix}") - set(libdir "\${exec_prefix}/lib${LIB_SUFFIX}") - set(bindir "\${exec_prefix}/bin") - set(includedir "\${prefix}/include") - configure_file("${CMAKE_SOURCE_DIR}/sdl-config.in" "${CMAKE_BINARY_DIR}/sdl-config" @ONLY) - install(PROGRAMS "${CMAKE_BINARY_DIR}/sdl-config" DESTINATION bin) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/sdl.m4" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/aclocal") endif() +if(STATICDEVEL AND SDL12DEVEL) + add_library(SDL-static STATIC ${SDL12COMPAT_SRCS}) + add_library(SDL::SDL-static ALIAS SDL-static) + target_include_directories(SDL-static PRIVATE ${SDL2_INCLUDE_DIRS}) + set_target_properties(SDL-static PROPERTIES COMPILE_DEFINITIONS "_REENTRANT") + target_link_libraries(SDL-static PRIVATE ${CMAKE_DL_LIBS}) + set_target_properties(SDL-static PROPERTIES + VERSION "${PROJECT_VERSION}" + OUTPUT_NAME "SDL") + + install(TARGETS SDL-static + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) +endif() diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md new file mode 100644 index 000000000..f518b5ce3 --- /dev/null +++ b/COMPATIBILITY.md @@ -0,0 +1,129 @@ +# Compatibility notes + +This is a list of quirks and known-issues for specific games, with possible +workarounds. + +We are currently building and maintaining a list of all known SDL 1.2 games +and their current state with sdl12-compat over here: + +https://docs.google.com/spreadsheets/d/1u8Rq3LVQYYgu28sBuxrZ371QolbiZu5z_LjENc4ddZs/edit?usp=sharing + + + +## Dynamite Jack (Linux) + +More modern builds are ported to SDL2, but the older 1.2 binaries will work +on X11 if you turn off OpenGL Scaling. + + export SDL12COMPAT_OPENGL_SCALING=0 + +Note that this game calls glXGetProcAddress() directly and likely will not +work as a native Wayland app (XWayland works fine, however). + +You have to set this environment variable yourself. Dynamic Jack's binary +is called "main" so we can't reasonably set up an automatic entry for it in +our quirks table. + + +## Hammerfight (Linux) + +You probably aren't getting usable mouse input, as the game talks directly to +XInput2 on X11 (and tries to use /dev/input if that fails). This is not an +sdl12-compat bug, as this is going outside of SDL to get multiple mice +input. The game is statically linked to a copy of +[ManyMouse](https://icculus.org/manymouse/), which provides multiple mice +access. + + +The simplest way to deal with this is change this line in the game's +Config.ini file: + + CORE_INIT_RI=true + +Make that `false` and it won't even try to initialize ManyMouse, and will use +standard SDL 1.2 mouse events for single-player input. (The "RI" stands for +"RawInput," which Hammerfight uses on Windows for multi-mice support). + +Of course, with this change, you can't have multiple players on the same +machine using separate mice. + +Hammerfight dlopen()'s the XInput2 libraries instead of linking to them +directly, so this does not prevent the game from working on Wayland. No +XWayland needed! + + +## Tucnak (Linux) + +There is an SDL2 target for libzia (which tucnak uses for rendering), and you +should use that in modern times. But if you're on the SDL 1.2 target, +tucnak tries to render from a background thread, and does its event loop on +another, so we can't cleanly hook in to make it draw from the main thread. + +sdl12-compat will force SDL2 to use X11, software rendering, and no texture +framebuffer, to avoid using OpenGL, to avoid the threading problems this +will cause. The app is perfectly usable in this configuration (and largely +matches how they expected you to use it with SDL 1.2 anyhow). + + +## Awesomenauts (Linux) + +Awesomenauts requires X11 because it talks directly to glX, can't use our +scaling code because it uses framebuffer objects without going through +SDL_GL_GetProcAddress() to get entry points, and does something weird with +OpenGL contexts on multiple threads. sdl12-compat detects this and forces +on the correct hints to make this work, but it limits how one can use the +game. + +This game will work in a Wayland environment, but only as an X11 app through +XWayland. + + +## Braid (Linux) + +Braid requires X11 because it talks directly to glX, and can't use our +scaling code because it uses framebuffer objects without going through +SDL_GL_GetProcAddress() to get entry points. sdl12-compat detects this +and forces on the correct hints to make this work, but it limits how +one can use the game. + +This game will work in a Wayland environment, but only as an X11 app through +XWayland. + + +## DOSBox (Linux) + +DOSBox has some pretty strict requirements for keyboard input. We detect +some common names for DOSBox binaries ("dosbox", "dosbox_i686", etc), and +force the correct hints to make it work, but if you have an uncommon binary +name and keyboard input isn't working as you expect, try exporting this +environment variable: + + export SDL12COMPAT_USE_KEYBOARD_LAYOUT=0 + + +## Multiwinia (Linux) + +Multiwinia calls SDL_Quit() when changing video modes but doesn't +reinitialize SDL before using it further. We detect this binary and force +the correct hints to make it work, but if you have an uncommon binary +name and the game isn't working, try this environment variable: + + export SDL12COMPAT_NO_QUIT_VIDEO=1 + +## Civilization: Call to Power (Linux) + +The Linux port of Civilization: Call to Power is very old, and has a number +of issues running on modern systems. The relevant ones for sdl12-compat are +that the game uses SDL 1.1 (not SDL 1.2), and that it uses CD audio. + +To fix the former, you'll need to either rename the sdl12-compat files, or +patch the game binary to look for libSDL-1.2.so.0. + +For the latter, you'll need to rip the game's CD audio, and set the +SDL12COMPAT_FAKE_CDROM_PATH environment variable. Make sure you set this to an +absolute path, as the game changes directories before initializing SDL. + +A more complete guide for getting the game to work on modern systems with +sdl12-compat is available [here](https://davidgow.net/hacks/civctp.html). + +(Some of these tricks may also work for other old Loki Entertainment ports, too.) diff --git a/HOW_TO_TEST_GAMES.md b/HOW_TO_TEST_GAMES.md new file mode 100644 index 000000000..8b37df5c6 --- /dev/null +++ b/HOW_TO_TEST_GAMES.md @@ -0,0 +1,181 @@ +# Testing games with sdl12-compat + +sdl12-compat is here to make sure older games not only work well, but work +_correctly_, and for that, we need an army of testers. + +Here's how to test games with sdl12-compat. + + +## The general idea + +- Build [SDL2](https://github.com/libsdl-org/SDL) or find a prebuilt binary. +- Build sdl12-compat (documentation is in README.md) +- Find a game to test. +- Make sure a game uses sdl12-compat instead of classic SDL 1.2 +- See if game works or blows up, report back. + + +## Find a game to test + +We are keeping a spreadsheet of known games that still use SDL 1.2 +[here](https://docs.google.com/spreadsheets/d/1u8Rq3LVQYYgu28sBuxrZ371QolbiZu5z_LjENc4ddZs/edit?usp=sharing). + +Find something that hasn't been tested, or hasn't been tested recently, and +give it a try! Then update the spreadsheet. + +Extra credit if you [file bug reports](https://github.com/libsdl-org/sdl12-compat/issues) +but we're grateful if you just make notes on the spreadsheet, too. + + +## Make sure the game works with real SDL 1.2 first! + +You'd be surprised how many games have bitrotted! If it doesn't work with +real 1.2 anymore, it's not a bug if sdl12-compat doesn't work either. That +being said, lots of games that stopped working _because_ of SDL 1.2 will +now work again with sdl12-compat, which is nice, but just make sure you have +a baseline before you start testing. + + +## Force it to use sdl12-compat instead. + +Either overwrite the copy of SDL-1.2 that the game uses with sdl12-compat, +or (on Linux) export LD_LIBRARY_PATH to point to your copy, so the system will +favor it when loading libraries. + +## Watch out for setuid/setgid binaries! + +On Linux, if you're testing a binary that's setgid to a "games" group (which +we ran into several times with Debian packages), or setuid root or whatever, +then the system will ignore the LD_LIBRARY_PATH variable, as a security +measure. + +The reason some games are packaged like this is usually because they want to +write to a high score list in a global, shared directory. Often times the +games will just carry on if they fail to do so. + +There are several ways to bypass this: + +- On some distros, you can run `ld.so` directly: + ```bash + LD_LIBRARY_PATH=/where/i/can/find/sdl12-compat ld.so /usr/games/mygame + ``` +- You can remove the setgid bit: + ```bash + # (it's `u-s` for the setuid bit) + sudo chmod g-s /usr/games/mygame + ``` +- You can install sdl12-compat system-wide, so the game uses that + instead of SDL 1.2 by default. +- If you don't have root access at all, you can try to copy the game + somewhere else or install a personal copy, or build from source code, + but these are drastic measures. + +Definitely read the next section ("Am I actually running sdl12-compat?") in +these scenarios to make sure you ended up with the right library! + +## Am I actually running sdl12-compat? + +The easiest way to know is to set some environment variables: + +```bash +export SDL12COMPAT_DEBUG_LOGGING=1 +``` + +If this is set, when loading sdl12-compat, it'll write something like this +to stderr (on Linux and Mac, at least)... + +``` +INFO: sdl12-compat, built on Sep 2 2022 at 11:27:37, talking to SDL2 2.25.0 +``` + +You can also use this: + +```bash +export SDL_EVENT_LOGGING=1 +``` + +Which will report every event SDL 2 sends to stderr: + +``` +INFO: SDL EVENT: SDL_WINDOWEVENT (timestamp=317 windowid=1 event=SDL_WINDOWEVENT_MOVED data1=1280 data2=674) +INFO: SDL EVENT: SDL_WINDOWEVENT (timestamp=318 windowid=1 event=SDL_WINDOWEVENT_EXPOSED data1=0 data2=0) +INFO: SDL EVENT: SDL_WINDOWEVENT (timestamp=318 windowid=1 event=SDL_WINDOWEVENT_ENTER data1=0 data2=0) +``` + +Since this is a new feature in SDL2, it'll only show up because sdl12-compat talks +to SDL2 and classic SDL 1.2 doesn't. + + +## Steam + +If testing a Steam game, you'll want to launch the game outside of the Steam +Client, so that Steam doesn't overwrite files you replaced and so you can +easily control environment variables. + +Since you'll be using the Steam Runtime, you don't have to find your own copy +of SDL2, as Steam provides it. + +On Linux, Steam stores games in ~/.local/share/Steam/steamapps/common, each +in its own usually-well-named subdirectory. + +You'll want to add a file named "steam_appid.txt" to the same directory as +the binary, which will encourage Steamworks to _not_ terminate the process +and have the Steam Client relaunch it. This file should just have the appid +for the game in question, which you can find from the store page. + +For example, the store page for Braid is: + +https://store.steampowered.com/app/26800/Braid/ + +See that `26800`? That's the appid. + +```bash +echo 26800 > steam_appid.txt +``` + +For Linux, you can make sure that, from the command line, the game still +runs with the Steam Runtime and has the Steam Overlay by launching it with a +little script: + +- [steamapp32](https://raw.githubusercontent.com/icculus/twisty-little-utilities/main/steamapp32) for x86 binaries. +- [steamapp64](https://raw.githubusercontent.com/icculus/twisty-little-utilities/main/steamapp64) for x86-64 binaries. + +(And make sure you have a 32-bit or 64-bit build of sdl12-compat!) + +And then make sure you force it to use _your_ sdl12-compat instead of the +system/Steam Runtime build: + +```bash +export LD_LIBRARY_PATH=/where/i/installed/sdl12-compat +``` + +Putting this all together, you might run [BIT.TRIP Runner2](https://store.steampowered.com/app/218060/) +like this: + +```bash +cd ~/.local/share/Steam/steamapps/common/bittriprunner2 +export LD_LIBRARY_PATH=/where/i/installed/sdl12-compat +export SDL12COMPAT_DEBUG_LOGGING=1 +echo 218060 > steam_appid.txt +steamapp32 ./runner2 +``` + + +## Windows + +Generally, Windows games just ship with an SDL.dll, and you just need to +overwrite it with an sdl12-compat build, then run as usual. + + +## macOS, etc. + +(write me.) + + +## Questions? + +If something isn't clear, make a note [here](https://github.com/libsdl-org/sdl12-compat/issues/new) +and we'll update this document. + +Thanks! + diff --git a/LICENSE.txt b/LICENSE.txt index 969b8c13d..6a085d050 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,5 @@ -Copyright (C) 1997-2021 Sam Lantinga - +Copyright (C) 1997-2026 Sam Lantinga + 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. @@ -7,7 +7,7 @@ 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 @@ -16,3 +16,7 @@ freely, subject to the following restrictions: misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. + + +This project includes code from dr_mp3 ( https://github.com/mackron/dr_libs ) +which can be treated as public domain or MIT-0 licensed, at your option. diff --git a/README.md b/README.md index 56cbc9cdb..9f463787f 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ This is the Simple DirectMedia Layer, a general API that provides low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D framebuffer across multiple platforms. -This code is a compatibility layer; it provides a binary-compatible API for -programs written against SDL 1.2, but it uses SDL 2.0 behind the scenes. If -you are writing new code, please target SDL 2.0 directly and do not use this -layer. +This code is a compatibility layer; it provides a binary and source +compatible API for programs written against SDL 1.2, but it uses SDL 2.0 +behind the scenes. If you are writing new code, please target SDL 3.0 +directly and do not use this layer. If you absolutely must have the real SDL 1.2 ("SDL 1.2 Classic"), please use the source tree at https://github.com/libsdl-org/SDL-1.2, which occasionally @@ -18,13 +18,14 @@ that. # How to use: -- Build the library. This will need access to SDL2's headers, CMake -( https://cmake.org/ ) and the build tools of your choice. Once built, you +- Build the library. This will need access to SDL2's headers (v2.0.7 or newer), +[CMake](https://cmake.org/) and the build tools of your choice. Once built, you will have a drop-in replacement that can be used with any existing binary that relies on SDL 1.2. You can copy this library over the existing 1.2 build, or force it to take priority over a system copy with LD_LIBRARY_PATH, etc. -At runtime, sdl12-compat needs to be able to find a copy of SDL2, so plan to -include it with the library if necessary. +At runtime, sdl12-compat needs to be able to find a copy of SDL2 (v2.0.7 or +newer -- v2.0.12 or newer for Windows), so plan to include it with the library +if necessary. - If you want to build an SDL 1.2 program from source code, we have included compatibility headers, so that sdl12-compat can completely replace SDL 1.2 @@ -34,3 +35,289 @@ new headers are also under the zlib license. Note that sdl12-compat itself does not use these headers, so if you just want the library, you don't need them. +# Building the library: + +These are quick-start instructions; there isn't anything out of the ordinary +here if you're used to using CMake. + +You'll need to use CMake to build sdl12-compat. Download at +[cmake.org](https://cmake.org/) or install from your package manager +(`sudo apt-get install cmake` on Ubuntu, etc). + +Please refer to the [CMake documentation](https://cmake.org/documentation/) +for complete details, as platform and build tool details vary. + +You'll need a copy of SDL2 to build sdl12-compat, because we need the +SDL2 headers. You can build this from source or install from a package +manager. Windows and Mac users can download prebuilt binaries from +[SDL's download page](https://libsdl.org/download-2.0.php); make sure you +get the "development libraries" and not "runtime binaries" there. + +Linux users might need some packages from their Linux distribution. On Ubuntu, +you might need to do: + +```bash +sudo apt-get install build-essential cmake libsdl2-2.0-0 libsdl2-dev libgl-dev +``` + +Now just point CMake at sdl12-compat's directory. Here's a command-line +example: + +```bash +cd sdl12-compat +cmake -Bbuild -DCMAKE_BUILD_TYPE=Release . +cmake --build build +``` + +On Windows or macOS, you might prefer to use CMake's GUI, but it's the same +idea: give it the directory where sdl12-compat is located, click "Configure," +choose your favorite compiler, then click "Generate." Now you have project +files! Click "Open Project" to launch your development environment. Then you +can build however you like with Visual Studio, Xcode, etc. + +If necessary, you might have to fill in the location of the SDL2 headers +when using CMake. sdl12-compat does not need SDL2's library to _build_, +just its headers (although it may complain about the missing library, +you can ignore that). From the command line, add +`-DSDL2_INCLUDE_DIR=/path/to/SDL2/include`, or find this in the CMake +GUI and set it appropriately, click "Configure" again, and then "Generate." + +When the build is complete, you'll have a shared library you can drop in +as a replacement for an existing SDL 1.2 build. This will also build +the original SDL 1.2 test apps, so you can verify the library is working. + + +# Building for older CPU architectures on Linux: + +There are a lot of binaries from many years ago that used SDL 1.2, which is +to say they are for CPU architectures that are likely not your current +system's. + +If you want to build a 32-bit x86 library on an x86-64 Linux machine, for +compatibility with older games, you should install some basic 32-bit +development libraries for your distribution. On Ubuntu, this would be: + + +```bash +sudo apt-get install gcc-multilib libsdl2-dev:i386 +``` + +...and then add `-m32` to your build options: + + +```bash +cd sdl12-compat +cmake -Bbuild32 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=-m32 +cmake --build build32 +``` + + +# Building for older CPU architectures on macOS: + +macOS users can try adding `-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64'` instead +of `-DCMAKE_C_FLAGS=-m32` to make a Universal Binary for both 64-bit Intel and +Apple Silicon machines. If you have an older (or much older!) version of Xcode, +you can try to build with "i386" or maybe even "powerpc" for 32-bit Intel or +PowerPC systems, but Xcode (and macOS itself) has not supported either of +these for quite some time, and you will likely struggle to get SDL2 to compile +here in small ways, as well...but with some effort, it's maybe _possible_ to +run SDL2 and sdl12-compat on Apple's abandoned architectures. + + +# Building for older CPU architectures on Windows: + +Windows users just select a 32-bit version of Visual Studio when running +CMake, when it asks you what compiler to target in the CMake GUI. + + +# Configuration options: + +sdl12-compat has a number of configuration options which can be used to work +around issues with individual applications, or to better fit your system or +preferences. + +These options are all specified as environment variables, and can be set by +running your application with them set on the command-line, for example: +``` +SDL12COMPAT_HIGHDPI=1 SDL12COMPAT_OPENGL_SCALING=0 %command% +``` +will run `%command%` with high-dpi monitor support enabled, but OpenGL +scaling support disabled. + +(While these environment variables are checked at various times throughout +the lifetime of the app, sdl12-compat expects these to be set before the +process starts and not change during the life of the process, and any +places where changing it later might affect operation is purely accidental +and might change. That is to say: don't write an SDL 1.2-based app with +plans to tweak these values on the fly!) + +The available options are: + +- SDL12COMPAT_DEBUG_LOGGING: (checked at startup) + If enabled, print debugging messages to stderr. These messages are + mostly useful to developers, or when trying to track down a specific + bug. + +- SDL12COMPAT_FAKE_CDROM_PATH: (checked during SDL_Init) + A path to a directory containing MP3 files (named trackXX.mp3, where + XX is a two-digit track number) to be used by applications which play + CD audio. Using an absolute path is recommended: relative paths are + not guaranteed to work correctly. + +- SDL12COMPAT_OPENGL_SCALING: (checked during SDL_Init) + Enables scaling of OpenGL applications to the current desktop resolution. + If disabled, applications can change the real screen resolution. This + option is enabled by default, but not all applications are compatible + with it: try changing this if you can only see a black screen. + +- SDL12COMPAT_FIX_BORDERLESS_FS_WIN: (checked during SDL_SetVideoMode) + Enables turning borderless windows at the desktop resolution into actual + fullscreen windows (so they'll go into a separate space on macOS and + properly hide dock windows on other desktop environments, etc). + If disabled, applications may not get the full display to theirselves as + they expect. This option is enabled by default, but this option is here + so it can be manually disabled, in case this causes some negative result + we haven't anticipated. + +- SDL12COMPAT_SCALE_METHOD: (checked during SDL_Init) + Choose the scaling method used when applications render at a non-native + resolution. The options are `nearest`, for nearest-neighbour sampling + (more pixelated) and `linear` for bilinear sampling (blurrier). + +- SDL12COMPAT_HIGHDPI: (checked during SDL_SetVideoMode) + Advertise the application as supporting high-DPI displays. Enabling + this will usually result in sharper graphics, but on some applications + text and other elements may become very small. + +- SDL12COMPAT_SYNC_TO_VBLANK: (checked during SDL_SetVideoMode) + Force the application to sync (or not sync) to the vertical blanking + interval (VSync). When enabled, this will cap the application's + framerate to the screen's refresh rate (and may resolve issues with + screen tearing). + +- SDL12COMPAT_USE_KEYBOARD_LAYOUT: (checked during SDL_Init) + Make all keyboard input take the current keyboard layout into account. + This may need to be disabled for applications which provide their own + keyboard layout support, or if the position of the keys on the keyboard + is more important than the character they produce. Note that text input + (in most applications) will take the keyboard layout into account + regardless of this option. + +- SDL12COMPAT_USE_GAME_CONTROLLERS: (checked during SDL_Init) + Use SDL2's higher-level Game Controller API to expose joysticks instead of + its lower-level joystick API. The benefit of this is that you can exert + more control over arbitrary hardware (deadzones, button mapping, device + name, etc), and button and axes layouts are consistent (what is physically + located where an Xbox360's "A" button is will always be SDL 1.2 joystick + button 0, "B" will be 1, etc). The downside is it might not expose all of + a given piece of hardware's functionality, or simply not make sense in + general...if you need to use a flight stick, for example, you should not + use this hint. If there is no known game controller mapping for a joystick, + and this hint is in use, it will not be listed as an availble device. + +- SDL12COMPAT_WINDOW_SCALING: (checked during SDL_SetVideoMode) + When creating non-fullscreen, non-resizable windows, use this variable to + size the window differently. If, for example, you have a 4K monitor and the + game is running in a window the size of a postage stamp, you might set this + to 2 to double the size of the window. Fractional values work, so "1.5" + might be a more-pleasing value on your hardware. You can even shrink the + window with values less than 1.0! When scaling a window like this, + sdl12-compat will use all the usual scaling options + (SDL12COMPAT_OPENGL_SCALING, SDL12COMPAT_SCALE_METHOD, etc). If sdl12-compat + can't scale the contents of the window for various technical reasons, it + will create the window at the originally-requested size. If this variable + isn't specified, it defaults to 1.0 (no scaling). + +- SDL12COMPAT_MAX_VIDMODE: (checked during SDL_Init) + This is a string in the form of `WxH`, where `W` is the maximum width + and `H` is the maximum height (for example: `640x480`). The list of valid + resolutions that will be reported by SDL_ListModes and SDL_VideoModeOK will + not include any dimensions that are wider or taller than these sizes. A size + of zero will be ignored, so for `0x480` a resolution of 1920x480 would be + accepted). If not specified, or set to `0x0`, no resolution clamping is done. + This is for old software-rendered games that might always choose the largest + resolution offered, but never conceived of 4K displays. In these cases, it + might be better for them to use a smaller resolution and let sdl12-compat + scale their output up with the GPU. + +- SDL_MOUSE_RELATIVE_SCALING: (checked during SDL_SetVideoMode) + If enabled, relative mouse motion is scaled when the application is + running at a non-native resolution. This may be required with some + applications which use their own mouse cursors. See also: + https://wiki.libsdl.org/SDL_HINT_MOUSE_RELATIVE_SCALING + +- SDL12COMPAT_ALLOW_THREADED_DRAWS: (checked during SDL_Init) + Enabled by default. + If disabled, calls to `SDL_UpdateRects()` from non-main threads are + converted into requests for the main thread to carry out the update later. + The thread that called `SDL_SetVideoMode()` is treated as the main thread. + +- SDL12COMPAT_ALLOW_THREADED_PUMPS: (checked during SDL_Init) + Enabled by default. + If disabled, calls to `SDL_PumpEvents()` from non-main threads are + completely ignored. + The thread that called `SDL_SetVideoMode()` is treated as the main thread. + +- SDL12COMPAT_ALLOW_SYSWM: (checked during SDL_Init) + Enabled by default. + If disabled, SDL_SYSWMEVENT events will not be delivered to the app, and + SDL_GetWMInfo() will fail; this is useful if you have a program that + tries to access X11 directly through SDL's interfaces, but can survive + without it, becoming compatible with, for example, Wayland, or perhaps + just avoiding a bug in target-specific code. Note that sdl12-compat already + disallows SysWM things unless SDL2 is using its "windows" or "x11" video + backends, because SDL 1.2 didn't have wide support for its SysWM APIs + outside of Windows and X11 anyhow. + +- SDL12COMPAT_NO_QUIT_VIDEO: (checked during SDL_QuitSubsystem) + If enabled, SDL_Quit() and SDL_QuitSubsystem() will never shut down the + video subsystem. This works around buggy applications which try to use + the video subsystem after shutting it down. + +- SDL12COMPAT_WINDOWED_MODE_LIST: (checked during SDL_ListModes) + If enabled, returns the list of available video modes in SDL_ListModes(), + even if the flags provided do not include SDL_FULLSCREEN. Otherwise + (by default), it will return -1 to tell the application that all modes + are available. This may cause some applications to fall back to an internal + list, which may not be as exhaustive as the one sdl12-compat provides. + Try this if a game is not listing all of the screen resolutions it should + support. + + +# Compatibility issues with OpenGL scaling + +The OpenGL scaling feature of sdl12-compat allows applications which wish to +run at a non-native screen resolution to do so without changing the system +resolution. It does this by redirecting OpenGL rendering calls to a "fake" +backbuffer which is scaled when rendering. + +This works well for simple applications, but for more complicated applications +which use Frame Buffer Objects, sdl12-compat needs to intercept and redirect +some OpenGL calls. Applications which access these functions without going +though SDL (even if via a library) may not successfully render anything, or +may render incorrectly if OpenGL scaling is enabled. + +In these cases, you can disable OpenGL scaling by setting the environment +variable: +``` +SDL12COMPAT_OPENGL_SCALING=0 +``` + +# Compatibility issues with applications directly accessing underlying APIs + +Some applications combine the use of SDL with direct access to the underlying +OS or window system. When running these applications on the same OS and SDL +video driver (e.g. a program written for X11 on Linux is run on X11 on Linux), +sdl12-compat is usually compatible. + +However, if you wish to run an application on a different video driver, the +application will be unable to access the underlying API it is expecting, and +may fail. This often occurs trying to run applications written for X11 under +Wayland, and particularly affects a number of popular OpenGL extension loaders. + +In this case, the best workaround is to run under a compatibility layer like +XWayland, and set the SDL_VIDEODRIVER environment variable to the driver the +program is expecting: +``` +SDL_VIDEODRIVER=x11 +``` diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py new file mode 100755 index 000000000..d7e7028ec --- /dev/null +++ b/build-scripts/build-release.py @@ -0,0 +1,1566 @@ +#!/usr/bin/env python3 + +""" +This script is shared between SDL2, SDL3, and all satellite libraries. +Don't specialize this script for doing project-specific modifications. +Rather, modify release-info.json. +""" + +import argparse +import collections +import dataclasses +from collections.abc import Callable +import contextlib +import datetime +import fnmatch +import glob +import io +import json +import logging +import multiprocessing +import os +from pathlib import Path +import platform +import re +import shlex +import shutil +import subprocess +import sys +import tarfile +import tempfile +import textwrap +import typing +import zipfile + + +logger = logging.getLogger(__name__) +GIT_HASH_FILENAME = ".git-hash" +REVISION_TXT = "REVISION.txt" + +RE_ILLEGAL_MINGW_LIBRARIES = re.compile(r"(?:lib)?(?:gcc|(?:std)?c[+][+]|(?:win)?pthread).*", flags=re.I) + + +def safe_isotime_to_datetime(str_isotime: str) -> datetime.datetime: + try: + return datetime.datetime.fromisoformat(str_isotime) + except ValueError: + pass + logger.warning("Invalid iso time: %s", str_isotime) + if str_isotime[-6:-5] in ("+", "-"): + # Commits can have isotime with invalid timezone offset (e.g. "2021-07-04T20:01:40+32:00") + modified_str_isotime = str_isotime[:-6] + "+00:00" + try: + return datetime.datetime.fromisoformat(modified_str_isotime) + except ValueError: + pass + raise ValueError(f"Invalid isotime: {str_isotime}") + + +def arc_join(*parts: list[str]) -> str: + assert all(p[:1] != "/" and p[-1:] != "/" for p in parts), f"None of {parts} may start or end with '/'" + return "/".join(p for p in parts if p) + + +@dataclasses.dataclass(frozen=True) +class VsArchPlatformConfig: + arch: str + configuration: str + platform: str + + def extra_context(self): + return { + "ARCH": self.arch, + "CONFIGURATION": self.configuration, + "PLATFORM": self.platform, + } + + +@contextlib.contextmanager +def chdir(path): + original_cwd = os.getcwd() + try: + os.chdir(path) + yield + finally: + os.chdir(original_cwd) + + +class Executer: + def __init__(self, root: Path, dry: bool=False): + self.root = root + self.dry = dry + + def run(self, cmd, cwd=None, env=None): + logger.info("Executing args=%r", cmd) + sys.stdout.flush() + if not self.dry: + subprocess.check_call(cmd, cwd=cwd or self.root, env=env, text=True) + + def check_output(self, cmd, cwd=None, dry_out=None, env=None, text=True): + logger.info("Executing args=%r", cmd) + sys.stdout.flush() + if self.dry: + return dry_out + return subprocess.check_output(cmd, cwd=cwd or self.root, env=env, text=text) + + +class SectionPrinter: + @contextlib.contextmanager + def group(self, title: str): + print(f"{title}:") + yield + + +class GitHubSectionPrinter(SectionPrinter): + def __init__(self): + super().__init__() + self.in_group = False + + @contextlib.contextmanager + def group(self, title: str): + print(f"::group::{title}") + assert not self.in_group, "Can enter a group only once" + self.in_group = True + yield + self.in_group = False + print("::endgroup::") + + +class VisualStudio: + def __init__(self, executer: Executer, year: typing.Optional[str]=None): + self.executer = executer + self.vsdevcmd = self.find_vsdevcmd(year) + self.msbuild = self.find_msbuild() + + @property + def dry(self) -> bool: + return self.executer.dry + + VS_YEAR_TO_VERSION = { + "2022": 17, + "2019": 16, + "2017": 15, + "2015": 14, + "2013": 12, + } + + def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path]: + vswhere_spec = ["-latest"] + if year is not None: + try: + version = self.VS_YEAR_TO_VERSION[year] + except KeyError: + logger.error("Invalid Visual Studio year") + return None + vswhere_spec.extend(["-version", f"[{version},{version+1})"]) + vswhere_cmd = ["vswhere"] + vswhere_spec + ["-property", "installationPath"] + vs_install_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp").strip()) + logger.info("VS install_path = %s", vs_install_path) + assert vs_install_path.is_dir(), "VS installation path does not exist" + vsdevcmd_path = vs_install_path / "Common7/Tools/vsdevcmd.bat" + logger.info("vsdevcmd path = %s", vsdevcmd_path) + if self.dry: + vsdevcmd_path.parent.mkdir(parents=True, exist_ok=True) + vsdevcmd_path.touch(exist_ok=True) + assert vsdevcmd_path.is_file(), "vsdevcmd.bat batch file does not exist" + return vsdevcmd_path + + def find_msbuild(self) -> typing.Optional[Path]: + vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", r"MSBuild\**\Bin\MSBuild.exe"] + msbuild_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp/MSBuild.exe").strip()) + logger.info("MSBuild path = %s", msbuild_path) + if self.dry: + msbuild_path.parent.mkdir(parents=True, exist_ok=True) + msbuild_path.touch(exist_ok=True) + assert msbuild_path.is_file(), "MSBuild.exe does not exist" + return msbuild_path + + def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path]): + assert projects, "Need at least one project to build" + + vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}" + msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={arch_platform.platform} /p:Configuration={arch_platform.configuration}" for project in projects]) + bat_contents = f"{vsdev_cmd_str} && {msbuild_cmd_str}\n" + bat_path = Path(tempfile.gettempdir()) / "cmd.bat" + with bat_path.open("w") as f: + f.write(bat_contents) + + logger.info("Running cmd.exe script (%s): %s", bat_path, bat_contents) + cmd = ["cmd.exe", "/D", "/E:ON", "/V:OFF", "/S", "/C", f"CALL {str(bat_path)}"] + self.executer.run(cmd) + + +class Archiver: + def __init__(self, zip_path: typing.Optional[Path]=None, tgz_path: typing.Optional[Path]=None, txz_path: typing.Optional[Path]=None): + self._zip_files = [] + self._tar_files = [] + self._added_files = set() + if zip_path: + self._zip_files.append(zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED)) + if tgz_path: + self._tar_files.append(tarfile.open(tgz_path, "w:gz")) + if txz_path: + self._tar_files.append(tarfile.open(txz_path, "w:xz")) + + @property + def added_files(self) -> set[str]: + return self._added_files + + def add_file_data(self, arcpath: str, data: bytes, mode: int, time: datetime.datetime): + for zf in self._zip_files: + file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) + zip_info = zipfile.ZipInfo(filename=arcpath, date_time=file_data_time) + zip_info.external_attr = mode << 16 + zip_info.compress_type = zipfile.ZIP_DEFLATED + zf.writestr(zip_info, data=data) + for tf in self._tar_files: + tar_info = tarfile.TarInfo(arcpath) + tar_info.type = tarfile.REGTYPE + tar_info.mode = mode + tar_info.size = len(data) + tar_info.mtime = int(time.timestamp()) + tf.addfile(tar_info, fileobj=io.BytesIO(data)) + + self._added_files.add(arcpath) + + def add_symlink(self, arcpath: str, target: str, time: datetime.datetime, files_for_zip): + logger.debug("Adding symlink (target=%r) -> %s", target, arcpath) + for zf in self._zip_files: + file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) + for f in files_for_zip: + zip_info = zipfile.ZipInfo(filename=f["arcpath"], date_time=file_data_time) + zip_info.external_attr = f["mode"] << 16 + zip_info.compress_type = zipfile.ZIP_DEFLATED + zf.writestr(zip_info, data=f["data"]) + for tf in self._tar_files: + tar_info = tarfile.TarInfo(arcpath) + tar_info.type = tarfile.SYMTYPE + tar_info.mode = 0o777 + tar_info.mtime = int(time.timestamp()) + tar_info.linkname = target + tf.addfile(tar_info) + + self._added_files.update(f["arcpath"] for f in files_for_zip) + + def add_git_hash(self, arcdir: str, commit: str, time: datetime.datetime): + arcpath = arc_join(arcdir, GIT_HASH_FILENAME) + data = f"{commit}\n".encode() + self.add_file_data(arcpath=arcpath, data=data, mode=0o100644, time=time) + + def add_file_path(self, arcpath: str, path: Path): + assert path.is_file(), f"{path} should be a file" + logger.debug("Adding %s -> %s", path, arcpath) + for zf in self._zip_files: + zf.write(path, arcname=arcpath) + for tf in self._tar_files: + tf.add(path, arcname=arcpath) + + def add_file_directory(self, arcdirpath: str, dirpath: Path): + assert dirpath.is_dir() + if arcdirpath and arcdirpath[-1:] != "/": + arcdirpath += "/" + for f in dirpath.iterdir(): + if f.is_file(): + arcpath = f"{arcdirpath}{f.name}" + logger.debug("Adding %s to %s", f, arcpath) + self.add_file_path(arcpath=arcpath, path=f) + + def close(self): + # Archiver is intentionally made invalid after this function + del self._zip_files + self._zip_files = None + del self._tar_files + self._tar_files = None + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + +class NodeInArchive: + def __init__(self, arcpath: str, path: typing.Optional[Path]=None, data: typing.Optional[bytes]=None, mode: typing.Optional[int]=None, symtarget: typing.Optional[str]=None, time: typing.Optional[datetime.datetime]=None, directory: bool=False): + self.arcpath = arcpath + self.path = path + self.data = data + self.mode = mode + self.symtarget = symtarget + self.time = time + self.directory = directory + + @classmethod + def from_fs(cls, arcpath: str, path: Path, mode: int=0o100644, time: typing.Optional[datetime.datetime]=None) -> "NodeInArchive": + if time is None: + time = datetime.datetime.fromtimestamp(os.stat(path).st_mtime) + return cls(arcpath=arcpath, path=path, mode=mode) + + @classmethod + def from_data(cls, arcpath: str, data: bytes, time: datetime.datetime) -> "NodeInArchive": + return cls(arcpath=arcpath, data=data, time=time, mode=0o100644) + + @classmethod + def from_text(cls, arcpath: str, text: str, time: datetime.datetime) -> "NodeInArchive": + return cls.from_data(arcpath=arcpath, data=text.encode(), time=time) + + @classmethod + def from_symlink(cls, arcpath: str, symtarget: str) -> "NodeInArchive": + return cls(arcpath=arcpath, symtarget=symtarget) + + @classmethod + def from_directory(cls, arcpath: str) -> "NodeInArchive": + return cls(arcpath=arcpath, directory=True) + + def __repr__(self) -> str: + return f"<{type(self).__name__}:arcpath={self.arcpath},path='{str(self.path)}',len(data)={len(self.data) if self.data else 'n/a'},directory={self.directory},symtarget={self.symtarget}>" + + +def configure_file(path: Path, context: dict[str, str]) -> bytes: + text = path.read_text() + return configure_text(text, context=context).encode() + + +def configure_text(text: str, context: dict[str, str]) -> str: + original_text = text + for txt, repl in context.items(): + text = text.replace(f"@<@{txt}@>@", repl) + success = all(thing not in text for thing in ("@<@", "@>@")) + if not success: + raise ValueError(f"Failed to configure {repr(original_text)}") + return text + + +def configure_text_list(text_list: list[str], context: dict[str, str]) -> list[str]: + return [configure_text(text=e, context=context) for e in text_list] + + +class ArchiveFileTree: + def __init__(self): + self._tree: dict[str, NodeInArchive] = {} + + def add_file(self, file: NodeInArchive): + self._tree[file.arcpath] = file + + def __iter__(self) -> typing.Iterable[NodeInArchive]: + yield from self._tree.values() + + def __contains__(self, value: str) -> bool: + return value in self._tree + + def get_latest_mod_time(self) -> datetime.datetime: + return max(item.time for item in self._tree.values() if item.time) + + def add_to_archiver(self, archive_base: str, archiver: Archiver): + remaining_symlinks = set() + added_files = dict() + + def calculate_symlink_target(s: NodeInArchive) -> str: + dest_dir = os.path.dirname(s.arcpath) + if dest_dir: + dest_dir += "/" + target = dest_dir + s.symtarget + while True: + new_target, n = re.subn(r"([^/]+/+[.]{2}/)", "", target) + target = new_target + if not n: + break + return target + + # Add files in first pass + for arcpath, node in self._tree.items(): + assert node is not None, f"{arcpath} -> node" + if node.data is not None: + archiver.add_file_data(arcpath=arc_join(archive_base, arcpath), data=node.data, time=node.time, mode=node.mode) + assert node.arcpath is not None, f"{node=}" + added_files[node.arcpath] = node + elif node.path is not None: + archiver.add_file_path(arcpath=arc_join(archive_base, arcpath), path=node.path) + assert node.arcpath is not None, f"{node=}" + added_files[node.arcpath] = node + elif node.symtarget is not None: + remaining_symlinks.add(node) + elif node.directory: + pass + else: + raise ValueError(f"Invalid Archive Node: {repr(node)}") + + assert None not in added_files + + # Resolve symlinks in second pass: zipfile does not support symlinks, so add files to zip archive + while True: + if not remaining_symlinks: + break + symlinks_this_time = set() + extra_added_files = {} + for symlink in remaining_symlinks: + symlink_files_for_zip = {} + symlink_target_path = calculate_symlink_target(symlink) + if symlink_target_path in added_files: + symlink_files_for_zip[symlink.arcpath] = added_files[symlink_target_path] + else: + symlink_target_path_slash = symlink_target_path + "/" + for added_file in added_files: + if added_file.startswith(symlink_target_path_slash): + path_in_symlink = symlink.arcpath + "/" + added_file.removeprefix(symlink_target_path_slash) + symlink_files_for_zip[path_in_symlink] = added_files[added_file] + if symlink_files_for_zip: + symlinks_this_time.add(symlink) + extra_added_files.update(symlink_files_for_zip) + files_for_zip = [{"arcpath": f"{archive_base}/{sym_path}", "data": sym_info.data, "mode": sym_info.mode} for sym_path, sym_info in symlink_files_for_zip.items()] + archiver.add_symlink(arcpath=f"{archive_base}/{symlink.arcpath}", target=symlink.symtarget, time=symlink.time, files_for_zip=files_for_zip) + # if not symlinks_this_time: + # logger.info("files added: %r", set(path for path in added_files.keys())) + assert symlinks_this_time, f"No targets found for symlinks: {remaining_symlinks}" + remaining_symlinks.difference_update(symlinks_this_time) + added_files.update(extra_added_files) + + def add_directory_tree(self, arc_dir: str, path: Path, time: datetime.datetime): + assert path.is_dir() + for files_dir, _, filenames in os.walk(path): + files_dir_path = Path(files_dir) + rel_files_path = files_dir_path.relative_to(path) + for filename in filenames: + self.add_file(NodeInArchive.from_fs(arcpath=arc_join(arc_dir, str(rel_files_path), filename), path=files_dir_path / filename, time=time)) + + def _add_files_recursively(self, arc_dir: str, paths: list[Path], time: datetime.datetime): + logger.debug(f"_add_files_recursively({arc_dir=} {paths=})") + for path in paths: + arcpath = arc_join(arc_dir, path.name) + if path.is_file(): + logger.debug("Adding %s as %s", path, arcpath) + self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) + elif path.is_dir(): + self._add_files_recursively(arc_dir=arc_join(arc_dir, path.name), paths=list(path.iterdir()), time=time) + else: + raise ValueError(f"Unsupported file type to add recursively: {path}") + + def add_file_mapping(self, arc_dir: str, file_mapping: dict[str, list[str]], file_mapping_root: Path, context: dict[str, str], time: datetime.datetime): + for meta_rel_destdir, meta_file_globs in file_mapping.items(): + rel_destdir = configure_text(meta_rel_destdir, context=context) + assert "@" not in rel_destdir, f"archive destination should not contain an @ after configuration ({repr(meta_rel_destdir)}->{repr(rel_destdir)})" + for meta_file_glob in meta_file_globs: + file_glob = configure_text(meta_file_glob, context=context) + assert "@" not in rel_destdir, f"archive glob should not contain an @ after configuration ({repr(meta_file_glob)}->{repr(file_glob)})" + if ":" in file_glob: + original_path, new_filename = file_glob.rsplit(":", 1) + assert ":" not in original_path, f"Too many ':' in {repr(file_glob)}" + assert "/" not in new_filename, f"New filename cannot contain a '/' in {repr(file_glob)}" + path = file_mapping_root / original_path + arcpath = arc_join(arc_dir, rel_destdir, new_filename) + if path.suffix == ".in": + data = configure_file(path, context=context) + logger.debug("Adding processed %s -> %s", path, arcpath) + self.add_file(NodeInArchive.from_data(arcpath=arcpath, data=data, time=time)) + else: + logger.debug("Adding %s -> %s", path, arcpath) + self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) + else: + relative_file_paths = glob.glob(file_glob, root_dir=file_mapping_root) + assert relative_file_paths, f"Glob '{file_glob}' does not match any file" + self._add_files_recursively(arc_dir=arc_join(arc_dir, rel_destdir), paths=[file_mapping_root / p for p in relative_file_paths], time=time) + + +class SourceCollector: + # TreeItem = collections.namedtuple("TreeItem", ("path", "mode", "data", "symtarget", "directory", "time")) + def __init__(self, root: Path, commit: str, filter: typing.Optional[Callable[[str], bool]], executer: Executer): + self.root = root + self.commit = commit + self.filter = filter + self.executer = executer + + def get_archive_file_tree(self) -> ArchiveFileTree: + git_archive_args = ["git", "archive", "--format=tar.gz", self.commit, "-o", "/dev/stdout"] + logger.info("Executing args=%r", git_archive_args) + contents_tgz = subprocess.check_output(git_archive_args, cwd=self.root, text=False) + tar_archive = tarfile.open(fileobj=io.BytesIO(contents_tgz), mode="r:gz") + filenames = tuple(m.name for m in tar_archive if (m.isfile() or m.issym())) + + file_times = self._get_file_times(paths=filenames) + git_contents = ArchiveFileTree() + for ti in tar_archive: + if self.filter and not self.filter(ti.name): + continue + data = None + symtarget = None + directory = False + file_time = None + if ti.isfile(): + contents_file = tar_archive.extractfile(ti.name) + data = contents_file.read() + file_time = file_times[ti.name] + elif ti.issym(): + symtarget = ti.linkname + file_time = file_times[ti.name] + elif ti.isdir(): + directory = True + else: + raise ValueError(f"{ti.name}: unknown type") + node = NodeInArchive(arcpath=ti.name, data=data, mode=ti.mode, symtarget=symtarget, time=file_time, directory=directory) + git_contents.add_file(node) + return git_contents + + def _get_file_times(self, paths: tuple[str, ...]) -> dict[str, datetime.datetime]: + dry_out = textwrap.dedent("""\ + time=2024-03-14T15:40:25-07:00 + + M\tCMakeLists.txt + """) + git_log_out = self.executer.check_output(["git", "log", "--name-status", '--pretty=time=%cI', self.commit], dry_out=dry_out, cwd=self.root).splitlines(keepends=False) + current_time = None + set_paths = set(paths) + path_times: dict[str, datetime.datetime] = {} + for line in git_log_out: + if not line: + continue + if line.startswith("time="): + current_time = safe_isotime_to_datetime(line.removeprefix("time=")) + continue + mod_type, file_paths = line.split(maxsplit=1) + assert current_time is not None + for file_path in file_paths.split("\t"): + if file_path in set_paths and file_path not in path_times: + path_times[file_path] = current_time + + # FIXME: find out why some files are not shown in "git log" + # assert set(path_times.keys()) == set_paths + if set(path_times.keys()) != set_paths: + found_times = set(path_times.keys()) + paths_without_times = set_paths.difference(found_times) + logger.warning("No times found for these paths: %s", paths_without_times) + max_time = max(time for time in path_times.values()) + for path in paths_without_times: + path_times[path] = max_time + + return path_times + + +class AndroidApiVersion: + def __init__(self, name: str, ints: tuple[int, ...]): + self.name = name + self.ints = ints + + def __repr__(self) -> str: + return f"<{self.name} ({'.'.join(str(v) for v in self.ints)})>" + +ANDROID_ABI_EXTRA_LINK_OPTIONS = {} + +class Releaser: + def __init__(self, release_info: dict, commit: str, revision: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str, deps_path: Path, overwrite: bool, github: bool, fast: bool): + self.release_info = release_info + self.project = release_info["name"] + self.version = self.extract_sdl_version(root=root, release_info=release_info) + self.root = root + self.commit = commit + self.revision = revision + self.dist_path = dist_path + self.section_printer = section_printer + self.executer = executer + self.cmake_generator = cmake_generator + self.cpu_count = multiprocessing.cpu_count() + self.deps_path = deps_path + self.overwrite = overwrite + self.github = github + self.fast = fast + self.arc_time = datetime.datetime.now() + + self.artifacts: dict[str, Path] = {} + + def get_context(self, extra_context: typing.Optional[dict[str, str]]=None) -> dict[str, str]: + ctx = { + "PROJECT_NAME": self.project, + "PROJECT_VERSION": self.version, + "PROJECT_COMMIT": self.commit, + "PROJECT_REVISION": self.revision, + "PROJECT_ROOT": str(self.root), + } + if extra_context: + ctx.update(extra_context) + return ctx + + @property + def dry(self) -> bool: + return self.executer.dry + + def prepare(self): + logger.debug("Creating dist folder") + self.dist_path.mkdir(parents=True, exist_ok=True) + + @classmethod + def _path_filter(cls, path: str) -> bool: + if ".gitmodules" in path: + return True + if path.startswith(".git"): + return False + return True + + @classmethod + def _external_repo_path_filter(cls, path: str) -> bool: + if not cls._path_filter(path): + return False + if path.startswith("test/") or path.startswith("tests/"): + return False + return True + + def create_source_archives(self) -> None: + source_collector = SourceCollector(root=self.root, commit=self.commit, executer=self.executer, filter=self._path_filter) + print(f"Collecting sources of {self.project}...") + archive_tree: ArchiveFileTree = source_collector.get_archive_file_tree() + latest_mod_time = archive_tree.get_latest_mod_time() + archive_tree.add_file(NodeInArchive.from_text(arcpath=REVISION_TXT, text=f"{self.revision}\n", time=latest_mod_time)) + archive_tree.add_file(NodeInArchive.from_text(arcpath=f"{GIT_HASH_FILENAME}", text=f"{self.commit}\n", time=latest_mod_time)) + archive_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["source"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=latest_mod_time) + + if "Makefile.am" in archive_tree: + patched_time = latest_mod_time + datetime.timedelta(minutes=1) + print(f"Makefile.am detected -> touching aclocal.m4, */Makefile.in, configure") + for node_data in archive_tree: + arc_name = os.path.basename(node_data.arcpath) + arc_name_we, arc_name_ext = os.path.splitext(arc_name) + if arc_name in ("aclocal.m4", "configure", "Makefile.in"): + print(f"Bumping time of {node_data.arcpath}") + node_data.time = patched_time + + archive_base = f"{self.project}-{self.version}" + zip_path = self.dist_path / f"{archive_base}.zip" + tgz_path = self.dist_path / f"{archive_base}.tar.gz" + txz_path = self.dist_path / f"{archive_base}.tar.xz" + + logger.info("Creating zip/tgz/txz source archives ...") + if self.dry: + zip_path.touch() + tgz_path.touch() + txz_path.touch() + else: + with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: + print(f"Adding source files of {self.project}...") + archive_tree.add_to_archiver(archive_base=archive_base, archiver=archiver) + + for extra_repo in self.release_info["source"].get("extra-repos", []): + extra_repo_root = self.root / extra_repo + assert (extra_repo_root / ".git").exists(), f"{extra_repo_root} must be a git repo" + extra_repo_commit = self.executer.check_output(["git", "rev-parse", "HEAD"], dry_out=f"gitsha-extra-repo-{extra_repo}", cwd=extra_repo_root).strip() + extra_repo_source_collector = SourceCollector(root=extra_repo_root, commit=extra_repo_commit, executer=self.executer, filter=self._external_repo_path_filter) + print(f"Collecting sources of {extra_repo} ...") + extra_repo_archive_tree = extra_repo_source_collector.get_archive_file_tree() + print(f"Adding source files of {extra_repo} ...") + extra_repo_archive_tree.add_to_archiver(archive_base=f"{archive_base}/{extra_repo}", archiver=archiver) + + for file in self.release_info["source"]["checks"]: + assert f"{archive_base}/{file}" in archiver.added_files, f"'{archive_base}/{file}' must exist" + + logger.info("... done") + + self.artifacts["src-zip"] = zip_path + self.artifacts["src-tar-gz"] = tgz_path + self.artifacts["src-tar-xz"] = txz_path + + if not self.dry: + with tgz_path.open("r+b") as f: + # Zero the embedded timestamp in the gzip'ed tarball + f.seek(4, 0) + f.write(b"\x00\x00\x00\x00") + + def create_dmg(self, configuration: str="Release") -> None: + dmg_in = self.root / self.release_info["dmg"]["path"] + xcode_project = self.root / self.release_info["dmg"]["project"] + assert xcode_project.is_dir(), f"{xcode_project} must be a directory" + assert (xcode_project / "project.pbxproj").is_file, f"{xcode_project} must contain project.pbxproj" + if not self.fast: + dmg_in.unlink(missing_ok=True) + build_xcconfig = self.release_info["dmg"].get("build-xcconfig") + if build_xcconfig: + shutil.copy(self.root / build_xcconfig, xcode_project.parent / "build.xcconfig") + + xcode_scheme = self.release_info["dmg"].get("scheme") + xcode_target = self.release_info["dmg"].get("target") + assert xcode_scheme or xcode_target, "dmg needs scheme or target" + assert not (xcode_scheme and xcode_target), "dmg cannot have both scheme and target set" + if xcode_scheme: + scheme_or_target = "-scheme" + target_like = xcode_scheme + else: + scheme_or_target = "-target" + target_like = xcode_target + self.executer.run(["xcodebuild", "ONLY_ACTIVE_ARCH=NO", "-project", xcode_project, scheme_or_target, target_like, "-configuration", configuration]) + if self.dry: + dmg_in.parent.mkdir(parents=True, exist_ok=True) + dmg_in.touch() + + assert dmg_in.is_file(), f"{self.project}.dmg was not created by xcodebuild" + + dmg_out = self.dist_path / f"{self.project}-{self.version}.dmg" + shutil.copy(dmg_in, dmg_out) + self.artifacts["dmg"] = dmg_out + + @property + def git_hash_data(self) -> bytes: + return f"{self.commit}\n".encode() + + def verify_mingw_library(self, triplet: str, path: Path): + objdump_output = self.executer.check_output([f"{triplet}-objdump", "-p", str(path)]) + libraries = re.findall(r"DLL Name: ([^\n]+)", objdump_output) + logger.info("%s (%s) libraries: %r", path, triplet, libraries) + illegal_libraries = list(filter(RE_ILLEGAL_MINGW_LIBRARIES.match, libraries)) + logger.error("Detected 'illegal' libraries: %r", illegal_libraries) + if illegal_libraries: + raise Exception(f"{path} links to illegal libraries: {illegal_libraries}") + + def create_mingw_archives(self) -> None: + build_type = "Release" + build_parent_dir = self.root / "build-mingw" + ARCH_TO_GNU_ARCH = { + # "arm64": "aarch64", + "x86": "i686", + "x64": "x86_64", + } + ARCH_TO_TRIPLET = { + # "arm64": "aarch64-w64-mingw32", + "x86": "i686-w64-mingw32", + "x64": "x86_64-w64-mingw32", + } + + new_env = dict(os.environ) + + cmake_prefix_paths = [] + mingw_deps_path = self.deps_path / "mingw-deps" + + if "dependencies" in self.release_info["mingw"]: + shutil.rmtree(mingw_deps_path, ignore_errors=True) + mingw_deps_path.mkdir() + + for triplet in ARCH_TO_TRIPLET.values(): + (mingw_deps_path / triplet).mkdir() + + def extract_filter(member: tarfile.TarInfo, path: str, /): + if member.name.startswith("SDL"): + member.name = "/".join(Path(member.name).parts[1:]) + return member + for dep in self.release_info.get("dependencies", {}): + extract_path = mingw_deps_path / f"extract-{dep}" + extract_path.mkdir() + with chdir(extract_path): + tar_path = self.deps_path / glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path)[0] + logger.info("Extracting %s to %s", tar_path, mingw_deps_path) + assert tar_path.suffix in (".gz", ".xz") + with tarfile.open(tar_path, mode=f"r:{tar_path.suffix.strip('.')}") as tarf: + tarf.extractall(filter=extract_filter) + for arch, triplet in ARCH_TO_TRIPLET.items(): + install_cmd = self.release_info["mingw"]["dependencies"][dep]["install-command"] + extra_configure_data = { + "ARCH": ARCH_TO_GNU_ARCH[arch], + "TRIPLET": triplet, + "PREFIX": str(mingw_deps_path / triplet), + } + install_cmd = configure_text(install_cmd, context=self.get_context(extra_configure_data)) + self.executer.run(shlex.split(install_cmd), cwd=str(extract_path)) + + dep_binpath = mingw_deps_path / triplet / "bin" + assert dep_binpath.is_dir(), f"{dep_binpath} for PATH should exist" + dep_pkgconfig = mingw_deps_path / triplet / "lib/pkgconfig" + assert dep_pkgconfig.is_dir(), f"{dep_pkgconfig} for PKG_CONFIG_PATH should exist" + + new_env["PATH"] = os.pathsep.join([str(dep_binpath), new_env["PATH"]]) + new_env["PKG_CONFIG_PATH"] = str(dep_pkgconfig) + cmake_prefix_paths.append(mingw_deps_path) + + new_env["CFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" + new_env["CXXFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" + + assert any(system in self.release_info["mingw"] for system in ("autotools", "cmake")) + assert not all(system in self.release_info["mingw"] for system in ("autotools", "cmake")) + + mingw_archs = set() + arc_root = f"{self.project}-{self.version}" + archive_file_tree = ArchiveFileTree() + + if "autotools" in self.release_info["mingw"]: + for arch in self.release_info["mingw"]["autotools"]["archs"]: + triplet = ARCH_TO_TRIPLET[arch] + new_env["CC"] = f"{triplet}-gcc" + new_env["CXX"] = f"{triplet}-g++" + new_env["RC"] = f"{triplet}-windres" + + assert arch not in mingw_archs + mingw_archs.add(arch) + + build_path = build_parent_dir / f"build-{triplet}" + install_path = build_parent_dir / f"install-{triplet}" + shutil.rmtree(install_path, ignore_errors=True) + build_path.mkdir(parents=True, exist_ok=True) + context = self.get_context({ + "ARCH": arch, + "DEP_PREFIX": str(mingw_deps_path / triplet), + }) + extra_args = configure_text_list(text_list=self.release_info["mingw"]["autotools"]["args"], context=context) + + with self.section_printer.group(f"Configuring MinGW {triplet} (autotools)"): + assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" + self.executer.run([ + self.root / "configure", + f"--prefix={install_path}", + f"--includedir=${{prefix}}/include", + f"--libdir=${{prefix}}/lib", + f"--bindir=${{prefix}}/bin", + f"--host={triplet}", + f"--build=x86_64-none-linux-gnu", + "CFLAGS=-O2", + "CXXFLAGS=-O2", + "LDFLAGS=-Wl,-s", + ] + extra_args, cwd=build_path, env=new_env) + with self.section_printer.group(f"Build MinGW {triplet} (autotools)"): + self.executer.run(["make", f"-j{self.cpu_count}"], cwd=build_path, env=new_env) + with self.section_printer.group(f"Install MinGW {triplet} (autotools)"): + self.executer.run(["make", "install"], cwd=build_path, env=new_env) + self.verify_mingw_library(triplet=ARCH_TO_TRIPLET[arch], path=install_path / "bin" / f"{self.project}.dll") + archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) + + print("Recording arch-dependent extra files for MinGW development archive ...") + extra_context = { + "TRIPLET": ARCH_TO_TRIPLET[arch], + } + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["autotools"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) + + if "cmake" in self.release_info["mingw"]: + assert self.release_info["mingw"]["cmake"]["shared-static"] in ("args", "both") + for arch in self.release_info["mingw"]["cmake"]["archs"]: + triplet = ARCH_TO_TRIPLET[arch] + new_env["CC"] = f"{triplet}-gcc" + new_env["CXX"] = f"{triplet}-g++" + new_env["RC"] = f"{triplet}-windres" + + assert arch not in mingw_archs + mingw_archs.add(arch) + + context = self.get_context({ + "ARCH": arch, + "DEP_PREFIX": str(mingw_deps_path / triplet), + }) + extra_args = configure_text_list(text_list=self.release_info["mingw"]["cmake"]["args"], context=context) + + build_path = build_parent_dir / f"build-{triplet}" + install_path = build_parent_dir / f"install-{triplet}" + shutil.rmtree(install_path, ignore_errors=True) + build_path.mkdir(parents=True, exist_ok=True) + if self.release_info["mingw"]["cmake"]["shared-static"] == "args": + args_for_shared_static = ([], ) + elif self.release_info["mingw"]["cmake"]["shared-static"] == "both": + args_for_shared_static = (["-DBUILD_SHARED_LIBS=ON"], ["-DBUILD_SHARED_LIBS=OFF"]) + for arg_for_shared_static in args_for_shared_static: + with self.section_printer.group(f"Configuring MinGW {triplet} (CMake)"): + assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" + self.executer.run([ + f"cmake", + f"-S", str(self.root), "-B", str(build_path), + f"-DCMAKE_BUILD_TYPE={build_type}", + f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + f"-DCMAKE_PREFIX_PATH={mingw_deps_path / triplet}", + f"-DCMAKE_INSTALL_PREFIX={install_path}", + f"-DCMAKE_INSTALL_INCLUDEDIR=include", + f"-DCMAKE_INSTALL_LIBDIR=lib", + f"-DCMAKE_INSTALL_BINDIR=bin", + f"-DCMAKE_INSTALL_DATAROOTDIR=share", + f"-DCMAKE_TOOLCHAIN_FILE={self.root}/build-scripts/cmake-toolchain-mingw64-{ARCH_TO_GNU_ARCH[arch]}.cmake", + f"-G{self.cmake_generator}", + ] + extra_args + ([] if self.fast else ["--fresh"]) + arg_for_shared_static, cwd=build_path, env=new_env) + with self.section_printer.group(f"Build MinGW {triplet} (CMake)"): + self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type], cwd=build_path, env=new_env) + with self.section_printer.group(f"Install MinGW {triplet} (CMake)"): + self.executer.run(["cmake", "--install", str(build_path)], cwd=build_path, env=new_env) + self.verify_mingw_library(triplet=ARCH_TO_TRIPLET[arch], path=install_path / "bin" / f"{self.project}.dll") + archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) + + print("Recording arch-dependent extra files for MinGW development archive ...") + extra_context = { + "TRIPLET": ARCH_TO_TRIPLET[arch], + } + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["cmake"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) + print("... done") + + print("Recording extra files for MinGW development archive ...") + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) + print("... done") + + print("Creating zip/tgz/txz development archives ...") + zip_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.zip" + tgz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.gz" + txz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.xz" + + with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: + archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) + archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) + print("... done") + + self.artifacts["mingw-devel-zip"] = zip_path + self.artifacts["mingw-devel-tar-gz"] = tgz_path + self.artifacts["mingw-devel-tar-xz"] = txz_path + + def _detect_android_api(self, android_home: str) -> typing.Optional[AndroidApiVersion]: + platform_dirs = list(Path(p) for p in glob.glob(f"{android_home}/platforms/android-*")) + re_platform = re.compile("^android-([0-9]+)(?:-ext([0-9]+))?$") + platform_versions: list[AndroidApiVersion] = [] + for platform_dir in platform_dirs: + logger.debug("Found Android Platform SDK: %s", platform_dir) + if not (platform_dir / "android.jar").is_file(): + logger.debug("Skipping SDK, missing android.jar") + continue + if m:= re_platform.match(platform_dir.name): + platform_versions.append(AndroidApiVersion(name=platform_dir.name, ints=(int(m.group(1)), int(m.group(2) or 0)))) + platform_versions.sort(key=lambda v: v.ints) + logger.info("Available platform versions: %s", platform_versions) + platform_versions = list(filter(lambda v: v.ints >= self._android_api_minimum.ints, platform_versions)) + logger.info("Valid platform versions (>=%s): %s", self._android_api_minimum.ints, platform_versions) + if not platform_versions: + return None + android_api = platform_versions[0] + logger.info("Selected API version %s", android_api) + return android_api + + def _get_prefab_json_text(self) -> str: + return textwrap.dedent(f"""\ + {{ + "schema_version": 2, + "name": "{self.project}", + "version": "{self.version}", + "dependencies": [] + }} + """) + + def _get_prefab_module_json_text(self, library_name: typing.Optional[str], export_libraries: list[str]) -> str: + for lib in export_libraries: + assert isinstance(lib, str), f"{lib} must be a string" + module_json_dict = { + "export_libraries": export_libraries, + } + if library_name: + module_json_dict["library_name"] = f"lib{library_name}" + return json.dumps(module_json_dict, indent=4) + + @property + def _android_api_minimum(self) -> AndroidApiVersion: + value = self.release_info["android"]["api-minimum"] + if isinstance(value, int): + ints = (value, ) + elif isinstance(value, str): + ints = tuple(split(".")) + else: + raise ValueError("Invalid android.api-minimum: must be X or X.Y") + match len(ints): + case 1: name = f"android-{ints[0]}" + case 2: name = f"android-{ints[0]}-ext-{ints[1]}" + case _: raise ValueError("Invalid android.api-minimum: must be X or X.Y") + return AndroidApiVersion(name=name, ints=ints) + + @property + def _android_api_target(self): + return self.release_info["android"]["api-target"] + + @property + def _android_ndk_minimum(self): + return self.release_info["android"]["ndk-minimum"] + + def _get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool) -> str: + abi_json_dict = { + "abi": abi, + "api": self._android_api_minimum.ints[0], + "ndk": self._android_ndk_minimum, + "stl": "c++_shared" if cpp else "none", + "static": not shared, + } + return json.dumps(abi_json_dict, indent=4) + + def _get_android_manifest_text(self) -> str: + return textwrap.dedent(f"""\ + + + + """) + + def create_android_archives(self, android_api: int, android_home: Path, android_ndk_home: Path) -> None: + cmake_toolchain_file = Path(android_ndk_home) / "build/cmake/android.toolchain.cmake" + if not cmake_toolchain_file.exists(): + logger.error("CMake toolchain file does not exist (%s)", cmake_toolchain_file) + raise SystemExit(1) + aar_path = self.root / "build-android" / f"{self.project}-{self.version}.aar" + android_dist_path = self.dist_path / f"{self.project}-devel-{self.version}-android.zip" + android_abis = self.release_info["android"]["abis"] + java_jars_added = False + module_data_added = False + android_deps_path = self.deps_path / "android-deps" + shutil.rmtree(android_deps_path, ignore_errors=True) + + for dep, depinfo in self.release_info["android"].get("dependencies", {}).items(): + dep_devel_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] + + dep_extract_path = self.deps_path / f"extract/android/{dep}" + shutil.rmtree(dep_extract_path, ignore_errors=True) + dep_extract_path.mkdir(parents=True, exist_ok=True) + + with self.section_printer.group(f"Extracting Android dependency {dep} ({dep_devel_zip})"): + with zipfile.ZipFile(dep_devel_zip, "r") as zf: + zf.extractall(dep_extract_path) + + dep_devel_aar = dep_extract_path / glob.glob("*.aar", root_dir=dep_extract_path)[0] + self.executer.run([sys.executable, str(dep_devel_aar), "-o", str(android_deps_path)]) + + for module_name, module_info in self.release_info["android"]["modules"].items(): + assert "type" in module_info and module_info["type"] in ("interface", "library"), f"module {module_name} must have a valid type" + + aar_file_tree = ArchiveFileTree() + android_devel_file_tree = ArchiveFileTree() + + for android_abi in android_abis: + extra_link_options = ANDROID_ABI_EXTRA_LINK_OPTIONS.get(android_abi, "") + with self.section_printer.group(f"Building for Android {android_api} {android_abi}"): + build_dir = self.root / "build-android" / f"{android_abi}-build" + install_dir = self.root / "install-android" / f"{android_abi}-install" + shutil.rmtree(install_dir, ignore_errors=True) + assert not install_dir.is_dir(), f"{install_dir} should not exist prior to build" + build_type = "Release" + cmake_args = [ + "cmake", + "-S", str(self.root), + "-B", str(build_dir), + # NDK 21e does not support -ffile-prefix-map + # f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + # f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', + f"-DCMAKE_EXE_LINKER_FLAGS={extra_link_options}", + f"-DCMAKE_SHARED_LINKER_FLAGS={extra_link_options}", + f"-DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file}", + f"-DCMAKE_PREFIX_PATH={str(android_deps_path)}", + f"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", + f"-DANDROID_HOME={android_home}", + f"-DANDROID_PLATFORM={android_api}", + f"-DANDROID_ABI={android_abi}", + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", + f"-DCMAKE_INSTALL_PREFIX={install_dir}", + "-DCMAKE_INSTALL_INCLUDEDIR=include ", + "-DCMAKE_INSTALL_LIBDIR=lib", + "-DCMAKE_INSTALL_DATAROOTDIR=share", + f"-DCMAKE_BUILD_TYPE={build_type}", + f"-G{self.cmake_generator}", + ] + self.release_info["android"]["cmake"]["args"] + ([] if self.fast else ["--fresh"]) + build_args = [ + "cmake", + "--build", str(build_dir), + "--verbose", + "--config", build_type, + ] + install_args = [ + "cmake", + "--install", str(build_dir), + "--config", build_type, + ] + self.executer.run(cmake_args) + self.executer.run(build_args) + self.executer.run(install_args) + + for module_name, module_info in self.release_info["android"]["modules"].items(): + arcdir_prefab_module = f"prefab/modules/{module_name}" + if module_info["type"] == "library": + library = install_dir / module_info["library"] + assert library.suffix in (".so", ".a") + assert library.is_file(), f"CMake should have built library '{library}' for module {module_name}" + arcdir_prefab_libs = f"{arcdir_prefab_module}/libs/android.{android_abi}" + aar_file_tree.add_file(NodeInArchive.from_fs(arcpath=f"{arcdir_prefab_libs}/{library.name}", path=library, time=self.arc_time)) + aar_file_tree.add_file(NodeInArchive.from_text(arcpath=f"{arcdir_prefab_libs}/abi.json", text=self._get_prefab_abi_json_text(abi=android_abi, cpp=False, shared=library.suffix == ".so"), time=self.arc_time)) + + if not module_data_added: + library_name = None + if module_info["type"] == "library": + library_name = Path(module_info["library"]).stem.removeprefix("lib") + export_libraries = module_info.get("export-libraries", []) + aar_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_module, "module.json"), text=self._get_prefab_module_json_text(library_name=library_name, export_libraries=export_libraries), time=self.arc_time)) + arcdir_prefab_include = f"prefab/modules/{module_name}/include" + if "includes" in module_info: + aar_file_tree.add_file_mapping(arc_dir=arcdir_prefab_include, file_mapping=module_info["includes"], file_mapping_root=install_dir, context=self.get_context(), time=self.arc_time) + else: + aar_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_include, ".keep"), text="\n", time=self.arc_time)) + module_data_added = True + + if not java_jars_added: + java_jars_added = True + if "jars" in self.release_info["android"]: + classes_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["classes"], context=self.get_context()) + sources_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["sources"], context=self.get_context()) + doc_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["doc"], context=self.get_context()) + assert classes_jar_path.is_file(), f"CMake should have compiled the java sources and archived them into a JAR ({classes_jar_path})" + assert sources_jar_path.is_file(), f"CMake should have archived the java sources into a JAR ({sources_jar_path})" + assert doc_jar_path.is_file(), f"CMake should have archived javadoc into a JAR ({doc_jar_path})" + + aar_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes.jar", path=classes_jar_path, time=self.arc_time)) + aar_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-sources.jar", path=sources_jar_path, time=self.arc_time)) + aar_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-doc.jar", path=doc_jar_path, time=self.arc_time)) + + assert ("jars" in self.release_info["android"] and java_jars_added) or "jars" not in self.release_info["android"], "Must have archived java JAR archives" + + aar_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["android"]["aar-files"], file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) + + aar_file_tree.add_file(NodeInArchive.from_text(arcpath="prefab/prefab.json", text=self._get_prefab_json_text(), time=self.arc_time)) + aar_file_tree.add_file(NodeInArchive.from_text(arcpath="AndroidManifest.xml", text=self._get_android_manifest_text(), time=self.arc_time)) + + with Archiver(zip_path=aar_path) as archiver: + aar_file_tree.add_to_archiver(archive_base="", archiver=archiver) + archiver.add_git_hash(arcdir="", commit=self.commit, time=self.arc_time) + + android_devel_file_tree.add_file(NodeInArchive.from_fs(arcpath=aar_path.name, path=aar_path)) + android_devel_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["android"]["files"], file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) + with Archiver(zip_path=android_dist_path) as archiver: + android_devel_file_tree.add_to_archiver(archive_base="", archiver=archiver) + archiver.add_git_hash(arcdir="", commit=self.commit, time=self.arc_time) + + self.artifacts[f"android-aar"] = android_dist_path + + def download_dependencies(self): + shutil.rmtree(self.deps_path, ignore_errors=True) + self.deps_path.mkdir(parents=True) + + if self.github: + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"dep-path={self.deps_path.absolute()}\n") + + for dep, depinfo in self.release_info.get("dependencies", {}).items(): + startswith = depinfo["startswith"] + dep_repo = depinfo["repo"] + # FIXME: dropped "--exclude-pre-releases" + dep_string_data = self.executer.check_output(["gh", "-R", dep_repo, "release", "list", "--exclude-drafts", "--json", "name,createdAt,tagName", "--jq", f'[.[]|select(.name|startswith("{startswith}"))]|max_by(.createdAt)']).strip() + dep_data = json.loads(dep_string_data) + dep_tag = dep_data["tagName"] + dep_version = dep_data["name"] + logger.info("Download dependency %s version %s (tag=%s) ", dep, dep_version, dep_tag) + self.executer.run(["gh", "-R", dep_repo, "release", "download", dep_tag], cwd=self.deps_path) + if self.github: + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"dep-{dep.lower()}-version={dep_version}\n") + + def verify_dependencies(self): + for dep, depinfo in self.release_info.get("dependencies", {}).items(): + if "mingw" in self.release_info: + mingw_matches = glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) + assert len(mingw_matches) == 1, f"Exactly one archive matches mingw {dep} dependency: {mingw_matches}" + if "dmg" in self.release_info: + dmg_matches = glob.glob(self.release_info["dmg"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) + assert len(dmg_matches) == 1, f"Exactly one archive matches dmg {dep} dependency: {dmg_matches}" + if "msvc" in self.release_info: + msvc_matches = glob.glob(self.release_info["msvc"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) + assert len(msvc_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {msvc_matches}" + if "android" in self.release_info: + android_matches = glob.glob(self.release_info["android"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) + assert len(android_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {android_matches}" + + @staticmethod + def _arch_to_vs_platform(arch: str, configuration: str="Release") -> VsArchPlatformConfig: + ARCH_TO_VS_PLATFORM = { + "x86": VsArchPlatformConfig(arch="x86", platform="Win32", configuration=configuration), + "x64": VsArchPlatformConfig(arch="x64", platform="x64", configuration=configuration), + "arm64": VsArchPlatformConfig(arch="arm64", platform="ARM64", configuration=configuration), + } + return ARCH_TO_VS_PLATFORM[arch] + + def build_msvc(self): + with self.section_printer.group("Find Visual Studio"): + vs = VisualStudio(executer=self.executer) + for arch in self.release_info["msvc"].get("msbuild", {}).get("archs", []): + self._build_msvc_msbuild(arch_platform=self._arch_to_vs_platform(arch=arch), vs=vs) + if "cmake" in self.release_info["msvc"]: + deps_path = self.root / "msvc-deps" + shutil.rmtree(deps_path, ignore_errors=True) + dep_roots = [] + for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): + dep_extract_path = deps_path / f"extract-{dep}" + msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] + with zipfile.ZipFile(msvc_zip, "r") as zf: + zf.extractall(dep_extract_path) + contents_msvc_zip = glob.glob(str(dep_extract_path / "*")) + assert len(contents_msvc_zip) == 1, f"There must be exactly one root item in the root directory of {dep}" + dep_roots.append(contents_msvc_zip[0]) + + for arch in self.release_info["msvc"].get("cmake", {}).get("archs", []): + self._build_msvc_cmake(arch_platform=self._arch_to_vs_platform(arch=arch), dep_roots=dep_roots) + with self.section_printer.group("Create SDL VC development zip"): + self._build_msvc_devel() + + def _copy_dep_files(self, arch_platform: VsArchPlatformConfig): + platform_context = self.get_context(arch_platform.extra_context()) + for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): + msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] + + src_globs = [configure_text(instr["src"], context=platform_context) for instr in depinfo["copy"]] + with zipfile.ZipFile(msvc_zip, "r") as zf: + for member in zf.namelist(): + member_path = "/".join(Path(member).parts[1:]) + for src_i, src_glob in enumerate(src_globs): + if fnmatch.fnmatch(member_path, src_glob): + dst = (self.root / configure_text(depinfo["copy"][src_i]["dst"], context=platform_context)).resolve() / Path(member_path).name + zip_data = zf.read(member) + if dst.exists(): + identical = False + if dst.is_file(): + orig_bytes = dst.read_bytes() + if orig_bytes == zip_data: + identical = True + if not identical: + logger.warning("Extracting dependency %s, will cause %s to be overwritten", dep, dst) + if not self.overwrite: + raise RuntimeError("Run with --overwrite to allow overwriting") + logger.debug("Extracting %s -> %s", member, dst) + + dst.parent.mkdir(exist_ok=True, parents=True) + dst.write_bytes(zip_data) + + def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStudio): + self._copy_dep_files(arch_platform) + + prebuilt_paths = set(self.root / full_prebuilt_path for prebuilt_path in self.release_info["msvc"]["msbuild"].get("prebuilt", []) for full_prebuilt_path in glob.glob(configure_text(prebuilt_path, context=platform_context), root_dir=self.root)) + msbuild_paths = set(self.root / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["msbuild"]["files-lib"], self.release_info["msvc"]["msbuild"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) + assert prebuilt_paths.issubset(msbuild_paths), f"msvc.msbuild.prebuilt must be a subset of (msvc.msbuild.files-lib, msvc.msbuild.files-devel)" + built_paths = msbuild_paths.difference(prebuilt_paths) + logger.info("MSbuild builds these files, to be included in the package: %s", built_paths) + if not self.fast: + for b in built_paths: + b.unlink(missing_ok=True) + + rel_projects: list[str] = self.release_info["msvc"]["msbuild"]["projects"] + projects = list(self.root / p for p in rel_projects) + + directory_build_props_src_relpath = self.release_info["msvc"]["msbuild"].get("directory-build-props") + for project in projects: + dir_b_props = project.parent / "Directory.Build.props" + dir_b_props.unlink(missing_ok = True) + if directory_build_props_src_relpath: + src = self.root / directory_build_props_src_relpath + logger.debug("Copying %s -> %s", src, dir_b_props) + shutil.copy(src=src, dst=dir_b_props) + + with self.section_printer.group(f"Build {arch_platform.arch} VS binary"): + vs.build(arch_platform=arch_platform, projects=projects) + + if self.dry: + for b in built_paths: + b.parent.mkdir(parents=True, exist_ok=True) + b.touch() + + for b in built_paths: + assert b.is_file(), f"{b} has not been created" + b.parent.mkdir(parents=True, exist_ok=True) + b.touch() + + zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" + zip_path.unlink(missing_ok=True) + + logger.info("Collecting files...") + archive_file_tree = ArchiveFileTree() + archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["msbuild"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) + archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) + + logger.info("Writing to %s", zip_path) + with Archiver(zip_path=zip_path) as archiver: + arc_root = f"" + archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) + archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) + self.artifacts[f"VC-{arch_platform.arch}"] = zip_path + + for p in built_paths: + assert p.is_file(), f"{p} should exist" + + def _arch_platform_to_build_path(self, arch_platform: VsArchPlatformConfig) -> Path: + return self.root / f"build-vs-{arch_platform.arch}" + + def _arch_platform_to_install_path(self, arch_platform: VsArchPlatformConfig) -> Path: + return self._arch_platform_to_build_path(arch_platform) / "prefix" + + def _build_msvc_cmake(self, arch_platform: VsArchPlatformConfig, dep_roots: list[Path]): + build_path = self._arch_platform_to_build_path(arch_platform) + install_path = self._arch_platform_to_install_path(arch_platform) + platform_context = self.get_context(extra_context=arch_platform.extra_context()) + + build_type = "Release" + extra_context = { + "ARCH": arch_platform.arch, + "PLATFORM": arch_platform.platform, + } + + self._copy_dep_files(arch_platform) + + built_paths = set(install_path / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["cmake"]["files-lib"], self.release_info["msvc"]["cmake"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) + logger.info("CMake builds these files, to be included in the package: %s", built_paths) + if not self.fast: + for b in built_paths: + b.unlink(missing_ok=True) + + shutil.rmtree(install_path, ignore_errors=True) + build_path.mkdir(parents=True, exist_ok=True) + with self.section_printer.group(f"Configure VC CMake project for {arch_platform.arch}"): + self.executer.run([ + "cmake", "-S", str(self.root), "-B", str(build_path), + "-A", arch_platform.platform, + "-DCMAKE_INSTALL_BINDIR=bin", + "-DCMAKE_INSTALL_DATAROOTDIR=share", + "-DCMAKE_INSTALL_INCLUDEDIR=include", + "-DCMAKE_INSTALL_LIBDIR=lib", + f"-DCMAKE_BUILD_TYPE={build_type}", + f"-DCMAKE_INSTALL_PREFIX={install_path}", + # MSVC debug information format flags are selected by an abstraction + "-DCMAKE_POLICY_DEFAULT_CMP0141=NEW", + # MSVC debug information format + "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase", + # Linker flags for executables + "-DCMAKE_EXE_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", + # Linker flag for shared libraries + "-DCMAKE_SHARED_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", + # MSVC runtime library flags are selected by an abstraction + "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW", + # Use statically linked runtime (-MT) (ideally, should be "MultiThreaded$<$:Debug>") + "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded", + f"-DCMAKE_PREFIX_PATH={';'.join(str(s) for s in dep_roots)}", + ] + self.release_info["msvc"]["cmake"]["args"] + ([] if self.fast else ["--fresh"])) + + with self.section_printer.group(f"Build VC CMake project for {arch_platform.arch}"): + self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type]) + with self.section_printer.group(f"Install VC CMake project for {arch_platform.arch}"): + self.executer.run(["cmake", "--install", str(build_path), "--config", build_type]) + + if self.dry: + for b in built_paths: + b.parent.mkdir(parents=True, exist_ok=True) + b.touch() + + zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" + zip_path.unlink(missing_ok=True) + + logger.info("Collecting files...") + archive_file_tree = ArchiveFileTree() + archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["cmake"]["files-lib"], file_mapping_root=install_path, context=platform_context, time=self.arc_time) + archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) + + logger.info("Creating %s", zip_path) + with Archiver(zip_path=zip_path) as archiver: + arc_root = f"" + archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) + archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) + + for p in built_paths: + assert p.is_file(), f"{p} should exist" + + def _build_msvc_devel(self) -> None: + zip_path = self.dist_path / f"{self.project}-devel-{self.version}-VC.zip" + arc_root = f"{self.project}-{self.version}" + + def copy_files_devel(ctx): + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["files-devel"], file_mapping_root=self.root, context=ctx, time=self.arc_time) + + + logger.info("Collecting files...") + archive_file_tree = ArchiveFileTree() + if "msbuild" in self.release_info["msvc"]: + for arch in self.release_info["msvc"]["msbuild"]["archs"]: + arch_platform = self._arch_to_vs_platform(arch=arch) + platform_context = self.get_context(arch_platform.extra_context()) + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["msbuild"]["files-devel"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) + copy_files_devel(ctx=platform_context) + if "cmake" in self.release_info["msvc"]: + for arch in self.release_info["msvc"]["cmake"]["archs"]: + arch_platform = self._arch_to_vs_platform(arch=arch) + platform_context = self.get_context(arch_platform.extra_context()) + archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["cmake"]["files-devel"], file_mapping_root=self._arch_platform_to_install_path(arch_platform), context=platform_context, time=self.arc_time) + copy_files_devel(ctx=platform_context) + + with Archiver(zip_path=zip_path) as archiver: + archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) + archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) + self.artifacts["VC-devel"] = zip_path + + @classmethod + def extract_sdl_version(cls, root: Path, release_info: dict) -> str: + with open(root / release_info["version"]["file"], "r") as f: + text = f.read() + major = next(re.finditer(release_info["version"]["re_major"], text, flags=re.M)).group(1) + minor = next(re.finditer(release_info["version"]["re_minor"], text, flags=re.M)).group(1) + micro = next(re.finditer(release_info["version"]["re_micro"], text, flags=re.M)).group(1) + return f"{major}.{minor}.{micro}" + + +def main(argv=None) -> int: + if sys.version_info < (3, 11): + logger.error("This script needs at least python 3.11") + return 1 + + parser = argparse.ArgumentParser(allow_abbrev=False, description="Create SDL release artifacts") + parser.add_argument("--root", metavar="DIR", type=Path, default=Path(__file__).absolute().parents[1], help="Root of project") + parser.add_argument("--release-info", metavar="JSON", dest="path_release_info", type=Path, default=Path(__file__).absolute().parent / "release-info.json", help="Path of release-info.json") + parser.add_argument("--dependency-folder", metavar="FOLDER", dest="deps_path", type=Path, default="deps", help="Directory containing pre-built archives of dependencies (will be removed when downloading archives)") + parser.add_argument("--out", "-o", metavar="DIR", dest="dist_path", type=Path, default="dist", help="Output directory") + parser.add_argument("--github", action="store_true", help="Script is running on a GitHub runner") + parser.add_argument("--commit", default="HEAD", help="Git commit/tag of which a release should be created") + parser.add_argument("--actions", choices=["download", "source", "android", "mingw", "msvc", "dmg"], required=True, nargs="+", dest="actions", help="What to do?") + parser.set_defaults(loglevel=logging.INFO) + parser.add_argument('--vs-year', dest="vs_year", help="Visual Studio year") + parser.add_argument('--android-api', dest="android_api", help="Android API version") + parser.add_argument('--android-home', dest="android_home", default=os.environ.get("ANDROID_HOME"), help="Android Home folder") + parser.add_argument('--android-ndk-home', dest="android_ndk_home", default=os.environ.get("ANDROID_NDK_HOME"), help="Android NDK Home folder") + parser.add_argument('--cmake-generator', dest="cmake_generator", default="Ninja", help="CMake Generator") + parser.add_argument('--debug', action='store_const', const=logging.DEBUG, dest="loglevel", help="Print script debug information") + parser.add_argument('--dry-run', action='store_true', dest="dry", help="Don't execute anything") + parser.add_argument('--force', action='store_true', dest="force", help="Ignore a non-clean git tree") + parser.add_argument('--overwrite', action='store_true', dest="overwrite", help="Allow potentially overwriting other projects") + parser.add_argument('--fast', action='store_true', dest="fast", help="Don't do a rebuild") + + args = parser.parse_args(argv) + logging.basicConfig(level=args.loglevel, format='[%(levelname)s] %(message)s') + args.deps_path = args.deps_path.absolute() + args.dist_path = args.dist_path.absolute() + args.root = args.root.absolute() + args.dist_path = args.dist_path.absolute() + if args.dry: + args.dist_path = args.dist_path / "dry" + + if args.github: + section_printer: SectionPrinter = GitHubSectionPrinter() + else: + section_printer = SectionPrinter() + + if args.github and "GITHUB_OUTPUT" not in os.environ: + os.environ["GITHUB_OUTPUT"] = "/tmp/github_output.txt" + + executer = Executer(root=args.root, dry=args.dry) + + root_git_hash_path = args.root / GIT_HASH_FILENAME + root_is_maybe_archive = root_git_hash_path.is_file() + if root_is_maybe_archive: + logger.warning("%s detected: Building from archive", GIT_HASH_FILENAME) + archive_commit = root_git_hash_path.read_text().strip() + if args.commit != archive_commit: + logger.warning("Commit argument is %s, but archive commit is %s. Using %s.", args.commit, archive_commit, archive_commit) + args.commit = archive_commit + revision = (args.root / REVISION_TXT).read_text().strip() + else: + args.commit = executer.check_output(["git", "rev-parse", args.commit], dry_out="e5812a9fd2cda317b503325a702ba3c1c37861d9").strip() + revision = executer.check_output(["git", "describe", "--always", "--tags", "--long", args.commit], dry_out="preview-3.1.3-96-g9512f2144").strip() + logger.info("Using commit %s", args.commit) + + try: + with args.path_release_info.open() as f: + release_info = json.load(f) + except FileNotFoundError: + logger.error(f"Could not find {args.path_release_info}") + + releaser = Releaser( + release_info=release_info, + commit=args.commit, + revision=revision, + root=args.root, + dist_path=args.dist_path, + executer=executer, + section_printer=section_printer, + cmake_generator=args.cmake_generator, + deps_path=args.deps_path, + overwrite=args.overwrite, + github=args.github, + fast=args.fast, + ) + + if root_is_maybe_archive: + logger.warning("Building from archive. Skipping clean git tree check.") + else: + porcelain_status = executer.check_output(["git", "status", "--ignored", "--porcelain"], dry_out="\n").strip() + if porcelain_status: + print(porcelain_status) + logger.warning("The tree is dirty! Do not publish any generated artifacts!") + if not args.force: + raise Exception("The git repo contains modified and/or non-committed files. Run with --force to ignore.") + + if args.fast: + logger.warning("Doing fast build! Do not publish generated artifacts!") + + with section_printer.group("Arguments"): + print(f"project = {releaser.project}") + print(f"version = {releaser.version}") + print(f"revision = {revision}") + print(f"commit = {args.commit}") + print(f"out = {args.dist_path}") + print(f"actions = {args.actions}") + print(f"dry = {args.dry}") + print(f"force = {args.force}") + print(f"overwrite = {args.overwrite}") + print(f"cmake_generator = {args.cmake_generator}") + + releaser.prepare() + + if "download" in args.actions: + releaser.download_dependencies() + + if set(args.actions).intersection({"msvc", "mingw", "android"}): + print("Verifying presence of dependencies (run 'download' action to download) ...") + releaser.verify_dependencies() + print("... done") + + if "source" in args.actions: + if root_is_maybe_archive: + raise Exception("Cannot build source archive from source archive") + with section_printer.group("Create source archives"): + releaser.create_source_archives() + + if "dmg" in args.actions: + if platform.system() != "Darwin" and not args.dry: + parser.error("framework artifact(s) can only be built on Darwin") + + releaser.create_dmg() + + if "msvc" in args.actions: + if platform.system() != "Windows" and not args.dry: + parser.error("msvc artifact(s) can only be built on Windows") + releaser.build_msvc() + + if "mingw" in args.actions: + releaser.create_mingw_archives() + + if "android" in args.actions: + if args.android_home is None or not Path(args.android_home).is_dir(): + parser.error("Invalid $ANDROID_HOME or --android-home: must be a directory containing the Android SDK") + if args.android_ndk_home is None or not Path(args.android_ndk_home).is_dir(): + parser.error("Invalid $ANDROID_NDK_HOME or --android-ndk-home: must be a directory containing the Android NDK") + if args.android_api is None: + with section_printer.group("Detect Android APIS"): + args.android_api = releaser._detect_android_api(android_home=args.android_home) + else: + try: + android_api_ints = tuple(int(v) for v in args.android_api.split(".")) + match len(android_api_ints): + case 1: android_api_name = f"android-{android_api_ints[0]}" + case 2: android_api_name = f"android-{android_api_ints[0]}-ext-{android_api_ints[1]}" + case _: raise ValueError + except ValueError: + logger.error("Invalid --android-api, must be a 'X' or 'X.Y' version") + args.android_api = AndroidApiVersion(ints=android_api_ints, name=android_api_name) + if args.android_api is None: + parser.error("Invalid --android-api, and/or could not be detected") + android_api_path = Path(args.android_home) / f"platforms/{args.android_api.name}" + if not android_api_path.is_dir(): + logger.warning(f"Android API directory does not exist ({android_api_path})") + with section_printer.group("Android arguments"): + print(f"android_home = {args.android_home}") + print(f"android_ndk_home = {args.android_ndk_home}") + print(f"android_api = {args.android_api}") + releaser.create_android_archives( + android_api=args.android_api.ints[0], + android_home=args.android_home, + android_ndk_home=args.android_ndk_home, + ) + with section_printer.group("Summary"): + print(f"artifacts = {releaser.artifacts}") + + if args.github: + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"project={releaser.project}\n") + f.write(f"version={releaser.version}\n") + for k, v in releaser.artifacts.items(): + f.write(f"{k}={v.name}\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/build-scripts/create-release.py b/build-scripts/create-release.py new file mode 100755 index 000000000..14916fa8b --- /dev/null +++ b/build-scripts/create-release.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import argparse +from pathlib import Path +import json +import logging +import re +import subprocess + +ROOT = Path(__file__).resolve().parents[1] + + +def determine_remote() -> str: + text = (ROOT / "build-scripts/release-info.json").read_text() + release_info = json.loads(text) + if "remote" in release_info: + return release_info["remote"] + project_with_version = release_info["name"] + project, _ = re.subn("([^a-zA-Z_])", "", project_with_version) + return f"libsdl-org/{project}" + + +def main(): + default_remote = determine_remote() + + parser = argparse.ArgumentParser(allow_abbrev=False) + parser.add_argument("--ref", required=True, help=f"Name of branch or tag containing release.yml") + parser.add_argument("--remote", "-R", default=default_remote, help=f"Remote repo (default={default_remote})") + parser.add_argument("--commit", help=f"Input 'commit' of release.yml (default is the hash of the ref)") + args = parser.parse_args() + + if args.commit is None: + args.commit = subprocess.check_output(["git", "rev-parse", args.ref], cwd=ROOT, text=True).strip() + + + print(f"Running release.yml workflow:") + print(f" remote = {args.remote}") + print(f" ref = {args.ref}") + print(f" commit = {args.commit}") + + subprocess.check_call(["gh", "-R", args.remote, "workflow", "run", "release.yml", "--ref", args.ref, "-f", f"commit={args.commit}"], cwd=ROOT) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/build-scripts/pkg-support/msvc/INSTALL.md b/build-scripts/pkg-support/msvc/INSTALL.md new file mode 100644 index 000000000..1d9d32c31 --- /dev/null +++ b/build-scripts/pkg-support/msvc/INSTALL.md @@ -0,0 +1,11 @@ + +# Using this package + +This package contains sdl12-compat built for Visual Studio. + +To use this package, edit your project properties: +- Add the include directory to "VC++ Directories" -> "Include Directories" +- Add the lib/_arch_ directory to "VC++ Directories" -> "Library Directories" +- Add SDL.lib and SDLmain.lib to Linker -> Input -> "Additional Dependencies" +- Copy lib/_arch_/SDL.dll, lib/_arch_/SDL2.dll, and lib/_arch_/SDL3.dll to your project directory. + diff --git a/build-scripts/pkg-support/msvc/README.md b/build-scripts/pkg-support/msvc/README.md new file mode 100644 index 000000000..2a7a2b147 --- /dev/null +++ b/build-scripts/pkg-support/msvc/README.md @@ -0,0 +1,21 @@ + +Simple DirectMedia Layer (SDL for short) is a cross-platform library +designed to make it easy to write multi-media software, such as games +and emulators. + +You can find the latest release and additional information at: +https://www.libsdl.org/ + +This code is a compatibility layer; it provides a binary and source +compatible API for programs written against SDL 1.2, but it uses SDL 2.0 +behind the scenes. If you are writing new code, please target SDL 3.0 +directly and do not use this layer. + +Installation instructions are available in [INSTALL.md](INSTALL.md). + +This library is distributed under the terms of the zlib license, +available in [LICENSE.txt](LICENSE.txt). + +Enjoy! + +Sam Lantinga (slouken@libsdl.org) diff --git a/build-scripts/pkg-support/msvc/x64/INSTALL.md.in b/build-scripts/pkg-support/msvc/x64/INSTALL.md.in new file mode 100644 index 000000000..48e1f5cc2 --- /dev/null +++ b/build-scripts/pkg-support/msvc/x64/INSTALL.md.in @@ -0,0 +1,7 @@ + +# Using this package + +This package contains @<@PROJECT_NAME@>@ built for x64 Windows. + +To use this package, simply replace an existing 64-bit SDL.dll with the ones included here. + diff --git a/build-scripts/pkg-support/msvc/x86/INSTALL.md.in b/build-scripts/pkg-support/msvc/x86/INSTALL.md.in new file mode 100644 index 000000000..89f00fd76 --- /dev/null +++ b/build-scripts/pkg-support/msvc/x86/INSTALL.md.in @@ -0,0 +1,7 @@ + +# Using this package + +This package contains @<@PROJECT_NAME@>@ built for x86 Windows. + +To use this package, simply replace an existing 32-bit SDL.dll with the ones included here. + diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json new file mode 100644 index 000000000..ef6b9c60a --- /dev/null +++ b/build-scripts/release-info.json @@ -0,0 +1,89 @@ +{ + "name": "sdl12-compat", + "remote": "libsdl-org/sdl12-compat", + "dependencies": { + "sdl2-compat": { + "startswith": "2.", + "repo": "libsdl-org/sdl2-compat" + } + }, + "version": { + "file": "include/SDL/SDL_version.h", + "re_major": "^#define SDL_MAJOR_VERSION\\s+([0-9]+)$", + "re_minor": "^#define SDL_MINOR_VERSION\\s+([0-9]+)$", + "re_micro": "^#define SDL_PATCHLEVEL\\s+([0-9]+)$" + }, + "source": { + "checks": [ + "src/SDL12_compat.c", + "include/SDL/SDL.h", + "test/testsprite.c" + ] + }, + "msvc": { + "cmake": { + "archs": [ + "x86", + "x64" + ], + "args": [ + "-DSDL12COMPAT_VENDOR_INFO=libsdl.org", + "-DSDL12COMPAT_INSTALL_SDL2=ON", + "-DSDL12COMPAT_INSTALL_SDL3=ON", + "-DSDL12TESTS=OFF", + "-DSDL12DEVEL=ON", + "-DSTATICDEVEL=OFF" + ], + "files-lib": { + "": [ + "bin/SDL.dll" + ] + }, + "files-devel": { + "lib/@<@ARCH@>@": [ + "bin/SDL.dll", + "lib/SDL.lib", + "lib/SDLmain.lib" + ] + } + }, + "files-lib": { + "": [ + "build-scripts/pkg-support/msvc/@<@ARCH@>@/INSTALL.md.in:INSTALL.md", + "LICENSE.txt", + "build-scripts/pkg-support/msvc/README.md", + "@<@ARCH@>@/SDL2.dll", + "@<@ARCH@>@/SDL3.dll" + ] + }, + "files-devel": { + "": [ + "build-scripts/pkg-support/msvc/INSTALL.md", + "LICENSE.txt", + "build-scripts/pkg-support/msvc/README.md" + ], + "include": [ + "include/SDL/*" + ], + "lib/@<@ARCH@>@": [ + "@<@ARCH@>@/SDL2.dll", + "@<@ARCH@>@/SDL3.dll" + ] + }, + "dependencies": { + "sdl2-compat": { + "artifact": "sdl2-compat-devel-2.??.??-VC.zip", + "copy": [ + { + "src": "lib/@<@ARCH@>@/SDL3.dll", + "dst": "@<@ARCH@>@" + }, + { + "src": "lib/@<@ARCH@>@/SDL2.dll", + "dst": "@<@ARCH@>@" + } + ] + } + } + } +} diff --git a/build-scripts/test-versioning.sh b/build-scripts/test-versioning.sh new file mode 100755 index 000000000..305c89a4d --- /dev/null +++ b/build-scripts/test-versioning.sh @@ -0,0 +1,101 @@ +#!/bin/sh +# Copyright 2022 Collabora Ltd. +# SPDX-License-Identifier: Zlib + +set -eu + +cd `dirname $0`/.. + +ref_major=$(sed -ne 's/^#define SDL_MAJOR_VERSION *//p' include/SDL/SDL_version.h) +ref_minor=$(sed -ne 's/^#define SDL_MINOR_VERSION *//p' include/SDL/SDL_version.h) +ref_micro=$(sed -ne 's/^#define SDL_PATCHLEVEL *//p' include/SDL/SDL_version.h) +ref_version="${ref_major}.${ref_minor}.${ref_micro}" + +tests=0 +failed=0 + +ok () { + tests=$(( tests + 1 )) + echo "ok - $*" +} + +not_ok () { + tests=$(( tests + 1 )) + echo "not ok - $*" + failed=1 +} + +version=$(sed -Ene 's/^project\(sdl[0-9]+_compat VERSION ([0-9.]*) LANGUAGES C\)$/\1/p' CMakeLists.txt) + +if [ "$ref_version" = "$version" ]; then + ok "CMakeLists.txt $version" +else + not_ok "CMakeLists.txt $version disagrees with SDL_version.h $ref_version" +fi + +tuple=$(sed -ne 's/^ *FILEVERSION *//p' src/version.rc | tr -d '\r') +ref_tuple="${ref_major},${ref_minor},${ref_micro},0" + +if [ "$ref_tuple" = "$tuple" ]; then + ok "version.rc FILEVERSION $tuple" +else + not_ok "version.rc FILEVERSION $tuple disagrees with SDL_version.h $ref_tuple" +fi + +tuple=$(sed -ne 's/^ *PRODUCTVERSION *//p' src/version.rc | tr -d '\r') + +if [ "$ref_tuple" = "$tuple" ]; then + ok "version.rc PRODUCTVERSION $tuple" +else + not_ok "version.rc PRODUCTVERSION $tuple disagrees with SDL_version.h $ref_tuple" +fi + +tuple=$(sed -Ene 's/^ *VALUE "FileVersion", "([0-9, ]*)\\0"\r?$/\1/p' src/version.rc | tr -d '\r') +ref_tuple="${ref_major}, ${ref_minor}, ${ref_micro}, 0" + +if [ "$ref_tuple" = "$tuple" ]; then + ok "version.rc FileVersion $tuple" +else + not_ok "version.rc FileVersion $tuple disagrees with SDL_version.h $ref_tuple" +fi + +tuple=$(sed -Ene 's/^ *VALUE "ProductVersion", "([0-9, ]*)\\0"\r?$/\1/p' src/version.rc | tr -d '\r') + +if [ "$ref_tuple" = "$tuple" ]; then + ok "version.rc ProductVersion $tuple" +else + not_ok "version.rc ProductVersion $tuple disagrees with SDL_version.h $ref_tuple" +fi + +micro=$(sed -Ene 's/^#define SDL12_COMPAT_VERSION ([0-9]+)$/\1/p' src/SDL12_compat.c) + +if [ "$ref_micro" = "$micro" ]; then + ok "SDL12_compat.c SDL12_COMPAT_VERSION $micro" +else + not_ok "SDL12_compat.c SDL12_COMPAT_VERSION $micro disagrees with SDL_version.h $ref_micro" +fi + +so_version="1.2.$ref_micro" +compat_version="1.0" +dylib_version="12.$ref_micro" + +ref_dylib_versions="$compat_version $dylib_version" +dylib_versions=$(sed -Ene 's/^LDFLAGS\+= -Wl,-compatibility_version,([0-9.]+) -Wl,-current_version,([0-9.]+)$/\1 \2/p' src/Makefile.darwin) + +if [ "$ref_dylib_versions" = "$dylib_versions" ]; then + ok "Makefile.darwin LDFLAGS $dylib_versions" +else + not_ok "Makefile.darwin LDFLAGS $dylib_versions disagrees with reference $ref_dylib_versions" +fi + +ref_so_version="$so_version" +so_version=$(sed -Ene 's/^SHLIB = libSDL-1.2.so.([0-9.]+)$/\1/p' src/Makefile.linux) + +if [ "$ref_so_version" = "$so_version" ]; then + ok "Makefile.linux SHLIB $so_version" +else + not_ok "Makefile.linux SHLIB $so_version disagrees with reference $ref_so_version" +fi + +echo "1..$tests" +exit "$failed" diff --git a/build-scripts/update-version.sh b/build-scripts/update-version.sh new file mode 100755 index 000000000..26936522a --- /dev/null +++ b/build-scripts/update-version.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +cd `dirname $0`/.. + +if [ -z $1 ]; then + echo "USAGE: $0 " 1>&2 + exit 1 +fi + +NEWVERSION="$1" +echo "Updating version to '1.2.$NEWVERSION' ..." + +perl -w -pi -e 's/(VERSION 1\.2\.)\d+/${1}'$NEWVERSION'/;' CMakeLists.txt +perl -w -pi -e 's/(DYLIB_CURRENT_VERSION 12\.)\d+/${1}'$NEWVERSION'/;' CMakeLists.txt +perl -w -pi -e 's/(\-current_version,12\.)\d+/${1}'$NEWVERSION'/;' src/Makefile.darwin +perl -w -pi -e 's/(libSDL\-1\.2\.so\.1\.2\.)\d+/${1}'$NEWVERSION'/;' src/Makefile.linux +perl -w -pi -e 's/(VERSION = 1\.2\.)\d+/${1}'$NEWVERSION'/;' src/Makefile.os2 +perl -w -pi -e 's/(VERSION = 1\.2\.)\d+/${1}'$NEWVERSION'/;' src/Makefile.w32 +perl -w -pi -e 's/(\#define SDL12_COMPAT_VERSION )\d+/${1}'$NEWVERSION'/;' src/SDL12_compat.c +perl -w -pi -e 's/(\#define SDL_PATCHLEVEL )\d+/${1}'$NEWVERSION'/;' include/SDL/SDL_version.h +perl -w -pi -e 's/(FILEVERSION 1,2,)\d+/${1}'$NEWVERSION'/;' src/version.rc +perl -w -pi -e 's/(PRODUCTVERSION 1,2,)\d+/${1}'$NEWVERSION'/;' src/version.rc +perl -w -pi -e 's/(VALUE "FileVersion", "1, 2, )\d+/${1}'$NEWVERSION'/;' src/version.rc +perl -w -pi -e 's/(VALUE "ProductVersion", "1, 2, )\d+/${1}'$NEWVERSION'/;' src/version.rc + +echo "All done." +echo "Run 'git diff' and make sure this looks correct before 'git commit'." + +exit 0 + diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 000000000..9a59b4f48 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,17 @@ +if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) +endforeach() diff --git a/configure b/configure deleted file mode 100755 index 7639dd876..000000000 --- a/configure +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -echo 'Please use CMake ( http://www.cmake.org/ ) to generate project files.' -exit 1 diff --git a/include/SDL/SDL.h b/include/SDL/SDL.h index f67e482ef..02da2ff44 100644 --- a/include/SDL/SDL.h +++ b/include/SDL/SDL.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_active.h b/include/SDL/SDL_active.h index d4d44f00a..b344827e6 100644 --- a/include/SDL/SDL_active.h +++ b/include/SDL/SDL_active.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_audio.h b/include/SDL/SDL_audio.h index 19bf4ae8f..05987bf5b 100644 --- a/include/SDL/SDL_audio.h +++ b/include/SDL/SDL_audio.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_byteorder.h b/include/SDL/SDL_byteorder.h index 59a8bf380..5b9ad06e0 100644 --- a/include/SDL/SDL_byteorder.h +++ b/include/SDL/SDL_byteorder.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_cdrom.h b/include/SDL/SDL_cdrom.h index 49205b2dc..62ad330b9 100644 --- a/include/SDL/SDL_cdrom.h +++ b/include/SDL/SDL_cdrom.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_config.h b/include/SDL/SDL_config.h index 1a71bc09a..9d7710e3b 100644 --- a/include/SDL/SDL_config.h +++ b/include/SDL/SDL_config.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -54,12 +54,13 @@ typedef unsigned __int64 uint64_t; #include #define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 + /* for now, let's try and say everything that we care about that isn't Windows has these C runtime functions available. We're trying to avoid a configure stage, though. Send patches if your platform lacks something. */ #ifndef _WIN32 #define HAVE_LIBC 1 -#define HAVE_ALLOCA_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_STDIO_H 1 #define STDC_HEADERS 1 @@ -72,7 +73,11 @@ stage, though. Send patches if your platform lacks something. */ #define HAVE_MATH_H 1 #endif -#if defined(unix) || defined(__APPLE__) +#if defined(__linux__) || defined(__sun) +#define HAVE_ALLOCA_H 1 +#endif + +#if defined(__unix__) || defined(__APPLE__) #define HAVE_ICONV_H 1 #define HAVE_SIGNAL_H 1 #endif @@ -124,13 +129,16 @@ stage, though. Send patches if your platform lacks something. */ #endif #endif +#if defined(__GLIBC__) +/* glibc certainly includes this, send patches if your OS does too */ +#define HAVE_MALLOC_H 1 +#endif + /* things that aren't necessarily in Linux, some are MSVC C runtime, some are BSD. Send patches. */ #if 0 -#define HAVE_MALLOC_H 1 #define HAVE_BCOPY 1 #define HAVE_ATOI 1 #define HAVE_ATOF 1 -#define HAVE_STRLCPY 1 #define HAVE_STRLCAT 1 #define HAVE__STRREV 1 #define HAVE__STRUPR 1 @@ -139,7 +147,6 @@ stage, though. Send patches if your platform lacks something. */ #define HAVE_RINDEX 1 #define HAVE_ITOA 1 #define HAVE__LTOA 1 -#define HAVE__UITOA 1 #define HAVE__ULTOA 1 #define HAVE__I64TOA 1 #define HAVE__UI64TOA 1 @@ -156,13 +163,24 @@ stage, though. Send patches if your platform lacks something. */ #define HAVE_SEM_TIMEDWAIT 1 #endif -#if defined(unix) || defined(__APPLE__) +#if defined(__unix__) || defined(__APPLE__) #define HAVE_ICONV 1 #define HAVE_SIGACTION 1 #define HAVE_SA_SIGACTION 1 #define HAVE_SETJMP 1 #endif -/* Don't define any of the SDL backend, under the assumption checking for these against the headers won't work anyhow. */ +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) /* macos and BSDs have this. */ +#define HAVE_STRLCPY 1 +#endif + +/* Don't define most of the SDL backends, under the assumption checking for these against the headers won't work anyhow. + The exception is the X11 backend; you need its define to know if you can use its syswm interface. */ + +# if defined(__unix__) && !defined(__APPLE__) && defined(__has_include) +# if __has_include() +# define SDL_VIDEO_DRIVER_X11 1 +# endif +# endif #endif /* _SDL_config_h */ diff --git a/include/SDL/SDL_copying.h b/include/SDL/SDL_copying.h index 09d52f34a..4695197db 100644 --- a/include/SDL/SDL_copying.h +++ b/include/SDL/SDL_copying.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_cpuinfo.h b/include/SDL/SDL_cpuinfo.h index 2a570f095..8e3fd4387 100644 --- a/include/SDL/SDL_cpuinfo.h +++ b/include/SDL/SDL_cpuinfo.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_endian.h b/include/SDL/SDL_endian.h index 9072aaa2b..bfd0b8cf8 100644 --- a/include/SDL/SDL_endian.h +++ b/include/SDL/SDL_endian.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -28,27 +28,49 @@ real SDL-1.2 available to you. */ #include "SDL_stdinc.h" -/* This is all lifted out of SDL2's zlib-licensed headers. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#include +#endif + +/* These are all lifted out of SDL2's zlib-licensed headers. */ -#define SDL_LIL_ENDIAN 1234 -#define SDL_BIG_ENDIAN 4321 +#define SDL_LIL_ENDIAN 1234 +#define SDL_BIG_ENDIAN 4321 #ifndef SDL_BYTEORDER /* Not defined in SDL_config.h? */ #ifdef __linux__ #include #define SDL_BYTEORDER __BYTE_ORDER -#elif defined(__OpenBSD__) +#elif defined(__sun) && defined(__SVR4) /* Solaris */ +#include +#if defined(_LITTLE_ENDIAN) +#define SDL_BYTEORDER SDL_LIL_ENDIAN +#elif defined(_BIG_ENDIAN) +#define SDL_BYTEORDER SDL_BIG_ENDIAN +#else +#error Unsupported endianness +#endif +#elif defined(__OpenBSD__) || defined(__DragonFly__) #include #define SDL_BYTEORDER BYTE_ORDER -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__NetBSD__) #include #define SDL_BYTEORDER BYTE_ORDER +/* predefs from newer gcc and clang versions: */ +#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__) +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define SDL_BYTEORDER SDL_LIL_ENDIAN +#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define SDL_BYTEORDER SDL_BIG_ENDIAN +#else +#error Unsupported endianness +#endif /**/ #else #if defined(__hppa__) || \ defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ (defined(__MIPS__) && defined(__MIPSEB__)) || \ - defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ - defined(__sparc__) + defined(__ppc__) || defined(__POWERPC__) || defined(__powerpc__) || defined(__PPC__) || \ + defined(__sparc__) || defined(__sparc) #define SDL_BYTEORDER SDL_BIG_ENDIAN #else #define SDL_BYTEORDER SDL_LIL_ENDIAN @@ -56,27 +78,53 @@ real SDL-1.2 available to you. */ #endif /* __linux__ */ #endif /* !SDL_BYTEORDER */ + #include "begin_code.h" -#if (defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 2))) || \ - (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) +/* various modern compilers may have builtin swap */ +#if defined (__has_builtin) +#define _SDL_HAS_BUILTIN(x) __has_builtin(x) +#else +#define _SDL_HAS_BUILTIN(x) 0 +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define HAS_BUILTIN_BSWAP16 (_SDL_HAS_BUILTIN(__builtin_bswap16)) || \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +# define HAS_BUILTIN_BSWAP32 (_SDL_HAS_BUILTIN(__builtin_bswap32)) || \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define HAS_BUILTIN_BSWAP64 (_SDL_HAS_BUILTIN(__builtin_bswap64)) || \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + + /* this one is broken */ +# define HAS_BROKEN_BSWAP (__GNUC__ == 2 && __GNUC_MINOR__ <= 95) +#else +# define HAS_BUILTIN_BSWAP16 0 +# define HAS_BUILTIN_BSWAP32 0 +# define HAS_BUILTIN_BSWAP64 0 +# define HAS_BROKEN_BSWAP 0 +#endif + +#if HAS_BUILTIN_BSWAP16 #define SDL_Swap16(x) __builtin_bswap16(x) -#elif defined(__GNUC__) && defined(__i386__) && \ - !(__GNUC__ == 2 && __GNUC_MINOR__ <= 95 /* broken gcc version */) +#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) +#pragma intrinsic(_byteswap_ushort) +#define SDL_Swap16(x) _byteswap_ushort(x) +#elif defined(__i386__) && !HAS_BROKEN_BSWAP static __inline__ Uint16 SDL_Swap16(Uint16 x) { __asm__("xchgb %b0,%h0": "=q"(x):"0"(x)); return x; } -#elif defined(__GNUC__) && defined(__x86_64__) +#elif defined(__x86_64__) static __inline__ Uint16 SDL_Swap16(Uint16 x) { __asm__("xchgb %b0,%h0": "=Q"(x):"0"(x)); return x; } -#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +#elif (defined(__powerpc__) || defined(__ppc__)) static __inline__ Uint16 SDL_Swap16(Uint16 x) { @@ -85,25 +133,15 @@ SDL_Swap16(Uint16 x) __asm__("rlwimi %0,%2,8,16,23": "=&r"(result):"0"(x >> 8), "r"(x)); return (Uint16)result; } -#elif defined(__GNUC__) && defined(__aarch64__) -static __inline__ Uint16 -SDL_Swap16(Uint16 x) -{ - __asm__("rev16 %w1, %w0" : "=r"(x) : "r"(x)); - return x; -} -#elif defined(__GNUC__) && (defined(__m68k__) && !defined(__mcoldfire__)) +#elif (defined(__m68k__) && !defined(__mcoldfire__)) static __inline__ Uint16 SDL_Swap16(Uint16 x) { __asm__("rorw #8,%0": "=d"(x): "0"(x):"cc"); return x; } -#elif defined(_MSC_VER) -#pragma intrinsic(_byteswap_ushort) -#define SDL_Swap16(x) _byteswap_ushort(x) #elif defined(__WATCOMC__) && defined(__386__) -extern _inline Uint16 SDL_Swap16(Uint16); +extern __inline Uint16 SDL_Swap16(Uint16); #pragma aux SDL_Swap16 = \ "xchg al, ah" \ parm [ax] \ @@ -116,25 +154,26 @@ SDL_Swap16(Uint16 x) } #endif -#if (defined(__clang__) && (__clang_major__ > 2 || (__clang_major__ == 2 && __clang_minor__ >= 6))) || \ - (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) +#if HAS_BUILTIN_BSWAP32 #define SDL_Swap32(x) __builtin_bswap32(x) -#elif defined(__GNUC__) && defined(__i386__) && \ - !(__GNUC__ == 2 && __GNUC_MINOR__ <= 95 /* broken gcc version */) +#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) +#pragma intrinsic(_byteswap_ulong) +#define SDL_Swap32(x) _byteswap_ulong(x) +#elif defined(__i386__) && !HAS_BROKEN_BSWAP static __inline__ Uint32 SDL_Swap32(Uint32 x) { __asm__("bswap %0": "=r"(x):"0"(x)); return x; } -#elif defined(__GNUC__) && defined(__x86_64__) +#elif defined(__x86_64__) static __inline__ Uint32 SDL_Swap32(Uint32 x) { __asm__("bswapl %0": "=r"(x):"0"(x)); return x; } -#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) +#elif (defined(__powerpc__) || defined(__ppc__)) static __inline__ Uint32 SDL_Swap32(Uint32 x) { @@ -145,14 +184,7 @@ SDL_Swap32(Uint32 x) __asm__("rlwimi %0,%2,24,0,7" : "=&r"(result): "0" (result), "r"(x)); return result; } -#elif defined(__GNUC__) && defined(__aarch64__) -static __inline__ Uint32 -SDL_Swap32(Uint32 x) -{ - __asm__("rev %w1, %w0": "=r"(x):"r"(x)); - return x; -} -#elif defined(__GNUC__) && (defined(__m68k__) && !defined(__mcoldfire__)) +#elif (defined(__m68k__) && !defined(__mcoldfire__)) static __inline__ Uint32 SDL_Swap32(Uint32 x) { @@ -160,14 +192,11 @@ SDL_Swap32(Uint32 x) return x; } #elif defined(__WATCOMC__) && defined(__386__) -extern _inline Uint32 SDL_Swap32(Uint32); +extern __inline Uint32 SDL_Swap32(Uint32); #pragma aux SDL_Swap32 = \ "bswap eax" \ parm [eax] \ modify [eax]; -#elif defined(_MSC_VER) -#pragma intrinsic(_byteswap_ulong) -#define SDL_Swap32(x) _byteswap_ulong(x) #else static __inline__ Uint32 SDL_Swap32(Uint32 x) @@ -177,11 +206,12 @@ SDL_Swap32(Uint32 x) } #endif -#if (defined(__clang__) && (__clang_major__ > 2 || (__clang_major__ == 2 && __clang_minor__ >= 6))) || \ - (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) +#if HAS_BUILTIN_BSWAP64 #define SDL_Swap64(x) __builtin_bswap64(x) -#elif defined(__GNUC__) && defined(__i386__) && \ - !(__GNUC__ == 2 && __GNUC_MINOR__ <= 95 /* broken gcc version */) +#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL) +#pragma intrinsic(_byteswap_uint64) +#define SDL_Swap64(x) _byteswap_uint64(x) +#elif defined(__i386__) && !HAS_BROKEN_BSWAP static __inline__ Uint64 SDL_Swap64(Uint64 x) { @@ -197,7 +227,7 @@ SDL_Swap64(Uint64 x) : "0" (v.s.a), "1"(v.s.b)); return v.u; } -#elif defined(__GNUC__) && defined(__x86_64__) +#elif defined(__x86_64__) static __inline__ Uint64 SDL_Swap64(Uint64 x) { @@ -205,16 +235,13 @@ SDL_Swap64(Uint64 x) return x; } #elif defined(__WATCOMC__) && defined(__386__) -extern _inline Uint64 SDL_Swap64(Uint64); +extern __inline Uint64 SDL_Swap64(Uint64); #pragma aux SDL_Swap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ modify [eax edx]; -#elif defined(_MSC_VER) -#pragma intrinsic(_byteswap_uint64) -#define SDL_Swap64(x) _byteswap_uint64(x) #else static __inline__ Uint64 SDL_Swap64(Uint64 x) @@ -232,6 +259,14 @@ SDL_Swap64(Uint64 x) } #endif + +/* remove extra macros */ +#undef HAS_BROKEN_BSWAP +#undef HAS_BUILTIN_BSWAP16 +#undef HAS_BUILTIN_BSWAP32 +#undef HAS_BUILTIN_BSWAP64 +#undef _SDL_HAS_BUILTIN + /** * \name Swap to native * Byteswap item from the specified endianness to the native endianness. @@ -241,24 +276,19 @@ SDL_Swap64(Uint64 x) #define SDL_SwapLE16(X) (X) #define SDL_SwapLE32(X) (X) #define SDL_SwapLE64(X) (X) -#define SDL_SwapFloatLE(X) (X) #define SDL_SwapBE16(X) SDL_Swap16(X) #define SDL_SwapBE32(X) SDL_Swap32(X) #define SDL_SwapBE64(X) SDL_Swap64(X) -#define SDL_SwapFloatBE(X) SDL_SwapFloat(X) #else #define SDL_SwapLE16(X) SDL_Swap16(X) #define SDL_SwapLE32(X) SDL_Swap32(X) #define SDL_SwapLE64(X) SDL_Swap64(X) -#define SDL_SwapFloatLE(X) SDL_SwapFloat(X) #define SDL_SwapBE16(X) (X) #define SDL_SwapBE32(X) (X) #define SDL_SwapBE64(X) (X) -#define SDL_SwapFloatBE(X) (X) #endif /* @} *//* Swap to native */ #include "close_code.h" -#endif - +#endif /* _SDL_endian_h */ diff --git a/include/SDL/SDL_error.h b/include/SDL/SDL_error.h index 66fad21f0..2f0b5de02 100644 --- a/include/SDL/SDL_error.h +++ b/include/SDL/SDL_error.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_events.h b/include/SDL/SDL_events.h index f48eb8386..18e4c770a 100644 --- a/include/SDL/SDL_events.h +++ b/include/SDL/SDL_events.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -185,11 +185,12 @@ typedef struct SDL_UserEvent void *data2; } SDL_UserEvent; +struct SDL_SysWMmsg; typedef struct SDL_SysWMmsg SDL_SysWMmsg; typedef struct SDL_SysWMEvent { Uint8 type; - SDL_SysWMmsg *msg; + struct SDL_SysWMmsg *msg; } SDL_SysWMEvent; typedef union SDL_Event diff --git a/include/SDL/SDL_getenv.h b/include/SDL/SDL_getenv.h index 9ffdc3e80..885ff883c 100644 --- a/include/SDL/SDL_getenv.h +++ b/include/SDL/SDL_getenv.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_joystick.h b/include/SDL/SDL_joystick.h index 9ce8fd45b..6c63f843a 100644 --- a/include/SDL/SDL_joystick.h +++ b/include/SDL/SDL_joystick.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_keyboard.h b/include/SDL/SDL_keyboard.h index 0ea4faa6a..1e091da34 100644 --- a/include/SDL/SDL_keyboard.h +++ b/include/SDL/SDL_keyboard.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_keysym.h b/include/SDL/SDL_keysym.h index 39c805f6f..cfc98867b 100644 --- a/include/SDL/SDL_keysym.h +++ b/include/SDL/SDL_keysym.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_loadso.h b/include/SDL/SDL_loadso.h index 2d40b0c87..22f05718e 100644 --- a/include/SDL/SDL_loadso.h +++ b/include/SDL/SDL_loadso.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_main.h b/include/SDL/SDL_main.h index 0037f55af..7513b2844 100644 --- a/include/SDL/SDL_main.h +++ b/include/SDL/SDL_main.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -31,6 +31,7 @@ real SDL-1.2 available to you. */ #include "begin_code.h" #if defined(__WIN32__) || defined(__MACOSX__) +#define main SDL_main extern int SDL_main(int argc, char *argv[]); #endif diff --git a/include/SDL/SDL_mouse.h b/include/SDL/SDL_mouse.h index 66f1552fd..8006b7b00 100644 --- a/include/SDL/SDL_mouse.h +++ b/include/SDL/SDL_mouse.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_mutex.h b/include/SDL/SDL_mutex.h index f60609c49..8d09eefd1 100644 --- a/include/SDL/SDL_mutex.h +++ b/include/SDL/SDL_mutex.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_name.h b/include/SDL/SDL_name.h index 1a63caff5..52c8a5a86 100644 --- a/include/SDL/SDL_name.h +++ b/include/SDL/SDL_name.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_opengl.h b/include/SDL/SDL_opengl.h index 8879daeed..81dd20826 100644 --- a/include/SDL/SDL_opengl.h +++ b/include/SDL/SDL_opengl.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -23,11 +23,10 @@ functionality to let you build an SDL-1.2-based project without having the real SDL-1.2 available to you. */ -/* This file is a verbatim copy of SDL 1.2's, as most of it is covered by -SGI Free Software License B, Version 1.1 and not LGPL, and the rest was -written by Sam Lantinga. The only exceptions are patches for doxygen comments -and a MacOS Classic #include fix; both were from external contributors and -both were removed here. */ +/* This file is a verbatim copy of SDL 1.2's, except for the + MacOS Classic include fixes being removed. */ + +/* This is a simple file to encapsulate the OpenGL API headers */ #include "SDL_config.h" @@ -36,28 +35,32 @@ both were removed here. */ #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX -#define NOMINMAX /* Don't defined min() and max() */ +#define NOMINMAX /* Don't define min() and max() */ #endif #include #endif #ifndef NO_SDL_GLEXT -#define __glext_h_ /* Don't let gl.h include glext.h */ -#define __gl_glext_h_ /* Don't let gl.h include glext.h */ +#define __glext_h_ /* Don't let gl.h include glext.h */ +#define __gl_glext_h_ /* Don't let gl.h include glext.h */ #endif #if defined(__MACOSX__) -#include /* Header File For The OpenGL Library */ -#include /* Header File For The GLU Library */ +#include /* Header File For The OpenGL Library */ +#ifndef NO_SDL_GLU +#include /* Header File For The GLU Library */ +#endif #else -#include /* Header File For The OpenGL Library */ -#include /* Header File For The GLU Library */ +#include /* Header File For The OpenGL Library */ +#ifndef NO_SDL_GLU +#include /* Header File For The GLU Library */ +#endif #endif #ifndef NO_SDL_GLEXT #undef __glext_h_ #undef __gl_glext_h_ #endif -/* This file taken from "GLext.h" from the Jeff Molofee OpenGL tutorials. - * It is included here because glext.h is not available on some systems. +/** glext.h + * This is included here because glext.h is not available on some systems. * If you don't want this version included, simply define "NO_SDL_GLEXT" */ #ifndef NO_SDL_GLEXT @@ -70,32 +73,26 @@ extern "C" { #endif /* -** License Applicability. Except to the extent portions of this file are -** made subject to an alternative license as permitted in the SGI Free -** Software License B, Version 1.1 (the "License"), the contents of this -** file are subject only to the provisions of the License. You may not use -** this file except in compliance with the License. You may obtain a copy -** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 -** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: -** -** http://oss.sgi.com/projects/FreeB +** Copyright (c) 2007 The Khronos Group Inc. ** -** Note that, as provided in the License, the Software is distributed on an -** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS -** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND -** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A -** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: ** -** Original Code. The Original Code is: OpenGL Sample Implementation, -** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, -** Inc. The Original Code is Copyright (c) 1991-2004 Silicon Graphics, Inc. -** Copyright in any portions created by third parties is as indicated -** elsewhere herein. All Rights Reserved. +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. ** -** Additional Notice Provisions: This software was created using the -** OpenGL(R) version 1.2.1 Sample Implementation published by SGI, but has -** not been independently verified as being compliant with the OpenGL(R) -** version 1.2.1 Specification. +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) @@ -116,9 +113,9 @@ extern "C" { /*************************************************************/ /* Header file version number, required by OpenGL ABI for Linux */ -/* glext.h last updated 2005/06/20 */ -/* Current version at http://oss.sgi.com/projects/ogl-sample/registry/ */ -#define GL_GLEXT_VERSION 29 +/* glext.h last updated 2008/03/24 */ +/* Current version at http://www.opengl.org/registry/ */ +#define GL_GLEXT_VERSION 40 #ifndef GL_VERSION_1_2 #define GL_UNSIGNED_BYTE_3_3_2 0x8032 @@ -523,6 +520,32 @@ extern "C" { #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 #endif +#ifndef GL_VERSION_2_1 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +#endif + #ifndef GL_ARB_multitexture #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 @@ -3057,7 +3080,6 @@ extern "C" { #define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 -#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB @@ -3102,6 +3124,331 @@ extern "C" { #ifndef GL_GREMEDY_string_marker #endif +#ifndef GL_EXT_packed_depth_stencil +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif + +#ifndef GL_EXT_stencil_clear_tag +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +#endif + +#ifndef GL_EXT_texture_sRGB +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif + +#ifndef GL_EXT_framebuffer_blit +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_EXT +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#endif + +#ifndef GL_EXT_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#endif + +#ifndef GL_MESAX_texture_stack +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif + +#ifndef GL_EXT_timer_query +#define GL_TIME_ELAPSED_EXT 0x88BF +#endif + +#ifndef GL_EXT_gpu_program_parameters +#endif + +#ifndef GL_APPLE_flush_buffer_range +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +#endif + +#ifndef GL_NV_gpu_program4 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +#endif + +#ifndef GL_NV_geometry_program4 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +#endif + +#ifndef GL_EXT_geometry_shader4 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +/* reuse GL_GEOMETRY_VERTICES_OUT_EXT */ +/* reuse GL_GEOMETRY_INPUT_TYPE_EXT */ +/* reuse GL_GEOMETRY_OUTPUT_TYPE_EXT */ +/* reuse GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT */ +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +/* reuse GL_LINES_ADJACENCY_EXT */ +/* reuse GL_LINE_STRIP_ADJACENCY_EXT */ +/* reuse GL_TRIANGLES_ADJACENCY_EXT */ +/* reuse GL_TRIANGLE_STRIP_ADJACENCY_EXT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT */ +/* reuse GL_PROGRAM_POINT_SIZE_EXT */ +#endif + +#ifndef GL_NV_vertex_program4 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +#endif + +#ifndef GL_EXT_gpu_shader4 +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#endif + +#ifndef GL_EXT_draw_instanced +#endif + +#ifndef GL_EXT_packed_float +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif + +#ifndef GL_EXT_texture_array +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT */ +#endif + +#ifndef GL_EXT_texture_buffer_object +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif + +#ifndef GL_EXT_texture_shared_exponent +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif + +#ifndef GL_NV_depth_buffer_float +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +#endif + +#ifndef GL_NV_fragment_program4 +#endif + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +#endif + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif + +#ifndef GL_NV_geometry_shader4 +#endif + +#ifndef GL_NV_parameter_buffer_object +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +#endif + +#ifndef GL_EXT_draw_buffers2 +#endif + +#ifndef GL_NV_transform_feedback +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_ATTRIBS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#endif + +#ifndef GL_EXT_bindable_uniform +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +#endif + +#ifndef GL_EXT_texture_integer +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +#endif + +#ifndef GL_GREMEDY_frame_terminator +#endif + /*************************************************************/ @@ -3152,6 +3499,50 @@ typedef unsigned short GLhalfARB; typedef unsigned short GLhalfNV; #endif +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && (defined(__GNUC__)||defined(__WATCOMC__)) +#include +#elif defined(_WIN32) +#if 0 /* handled by SDL_config_windows.h */ +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif /* */ +#else +#include /* Fallback option */ +#endif +#endif + +#ifndef GL_EXT_timer_query +typedef int64_t GLint64EXT; +typedef uint64_t GLuint64EXT; +#endif + #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 #ifdef GL_GLEXT_PROTOTYPES @@ -3664,6 +4055,24 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); #endif +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint, GLsizei, GLboolean, const GLfloat *); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint, GLsizei, GLboolean, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif + #ifndef GL_ARB_multitexture #define GL_ARB_multitexture 1 #ifdef GL_GLEXT_PROTOTYPES @@ -4371,8 +4780,8 @@ typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum f typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); #endif -#ifndef GL_EXT_color_matrix -#define GL_EXT_color_matrix 1 +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 #endif #ifndef GL_SGI_color_table @@ -6565,6 +6974,378 @@ GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei, const GLvoid *); typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid *string); #endif +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#endif + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei, GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#endif + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum, GLsizei, GLenum, GLsizei, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#endif + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint, GLenum, GLint64EXT *); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint, GLenum, GLuint64EXT *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64EXT *params); +#endif + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum, GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum, GLuint, GLsizei, const GLfloat *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum, GLenum, GLint); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum, GLintptr, GLsizeiptr); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#endif + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum, GLuint, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum, GLuint, const GLint *); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum, GLuint, GLsizei, const GLint *); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum, GLuint, const GLuint *); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum, GLuint, GLsizei, const GLuint *); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum, GLuint, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum, GLuint, const GLint *); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum, GLuint, GLsizei, const GLint *); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum, GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum, GLuint, const GLuint *); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum, GLuint, GLsizei, const GLuint *); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum, GLuint, GLint *); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum, GLuint, GLuint *); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum, GLuint, GLint *); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum, GLuint, GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#endif + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum, GLint); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum, GLenum, GLuint, GLint); +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum, GLenum, GLuint, GLint, GLint); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum, GLenum, GLuint, GLint, GLenum); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint, GLenum, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#endif + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint, GLint); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint, GLint, GLint); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint, GLint, GLint, GLint); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint, GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint, GLuint); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint, GLuint, GLuint); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint, const GLint *); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint, const GLuint *); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint, const GLbyte *); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint, const GLshort *); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint, const GLubyte *); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint, const GLushort *); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint, GLint, GLenum, GLsizei, const GLvoid *); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint, GLenum, GLint *); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint, GLenum, GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#endif + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint, GLint, GLuint *); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint, GLuint, const GLchar *); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint, const GLchar *); +GLAPI void APIENTRY glUniform1uiEXT (GLint, GLuint); +GLAPI void APIENTRY glUniform2uiEXT (GLint, GLuint, GLuint); +GLAPI void APIENTRY glUniform3uiEXT (GLint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glUniform4uiEXT (GLint, GLuint, GLuint, GLuint, GLuint); +GLAPI void APIENTRY glUniform1uivEXT (GLint, GLsizei, const GLuint *); +GLAPI void APIENTRY glUniform2uivEXT (GLint, GLsizei, const GLuint *); +GLAPI void APIENTRY glUniform3uivEXT (GLint, GLsizei, const GLuint *); +GLAPI void APIENTRY glUniform4uivEXT (GLint, GLsizei, const GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +#endif + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum, GLint, GLsizei, GLsizei); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum, GLsizei, GLenum, const GLvoid *, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +#endif + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#endif + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#endif + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum, GLenum, GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#endif + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#endif + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#endif + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble, GLdouble); +GLAPI void APIENTRY glClearDepthdNV (GLdouble); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble, GLdouble); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#endif + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum, GLsizei, GLsizei, GLenum, GLsizei, GLsizei); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#endif + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum, GLuint, GLuint, GLsizei, const GLfloat *); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum, GLuint, GLuint, GLsizei, const GLint *); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum, GLuint, GLuint, GLsizei, const GLuint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLuint *params); +#endif + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint, GLboolean, GLboolean, GLboolean, GLboolean); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum, GLuint, GLboolean *); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum, GLuint, GLint *); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum, GLuint); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum, GLuint); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum, GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +#endif + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint, const GLint *, GLenum); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum, GLuint, GLuint, GLintptr, GLsizeiptr); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum, GLuint, GLuint, GLintptr); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum, GLuint, GLuint); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint, GLsizei, const GLint *, GLenum); +GLAPI void APIENTRY glActiveVaryingNV (GLuint, const GLchar *); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint, const GLchar *); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint, GLuint, GLsizei, GLsizei *, GLsizei *, GLenum *, GLchar *); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint, GLuint, GLint *); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +#endif + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint, GLint, GLuint); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint, GLint); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint, GLint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#endif + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum, GLenum, const GLint *); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum, GLenum, const GLuint *); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum, GLenum, GLint *); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum, GLenum, GLuint *); +GLAPI void APIENTRY glClearColorIiEXT (GLint, GLint, GLint, GLint); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint, GLuint, GLuint, GLuint); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#endif + #ifdef __cplusplus } @@ -6572,4 +7353,3 @@ typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const GLvoid #endif /* GL_GLEXT_LEGACY */ #endif /* NO_SDL_GLEXT */ - diff --git a/include/SDL/SDL_platform.h b/include/SDL/SDL_platform.h index 5e6424575..1933e299b 100644 --- a/include/SDL/SDL_platform.h +++ b/include/SDL/SDL_platform.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -27,7 +27,8 @@ real SDL-1.2 available to you. */ #define SDL_platform_h_ /* this was taken from SDL2's zlib-licensed headers. This drops BeOS (not Haiku) -and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ +and MacOS Classic, but SDL2 doesn't run on them anyhow. Mac OS X target >= 10.6 +requirement is dropped too. Send patches. :) */ #if defined(_AIX) #undef __AIX__ @@ -66,11 +67,42 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ #undef __LINUX__ /* do we need to do this? */ #define __ANDROID__ 1 #endif +#if defined(__NGAGE__) +#undef __NGAGE__ +#define __NGAGE__ 1 +#endif #if defined(__APPLE__) /* lets us know what version of Mac OS X we're compiling on */ -#include "AvailabilityMacros.h" -#include "TargetConditionals.h" +#include +#ifndef __has_extension /* Older compilers don't support this */ +#define __has_extension(x) 0 +#include +#undef __has_extension +#else +#include +#endif + +/* Fix building with older SDKs that don't define these + See this for more information: + https://stackoverflow.com/questions/12132933/preprocessor-macro-for-os-x-targets +*/ +#ifndef TARGET_OS_MACCATALYST +#define TARGET_OS_MACCATALYST 0 +#endif +#ifndef TARGET_OS_IOS +#define TARGET_OS_IOS 0 +#endif +#ifndef TARGET_OS_IPHONE +#define TARGET_OS_IPHONE 0 +#endif +#ifndef TARGET_OS_TV +#define TARGET_OS_TV 0 +#endif +#ifndef TARGET_OS_SIMULATOR +#define TARGET_OS_SIMULATOR 0 +#endif + #if TARGET_OS_TV #undef __TVOS__ #define __TVOS__ 1 @@ -84,9 +116,6 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ /* if not compiling for iOS */ #undef __MACOSX__ #define __MACOSX__ 1 -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 -# error SDL for Mac OS X only supports deploying on 10.6 and above. -#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */ #endif /* TARGET_OS_IPHONE */ #endif /* defined(__APPLE__) */ @@ -120,7 +149,7 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ #endif #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) -/* Try to find out if we're compiling for WinRT or non-WinRT */ +/* Try to find out if we're compiling for WinRT, GDK or non-WinRT/GDK */ #if defined(_MSC_VER) && defined(__has_include) #if __has_include() #define HAVE_WINAPIFAMILY_H 1 @@ -142,9 +171,24 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ #define WINAPI_FAMILY_WINRT 0 #endif /* HAVE_WINAPIFAMILY_H */ +#if (HAVE_WINAPIFAMILY_H) && defined(WINAPI_FAMILY_PHONE_APP) +#define SDL_WINAPI_FAMILY_PHONE (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) +#else +#define SDL_WINAPI_FAMILY_PHONE 0 +#endif + #if WINAPI_FAMILY_WINRT #undef __WINRT__ #define __WINRT__ 1 +#elif defined(_GAMING_DESKTOP) /* GDK project configuration always defines _GAMING_XXX */ +#undef __WINGDK__ +#define __WINGDK__ 1 +#elif defined(_GAMING_XBOX_XBOXONE) +#undef __XBOXONE__ +#define __XBOXONE__ 1 +#elif defined(_GAMING_XBOX_SCARLETT) +#undef __XBOXSERIES__ +#define __XBOXSERIES__ 1 #else #undef __WINDOWS__ #define __WINDOWS__ 1 @@ -155,10 +199,20 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ #undef __WIN32__ #define __WIN32__ 1 #endif -#if defined(__PSP__) +/* This is to support generic "any GDK" separate from a platform-specific GDK */ +#if defined(__WINGDK__) || defined(__XBOXONE__) || defined(__XBOXSERIES__) +#undef __GDK__ +#define __GDK__ 1 +#endif +#if defined(__PSP__) || defined(__psp__) +#ifdef __PSP__ #undef __PSP__ +#endif #define __PSP__ 1 #endif +#if defined(PS2) +#define __PS2__ 1 +#endif /* The NACL compiler defines __native_client__ and __pnacl__ * Ref: http://www.chromium.org/nativeclient/pnacl/stability-of-the-pnacl-bitcode-abi @@ -180,5 +234,9 @@ and MacOS Classic, but SDL2 doesn't run on them anyhow. Send patches. :) */ #define __VITA__ 1 #endif +#if defined(__3DS__) +#undef __3DS__ +#define __3DS__ 1 #endif +#endif diff --git a/include/SDL/SDL_quit.h b/include/SDL/SDL_quit.h index 61f3bada8..c1c8a7ab8 100644 --- a/include/SDL/SDL_quit.h +++ b/include/SDL/SDL_quit.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_rwops.h b/include/SDL/SDL_rwops.h index 0ca9cfec3..98788f0c0 100644 --- a/include/SDL/SDL_rwops.h +++ b/include/SDL/SDL_rwops.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_stdinc.h b/include/SDL/SDL_stdinc.h index a6e364acf..5f832de75 100644 --- a/include/SDL/SDL_stdinc.h +++ b/include/SDL/SDL_stdinc.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -83,10 +83,9 @@ SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int)); #ifdef HAVE_STDIO_H #include #endif -#ifdef HAVE_STDLIB_H +#if defined(HAVE_STDLIB_H) #include -#endif -#ifdef HAVE_MALLOC_H +#elif defined(HAVE_MALLOC_H) #include #endif #ifdef HAVE_STDDEF_H @@ -101,10 +100,9 @@ SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int)); #ifdef HAVE_STRINGS_H #include #endif -#ifdef HAVE_INTTYPES_H +#if defined(HAVE_INTTYPES_H) #include -#endif -#ifdef HAVE_STDINT_H +#elif defined(HAVE_STDINT_H) #include #endif #ifdef HAVE_CTYPE_H @@ -342,7 +340,7 @@ extern DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, const char *str2); #ifdef HAVE_STRNCASECMP #define SDL_strncasecmp strncasecmp #elif defined(HAVE__STRNICMP) -#define SDL_strcasecmp _strnicmp +#define SDL_strncasecmp _strnicmp #else extern DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen); #endif @@ -410,7 +408,7 @@ extern DECLSPEC int SDLCALL SDL_vsnprintf(char *text, size_t maxlen, const char #define SDL_itoa(value, string, radix) SDL_ltoa((long)value, string, radix) #define SDL_uitoa(value, string, radix) SDL_ultoa((long)value, string, radix) -#define SDL_atoi(X) SDL_strtol(X, NULL, 0) +#define SDL_atoi(X) SDL_strtol(X, NULL, 10) #define SDL_atof(X) SDL_strtod(X, NULL) #define SDL_ICONV_ERROR (size_t)-1 diff --git a/include/SDL/SDL_syswm.h b/include/SDL/SDL_syswm.h index 6079a9b4f..9284b8b2e 100644 --- a/include/SDL/SDL_syswm.h +++ b/include/SDL/SDL_syswm.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -40,14 +40,14 @@ real SDL-1.2 available to you. */ # include "begin_code.h" - typedef struct SDL_SysWMmsg + struct SDL_SysWMmsg { SDL_version version; HWND hwnd; UINT msg; WPARAM wParam; LPARAM lParam; - } SDL_SysWMmsg; + }; typedef struct SDL_SysWMinfo { SDL_version version; @@ -57,16 +57,9 @@ real SDL-1.2 available to you. */ # include "close_code.h" -# elif defined(unix) /* shrug */ - -# ifdef __APPLE__ -# define Cursor X11Cursor -# endif +# elif defined(SDL_VIDEO_DRIVER_X11) # include # include -# ifdef __APPLE__ -# undef Cursor -# endif # include "begin_code.h" @@ -75,14 +68,14 @@ real SDL-1.2 available to you. */ SDL_SYSWM_X11 } SDL_SYSWM_TYPE; - typedef struct SDL_SysWMmsg + struct SDL_SysWMmsg { SDL_version version; SDL_SYSWM_TYPE subsystem; union { XEvent xevent; } event; - } SDL_SysWMmsg; + }; typedef struct SDL_SysWMinfo { @@ -102,15 +95,16 @@ real SDL-1.2 available to you. */ } SDL_SysWMinfo; # include "close_code.h" + # else # include "begin_code.h" - typedef struct SDL_SysWMmsg + struct SDL_SysWMmsg { SDL_version version; int data; - } SDL_SysWMmsg; + }; typedef struct SDL_SysWMinfo { @@ -128,6 +122,9 @@ real SDL-1.2 available to you. */ extern DECLSPEC int SDLCALL SDL_GetWMInfo(SDL_SysWMinfo *info); +typedef struct SDL_Window SDL_Window; +extern DECLSPEC SDL_Window * SDLCALL SDL12COMPAT_GetWindow(void); + #include "close_code.h" #endif diff --git a/include/SDL/SDL_thread.h b/include/SDL/SDL_thread.h index 906b2705c..f3999bf90 100644 --- a/include/SDL/SDL_thread.h +++ b/include/SDL/SDL_thread.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_timer.h b/include/SDL/SDL_timer.h index 07ccb5f0d..56f3ee1d7 100644 --- a/include/SDL/SDL_timer.h +++ b/include/SDL/SDL_timer.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_types.h b/include/SDL/SDL_types.h index 9ffdc3e80..885ff883c 100644 --- a/include/SDL/SDL_types.h +++ b/include/SDL/SDL_types.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/SDL_version.h b/include/SDL/SDL_version.h index 656556a80..82932cf98 100644 --- a/include/SDL/SDL_version.h +++ b/include/SDL/SDL_version.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -33,7 +33,7 @@ real SDL-1.2 available to you. */ /* We bumped the patchlevel to 50 for sdl12-compat */ #define SDL_MAJOR_VERSION 1 #define SDL_MINOR_VERSION 2 -#define SDL_PATCHLEVEL 50 +#define SDL_PATCHLEVEL 76 typedef struct SDL_version { @@ -43,13 +43,13 @@ typedef struct SDL_version } SDL_version; #define SDL_VERSION(X) { \ - (X)->major = 1; \ - (X)->minor = 2; \ - (X)->patch = 50; \ + (X)->major = SDL_MAJOR_VERSION; \ + (X)->minor = SDL_MINOR_VERSION; \ + (X)->patch = SDL_PATCHLEVEL; \ } #define SDL_VERSIONNUM(X, Y, Z) ((X)*1000 + (Y)*100 + (Z)) -#define SDL_COMPILEDVERSION SDL_VERSIONNUM(1, 2, 50) +#define SDL_COMPILEDVERSION SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) #define SDL_VERSION_ATLEAST(X, Y, Z) (SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z)) extern DECLSPEC const SDL_version * SDLCALL SDL_Linked_Version(void); diff --git a/include/SDL/SDL_video.h b/include/SDL/SDL_video.h index 20b51d83c..01f23b06c 100644 --- a/include/SDL/SDL_video.h +++ b/include/SDL/SDL_video.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -89,7 +89,7 @@ typedef struct SDL_Surface Uint16 pitch; void *pixels; int offset; - void *hwdata; + struct private_hwdata *hwdata; SDL_Rect clip_rect; Uint32 unused1; Uint32 locked; @@ -222,6 +222,10 @@ extern DECLSPEC int SDLCALL SDL_WM_ToggleFullScreen(SDL_Surface *surface); extern DECLSPEC SDL_GrabMode SDLCALL SDL_WM_GrabInput(SDL_GrabMode mode); extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); +/* this was never in an real SDL-1.2 release, but apparently StepMania was maintaining a fork with this API for literally years. */ +#define SDL_REFRESH_DEFAULT 0 +extern DECLSPEC void SDLCALL SDL_SetRefreshRate(int rate); + #define SDL_SWSURFACE 0x00000000 #define SDL_HWSURFACE 0x00000001 #define SDL_ASYNCBLIT 0x00000004 diff --git a/include/SDL/begin_code.h b/include/SDL/begin_code.h index e2d093e5a..08aa90ad4 100644 --- a/include/SDL/begin_code.h +++ b/include/SDL/begin_code.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/SDL/close_code.h b/include/SDL/close_code.h index a7ad15977..1a99ffa17 100644 --- a/include/SDL/close_code.h +++ b/include/SDL/close_code.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/sdl-config.in b/sdl-config.in index f7895c22f..ce332b3a6 100755 --- a/sdl-config.in +++ b/sdl-config.in @@ -7,10 +7,11 @@ # Copied and modified from SDL2's sdl2-compat. -prefix=@prefix@ -exec_prefix=@exec_prefix@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} exec_prefix_set=no -libdir=@libdir@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ @ENABLE_STATIC_FALSE@usage="\ @ENABLE_STATIC_FALSE@Usage: $0 [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--cflags] [--libs]" @@ -46,17 +47,17 @@ while test $# -gt 0; do echo $exec_prefix ;; --version) - echo @SDL_VERSION@ + echo @PROJECT_VERSION@ ;; --cflags) - echo -I@includedir@/SDL2 @SDL_CFLAGS@ + echo -I${includedir}/SDL @SDL_CFLAGS@ ;; @ENABLE_SHARED_TRUE@ --libs) -@ENABLE_SHARED_TRUE@ echo -L@libdir@ @SDL_RLD_FLAGS@ @SDL_LIBS@ +@ENABLE_SHARED_TRUE@ echo -L${libdir} @SDL_RLD_FLAGS@ @SDL_LIBS@ @ENABLE_SHARED_TRUE@ ;; @ENABLE_STATIC_TRUE@@ENABLE_SHARED_TRUE@ --static-libs) @ENABLE_STATIC_TRUE@@ENABLE_SHARED_FALSE@ --libs|--static-libs) -@ENABLE_STATIC_TRUE@ echo -L@libdir@ @SDL_LIBS@ @SDL_STATIC_LIBS@ +@ENABLE_STATIC_TRUE@ echo -L${libdir} @SDL_LIBS@ @SDL_STATIC_LIBS@ @ENABLE_STATIC_TRUE@ ;; *) echo "${usage}" 1>&2 diff --git a/sdl.m4 b/sdl.m4 new file mode 100644 index 000000000..53f78896e --- /dev/null +++ b/sdl.m4 @@ -0,0 +1,180 @@ +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +# serial 3 + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN([AM_PATH_SDL], +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + min_sdl_version=ifelse([$1], ,1.2.0,$1) + + if test "x$sdl_prefix$sdl_exec_prefix" = x ; then + PKG_CHECK_MODULES([SDL], [sdl >= $min_sdl_version], + [sdl_pc=yes], + [sdl_pc=no]) + else + sdl_pc=no + if test x$sdl_exec_prefix != x ; then + sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + fi + + if test "x$sdl_pc" = xyes ; then + no_sdl="" + SDL_CONFIG="$PKG_CONFIG sdl" + else + as_save_PATH="$PATH" + if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then + PATH="$prefix/bin:$prefix/usr/bin:$PATH" + fi + AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) + PATH="$as_save_PATH" + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdl_config_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdl_config_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include "SDL.h" + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + FILE *fp = fopen("conf.sdltest", "w"); + + if (fp) fclose(fp); + + if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +]])], [], [no_sdl=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + fi + if test "x$no_sdl" = x ; then + ifelse([$2], , :, [$2]) + else + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include "SDL.h" + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +]], [[ return 0; ]])], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) diff --git a/sdl12_compat.pc.in b/sdl12_compat.pc.in index ae32bc01b..30c5028a0 100644 --- a/sdl12_compat.pc.in +++ b/sdl12_compat.pc.in @@ -8,7 +8,7 @@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: sdl12_compat Description: An SDL-1.2 compatibility layer that uses SDL 2.0 behind the scenes. Version: @PROJECT_VERSION@ -Provides: sdl = 1.2.50 -Libs: -L${libdir} -lSDL -lpthread -Libs.private: -lSDL -lpthread -lm -ldl -lpthread -Cflags: -I${includedir}/SDL -D_GNU_SOURCE=1 -D_REENTRANT +Provides: sdl = @PROJECT_VERSION@ +Libs: -L${libdir} @SDL_RLD_FLAGS@ @SDL_LIBS@ +@ENABLE_STATIC_TRUE@Libs.private: -L${libdir} @SDL_LIBS@ @SDL_STATIC_LIBS@ +Cflags: -I${includedir}/SDL @SDL_CFLAGS@ diff --git a/src/Makefile.darwin b/src/Makefile.darwin index acac1a5ca..24b799e73 100644 --- a/src/Makefile.darwin +++ b/src/Makefile.darwin @@ -5,19 +5,20 @@ INCLUDES = -Iinclude ifeq ($(CROSS),) -CC=gcc +CC = gcc else -CC=$(CROSS)-gcc +CC = $(CROSS)-gcc endif LD = $(CC) CPPFLAGS = -DNDEBUG -D_THREAD_SAFE +CPPFLAGS+= -DSDL_DISABLE_IMMINTRIN_H CFLAGS = -mmacosx-version-min=10.6 -fPIC -O3 -Wall LDFLAGS = -mmacosx-version-min=10.6 -dynamiclib -Wl,-undefined,error -Wl,-single_module #LDFLAGS+= -Wl,-headerpad_max_install_names LDFLAGS+= -Wl,-install_name,"/usr/local/lib/$(DYLIB)" -LDFLAGS+= -Wl,-compatibility_version,1.0 -Wl,-current_version,12.50 +LDFLAGS+= -Wl,-compatibility_version,1.0 -Wl,-current_version,12.76 LDLIBS = -Wl,-framework,AppKit # this is needed for x86_64 - cross-gcc might not add it. #LDLIBS += -Wl,-lbundle1.o @@ -33,12 +34,14 @@ all: $(DYLIB) $(DYLIB): $(OBJ) $(LD) -o $@ $(LDFLAGS) $(OBJ) $(LDLIBS) + ln -sf $(DYLIB) libSDL.dylib + .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -o $@ -c $< .m.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -o $@ -c $< distclean: clean - $(RM) $(DYLIB) + $(RM) *.dylib clean: $(RM) *.o diff --git a/src/Makefile.linux b/src/Makefile.linux index 0f5d85973..35ec7cce9 100644 --- a/src/Makefile.linux +++ b/src/Makefile.linux @@ -4,30 +4,32 @@ # change INCLUDES so it points to SDL2 headers directory: INCLUDES = -Iinclude -CC=gcc +CC = gcc LD = $(CC) CPPFLAGS = -DNDEBUG -D_THREAD_SAFE -D_REENTRANT +CPPFLAGS+= -DSDL_DISABLE_IMMINTRIN_H CFLAGS = -fPIC -O3 -Wall LDFLAGS = -shared -Wl,-soname,libSDL-1.2.so.0 -#make sure this is supported: +# make sure this is supported: LDFLAGS+= -Wl,--no-undefined LDLIBS = -ldl -SHLIB = libSDL-1.2.so.1.2.50 +SHLIB = libSDL-1.2.so.1.2.76 OBJ = SDL12_compat.o .SUFFIXES: -.SUFFIXES: .o .c .m +.SUFFIXES: .o .c all: $(SHLIB) $(SHLIB): $(OBJ) $(LD) -o $@ $(LDFLAGS) $(OBJ) $(LDLIBS) - ln -s $(SHLIB) libSDL-1.2.so - ln -s $(SHLIB) libSDL-1.2.so.0 + ln -sf $(SHLIB) libSDL-1.2.so.0 + ln -sf libSDL-1.2.so.0 libSDL-1.2.so + ln -sf libSDL-1.2.so.0 libSDL.so .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -o $@ -c $< diff --git a/src/Makefile.mingw b/src/Makefile.mingw index 085ffcbff..1c951ca7f 100644 --- a/src/Makefile.mingw +++ b/src/Makefile.mingw @@ -5,19 +5,22 @@ INCLUDES = -Iinclude ifeq ($(CROSS),) -CC=gcc -RC=windres +CC = gcc +RC = windres else -CC=$(CROSS)-gcc -RC=$(CROSS)-windres +CC = $(CROSS)-gcc +RC = $(CROSS)-windres endif LD = $(CC) CPPFLAGS = -DDLL_EXPORT -DNDEBUG +CPPFLAGS+= -DSDL_DISABLE_IMMINTRIN_H CFLAGS = -O3 -Wall LDFLAGS = -nostdlib -shared -Wl,--no-undefined -Wl,--enable-auto-image-base -Wl,--out-implib,$(LIB) LDLIBS = -lkernel32 -luser32 +# libgcc is needed for 32 bit (x86) builds: +LDLIBS += -static-libgcc -lgcc LIB = libSDL.dll.a DLL = SDL.dll diff --git a/src/Makefile.os2 b/src/Makefile.os2 index 4f117c01f..c2cc7ae8c 100644 --- a/src/Makefile.os2 +++ b/src/Makefile.os2 @@ -1,14 +1,14 @@ # OpenWatcom makefile to build SDL for OS/2 - +# !ifndef %WATCOM !error Environment variable WATCOM is not specified! !endif DLLNAME = SDL12 -VERSION = 1.2.50 +VERSION = 1.2.76 # change SDL2INC to point to the SDL2 headers -SDL2INC = C:\SDL2DEV\h\SDL2 +SDL2INC = include INCPATH = -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h" -I"$(SDL2INC)" LIBNAME = $(DLLNAME) @@ -16,15 +16,21 @@ DLLFILE = $(LIBNAME).dll LIBFILE = $(LIBNAME).lib LNKFILE = $(LIBNAME).lnk -CFLAGS_DEF=-bt=os2 -d0 -zq -bm -5s -fp5 -fpi87 -sg -oteanbmier $(INCPATH) -CFLAGS_DLL=$(CFLAGS_DEF) -bd - -# Special flags for building SDL -CFLAGS=$(CFLAGS_DLL) -otexan -wx -ei -# avoid bogus W200 from cpuid code: +CFLAGS = -bt=os2 -d0 -zq -bm -5s -fp5 -fpi87 -sg -oeatxhn -ei +# max warnings: +CFLAGS+= -wx +# avoid bogus W200 from cpuid code CFLAGS+= -wcd=200 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 +# the include paths : +CFLAGS+= $(INCPATH) +# building dll: +CFLAGS+= -bd +# for DECLSPEC +CFLAGS+= -DBUILD_SDL +# misc +CFLAGS+= -DNDEBUG -DSDL_DISABLE_IMMINTRIN_H DESCRIPTION = Simple DirectMedia Layer 1.2 diff --git a/src/Makefile.vc b/src/Makefile.vc index bdfe1428d..151b4b250 100644 --- a/src/Makefile.vc +++ b/src/Makefile.vc @@ -1,10 +1,14 @@ # Makefile for Win32 using MSVC: # nmake /f Makefile.vc +# +# If you specifically want to build for x86: +# nmake /f Makefile.vc CPU=x86 # change INCLUDES so it points to SDL2 headers directory: INCLUDES = -Iinclude CPPFLAGS = -DNDEBUG -DDLL_EXPORT +CPPFLAGS = $(CPPFLAGS) -DSDL_DISABLE_IMMINTRIN_H CC = cl LD = link @@ -14,6 +18,10 @@ CFLAGS = /nologo /O2 /MD /W3 /GS- LDFLAGS = /nologo /DLL /NODEFAULTLIB /RELEASE LDLIBS = kernel32.lib user32.lib +!if "$(CPU)" == "x86" +CFLAGS = $(CFLAGS) /arch:SSE +!endif + DLLNAME = SDL.dll IMPNAME = SDL.lib @@ -23,6 +31,7 @@ all: $(DLLNAME) $(DLLNAME): $(OBJ) $(LD) /OUT:$@ $(LDFLAGS) $(OBJ) $(LDLIBS) + .c.obj: $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) /Fo$@ -c $< .rc.res: diff --git a/src/Makefile.w32 b/src/Makefile.w32 index 833a202c0..3d73be896 100644 --- a/src/Makefile.w32 +++ b/src/Makefile.w32 @@ -1,52 +1,72 @@ -# OpenWatcom makefile to build SDL for Win32. - +# OpenWatcom makefile to build SDL for Win32 +# !ifndef %WATCOM !error Environment variable WATCOM is not specified! !endif DLLNAME = SDL -VERSION = 1.2.50 +VERSION = 1.2.76 # change SDL2INC to point to the SDL2 headers SDL2INC = include -INCPATH = -I"$(%WATCOM)/h/nt" -I"$(%WATCOM)/h" -I"$(SDL2INC)" +INCPATH = -I"$(%WATCOM)/h/nt" -I"$(%WATCOM)/h" LIBNAME = $(DLLNAME) DLLFILE = $(LIBNAME).dll LIBFILE = $(LIBNAME).lib LNKFILE = $(LIBNAME).lnk -CFLAGS_DEF=-bt=nt -d0 -zq -bm -5s -fp5 -fpi87 -sg -oteanbmier $(INCPATH) -# we override the DECLSPEC define in begin_code.h, because we are using -# an exports file to remove the _cdecl '_' prefix from the symbol names -CFLAGS_DLL=$(CFLAGS_DEF) -bd -DDLL_EXPORT -DDECLSPEC= - -# Special flags for building SDL -CFLAGS=$(CFLAGS_DLL) -otexan -wx -ei -# avoid bogus W200 from cpuid code: +CFLAGS =-bt=nt -d0 -zq -bm -5s -fp5 -fpi87 -sg -oeatxhn -ei +# max warnings: +CFLAGS+= -wx +# avoid bogus W200 from cpuid code CFLAGS+= -wcd=200 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 +# the include paths : +CFLAGS+= $(INCPATH) +# misc +CFLAGS+= -DNDEBUG -DSDL_DISABLE_IMMINTRIN_H + +CFLAGS_DLL = $(CFLAGS) +# building dll: +CFLAGS_DLL+= -bd +# the include paths : +CFLAGS_DLL+= -I"$(SDL2INC)" +# we override the DECLSPEC define in begin_code.h, because we are using +# an exports file to remove the _cdecl '_' prefix from the symbol names +CFLAGS_DLL+= -DDECLSPEC= -DESCRIPTION = Simple DirectMedia Layer 1.2 +CFLAGS_SDLMAIN = $(CFLAGS) +# the include paths : +CFLAGS_SDLMAIN+= -I"../include/SDL" object_files= SDL12_compat.obj +resource_obj= version.res .extensions: -.extensions: .lib .dll .obj .c .asm +.extensions: .lib .dll .obj .c .asm .res .rc .c.obj: - wcc386 $(CFLAGS) -fo=$^@ $< + wcc386 $(CFLAGS_DLL) -fo=$^@ $< +.rc.res: + wrc -q -r -bt=nt -I"$(%WATCOM)/h/nt" -fo=$^@ $< -all: $(DLLFILE) $(LIBFILE) .symbolic +all: $(DLLFILE) $(LIBFILE) SDLmain.lib .symbolic -$(DLLFILE): compiling_info $(object_files) $(LNKFILE) +$(DLLFILE): compiling_info $(object_files) $(resource_obj) $(LNKFILE) @echo * Linking: $@ @wlink @$(LNKFILE) $(LIBFILE): $(DLLFILE) @echo * Creating LIB file: $@ - wlib -q -b -n -c -pa -s -t -zld -ii -io $* $(DLLFILE) + wlib -q -b -n -c -pa -s -t -zld -ii -io $* @SDL12.lbc + +SDLmain.lib: SDL_win32_main.obj + wlib -q -b -n $@ -+SDL_win32_main.obj + +SDL_win32_main.obj : SDLmain/win32/SDL_win32_main.c + wcc386 $(CFLAGS_SDLMAIN) -fo=$^@ $< compiling_info : .symbolic @echo * Compiling... @@ -57,22 +77,24 @@ $(LNKFILE): @%append $@ SYSTEM nt_dll INITINSTANCE TERMINSTANCE @%append $@ NAME $(DLLFILE) @for %i in ($(object_files)) do @%append $@ FILE %i - @%append $@ EXPORT=SDL12.lbc + @%append $@ EXPORT=SDL12.exports + @%append $@ OPTION IMPF=SDL12.lbc + @%append $@ OPTION RESOURCE=$(resource_obj) @%append $@ OPTION QUIET - @%append $@ OPTION IMPF=$^&.exp @%append $@ OPTION MAP=$^&.map - @%append $@ OPTION DESCRIPTION '@$#libsdl org:$(VERSION)$#@$(DESCRIPTION)' @%append $@ OPTION ELIMINATE @%append $@ OPTION SHOWDEAD clean: .SYMBOLIC @echo * Clean: $(LIBNAME) $(VERSION) @if exist *.obj rm *.obj + @if exist *.res rm *.res + @if exist *.lbc rm *.lbc @if exist *.map rm *.map - @if exist *.exp rm *.exp @if exist $(LNKFILE) rm $(LNKFILE) distclean: clean .SYMBOLIC @if exist *.err rm *.err @if exist $(DLLFILE) rm $(DLLFILE) @if exist $(LIBFILE) rm $(LIBFILE) + @if exist SDLmain.lib rm SDLmain.lib diff --git a/src/SDL12.lbc b/src/SDL12.exports similarity index 98% rename from src/SDL12.lbc rename to src/SDL12.exports index 9b49755f9..f4e405fb8 100644 --- a/src/SDL12.lbc +++ b/src/SDL12.exports @@ -193,6 +193,7 @@ ++'_SDL_GetVideoInfo'.'SDL.dll'.'SDL_GetVideoInfo'.'SDL_GetVideoInfo' ++'_SDL_ListModes'.'SDL.dll'.'SDL_ListModes'.'SDL_ListModes' ++'_SDL_VideoModeOK'.'SDL.dll'.'SDL_VideoModeOK'.'SDL_VideoModeOK' +++'_SDL_SetRefreshRate'.'SDL.dll'.'SDL_SetRefreshRate'.'SDL_SetRefreshRate' ++'_SDL_SetVideoMode'.'SDL.dll'.'SDL_SetVideoMode'.'SDL_SetVideoMode' ++'_SDL_DisplayFormat'.'SDL.dll'.'SDL_DisplayFormat'.'SDL_DisplayFormat' ++'_SDL_DisplayFormatAlpha'.'SDL.dll'.'SDL_DisplayFormatAlpha'.'SDL_DisplayFormatAlpha' @@ -234,3 +235,4 @@ ++'_SDL_HasSSE'.'SDL.dll'.'SDL_HasSSE'.'SDL_HasSSE' ++'_SDL_HasSSE2'.'SDL.dll'.'SDL_HasSSE2'.'SDL_HasSSE2' ++'_SDL_HasAltiVec'.'SDL.dll'.'SDL_HasAltiVec'.'SDL_HasAltiVec' +++'_SDL12COMPAT_GetWindow'.'SDL.dll'.'SDL12COMPAT_GetWindow'.'SDL12COMPAT_GetWindow' diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c index 6b3782566..616a0ea4a 100644 --- a/src/SDL12_compat.c +++ b/src/SDL12_compat.c @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -23,20 +23,28 @@ #include "SDL20_include_wrapper.h" -#if !SDL_VERSION_ATLEAST(2,0,0) -#error You need to compile against SDL 2.0 headers. -#endif - /* * We report the library version as 1.2.$(SDL12_COMPAT_VERSION). This number - * should be way ahead of what SDL-1.2 Classic would report, so apps can - * decide if they're running under the compat layer, if they really care. + * should be way ahead of what SDL-1.2 Classic would report, so apps can + * decide if they're running under the compat layer, if they really care. */ -#define SDL12_COMPAT_VERSION 50 +#define SDL12_COMPAT_VERSION 76 #include #include -#ifndef _WIN32 +#include +#if defined(_MSC_VER) && (_MSC_VER < 1600) +/* intptr_t already handled by stddef.h. */ +#else +#include +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#else #include /* fprintf(), etc. */ #include /* for abort() */ #include @@ -49,35 +57,63 @@ #undef snprintf #undef vsnprintf -/* !!! IMPLEMENT_ME X11_KeyToUnicode ? */ +#ifdef __linux__ +#include /* for readlink() */ +#endif -#define SDL_BlitSurface SDL_UpperBlit +#if defined(__unix__) || defined(__APPLE__) +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif +#define SDL12_MAXPATH PATH_MAX +#elif defined _WIN32 +#define SDL12_MAXPATH MAX_PATH +#elif defined __OS2__ +#define SDL12_MAXPATH CCHMAXPATH +#else +#define SDL12_MAXPATH 1024 +#endif #ifdef __cplusplus extern "C" { #endif -#if 0 -#define FIXME(x) do {} while (0) +/* on x86 Linux builds, we have the public entry points force stack alignment to 16 bytes + on entry. This won't be a massive performance hit, but it might help extremely old + binaries that want to call into SDL to not crash in hard-to-diagnose ways. It's not a + panacea to the stack alignment problem, but it might help a little. + + The force_align_arg_pointer attribute requires gcc >= 4.2.x. */ +#if defined(__clang__) +#define HAVE_FORCE_ALIGN_ARG_POINTER +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +#define HAVE_FORCE_ALIGN_ARG_POINTER +#endif +#if defined(__linux__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER) +#define FORCEALIGNATTR __attribute__((force_align_arg_pointer)) #else -#define FIXME(x) \ - do { \ - static SDL_bool seen = SDL_FALSE; \ - if (!seen) { \ - SDL20_Log("FIXME: %s (%s:%d)\n", x, __FUNCTION__, __LINE__); \ - seen = SDL_TRUE; \ - } \ - } while (0) +#define FORCEALIGNATTR #endif +#define DECLSPEC12 DECLSPEC FORCEALIGNATTR + +/** Enable this to have warnings about wrong prototypes in SDL20_syms.h. + * It won't compile but it helps to make sure it's sync'ed with SDL2 headers. + */ +#if 0 +#define SDL20_SYM(rc,fn,params,args,ret) \ + typedef rc (SDLCALL *SDL20_##fn##_t) params; \ + static SDL20_##fn##_t SDL20_##fn = IGNORE_THIS_VERSION_OF_SDL_##fn; +#else #define SDL20_SYM(rc,fn,params,args,ret) \ typedef rc (SDLCALL *SDL20_##fn##_t) params; \ static SDL20_##fn##_t SDL20_##fn = NULL; +#endif #include "SDL20_syms.h" /* Things that _should_ be binary compatible pass right through... */ #define SDL20_SYM_PASSTHROUGH(rc,fn,params,args,ret) \ - DECLSPEC rc SDLCALL SDL_##fn params { ret SDL20_##fn args; } + DECLSPEC12 rc SDLCALL SDL_##fn params { ret SDL20_##fn args; } #include "SDL20_syms.h" @@ -87,11 +123,27 @@ extern "C" { #define SDL20_InvalidParamError(param) SDL20_SetError("Parameter '%s' is invalid", (param)) #define SDL20_zero(x) SDL20_memset(&(x), 0, sizeof((x))) #define SDL20_zerop(x) SDL20_memset((x), 0, sizeof(*(x))) +#define SDL20_zeroa(x) SDL20_memset((x), 0, sizeof((x))) #define SDL_ReportAssertion SDL20_ReportAssertion +/* for SDL_assert() : */ +#define SDL_enabled_assert(condition) \ +do { \ + while ( !(condition) ) { \ + static struct SDL_AssertData sdl_assert_data = { 0, 0, #condition, 0, 0, 0, 0 }; \ + const SDL_AssertState sdl_assert_state = SDL20_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \ + if (sdl_assert_state == SDL_ASSERTION_RETRY) { \ + continue; /* go again. */ \ + } else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \ + SDL_TriggerBreakpoint(); \ + } \ + break; /* not retrying. */ \ + } \ +} while (SDL_NULL_WHILE_LOOP_CONDITION) + /* From SDL2.0's SDL_bits.h: a force-inlined function. */ #if defined(__WATCOMC__) && defined(__386__) -extern _inline int _SDL20_bsr_watcom (Uint32); +extern __inline int _SDL20_bsr_watcom(Uint32); #pragma aux _SDL20_bsr_watcom = \ "bsr eax, eax" \ parm [eax] nomemory \ @@ -149,6 +201,11 @@ SDL20_MostSignificantBitIndex32(Uint32 x) #endif } +/* SDL_truncf needs SDL >=2.0.14, so copy it here. */ +static float SDL20_truncf(float x) +{ + return (x < 0.0f) ? (float)SDL20_ceil(x) : (float)SDL20_floor(x); +} #define SDL12_DEFAULT_REPEAT_DELAY 500 #define SDL12_DEFAULT_REPEAT_INTERVAL 30 @@ -165,6 +222,8 @@ SDL20_MostSignificantBitIndex32(Uint32 x) #define SDL12_LOGPAL 1 #define SDL12_PHYSPAL 2 +#define SDL12_REFRESH_DEFAULT 0 + #ifndef SDL_SIMD_ALIGNED #define SDL_SIMD_ALIGNED 0x00000008 #endif @@ -555,6 +614,53 @@ typedef struct SDL12_keysym #define SDL12_RELEASED 0 #define SDL12_PRESSED 1 +#if defined(SDL_VIDEO_DRIVER_X11) /* SDL_VIDEO_DRIVER_X11 refers to the SDL2 headers. */ +typedef enum /* this is only used for 1.2 X11 syswm */ +{ + SDL12_SYSWM_X11 +} SDL12_SYSWM_TYPE; +#endif + +typedef struct SDL12_SysWMmsg +{ + SDL_version version; +#if defined(_WIN32) + HWND hwnd; + UINT msg; + WPARAM wParam; + LPARAM lParam; +#elif defined(SDL_VIDEO_DRIVER_X11) + SDL12_SYSWM_TYPE subsystem; + union { XEvent xevent; } event; +#else + int data; /* unused at the moment. */ +#endif +} SDL12_SysWMmsg; + +typedef struct SDL12_SysWMinfo +{ + SDL_version version; +#if defined(_WIN32) + HWND window; + HGLRC hglrc; +#elif defined(SDL_VIDEO_DRIVER_X11) + SDL12_SYSWM_TYPE subsystem; + union { + struct { + Display *display; + Window window; + void (*lock_func)(void); + void (*unlock_func)(void); + Window fswindow; + Window wmwindow; + Display *gfxdisplay; + } x11; + } info; +#else + int data; /* unused at the moment. */ +#endif +} SDL12_SysWMinfo; + typedef enum { SDL12_NOEVENT = 0, @@ -679,7 +785,7 @@ typedef struct typedef struct { Uint8 type; - void *msg; + SDL12_SysWMmsg *msg; } SDL12_SysWMEvent; typedef union @@ -747,6 +853,34 @@ typedef enum } SDL12_GLattr; +typedef enum +{ + SDL12_CD_TRAYEMPTY, + SDL12_CD_STOPPED, + SDL12_CD_PLAYING, + SDL12_CD_PAUSED, + SDL12_CD_ERROR = -1 +} SDL12_CDstatus; + +typedef struct +{ + Uint8 id; + Uint8 type; + Uint16 unused; + Uint32 length; + Uint32 offset; +} SDL12_CDtrack; + +typedef struct +{ + int id; + SDL12_CDstatus status; + int numtracks; + int cur_track; + int cur_frame; + SDL12_CDtrack track[100]; /* in 1.2, this was SDL_MAX_TRACKS+1 */ +} SDL12_CD; + typedef struct { Uint32 format; @@ -757,9 +891,35 @@ typedef struct typedef struct { - int device_index; - SDL_Joystick *joystick; -} JoystickOpenedItem; + char *name; + SDL_atomic_t refcount; + SDL_JoystickID instance_id; + union { + SDL_Joystick *joystick; + SDL_GameController *controller; + } dev; +} SDL12_Joystick; + + +struct SDL12_AudioCVT; +typedef void (SDLCALL *SDL12_AudioCVTFilter)(struct SDL12_AudioCVT *cvt, Uint16 format); + +/* this is identical to SDL2, except SDL2 forced the structure packing in + some instances, so we can't passthrough without converting the struct. :( */ +typedef struct SDL12_AudioCVT +{ + int needed; + Uint16 src_format; + Uint16 dst_format; + double rate_incr; + Uint8 *buf; + int len; + int len_cvt; + int len_mult; + double len_ratio; + SDL12_AudioCVTFilter filters[10]; + int filter_index; +} SDL12_AudioCVT; #include "SDL_opengl.h" #include "SDL_opengl_glext.h" @@ -778,43 +938,107 @@ typedef struct OpenGLEntryPoints #include "SDL20_syms.h" } OpenGLEntryPoints; +typedef struct QueuedOverlayItem +{ + SDL12_Overlay *overlay12; + SDL12_Rect dstrect12; + struct QueuedOverlayItem *next; +} QueuedOverlayItem; + +typedef struct SDL12_TimerID_Data +{ + SDL_TimerID timer_id; + SDL12_NewTimerCallback callback; + void *param; + struct SDL12_TimerID_Data *next; + struct SDL12_TimerID_Data *prev; +} SDL12_TimerID_Data; + +/* This changed from an opaque pointer to an int in 2.0. */ +typedef SDL12_TimerID_Data *SDL12_TimerID; + +#define SDL12_MAXEVENTS 128 +typedef struct EventQueueType +{ + SDL12_SysWMmsg syswm_msg; /* save space for a copy of this in case we use it. */ + SDL12_Event event12; + struct EventQueueType *next; +} EventQueueType; + -/* !!! FIXME: grep for VideoWindow20 places that might care if it's NULL */ -/* !!! FIXME: go through all of these. */ +static Uint32 InitializedSubsystems20 = 0; +static Uint32 LinkedSDL2VersionInt = 0; +static SDL_bool IsDummyVideo = SDL_FALSE; static VideoModeList *VideoModes = NULL; static int VideoModesCount = 0; /* this counts items in VideoModeList, not total video modes. */ static SDL12_VideoInfo VideoInfo12; static SDL12_Palette VideoInfoPalette12; static SDL12_PixelFormat VideoInfoVfmt12; static SDL_PixelFormat *VideoInfoVfmt20 = NULL; +static SDL_bool VideoWindowGrabWanted = SDL_FALSE; static SDL_bool VideoWindowGrabbed = SDL_FALSE; static SDL_bool VideoCursorHidden = SDL_FALSE; +static SDL_bool SetVideoModeInProgress = SDL_FALSE; static SDL_Window *VideoWindow20 = NULL; static SDL_Renderer *VideoRenderer20 = NULL; +static SDL_mutex *VideoRendererLock = NULL; static SDL_Texture *VideoTexture20 = NULL; +static SDL12_Surface VideoSurface12Location; static SDL12_Surface *VideoSurface12 = NULL; static SDL_Palette *VideoPhysicalPalette20 = NULL; static Uint32 VideoSurfacePresentTicks = 0; static Uint32 VideoSurfaceLastPresentTicks = 0; static SDL_Surface *VideoConvertSurface20 = NULL; static SDL_GLContext VideoGLContext20 = NULL; -static SDL12_Overlay *QueuedDisplayOverlay12 = NULL; -static SDL12_Rect QueuedDisplayOverlayDstRect12; +static QueuedOverlayItem QueuedDisplayOverlays; /* the head node */ +static QueuedOverlayItem *QueuedDisplayOverlaysTail = &QueuedDisplayOverlays; static char *WindowTitle = NULL; static char *WindowIconTitle = NULL; static SDL_Surface *VideoIcon20 = NULL; static int EnabledUnicode = 0; +static Uint32 KeyRepeatNextTicks = 0; +static Uint32 KeyRepeatDelay = 0; +static Uint32 KeyRepeatInterval = 0; +static SDL12_Event KeyRepeatEvent; +/* Windows SDL1.2 never uses translated keyboard layouts for compatibility with +DirectInput, which didn't support them. Other platforms (MacOS, Linux) seem to, +but with varying levels of bugginess. So default to Translated Layouts on +all non-Windows platforms (but have an option to change this, as many apps are +buggy with non-US layouts, or provide their own keyboard layout translation, +such as DOSBox. */ +#if !defined(_WIN32) +static SDL_bool TranslateKeyboardLayout = SDL_TRUE; +#else +static SDL_bool TranslateKeyboardLayout = SDL_FALSE; +#endif static int VideoDisplayIndex = 0; -static int CDRomInit = 0; +static SDL_bool SupportSysWM = SDL_FALSE; +static SDL_bool EventThreadEnabled = SDL_FALSE; +static SDL_bool CDRomInit = SDL_FALSE; +static char *CDRomPath = NULL; +static SDL12_CD *CDRomDevice = NULL; static SDL12_EventFilter EventFilter12 = NULL; static SDL12_Cursor *CurrentCursor12 = NULL; static Uint8 EventStates[SDL12_NUMEVENTS]; static int SwapInterval = 0; -static JoystickOpenedItem JoystickOpenList[16]; +static float OpenGLBuffersSwapTickInterval = 0.f; +static Uint32 OpenGLBuffersLastSwapTicks = 0; +static SDL_bool JoysticksAreGameControllers = SDL_FALSE; +static SDL12_Joystick *JoystickList = NULL; +static int NumJoysticks = 0; static Uint8 KeyState[SDLK12_LAST]; static SDL_bool MouseInputIsRelative = SDL_FALSE; static SDL_Point MousePosition = { 0, 0 }; +static struct { /* SDL_FPoint */ + float x, y; +} MouseRelativeRemainder = { 0.f, 0.f }; +static SDL_bool UseMouseRelativeScaling = SDL_FALSE; static OpenGLEntryPoints OpenGLFuncs; +static int OpenGLBlitLockCount = 0; +static GLuint OpenGLBlitTexture = 0; +static SDL_bool WantDebugLogging = SDL_FALSE; +static SDL_bool WantScaleMethodNearest = SDL_FALSE; +static SDL_bool WantOpenGLScaling = SDL_FALSE; static int OpenGLLogicalScalingWidth = 0; static int OpenGLLogicalScalingHeight = 0; static GLuint OpenGLLogicalScalingFBO = 0; @@ -826,31 +1050,204 @@ static GLuint OpenGLLogicalScalingMultisampleColor = 0; static GLuint OpenGLLogicalScalingMultisampleDepth = 0; static GLuint OpenGLCurrentReadFBO = 0; static GLuint OpenGLCurrentDrawFBO = 0; - - -/* !!! FIXME: need a mutex for the event queue. */ -#define SDL12_MAXEVENTS 128 -typedef struct EventQueueType -{ - SDL12_Event event12; - struct EventQueueType *next; -} EventQueueType; - +static SDL_bool ForceGLSwapBufferContext = SDL_FALSE; +static SDL12_TimerID AddedTimers = NULL; /* we'll protect this with EventQueueMutex for laziness/convenience. */ +static SDL_mutex *EventQueueMutex = NULL; static EventQueueType EventQueuePool[SDL12_MAXEVENTS]; static EventQueueType *EventQueueHead = NULL; static EventQueueType *EventQueueTail = NULL; static EventQueueType *EventQueueAvailable = NULL; +static unsigned long SetVideoModeThread = 0; +static SDL_bool VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE; +static SDL_bool AllowThreadedDraws = SDL_FALSE; +static SDL_bool AllowThreadedPumps = SDL_FALSE; +static SDL_bool WantCompatibilityAudioCVT = SDL_FALSE; +static SDL_bool PreserveDestinationAlpha = SDL_TRUE; +static int DesiredRefreshRate = SDL12_REFRESH_DEFAULT; +static int CurrentRefreshRate = SDL12_REFRESH_DEFAULT; + +static int ProcessingModalLoop; +static SDL_bool HasPendingResizeEvent; +static SDL12_Event PendingResizeEvent; /* This is a KEYDOWN event which is being held for a follow-up TEXTINPUT */ static SDL12_Event PendingKeydownEvent; +/* SDL_atoi() before SDL2-2.0.17 is non-compliant */ +static SDL_INLINE int SDLCALL +SDL20_atoi(const char *str) +{ + return SDL20_strtol(str, NULL, 10); +} + +#ifdef __linux__ +/* you can use SDL20_atoi once we're past startup. */ +static int +SDL12COMPAT_atoi(const char *str) +{ + int retval = 0; + int multiplier = 1; + int signmult = 1; + const char *ptr; + + while (*str == ' ') { + str++; + } + + if (*str == '-') { + signmult = -1; + str++; + while (*str == ' ') { + str++; + } + } + + ptr = str; + while (SDL_TRUE) { + if ((*ptr < '0') || (*ptr > '9')) { + break; + } + ptr++; + } + ptr--; + + while (ptr != str) { + retval += ((int) (*ptr - '0')) * multiplier; + multiplier *= 10; + ptr--; + } + + return (retval + (((int) (*ptr - '0')) * multiplier)) * signmult; +} +#endif /* __linux__ */ + +static char * +SDL12COMPAT_stpcpy(char *dst, const char *src) +{ + while ((*dst++ = *src++) != '\0') { + /**/; + } + return --dst; +} + +static void +SDL12COMPAT_itoa(char *dst, int val) +{ + char *ptr, temp; + + if (val < 0) { + *dst++ = '-'; + val = -val; + } + ptr = dst; + + do { + *ptr++ = '0' + (val % 10); + val /= 10; + } while (val > 0); + *ptr-- = '\0'; + + /* correct the order of digits */ + do { + temp = *dst; + *dst++ = *ptr; + *ptr-- = temp; + } while (ptr > dst); +} + +/* you can use SDL20_strlen once we're past startup. */ +static int SDL12COMPAT_strlen(const char *str) +{ + volatile const char *ptr = str; + while (*ptr) { + ++ptr; + } + return (int)(ptr - str); +} + +/* you can use SDL20_strcmp once we're past startup. */ +static SDL_bool SDL12COMPAT_strequal(const char *a, const char *b) +{ + for ( ;; ) { + const char cha = *a; + if (cha != *b) { + return SDL_FALSE; + } + if (!cha) { + break; + } + a++; + b++; + } + return SDL_TRUE; +} + +/* SDL3 (and thus sdl2-compat) will build an SDL_Environment, which isn't useful if the SDL-1.2 app is calling getenv()/setenv() directly, so use system APIs instead. */ +/* despite the "unsafe" name (they are NOT thread-safe), these are actually _safe_ to call at startup, since it won't call into SDL2 before everything is properly initialized! */ +static char *SDL12COMPAT_getenv_unsafe(const char *name) +{ + #ifdef _WIN32 + static char buf[256]; /* overflows will just report as environment variable being unset. But most of our environment vars don't come through here. */ + const DWORD rc = GetEnvironmentVariableA(name, buf, (DWORD) sizeof (buf)); + return ((rc != 0) && (rc < sizeof (buf))) ? buf : NULL; + #else /* we might need other platforms, or a simple `return NULL;` for platforms without an environment table. */ + return getenv(name); + #endif +} + +static void SDL12COMPAT_setenv_unsafe(const char *name, const char *value) +{ + #ifdef _WIN32 + SetEnvironmentVariableA(name, value); + #elif defined (__WATCOMC__) + setenv(name, value, 1); /* OW19 has no unsetenv(). NULL newvalue passed to setenv() behaves as unsetenv(). */ + #else /* we might need other platforms, or a simple `return;` for platforms without an environment table. */ + if (value) { + setenv(name, value, 1); + } else { + unsetenv(name); + } + #endif +} + +static const char *SDL12COMPAT_GetEnvAtStartup(const char *name) +{ + return SDL12COMPAT_getenv_unsafe(name); /* don't talk to SDL2 yet, we aren't set up. Go right to the OS interfaces. */ +} + +static void SDL12COMPAT_SetEnvAtStartup(const char *name, const char *value) +{ + SDL12COMPAT_setenv_unsafe(name, value); /* don't talk to SDL2 yet, we aren't set up. Go right to the OS interfaces. */ +} + +/* log a string using platform-specific code for before SDL2 is fully available. */ +static void SDL12COMPAT_LogAtStartup(const char *str) +{ + #ifdef _WIN32 + OutputDebugStringA(str); + #elif defined(__APPLE__) + extern void SDL12COMPAT_NSLog(const char *prefix, const char *text); + SDL12COMPAT_NSLog(NULL, str); + #else + fputs(str, stderr); + fputs("\n", stderr); + #endif +} + +/* this can't call into SDL20_getenv because things aren't set up yet, so try for platform-specific getenv checks. */ +static SDL_bool SDL12COMPAT_CheckDebugLogging(void) +{ + const char *value = SDL12COMPAT_GetEnvAtStartup("SDL12COMPAT_DEBUG_LOGGING"); + if (!value) { + value = SDL12COMPAT_GetEnvAtStartup("DEBUG_INVOCATION"); + } + return (value && SDL12COMPAT_strequal(value, "1")) ? SDL_TRUE : SDL_FALSE; +} + /* Obviously we can't use SDL_LoadObject() to load SDL2. :) */ static char loaderror[256]; #if defined(_WIN32) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN 1 - #endif - #include + #define DIRSEP "\\" #define SDL20_LIBNAME "SDL2.dll" /* require SDL2 >= 2.0.12 for SDL_CreateThread binary compatibility */ #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,12) @@ -858,19 +1255,19 @@ static char loaderror[256]; #define LoadSDL20Library() ((Loaded_SDL20 = LoadLibraryA(SDL20_LIBNAME)) != NULL) #define LookupSDL20Sym(sym) (void *)GetProcAddress(Loaded_SDL20, sym) #define CloseSDL20Library() { if (Loaded_SDL20) { FreeLibrary(Loaded_SDL20); Loaded_SDL20 = NULL; } } - #define strcpy_fn lstrcpyA - #define sprintf_fn wsprintfA #elif defined(__OS2__) #include + #define DIRSEP "\\" #define SDL20_LIBNAME "SDL2.dll" - #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,9) - #define strcpy_fn strcpy - #define sprintf_fn sprintf + #define SDL20_LIBNAME2 "SDL2" /* if loading from LIBPATH */ + #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,7) static HMODULE Loaded_SDL20 = NULLHANDLE; static SDL_bool LoadSDL20Library(void) { char err[256]; if (DosLoadModule(err, sizeof(err), SDL20_LIBNAME, &Loaded_SDL20) != 0) { - return SDL_FALSE; + if (DosLoadModule(err, sizeof(err), SDL20_LIBNAME2, &Loaded_SDL20) != 0) { + return SDL_FALSE; + } } return SDL_TRUE; } @@ -890,10 +1287,10 @@ static char loaderror[256]; #include #include #define SDL20_LIBNAME "libSDL2-2.0.0.dylib" + /* SDL2 cmake'ry is (was?) messy: */ + #define SDL20_LIBNAME2 "libSDL2-2.0.dylib" #define SDL20_FRAMEWORK "SDL2.framework/Versions/A/SDL2" - #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,9) - #define strcpy_fn strcpy - #define sprintf_fn sprintf + #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,7) static void *Loaded_SDL20 = NULL; #define LookupSDL20Sym(sym) dlsym(Loaded_SDL20, sym) #define CloseSDL20Library() { if (Loaded_SDL20) { dlclose(Loaded_SDL20); Loaded_SDL20 = NULL; } } @@ -901,16 +1298,19 @@ static char loaderror[256]; /* I don't know if this is the _right_ order to try, but this seems reasonable */ static const char * const dylib_locations[] = { "@loader_path/" SDL20_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2-2.0.0.dylib */ + "@loader_path/" SDL20_LIBNAME2, /* MyApp.app/Contents/MacOS/libSDL2-2.0.dylib */ "@loader_path/../Frameworks/" SDL20_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2.framework */ "@executable_path/" SDL20_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2-2.0.0.dylib */ + "@executable_path/" SDL20_LIBNAME2, /* MyApp.app/Contents/MacOS/libSDL2-2.0.dylib */ "@executable_path/../Frameworks/" SDL20_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2.framework */ NULL, /* /Users/username/Library/Frameworks/SDL2.framework */ - "/Library/Frameworks" SDL20_FRAMEWORK, /* /Library/Frameworks/SDL2.framework */ - SDL20_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */ + "/Library/Frameworks/" SDL20_FRAMEWORK, /* /Library/Frameworks/SDL2.framework */ + SDL20_LIBNAME, /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */ + SDL20_LIBNAME2 }; int i; - for (i = 0; i < SDL_arraysize(dylib_locations); i++) { + for (i = 0; i < (int) SDL_arraysize(dylib_locations); i++) { const char *location = dylib_locations[i]; if (location) { Loaded_SDL20 = dlopen(location, RTLD_LOCAL|RTLD_NOW); @@ -921,12 +1321,12 @@ static char loaderror[256]; homedir = pwent->pw_dir; } if (!homedir) { - homedir = getenv("HOME"); + homedir = SDL12COMPAT_getenv_unsafe("HOME"); } if (homedir) { char framework[512]; const int rc = snprintf(framework, sizeof (framework), "%s/Library/Frameworks/" SDL20_FRAMEWORK, homedir); - if ((rc > 0) && (rc < sizeof (framework))) { + if ((rc > 0) && (rc < (int) sizeof(framework))) { Loaded_SDL20 = dlopen(framework, RTLD_LOCAL|RTLD_NOW); } } @@ -942,17 +1342,19 @@ static char loaderror[256]; #elif defined(__unix__) #include #define SDL20_LIBNAME "libSDL2-2.0.so.0" - #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,9) + #define SDL20_REQUIRED_VER SDL_VERSIONNUM(2,0,7) static void *Loaded_SDL20 = NULL; #define LoadSDL20Library() ((Loaded_SDL20 = dlopen(SDL20_LIBNAME, RTLD_LOCAL|RTLD_NOW)) != NULL) #define LookupSDL20Sym(sym) dlsym(Loaded_SDL20, sym) #define CloseSDL20Library() { if (Loaded_SDL20) { dlclose(Loaded_SDL20); Loaded_SDL20 = NULL; } } - #define strcpy_fn strcpy - #define sprintf_fn sprintf #else #error Please define your platform. #endif +#ifndef DIRSEP +#define DIRSEP "/" +#endif + static void * LoadSDL20Symbol(const char *fn, int *okay) { @@ -960,7 +1362,8 @@ LoadSDL20Symbol(const char *fn, int *okay) if (*okay) { /* only bother trying if we haven't previously failed. */ retval = LookupSDL20Sym(fn); if (retval == NULL) { - sprintf_fn(loaderror, "%s missing in SDL2 library.", fn); + char *p = SDL12COMPAT_stpcpy(loaderror, fn); + SDL12COMPAT_stpcpy(p, " missing in SDL2 library."); *okay = 0; } } @@ -975,77 +1378,479 @@ UnloadSDL20(void) CloseSDL20Library(); } -static int -LoadSDL20(void) +typedef struct QuirkEntryType { - int okay = 1; - if (!Loaded_SDL20) { - okay = LoadSDL20Library(); - if (!okay) { - strcpy_fn(loaderror, "Failed loading SDL2 library."); + const char *exe_name; + const char *hint_name; + const char *hint_value; +} QuirkEntryType; + +static QuirkEntryType quirks[] = { +#if defined(__unix__) + /* freedroid use RLE-encoded surfaces, where SDL-1.2 didn't preserve destination alpha. This works around an sdl12-compat incompatibility. */ + {"freedroid", "SDL12COMPAT_PRESERVE_DEST_ALPHA", "0"}, + + /* Awesomenauts uses Cg, and does weird things with the GL context. */ + {"Awesomenauts.bin.x86", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"Awesomenauts.bin.x86", "SDL12COMPAT_FORCE_GL_SWAPBUFFER_CONTEXT", "1"}, + + /* Braid uses Cg, which uses glXGetProcAddress(). */ + {"braid", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* Closure uses Cg, and doesn't render anything with OpenGL scaling. */ + {"Closure.bin.x86", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"Closure.bin.x86_64", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* GOG's DOSBox builds have architecture-specific filenames. */ + {"dosbox", "SDL12COMPAT_USE_KEYBOARD_LAYOUT", "0"}, + {"dosbox_i686", "SDL12COMPAT_USE_KEYBOARD_LAYOUT", "0"}, + {"dosbox_x86_64", "SDL12COMPAT_USE_KEYBOARD_LAYOUT", "0"}, + + /* Cave Story's original doukutsu.bin misuses audio. */ + /* The later doukutsu_32bits and doukutsu_64bits work fine, however. */ + {"doukutsu.bin", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + + /* Tucnak's 1.2 target wants to render and run its event loop from + background threads, which upsets OpenGL. Force software renderer and + x11. If you want Wayland, etc, use tuknak's existing SDL2 target. */ + {"tucnak", "SDL_VIDEODRIVER", "x11"}, + {"tucnak", "SDL_RENDER_DRIVER", "software"}, + {"tucnak", "SDL_FRAMEBUFFER_ACCELERATION", "false"}, + + /* looks for X11 display and does unnecessary X11 things. Causes problems with SDL2/x11. */ + /* also has some issue with audio conversion I didn't look more closely at. */ + {"fillets", "SDL12COMPAT_ALLOW_SYSWM", "0"}, + {"fillets", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + + /* Hyperspace Delivery Boy relies on the exact imprecision of the format conversion in some + earlier versions of SDL 1.2. It also recommends 16-bit in the README, so force it. */ + {"hdb", "SDL12COMPAT_MAX_BPP", "16"}, + + /* Mark of the Ninja doesn't work with OpenGL scaling */ + {"ninja-bin32", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"ninja-bin64", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* Misuses SDL_AudioCVT */ + {"pink-pony", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + {"pink-pony.bin", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + + /* doesn't render with GL scaling enabled */ + {"scorched3d", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"scorched3dc", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* Trine (the old Humble Bundle version from 2011) doesn't render in-game with GL scaling enabled. */ + {"trine-bin32", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"trine-bin64", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* Trine (the old Humble Bundle version from 2011)'s launcher needs X11, and needs XInitThreads _before_ GTK+ gets in there. */ + {"trine-launcher32", "SDL_VIDEODRIVER", "x11"}, + {"trine-launcher32", "SDL12COMPAT_FORCE_XINITTHREADS", "1"}, + {"trine-launcher64", "SDL_VIDEODRIVER", "x11"}, + {"trine-launcher64", "SDL12COMPAT_FORCE_XINITTHREADS", "1"}, + + /* boswars has a bug where SDL_AudioCVT must not require extra buffer space. See Issue #232. */ + {"boswars", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + + /* Loki HOMM3 */ + {"heroes3.dynamic", "SDL12COMPAT_COMPATIBILITY_AUDIOCVT", "1"}, + + /* grafx2 tries to do all sorts of stuff by talking directly to the X server, causing problems. */ + {"grafx2", "SDL12COMPAT_ALLOW_SYSWM", "0"}, + + /* The 32-bit Steam build only of Multiwinia Quits but doesn't re-Init */ + {"multiwinia.bin.x86", "SDL12COMPAT_NO_QUIT_VIDEO", "1"}, + + /* SimCity 3000 tries to call SDL_DestroyMutex after we have been unloaded */ + {"sc3u.dynamic", "SDL12COMPAT_NO_UNLOAD", "1"}, + + /* Loki Soldier of Fortune - Sliding/ice-skating when moving if framerate is too high */ + {"sof-bin", "SDL12COMPAT_SYNC_TO_VBLANK", "1"}, + {"sof-bin", "SDL12COMPAT_MAX_FPS", "120"}, + + /* Loki Unreal Tournament '99 runs at hyperspeed if the framerate is too high. Force it to vsync. You should use the newer OldUnreal binaries with SDL2 instead! */ + {"ut-bin", "SDL12COMPAT_SYNC_TO_VBLANK", "1"}, + {"ut-bin", "SDL12COMPAT_MAX_FPS", "120"}, + + /* Jamestown (the old Humble Bundle version from 2011) doesn't render in-game with GL scaling enabled. */ + {"Jamestown-x86", "SDL12COMPAT_OPENGL_SCALING", "0"}, + {"Jamestown-amd64", "SDL12COMPAT_OPENGL_SCALING", "0"}, + + /* Sacred Gold manually frees SDL surface pixels */ + {"sacred", "SDL_SURFACE_MALLOC", "1"}, + {"sacred", "SDL_VIDEODRIVER", "x11"}, + + /* Creatures Internet Edition calls SDL_GetWMInfo() and exit if it fails */ + {"lc2e", "SDL_VIDEODRIVER", "x11"}, + +#elif defined(_WIN32) + // Penumbra: Overture doesn't like GL scaling. + {"Penumbra.exe", "SDL12COMPAT_OPENGL_SCALING", "0"}, + +#else + /* TODO: Add any quirks needed for this system. */ + + /* A dummy entry to keep compilers happy. */ + {"", "", "0"} +#endif +}; + +#ifdef __linux__ +static void OS_GetExeName(char *buf, const unsigned maxpath) { + int ret; + buf[0] = '\0'; + ret = readlink("/proc/self/exe", buf, maxpath); + (void)ret; +} +#elif defined(_WIN32) +static void OS_GetExeName(char *buf, const unsigned maxpath) { + buf[0] = '\0'; + GetModuleFileNameA(NULL, buf, maxpath); +} +#elif defined(__OS2__) +static void OS_GetExeName(char *buf, const unsigned maxpath) { + PPIB pib; + DosGetInfoBlocks(NULL, &pib); + buf[0] = '\0'; + DosQueryModuleName(pib->pib_hmte, maxpath, buf); +} +#elif defined(__APPLE__) || defined(__FREEBSD__) +static void OS_GetExeName(char *buf, const unsigned maxpath) { + const char *progname = getprogname(); + if (progname != NULL) { + strlcpy(buf, progname, maxpath); + } else { + buf[0] = '\0'; + } +} +#else +#warning Please implement this for your platform. +static void OS_GetExeName(char *buf, const unsigned maxpath) { + buf[0] = '\0'; + (void)maxpath; +} +#endif + +static const char * +SDL12Compat_GetExeName(void) +{ + static const char *exename = NULL; + if (exename == NULL) { + static char path_buf[SDL12_MAXPATH]; + static char *base_path; + OS_GetExeName(path_buf, SDL12_MAXPATH); + base_path = SDL20_strrchr(path_buf, *DIRSEP); + if (base_path) { + /* We have a '\\' component. */ + exename = base_path + 1; } else { - #define SDL20_SYM(rc,fn,params,args,ret) SDL20_##fn = (SDL20_##fn##_t) LoadSDL20Symbol("SDL_" #fn, &okay); - #include "SDL20_syms.h" - if (okay) { - SDL_version v; - SDL20_GetVersion(&v); - okay = (SDL_VERSIONNUM(v.major,v.minor,v.patch) >= SDL20_REQUIRED_VER); - if (!okay) { - sprintf_fn(loaderror, "SDL2 %d.%d.%d library is too old.", v.major, v.minor, v.patch); - } else { - #if defined(__DATE__) && defined(__TIME__) - SDL20_Log("sdl12-compat, built on " __DATE__ " at " __TIME__ ", talking to SDL2 %d.%d.%d", v.major, v.minor, v.patch); - #else - SDL20_Log("sdl12-compat, talking to SDL2 %d.%d.%d", v.major, v.minor, v.patch); - #endif - } - } - if (!okay) { - UnloadSDL20(); - } + /* No slashes, return the whole module filanem. */ + exename = path_buf; } } - return okay; + return exename; } -#if defined(_WIN32) -static void error_dialog(const char *errorMsg) +static const char * +SDL12Compat_GetHint(const char *name) { - MessageBoxA(NULL, errorMsg, "Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); + return SDL12COMPAT_getenv_unsafe(name); } -#elif defined(__APPLE__) -extern void error_dialog(const char *errorMsg); -#else -static void error_dialog(const char *errorMsg) + +static SDL_bool +SDL12Compat_GetHintBoolean(const char *name, SDL_bool default_value) { - fprintf(stderr, "%s\n", errorMsg); + const char *val = SDL12Compat_GetHint(name); + + if (!val) { + return default_value; + } + + return (SDL20_atoi(val) != 0) ? SDL_TRUE : SDL_FALSE; } -#endif -#if defined(__GNUC__) && !defined(_WIN32) -static void dllinit(void) __attribute__((constructor)); -static void dllinit(void) +static float +SDL12Compat_GetHintFloat(const char *name, float default_value) { - if (!LoadSDL20()) { - error_dialog(loaderror); - abort(); + const char *val = SDL12Compat_GetHint(name); + + if (!val) { + return default_value; } + + return (float) SDL20_atof(val); } -static void dllquit(void) __attribute__((destructor)); -static void dllquit(void) + +static int +SDL12Compat_GetHintInt(const char *name, int default_value) { - UnloadSDL20(); + const char *val = SDL12Compat_GetHint(name); + + if (!val) { + return default_value; + } + + return SDL20_atoi(val); } -#elif defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__)) -#if defined(_MSC_VER) && !defined(__FLTUSED__) -#define __FLTUSED__ -__declspec(selectany) int _fltused = 1; -#endif -#if defined(__MINGW32__) -#define _DllMainCRTStartup DllMainCRTStartup -#endif -#if defined(__WATCOMC__) +/* DO NOT USE SDL2 FUNCTIONS IN HERE! */ +static void +SDL12Compat_ApplyQuirks(SDL_bool force_x11) +{ + const char *exe_name = SDL12Compat_GetExeName(); + int i; + + if (WantDebugLogging) { + const char *lead = "sdl12-compat: This app appears to be named:"; + char msg[256]; + if ((SDL12COMPAT_strlen(lead) + SDL12COMPAT_strlen(exe_name) + 2) <= (int) (sizeof (msg))) { + char *p = msg; + p = SDL12COMPAT_stpcpy(p, lead); + p = SDL12COMPAT_stpcpy(p, " "); + p = SDL12COMPAT_stpcpy(p, exe_name); + SDL12COMPAT_LogAtStartup(msg); + } else { + SDL12COMPAT_LogAtStartup(lead); + SDL12COMPAT_LogAtStartup(exe_name); + } + } + + #ifdef __linux__ + if (force_x11) { + const char *videodriver_env = SDL12COMPAT_GetEnvAtStartup("SDL_VIDEODRIVER"); + if (videodriver_env && !SDL12COMPAT_strequal(videodriver_env, "x11")) { + if (WantDebugLogging) { + SDL12COMPAT_LogAtStartup("sdl12-compat: This app looks like it requires X11, but the SDL_VIDEODRIVER environment variable is currently set to:"); + SDL12COMPAT_LogAtStartup(""); + SDL12COMPAT_LogAtStartup(videodriver_env); + SDL12COMPAT_LogAtStartup(""); + SDL12COMPAT_LogAtStartup("If you have issues, try setting SDL_VIDEODRIVER=x11"); + + } + } else { + if (WantDebugLogging) { + SDL12COMPAT_LogAtStartup("sdl12-compat: We are forcing this app to use X11, because it probably talks to an X server directly, outside of SDL. If possible, this app should be fixed, to be compatible with Wayland, etc."); + } + SDL12COMPAT_SetEnvAtStartup("SDL_VIDEODRIVER", "x11"); + } + } + #else + (void)force_x11; + #endif + + if (*exe_name == '\0') { + return; + } + + for (i = 0; i < (int) SDL_arraysize(quirks); i++) { + if (SDL12COMPAT_strequal(exe_name, quirks[i].exe_name)) { + const char *var = SDL12COMPAT_GetEnvAtStartup(quirks[i].hint_name); + if (!var) { + if (WantDebugLogging) { + char msg[256]; + char *p = msg; + p = SDL12COMPAT_stpcpy(p, "sdl12-compat: Applying compatibility quirk "); + p = SDL12COMPAT_stpcpy(p, quirks[i].hint_name); + p = SDL12COMPAT_stpcpy(p, "=\""); + p = SDL12COMPAT_stpcpy(p, quirks[i].hint_value); + p = SDL12COMPAT_stpcpy(p, "\"."); + SDL12COMPAT_LogAtStartup(msg); + } + SDL12COMPAT_SetEnvAtStartup(quirks[i].hint_name, quirks[i].hint_value); + } else { + if (WantDebugLogging) { + char msg[256]; + char varbuf[32]; + char *p = msg; + int j; + + /* copy the start of untrusted string var to a small array to prevent buffer overflows */ + for (j = 0; j < ((int) SDL_arraysize(varbuf)); j++) { + varbuf[j] = var[j]; + if (var[j] == 0) { + break; + } + } + + if (j == SDL_arraysize(varbuf)) { /* truncate and terminate the string if necessary. */ + SDL12COMPAT_stpcpy(varbuf + (SDL_arraysize(varbuf) - 6), "[...]"); + } + + p = SDL12COMPAT_stpcpy(p, "sdl12-compat: Not applying compatibility quirk "); + p = SDL12COMPAT_stpcpy(p, quirks[i].hint_name); + p = SDL12COMPAT_stpcpy(p, "=\""); + p = SDL12COMPAT_stpcpy(p, quirks[i].hint_value); + p = SDL12COMPAT_stpcpy(p, "\" due to environment variable override (\""); + p = SDL12COMPAT_stpcpy(p, varbuf); + p = SDL12COMPAT_stpcpy(p, "\")."); + SDL12COMPAT_LogAtStartup(msg); + } + } + } + } +} + +/* DO NOT CALL THINGS THAT USE SDL ALLOCATORS HERE. It runs before main(), so app-supplied allocators are not set at this point. + This means no SDL_Log, no hint subsystem, nothing that might call SDL_SetError! In fact, favor code in this file, using + platform-specific #ifdefs, to calling into SDL2 at all, if you can help it. */ +static int +LoadSDL20(void) +{ + int okay = 1; + if (!Loaded_SDL20) { + SDL_bool force_x11 = SDL_FALSE; + + #ifdef __linux__ + void *global_symbols = dlopen(NULL, RTLD_LOCAL|RTLD_NOW); + + /* Use linked libraries to detect what quirks we are likely to need */ + if (global_symbols != NULL) { + if (dlsym(global_symbols, "glxewInit") != NULL) { /* GLEW (e.g. Frogatto, SLUDGE) */ + force_x11 = SDL_TRUE; + } else if (dlsym(global_symbols, "cgGLEnableProgramProfiles") != NULL) { /* NVIDIA Cg (e.g. Awesomenauts, Braid) */ + force_x11 = SDL_TRUE; + } else if (dlsym(global_symbols, "_Z7ssgInitv") != NULL) { /* ::ssgInit(void) in plib (e.g. crrcsim) */ + force_x11 = SDL_TRUE; + } + dlclose(global_symbols); + } + #endif + + WantDebugLogging = SDL12COMPAT_CheckDebugLogging(); + + okay = LoadSDL20Library(); + if (!okay) { + SDL12COMPAT_stpcpy(loaderror, "sdl12-compat: Failed loading SDL2 library."); + } else { + #define SDL20_SYM(rc,fn,params,args,ret) SDL20_##fn = (SDL20_##fn##_t) LoadSDL20Symbol("SDL_" #fn, &okay); + #include "SDL20_syms.h" + if (okay) { + char sdl2verstr[16]; + char sdl2reqverstr[16]; + char sdl12compatverstr[16]; + SDL_version v; + SDL_version reqv; + char *p; + + SDL20_GetVersion(&v); + + reqv.major = (SDL20_REQUIRED_VER / 1000); + reqv.minor = ((SDL20_REQUIRED_VER % 1000) / 100); + reqv.patch = (SDL20_REQUIRED_VER % 100); + + #define SETVERSTR(str, major, minor, micro) { \ + char value[16]; \ + p = str; \ + SDL12COMPAT_itoa(value, major); p = SDL12COMPAT_stpcpy(p, value); *p++ = '.'; \ + SDL12COMPAT_itoa(value, minor); p = SDL12COMPAT_stpcpy(p, value); *p++ = '.'; \ + SDL12COMPAT_itoa(value, micro); p = SDL12COMPAT_stpcpy(p, value); \ + } + + SETVERSTR(sdl2verstr, v.major, v.minor, v.patch); + SETVERSTR(sdl2reqverstr, reqv.major, reqv.minor, reqv.patch); + SETVERSTR(sdl12compatverstr, 1, 2, SDL12_COMPAT_VERSION); + + #undef SETVERSTR + + LinkedSDL2VersionInt = SDL_VERSIONNUM(v.major, v.minor, v.patch); + okay = (LinkedSDL2VersionInt >= SDL20_REQUIRED_VER); + if (!okay) { + p = loaderror; + p = SDL12COMPAT_stpcpy(p, "sdl12-compat "); + p = SDL12COMPAT_stpcpy(p, sdl12compatverstr); + p = SDL12COMPAT_stpcpy(p, ": SDL2 library is too old (have "); + p = SDL12COMPAT_stpcpy(p, sdl2verstr); + p = SDL12COMPAT_stpcpy(p, ", but need at least "); + p = SDL12COMPAT_stpcpy(p, sdl2reqverstr); + p = SDL12COMPAT_stpcpy(p, ")."); + } else { + if (WantDebugLogging) { + char debugmsg[128]; /* can't use SDL log or malloc, just write to a stack buffer and do a simple platform-specific logging. */ + + p = debugmsg; + p = SDL12COMPAT_stpcpy(p, "sdl12-compat "); + p = SDL12COMPAT_stpcpy(p, sdl12compatverstr); + p = SDL12COMPAT_stpcpy(p, ", "); + + #if defined(__DATE__) && defined(__TIME__) + p = SDL12COMPAT_stpcpy(p, "built on " __DATE__ " at " __TIME__ ", "); + #endif + + p = SDL12COMPAT_stpcpy(p, "talking to SDL2 "); + p = SDL12COMPAT_stpcpy(p, sdl2verstr); + + SDL12COMPAT_LogAtStartup(debugmsg); + } + + SDL12Compat_ApplyQuirks(force_x11); /* Apply and maybe print a list of any enabled quirks. */ + + #ifdef __linux__ + { + const char *envvar = SDL12COMPAT_GetEnvAtStartup("SDL_VIDEODRIVER"); + if (envvar && SDL12COMPAT_strequal(envvar, "x11")) { + envvar = SDL12COMPAT_GetEnvAtStartup("SDL12COMPAT_FORCE_XINITTHREADS"); + if (envvar && (SDL12COMPAT_atoi(envvar) != 0)) { + void *lib = dlopen("libX11.so.6", RTLD_GLOBAL|RTLD_NOW); + if (lib) { + int (*pXInitThreads)(void) = (int(*)(void)) dlsym(lib, "XInitThreads"); + if (pXInitThreads) { + pXInitThreads(); + } + /* leave the library open, so the XInitThreads sticks. */ + } + } + } + } + #endif + } + } + if (!okay) { + UnloadSDL20(); + } + } + } + return okay; +} + +#if defined(_WIN32) +static void error_dialog(const char *errorMsg) +{ + MessageBoxA(NULL, errorMsg, "Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); +} +#elif defined(__APPLE__) +extern void error_dialog(const char *errorMsg); +#else +static void error_dialog(const char *errorMsg) +{ + fprintf(stderr, "%s\n", errorMsg); +} +#endif + +#if defined(__GNUC__) && !defined(_WIN32) +static void dllinit(void) __attribute__((constructor)); +static void dllinit(void) +{ + if (!LoadSDL20()) { + error_dialog(loaderror); + abort(); + } +} +static void dllquit(void) __attribute__((destructor)); +static void dllquit(void) +{ + if (!SDL12Compat_GetHintBoolean("SDL12COMPAT_NO_UNLOAD", SDL_FALSE)) { + UnloadSDL20(); + } +} + +#elif defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__)) +#if defined(_MSC_VER) && !defined(__FLTUSED__) +#define __FLTUSED__ +__declspec(selectany) int _fltused = 1; +#endif +#if defined(__MINGW32__) +#define _DllMainCRTStartup DllMainCRTStartup +#endif +#if defined(__WATCOMC__) #define _DllMainCRTStartup LibMain #endif BOOL WINAPI _DllMainCRTStartup(HANDLE dllhandle, DWORD reason, LPVOID reserved) @@ -1089,41 +1894,33 @@ unsigned _System LibMain(unsigned hmod, unsigned termination) } #else - #error Please define your platform + #error Please define an init procedure for your platform. #endif +/* Forward declarations */ +DECLSPEC12 void SDLCALL SDL_CloseAudio(void); +DECLSPEC12 void SDLCALL SDL_PumpEvents(void); + #ifdef _WIN32 /* SDL_main functions: * SDL2 doesn't define SDL_MAIN_NEEDED for _WIN32, * therefore no need to call SDL_SetMainReady(). */ -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetModuleHandle(void *handle) { (void) handle;/* handled internally by SDL2 - nothing to do.. */ } - -DECLSPEC int SDLCALL -SDL_RegisterApp(char *name, Uint32 style, void *hInst) -{ - (void) name; (void) style; (void) hInst; - return 0; -} - -DECLSPEC void SDLCALL -SDL_UnregisterApp(void) -{ -} #endif -DECLSPEC const SDL_version * SDLCALL +DECLSPEC12 const SDL_version * SDLCALL SDL_Linked_Version(void) { static const SDL_version version = { 1, 2, SDL12_COMPAT_VERSION }; return &version; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_sscanf(const char *text, const char *fmt, ...) { int retval; @@ -1134,7 +1931,7 @@ SDL_sscanf(const char *text, const char *fmt, ...) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...) { int retval; @@ -1145,7 +1942,7 @@ SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...) return retval; } -DECLSPEC void * SDLCALL +DECLSPEC12 void * SDLCALL SDL_revcpy(void *_dst, const void *_src, size_t len) { if (len > 0) { @@ -1161,7 +1958,7 @@ SDL_revcpy(void *_dst, const void *_src, size_t len) /* SDL2 doesn't have MMXExt / 3dNowExt. */ -#if defined(__GNUC__) && defined(__i386__) +#if (defined(__GNUC__) || defined(__llvm__)) && defined(__i386__) #define cpuid(func, a, b, c, d) \ __asm__ __volatile__ ( \ " pushl %%ebx \n" \ @@ -1170,7 +1967,7 @@ SDL_revcpy(void *_dst, const void *_src, size_t len) " movl %%ebx, %%esi \n" \ " popl %%ebx \n" : \ "=a" (a), "=S" (b), "=c" (c), "=d" (d) : "a" (func)) -#elif defined(__GNUC__) && defined(__x86_64__) +#elif (defined(__GNUC__) || defined(__llvm__)) && defined(__x86_64__) #define cpuid(func, a, b, c, d) \ __asm__ __volatile__ ( \ " pushq %%rbx \n" \ @@ -1213,7 +2010,7 @@ static int get_cpu_ext_features(void) { if (SDL20_HasMMX()) { int a, b, c, d; cpuid(0x80000000, a, b, c, d); - if (a >= 0x80000001) { + if ((unsigned int)a >= 0x80000001) { cpuid(0x80000001, a, b, c, d); cpu_ext_features = d; } @@ -1222,214 +2019,562 @@ static int get_cpu_ext_features(void) { return cpu_ext_features; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_HasMMXExt(void) { return (get_cpu_ext_features() & 0x00400000)? SDL_TRUE : SDL_FALSE; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_Has3DNowExt(void) { return (get_cpu_ext_features() & 0x40000000)? SDL_TRUE : SDL_FALSE; } -DECLSPEC SDL_Joystick * SDLCALL -SDL_JoystickOpen(int device_index) + +/* SDL 1.2 has no concept of joystick hotplug, so you only have + access to sticks seen during SDL_Init() and if one of them is + unplugged, all you can do is fail to open it, or stop reporting + new input. + + Also, we optionally let the app see SDL2 Game Controllers as + SDL 1.2 joysticks, which gives them a stable button/axis layout + and can deal with other hardware quirks. +*/ + +static SDL_bool +BogusJoystick(SDL12_Joystick *stick12) { - size_t i; - SDL20_LockJoysticks(); - for (i = 0; i < SDL_arraysize(JoystickOpenList); i++) { - if (JoystickOpenList[i].joystick == NULL) { - break; - } + const int device_index = (int) (stick12 - JoystickList); + if (!stick12 || (device_index < 0) || (device_index >= NumJoysticks)) { + SDL20_SetError("Invalid SDL_Joystick"); + return SDL_TRUE; } + return SDL_FALSE; +} - if (i == SDL_arraysize(JoystickOpenList)) { - SDL20_UnlockJoysticks(); - SDL20_SetError("Too many open joysticks"); - return NULL; +static SDL_bool +BogusJoystickIndex(const int device_index) +{ + if ((device_index < 0) || (device_index >= NumJoysticks)) { + SDL20_SetError("Invalid SDL_Joystick"); + return SDL_TRUE; } + return SDL_FALSE; +} - JoystickOpenList[i].joystick = SDL20_JoystickOpen(device_index); - if (JoystickOpenList[i].joystick) { - JoystickOpenList[i].device_index = device_index; +static int +FindJoystick12IndexByInstanceId(const SDL_JoystickID instance_id) +{ + int i; + for (i = 0; i < NumJoysticks; i++) { + if (JoystickList[i].instance_id == instance_id) { + return (SDL20_AtomicGet(&JoystickList[i].refcount) > 0) ? i : -1; + } } - - SDL20_UnlockJoysticks(); - return JoystickOpenList[i].joystick; + return -1; } -DECLSPEC void SDLCALL -SDL_JoystickClose(SDL_Joystick *joystick) +static void +Init12Joystick(void) { - size_t i; + int numsticks20; + int i; + + JoysticksAreGameControllers = SDL12Compat_GetHintBoolean("SDL12COMPAT_USE_GAME_CONTROLLERS", SDL_FALSE); + NumJoysticks = 0; + SDL20_LockJoysticks(); - for (i = 0; i < SDL_arraysize(JoystickOpenList); i++) { - if (JoystickOpenList[i].joystick == joystick) { - break; - } + + numsticks20 = SDL20_NumJoysticks(); + + if (numsticks20 > 255) { + numsticks20 = 255; /* it has to fit in a Uint8 for the joystick events. */ } - if (i < SDL_arraysize(JoystickOpenList)) { - JoystickOpenList[i].joystick = NULL; + JoystickList = (SDL12_Joystick *) ((numsticks20 > 0) ? SDL20_calloc(numsticks20, sizeof (SDL12_Joystick)) : NULL); + if (JoystickList != NULL) { + for (i = 0; i < numsticks20; i++) { + const char *name; + SDL_bool opened; + + if (JoysticksAreGameControllers && !SDL20_IsGameController(i)) { + continue; + } + + name = JoysticksAreGameControllers ? SDL20_GameControllerNameForIndex(i) : SDL20_JoystickNameForIndex(i); + if (!name) { + name = JoysticksAreGameControllers ? "Generic SDL2 Game Controller" : "Generic SDL2 Joystick"; + } + + JoystickList[NumJoysticks].name = SDL20_strdup(name); + if (!JoystickList[NumJoysticks].name) { + continue; + } + + if (JoysticksAreGameControllers) { + JoystickList[NumJoysticks].dev.controller = SDL20_GameControllerOpen(i); + opened = JoystickList[NumJoysticks].dev.controller != NULL ? SDL_TRUE : SDL_FALSE; + } else { + JoystickList[NumJoysticks].dev.joystick = SDL20_JoystickOpen(i); + opened = JoystickList[NumJoysticks].dev.joystick != NULL ? SDL_TRUE : SDL_FALSE; + } + + if (!opened) { + SDL20_free(JoystickList[NumJoysticks].name); + JoystickList[NumJoysticks].name = NULL; + } + + JoystickList[NumJoysticks].instance_id = SDL20_JoystickGetDeviceInstanceID(i); + + NumJoysticks++; + } } SDL20_UnlockJoysticks(); - SDL20_JoystickClose(joystick); + if ((NumJoysticks == 0) && (JoystickList)) { + SDL20_free(JoystickList); + JoystickList = NULL; + } else if (NumJoysticks < numsticks20) { /* shrink the array if possible. */ + void *ptr = SDL20_realloc(JoystickList, sizeof (*JoystickList) * NumJoysticks); + if (ptr) { + JoystickList = (SDL12_Joystick *) ptr; + } + } } -DECLSPEC const char * SDLCALL -SDL_JoystickName(int device_index) +static void +Quit12Joystick(void) { - return SDL20_JoystickNameForIndex(device_index); + int i; + for (i = 0; i < NumJoysticks; i++) { + SDL12_Joystick *stick12 = &JoystickList[i]; + if (JoysticksAreGameControllers) { + SDL20_GameControllerClose(stick12->dev.controller); + } else { + SDL20_JoystickClose(stick12->dev.joystick); + } + SDL20_free(stick12->name); + } + + SDL20_free(JoystickList); + JoystickList = NULL; + NumJoysticks = 0; } -DECLSPEC int SDLCALL -SDL_JoystickIndex(SDL_Joystick *joystick) +DECLSPEC12 int SDLCALL +SDL_NumJoysticks(void) { - size_t i; - SDL20_LockJoysticks(); - for (i = 0; i < SDL_arraysize(JoystickOpenList); i++) { - if (JoystickOpenList[i].joystick == joystick) { - break; - } - } + return NumJoysticks; +} - if (i < SDL_arraysize(JoystickOpenList)) { - SDL20_UnlockJoysticks(); - return JoystickOpenList[i].device_index; +DECLSPEC12 int SDLCALL +SDL_JoystickNumAxes(SDL12_Joystick *stick12) +{ + if (BogusJoystick(stick12)) { + return -1; } - - SDL20_UnlockJoysticks(); - return SDL20_SetError("Can't find joystick"); + return JoysticksAreGameControllers ? (SDL_CONTROLLER_AXIS_MAX + 1) : SDL20_JoystickNumAxes(stick12->dev.joystick); } -DECLSPEC int SDLCALL -SDL_JoystickOpened(int device_index) +DECLSPEC12 int SDLCALL +SDL_JoystickNumBalls(SDL12_Joystick *stick12) { - int retval = 0; - size_t i; - SDL20_LockJoysticks(); - for (i = 0; i < SDL_arraysize(JoystickOpenList); i++) { - if ((JoystickOpenList[i].joystick) && (JoystickOpenList[i].device_index == device_index)) { - retval = 1; - break; - } + if (BogusJoystick(stick12)) { + return -1; } - SDL20_UnlockJoysticks(); - return retval; + return JoysticksAreGameControllers ? 0 : SDL20_JoystickNumBalls(stick12->dev.joystick); } -static SDL_PixelFormat * -PixelFormat12to20(SDL_PixelFormat *format20, SDL_Palette *palette20, const SDL12_PixelFormat *format12) +DECLSPEC12 int SDLCALL +SDL_JoystickNumHats(SDL12_Joystick *stick12) { - if (format12->palette) { - palette20->ncolors = format12->palette->ncolors; - palette20->colors = format12->palette->colors; - palette20->version = 1; - palette20->refcount = 1; - format20->palette = palette20; - } else { - format20->palette = NULL; + if (BogusJoystick(stick12)) { + return -1; } + return JoysticksAreGameControllers ? 0 : SDL20_JoystickNumHats(stick12->dev.joystick); +} - format20->format = SDL20_MasksToPixelFormatEnum(format12->BitsPerPixel, format12->Rmask, format12->Gmask, format12->Bmask, format12->Amask); - format20->BitsPerPixel = format12->BitsPerPixel; - format20->BytesPerPixel = format12->BytesPerPixel; - format20->Rmask = format12->Rmask; - format20->Gmask = format12->Gmask; - format20->Bmask = format12->Bmask; - format20->Amask = format12->Amask; - format20->Rloss = format12->Rloss; - format20->Gloss = format12->Gloss; - format20->Bloss = format12->Bloss; - format20->Aloss = format12->Aloss; - format20->Rshift = format12->Rshift; - format20->Gshift = format12->Gshift; - format20->Bshift = format12->Bshift; - format20->Ashift = format12->Ashift; - format20->refcount = 1; - format20->next = NULL; - return format20; +DECLSPEC12 int SDLCALL +SDL_JoystickNumButtons(SDL12_Joystick *stick12) +{ + if (BogusJoystick(stick12)) { + return -1; + } + return JoysticksAreGameControllers ? (SDL_CONTROLLER_BUTTON_MAX + 1) : SDL20_JoystickNumButtons(stick12->dev.joystick); } -static SDL12_PixelFormat * -PixelFormat20to12(SDL12_PixelFormat *format12, SDL12_Palette *palette12, const SDL_PixelFormat *format20) +DECLSPEC12 void SDLCALL +SDL_JoystickUpdate(void) { - if (format20->palette) { - palette12->ncolors = format20->palette->ncolors; - palette12->colors = format20->palette->colors; - format12->palette = palette12; + if (JoysticksAreGameControllers) { + SDL20_GameControllerUpdate(); } else { - format12->palette = NULL; + SDL20_JoystickUpdate(); } - - format12->BitsPerPixel = format20->BitsPerPixel; - format12->BytesPerPixel = format20->BytesPerPixel; - format12->Rloss = format20->Rloss; - format12->Gloss = format20->Gloss; - format12->Bloss = format20->Bloss; - format12->Aloss = format20->Aloss; - format12->Rshift = format20->Rshift; - format12->Gshift = format20->Gshift; - format12->Bshift = format20->Bshift; - format12->Ashift = format20->Ashift; - format12->Rmask = format20->Rmask; - format12->Gmask = format20->Gmask; - format12->Bmask = format20->Bmask; - format12->Amask = format20->Amask; - format12->colorkey = 0; /* this is a surface, not pixelformat, properties in SDL2. */ - format12->alpha = 255; /* this is a surface, not pixelformat, properties in SDL2. */ - return format12; } -static int -GetVideoDisplay(void) +DECLSPEC12 int SDLCALL +SDL_JoystickEventState(int state) { - const char *variable; - variable = SDL20_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY"); - if (!variable) { - variable = SDL20_getenv("SDL_VIDEO_FULLSCREEN_HEAD"); + switch (state) { + case SDL_DISABLE: + case SDL_ENABLE: + if (JoysticksAreGameControllers) { + SDL20_JoystickEventState(state); + return SDL20_GameControllerEventState(state); + } + SDL20_GameControllerEventState(state); + return SDL20_JoystickEventState(state); + default: /* everything else is treated as SDL_QUERY */ + /* we turn joystick and controller event state off together, so we only have to query one. */ + return SDL20_JoystickEventState(SDL_QUERY); } - if (variable) { - return SDL20_atoi(variable); - } else { +} + +DECLSPEC12 Sint16 SDLCALL +SDL_JoystickGetAxis(SDL12_Joystick *stick12, int axis) +{ + if (BogusJoystick(stick12)) { return 0; } + return JoysticksAreGameControllers ? SDL20_GameControllerGetAxis(stick12->dev.controller, axis) : SDL20_JoystickGetAxis(stick12->dev.joystick, axis); } -/* This sets up VideoModes and VideoModesCount. You end up with arrays by pixel - format, each with a value that 1.2's SDL_ListModes() can return. */ +DECLSPEC12 Uint8 SDLCALL +SDL_JoystickGetHat(SDL12_Joystick *stick12, int hat) +{ + if (BogusJoystick(stick12)) { + return 0; + } + return JoysticksAreGameControllers ? 0 : SDL20_JoystickGetHat(stick12->dev.joystick, hat); +} + +DECLSPEC12 int SDLCALL +SDL_JoystickGetBall(SDL12_Joystick *stick12, int ball, int *dx, int *dy) +{ + if (BogusJoystick(stick12)) { + return 0; + } + if (JoysticksAreGameControllers) { + if (dx) { *dx = 0; } + if (dy) { *dy = 0; } + return SDL20_SetError("No joystick balls available"); + } + return SDL20_JoystickGetBall(stick12->dev.joystick, ball, dx, dy); +} + +DECLSPEC12 Uint8 SDLCALL +SDL_JoystickGetButton(SDL12_Joystick *stick12, int button) +{ + if (BogusJoystick(stick12)) { + return 0; + } + return JoysticksAreGameControllers ? SDL20_GameControllerGetButton(stick12->dev.controller, button) : SDL20_JoystickGetButton(stick12->dev.joystick, button); +} + +DECLSPEC12 SDL12_Joystick * SDLCALL +SDL_JoystickOpen(int device_index) +{ + if (BogusJoystickIndex(device_index)) { + return NULL; + } + + /* multiple opens just increments a refcount and returns the same object in SDL 1.2 Classic. */ + SDL20_AtomicAdd(&JoystickList[device_index].refcount, 1); + return &JoystickList[device_index]; +} + +DECLSPEC12 void SDLCALL +SDL_JoystickClose(SDL12_Joystick *stick12) +{ + if (!BogusJoystick(stick12)) { + /* we don't actually close anything here, just drop the refcount. */ + if (SDL20_AtomicAdd(&stick12->refcount, -1) == 0) { + SDL20_AtomicAdd(&stick12->refcount, 1); /* whoops, wasn't open, bounce it back to zero. */ + } + } +} + +DECLSPEC12 const char * SDLCALL +SDL_JoystickName(int device_index) +{ + return BogusJoystickIndex(device_index) ? NULL : JoystickList[device_index].name; +} + +DECLSPEC12 int SDLCALL +SDL_JoystickIndex(SDL12_Joystick *stick12) +{ + return BogusJoystick(stick12) ? -1 : (int) (stick12 - JoystickList); +} + +DECLSPEC12 int SDLCALL +SDL_JoystickOpened(int device_index) +{ + if (BogusJoystickIndex(device_index)) { + return 0; /* SDL 1.2 classic doesn't return an error here, either. */ + } + return SDL20_AtomicGet(&JoystickList[device_index].refcount) ? 1 : 0; +} + +static SDL_PixelFormatEnum +BPPToPixelFormat(unsigned bpp) +{ + #if !SDL_VERSION_ATLEAST(2,0,14) + #define SDL_PIXELFORMAT_XRGB8888 SDL_PIXELFORMAT_RGB888 + #endif + switch (bpp) { + case 8: return SDL_PIXELFORMAT_INDEX8; + case 16: return SDL_PIXELFORMAT_RGB565; + case 24: return SDL_PIXELFORMAT_BGR24; + case 32: return SDL_PIXELFORMAT_XRGB8888; + default: SDL20_SetError("Unsupported bits-per-pixel"); return SDL_PIXELFORMAT_UNKNOWN; + } +} + +static SDL_PixelFormat * +PixelFormat12to20(SDL_PixelFormat *format20, SDL_Palette *palette20, const SDL12_PixelFormat *format12) +{ + if (format12->palette) { + palette20->ncolors = format12->palette->ncolors; + palette20->colors = format12->palette->colors; + palette20->version = 1; + palette20->refcount = 1; + format20->palette = palette20; + } else { + format20->palette = NULL; + } + + format20->format = SDL20_MasksToPixelFormatEnum(format12->BitsPerPixel, format12->Rmask, format12->Gmask, format12->Bmask, format12->Amask); + format20->BitsPerPixel = format12->BitsPerPixel; + format20->BytesPerPixel = format12->BytesPerPixel; + + /* Paletted surfaces shouldn't have masks in SDL 2.0 */ + if (format12->palette) { + format20->Rmask = 0; + format20->Gmask = 0; + format20->Bmask = 0; + format20->Amask = 0; + format20->Rloss = 8; + format20->Gloss = 8; + format20->Bloss = 8; + format20->Aloss = 8; + format20->Rshift = 0; + format20->Gshift = 0; + format20->Bshift = 0; + format20->Ashift = 0; + } else { + format20->Rmask = format12->Rmask; + format20->Gmask = format12->Gmask; + format20->Bmask = format12->Bmask; + format20->Amask = format12->Amask; + format20->Rloss = format12->Rloss; + format20->Gloss = format12->Gloss; + format20->Bloss = format12->Bloss; + format20->Aloss = format12->Aloss; + format20->Rshift = format12->Rshift; + format20->Gshift = format12->Gshift; + format20->Bshift = format12->Bshift; + format20->Ashift = format12->Ashift; + } + format20->refcount = 1; + format20->next = NULL; + return format20; +} + +static SDL12_PixelFormat * +PixelFormat20to12(SDL12_PixelFormat *format12, SDL12_Palette *palette12, const SDL_PixelFormat *format20) +{ + if (format20->palette) { + palette12->ncolors = format20->palette->ncolors; + palette12->colors = format20->palette->colors; + format12->palette = palette12; + } else { + format12->palette = NULL; + } + + format12->BitsPerPixel = format20->BitsPerPixel; + format12->BytesPerPixel = format20->BytesPerPixel; + format12->Rloss = format20->Rloss; + format12->Gloss = format20->Gloss; + format12->Bloss = format20->Bloss; + format12->Aloss = format20->Aloss; + format12->Rshift = format20->Rshift; + format12->Gshift = format20->Gshift; + format12->Bshift = format20->Bshift; + format12->Ashift = format20->Ashift; + format12->Rmask = format20->Rmask; + format12->Gmask = format20->Gmask; + format12->Bmask = format20->Bmask; + format12->Amask = format20->Amask; + format12->colorkey = 0; /* this is a surface, not pixelformat, properties in SDL2. */ + format12->alpha = 255; /* this is a surface, not pixelformat, properties in SDL2. */ + return format12; +} + +static int +GetVideoDisplay(void) +{ + const char *variable; + variable = SDL12COMPAT_getenv_unsafe("SDL_VIDEO_FULLSCREEN_DISPLAY"); + if (!variable) { + variable = SDL12COMPAT_getenv_unsafe("SDL_VIDEO_FULLSCREEN_HEAD"); + } + if (variable) { + int preferred_display = SDL20_atoi(variable); + if (preferred_display < 0 || preferred_display >= SDL20_GetNumVideoDisplays()) { + return 0; + } + return SDL20_atoi(variable); + } + return 0; +} + +/* returns true if mode1 should sort before mode2 */ +static int +VidModeSizeGreater(SDL12_Rect *mode1, SDL12_Rect *mode2) +{ + if (mode1->w > mode2->w) { + return 1; + } + if (mode2->w > mode1->w) { + return 0; + } + return (mode1->h > mode2->h); +} + +static int +AddVidModeToList(VideoModeList *vmode, SDL12_Rect *mode, const Uint16 maxw, const Uint16 maxh) +{ + void *ptr = NULL; + int i; + + if ((maxw && (mode->w > maxw)) || (maxh && (mode->h > maxh))) { + return 0; /* clamp this one out as too big. */ + } + + /* make sure we don't have this one already (with a different refresh rate, etc). */ + for (i = 0; i < vmode->nummodes; i++) { + if ((vmode->modeslist12[i].w == mode->w) && (vmode->modeslist12[i].h == mode->h)) { + break; + } + } + + if (i < vmode->nummodes) { + return 0; /* already have this one. */ + } + + ptr = SDL20_realloc(vmode->modeslist12, sizeof (SDL12_Rect) * (vmode->nummodes + 1)); + if (ptr == NULL) { + return SDL20_OutOfMemory(); + } + vmode->modeslist12 = (SDL12_Rect *) ptr; + + SDL20_memcpy(&vmode->modeslist12[vmode->nummodes], mode, sizeof(vmode->modeslist12[vmode->nummodes])); + vmode->nummodes++; + + return 0; +} + +/* A list of fake video modes which are included. */ +static SDL12_Rect fake_modes[] = { + { 0, 0, 7680, 4320 }, + { 0, 0, 6144, 3160 }, + { 0, 0, 5120, 2880 }, + { 0, 0, 4096, 2304 }, + { 0, 0, 3840, 2160 }, + { 0, 0, 3200, 1800 }, + { 0, 0, 2880, 1600 }, + { 0, 0, 2560, 1600 }, + { 0, 0, 2048, 1536 }, + { 0, 0, 1920, 1440 }, + { 0, 0, 1920, 1200 }, + { 0, 0, 1920, 1080 }, + { 0, 0, 1680, 1050 }, + { 0, 0, 1600, 1200 }, + { 0, 0, 1600, 900 }, + { 0, 0, 1440, 1080 }, + { 0, 0, 1440, 900 }, + { 0, 0, 1400, 1050 }, + { 0, 0, 1368, 768 }, + { 0, 0, 1280, 1024 }, + { 0, 0, 1280, 960 }, + { 0, 0, 1280, 800 }, + { 0, 0, 1280, 720 }, + { 0, 0, 1152, 864 }, + { 0, 0, 1024, 768 }, + { 0, 0, 864, 486 }, + { 0, 0, 800, 600 }, + { 0, 0, 720, 480 }, + { 0, 0, 640, 480 } +}; + +/* This sets up VideoModes and VideoModesCount. You end up with arrays by pixel + format, each with a value that 1.2's SDL_ListModes() can return. */ static int Init12VidModes(void) { const int total = SDL20_GetNumDisplayModes(VideoDisplayIndex); + const char *maxmodestr; + const unsigned max_bpp = SDL12Compat_GetHintInt("SDL12COMPAT_MAX_BPP", 32); VideoModeList *vmode = NULL; void *ptr = NULL; int i, j; + SDL12_Rect prev_mode = { 0, 0, 0, 0 }, current_mode = { 0, 0, 0, 0 }; + /* We only want to enable fake modes if OpenGL Logical Scaling is enabled. */ + const SDL_bool use_fake_modes = SDL12Compat_GetHintBoolean("SDL12COMPAT_OPENGL_SCALING", SDL_TRUE); + Uint16 maxw = 0; + Uint16 maxh = 0; if (VideoModesCount > 0) { return 0; /* already did this. */ } + PreserveDestinationAlpha = SDL12Compat_GetHintBoolean("SDL12COMPAT_PRESERVE_DEST_ALPHA", SDL_TRUE); + WantOpenGLScaling = use_fake_modes; + SDL_assert(VideoModes == NULL); + maxmodestr = SDL12Compat_GetHint("SDL12COMPAT_MAX_VIDMODE"); + if (maxmodestr) { + unsigned int w = 0, h = 0; + SDL_sscanf(maxmodestr, "%ux%u", &w, &h); + if (w > 0xFFFF) w = 0xFFFF; + if (h > 0xFFFF) h = 0xFFFF; + maxw = w; + maxh = h; + } + for (i = 0; i < total; ++i) { SDL_DisplayMode mode; if (SDL20_GetDisplayMode(VideoDisplayIndex, i, &mode) < 0) { continue; } - if (!mode.w || !mode.h) { - SDL_assert(0 && "Can this actually happen?"); - continue; + + if ((mode.w == 0) && (mode.h == 0)) { + /* SDL2 has a bug in its dummy driver before 2.0.16 that causes it to report a bogus video mode. */ + if (IsDummyVideo && (LinkedSDL2VersionInt <= SDL_VERSIONNUM(2, 0, 15))) { + mode.w = 1024; + mode.h = 768; + mode.format = SDL_PIXELFORMAT_RGB888; + } } + + if ((mode.w <= 0) || (mode.h <= 0)) { + continue; /* bogus mode for whatever reason, ignore it. */ + } + if (mode.w > 65535 || mode.h > 65535) { continue; /* can't fit to 16-bits for SDL12_Rect */ } + if (SDL_BITSPERPIXEL(mode.format) > max_bpp) { + /* If we see any mode > max_bpp, reduce its bpp. */ + mode.format = BPPToPixelFormat(max_bpp); + } + if (!vmode || (mode.format != vmode->format)) { /* SDL20_GetDisplayMode() sorts on bpp first. We know when to change arrays. */ ptr = (VideoModeList *) SDL20_realloc(VideoModes, sizeof (VideoModeList) * (VideoModesCount+1)); if (!ptr) { @@ -1444,29 +2589,37 @@ Init12VidModes(void) VideoModesCount++; } - /* make sure we don't have this one already (with a different refresh rate, etc). */ - for (j = 0; j < vmode->nummodes; j++) { - if ((vmode->modeslist12[j].w == mode.w) && (vmode->modeslist12[j].h == mode.h)) { - break; - } - } + current_mode.w = mode.w; + current_mode.h = mode.h; - if (j < vmode->nummodes) { - continue; /* already have this one. */ + /* Attempt to add all of the fake modes. */ + if (use_fake_modes) { + for (j = 0; j < (int) SDL_arraysize(fake_modes); ++j) { + if (VidModeSizeGreater(&prev_mode, &fake_modes[j]) && VidModeSizeGreater(&fake_modes[j], ¤t_mode)) { + if (AddVidModeToList(vmode, &fake_modes[j], maxw, maxh)) { + return SDL20_OutOfMemory(); + } + } + } } - ptr = SDL20_realloc(vmode->modeslist12, sizeof (SDL12_Rect) * (vmode->nummodes + 1)); - if (ptr == NULL) { + if (AddVidModeToList(vmode, ¤t_mode, maxw, maxh)) { return SDL20_OutOfMemory(); } - vmode->modeslist12 = (SDL12_Rect *) ptr; - vmode->modeslist12[vmode->nummodes].x = 0; - vmode->modeslist12[vmode->nummodes].y = 0; - vmode->modeslist12[vmode->nummodes].w = mode.w; - vmode->modeslist12[vmode->nummodes].h = mode.h; + prev_mode.w = mode.w; + prev_mode.h = mode.h; + } - vmode->nummodes++; + /* we need to try to add fake modes to the end of the list once there are no more real modes */ + if (use_fake_modes) { + for (i = 0; i < (int) SDL_arraysize(fake_modes); ++i) { + if (VidModeSizeGreater(&prev_mode, &fake_modes[i])) { + if (AddVidModeToList(vmode, &fake_modes[i], maxw, maxh)) { + return SDL20_OutOfMemory(); + } + } + } } /* link up modes12 for SDL_ListModes()'s use... */ @@ -1481,17 +2634,80 @@ Init12VidModes(void) } } + QueuedDisplayOverlays.next = NULL; + QueuedDisplayOverlaysTail = &QueuedDisplayOverlays; + + return 0; +} + +/* we should have a default cursor */ +#include "default_cursor.h" +DECLSPEC12 void SDLCALL SDL_FreeCursor(SDL12_Cursor *); + +static int +HasWmAvailable(const char *driver) +{ + /* This is not perfect, but this emcompasses everything that SDL 2.22.0 + offers. Most things are console or framebuffer targets, so it's + easier to list things that are actual GUI user interfaces here. */ + static const char * const gui_targets[] = { + #ifdef _WIN32 + "windows", "winrt", + #endif + #ifdef __APPLE__ + "cocoa", + #endif + #ifdef __OS2__ + "DIVE", "VMAN", + #endif + #ifdef __HAIKU__ + "haiku", + #endif + #ifdef __QNX__ + "qnx", /* I _think_ this has a window manager... */ + #endif + "x11", "wayland" /* just assume anything can MAYBE have these, even if they wouldn't. */ + }; + int i; + + for (i = 0; i < (int) SDL_arraysize(gui_targets); i++) { + if (SDL20_strcasecmp(driver, gui_targets[i]) == 0) { + return 1; + } + } + return 0; } +DECLSPEC12 int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); + static int Init12Video(void) { + const char *driver = SDL20_GetCurrentVideoDriver(); + const char *scale_method_env = SDL12Compat_GetHint("SDL12COMPAT_SCALE_METHOD"); + const unsigned max_bpp = SDL12Compat_GetHintInt("SDL12COMPAT_MAX_BPP", 32); SDL_DisplayMode mode; int i; - for (i = 0; i < SDL12_MAXEVENTS-1; i++) + AllowThreadedDraws = SDL12Compat_GetHintBoolean("SDL12COMPAT_ALLOW_THREADED_DRAWS", SDL_TRUE); + AllowThreadedPumps = SDL12Compat_GetHintBoolean("SDL12COMPAT_ALLOW_THREADED_PUMPS", SDL_TRUE); + + WantScaleMethodNearest = (scale_method_env && !SDL20_strcmp(scale_method_env, "nearest")) ? SDL_TRUE : SDL_FALSE; + + /* Only override this if the env var is set, as the default is platform-specific. */ + TranslateKeyboardLayout = SDL12Compat_GetHintBoolean("SDL12COMPAT_USE_KEYBOARD_LAYOUT", TranslateKeyboardLayout); + + IsDummyVideo = ((driver != NULL) && (SDL20_strcmp(driver, "dummy") == 0)) ? SDL_TRUE : SDL_FALSE; + + EventQueueMutex = SDL20_CreateMutex(); + if (EventQueueMutex == NULL) { + return -1; + } + + for (i = 0; i < SDL12_MAXEVENTS-1; i++) { EventQueuePool[i].next = &EventQueuePool[i+1]; + } EventQueuePool[SDL12_MAXEVENTS-1].next = NULL; EventQueueHead = EventQueueTail = NULL; @@ -1500,13 +2716,35 @@ Init12Video(void) SDL20_memset(&PendingKeydownEvent, 0, sizeof(SDL12_Event)); SDL20_memset(EventStates, SDL_ENABLE, sizeof (EventStates)); /* on by default */ + EventStates[SDL12_SYSWMEVENT] = SDL_IGNORE; /* off by default. */ +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + SDL20_EventState(SDL_SYSWMEVENT, SDL_ENABLE); +#else + SDL20_EventState(SDL_SYSWMEVENT, SDL_IGNORE); +#endif + +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + SupportSysWM = (SDL20_strcmp(driver, "windows") == 0) ? SDL_TRUE : SDL_FALSE; +#elif defined(SDL_VIDEO_DRIVER_X11) + SupportSysWM = (SDL20_strcmp(driver, "x11") == 0) ? SDL_TRUE : SDL_FALSE; +#else + SupportSysWM = SDL_FALSE; +#endif + + if (!SDL12Compat_GetHintBoolean("SDL12COMPAT_ALLOW_SYSWM", SDL_TRUE)) { + SupportSysWM = SDL_FALSE; + } + SDL_EnableKeyRepeat(0, 0); + + SDL20_DelEventWatch(EventFilter20to12, NULL); SDL20_AddEventWatch(EventFilter20to12, NULL); VideoDisplayIndex = GetVideoDisplay(); SwapInterval = 0; + VideoWindowGrabWanted = SDL_FALSE; VideoWindowGrabbed = SDL_FALSE; VideoCursorHidden = SDL_FALSE; SDL20_ShowCursor(1); @@ -1518,37 +2756,89 @@ Init12Video(void) SDL20_StopTextInput(); if (SDL20_GetDesktopDisplayMode(VideoDisplayIndex, &mode) == 0) { - VideoInfoVfmt20 = SDL20_AllocFormat(mode.format); + if (SDL_BITSPERPIXEL(mode.format) > max_bpp) { + VideoInfoVfmt20 = SDL20_AllocFormat(BPPToPixelFormat(max_bpp)); + } else { + VideoInfoVfmt20 = SDL20_AllocFormat(mode.format); + } VideoInfo12.vfmt = PixelFormat20to12(&VideoInfoVfmt12, &VideoInfoPalette12, VideoInfoVfmt20); VideoInfo12.current_w = mode.w; VideoInfo12.current_h = mode.h; - VideoInfo12.wm_available = 1; /* FIXME ? */ + VideoInfo12.wm_available = HasWmAvailable(driver); VideoInfo12.video_mem = 1024 * 256; /* good enough. */ } return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_VideoInit(const char *driver, Uint32 flags) { - (void) flags; - return SDL20_VideoInit(driver); + int retval; + + (void) flags; /* unused. */ + + retval = SDL20_VideoInit(driver); + if (retval != -1) { + retval = Init12Video(); + if (retval == -1) { + SDL20_VideoQuit(); + } + } + return retval; +} + +static void +Init12Audio(void) +{ + WantCompatibilityAudioCVT = SDL12Compat_GetHintBoolean("SDL12COMPAT_COMPATIBILITY_AUDIOCVT", SDL_FALSE); } -DECLSPEC int SDLCALL + +static void InitializeCDSubsystem(void); +static void QuitCDSubsystem(void); + + +DECLSPEC12 int SDLCALL SDL_InitSubSystem(Uint32 sdl12flags) { + const char *videodriver = SDL12COMPAT_getenv_unsafe("SDL_VIDEODRIVER"); + const char *audiodriver = SDL12COMPAT_getenv_unsafe("SDL_AUDIODRIVER"); Uint32 sdl20flags = 0; int rc; +#ifdef __WINDOWS__ + /* DOSBox (and probably other things), try to force the "windib" video + backend, but it doesn't exist in SDL2. Force to "windows" instead. */ + if (videodriver && + (SDL20_strcmp(videodriver, "windib") == 0 || + SDL20_strcmp(videodriver, "directx") == 0)) { + videodriver = "windows"; + } +#endif + #ifdef __MACOSX__ extern void sdl12_compat_macos_init(void); sdl12_compat_macos_init(); #endif - FIXME("support SDL_INIT_EVENTTHREAD where it makes sense?"); - #define SETFLAG(flag) if (sdl12flags & SDL12_INIT_##flag) sdl20flags |= SDL_INIT_##flag + /* note that currently we ignore SDL12_INIT_NOPARACHUTE, since + there _isn't_ a parachute in SDL2, and mostly it was meant to deal + with X11's XVidMode leaving the display resolution busted if the app + didn't call SDL_Quit() before leaving...but most sdl12-compat cases + (except OpenGL rendering without logical scaling) use + FULLSCREEN_DESKTOP for fullscreen modes, and modern X11 doesn't + have this problem, so we're ignoring the parachute until a reasonable + need arises. */ + + #define SETFLAG(flag) { \ + const Uint32 f12 = SDL12_INIT_##flag; \ + const Uint32 f20 = SDL_INIT_##flag; \ + if ((sdl12flags & f12) && ((InitializedSubsystems20 & f20) == 0)) { \ + sdl20flags |= f20; \ + } \ + } + SETFLAG(TIMER); SETFLAG(AUDIO); SETFLAG(VIDEO); @@ -1556,30 +2846,55 @@ SDL_InitSubSystem(Uint32 sdl12flags) SETFLAG(NOPARACHUTE); #undef SETFLAG - /* There's no CDROM in 2.0, but we'll just pretend it succeeded. */ - if (sdl12flags & SDL12_INIT_CDROM) - CDRomInit = 1; + /* There's no CDROM in 2.0, but we fake it. */ + if (sdl12flags & SDL12_INIT_CDROM) { + /* this never reports failure, even if there's a legit problem. You just won't see any drives. */ + InitializeCDSubsystem(); + } + + /* In SDL3 (via sdl2-compat), these will ignore changes to environment variables after startup, but SDL-1.2 apps might + change envvars on the fly, so we need to manually force these into SDL2 hints. */ + + if (videodriver) { + SDL20_SetHintWithPriority(SDL_HINT_VIDEODRIVER, videodriver, SDL_HINT_OVERRIDE); + } + + if (audiodriver) { + SDL20_SetHintWithPriority(SDL_HINT_AUDIODRIVER, audiodriver, SDL_HINT_OVERRIDE); + } rc = SDL20_Init(sdl20flags); if ((rc == 0) && (sdl20flags & SDL_INIT_VIDEO)) { if (Init12Video() < 0) { - return -1; + rc = -1; } + + /* SDL_INIT_EVENTTHREAD takes effect when SDL_INIT_VIDEO is also set */ + EventThreadEnabled = (sdl12flags & SDL12_INIT_EVENTTHREAD) ? SDL_TRUE : SDL_FALSE; + } + + if ((rc == 0) && (sdl20flags & SDL_INIT_AUDIO)) { + Init12Audio(); } + if ((rc == 0) && (sdl20flags & SDL_INIT_JOYSTICK)) { + Init12Joystick(); /* if this fails, we just won't report any sticks. */ + } + + InitializedSubsystems20 |= sdl20flags; + return rc; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_Init(Uint32 sdl12flags) { - FIXME("there is never a parachute in SDL2, should we catch segfaults ourselves?"); return SDL_InitSubSystem(sdl12flags); /* there's no difference betwee Init and InitSubSystem in SDL2. */ } static void -InitFlags12To20(const Uint32 flags12, Uint32 *_flags20, Uint32 *_extraflags) +InitFlags12to20(const Uint32 flags12, Uint32 *_flags20, Uint32 *_extraflags) { Uint32 flags20 = 0; Uint32 extraflags = 0; @@ -1617,21 +2932,40 @@ InitFlags20to12(const Uint32 flags20) } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_WasInit(Uint32 sdl12flags) { Uint32 sdl20flags, extraflags; - InitFlags12To20(sdl12flags, &sdl20flags, &extraflags); + InitFlags12to20(sdl12flags, &sdl20flags, &extraflags); return InitFlags20to12(SDL20_WasInit(sdl20flags)) | extraflags; } +static void +FreeSurfaceContents(SDL12_Surface *surface12) +{ + if (surface12->surface20) { + if (surface12->pixels == NULL) { + surface12->surface20->pixels = NULL; + } + SDL20_FreeSurface(surface12->surface20); + surface12->surface20 = NULL; + } + if (surface12->format) { + SDL20_free(surface12->format->palette); + SDL20_free(surface12->format); + surface12->format = NULL; + } +} + static SDL12_Surface *EndVidModeCreate(void); static void Quit12Video(void) { int i; + SDL_EnableKeyRepeat(0, 0); + SDL20_FreeSurface(VideoIcon20); VideoIcon20 = NULL; @@ -1649,45 +2983,78 @@ Quit12Video(void) VideoInfoVfmt20 = NULL; EventFilter12 = NULL; EventQueueAvailable = EventQueueHead = EventQueueTail = NULL; - CurrentCursor12 = NULL; + SDL20_memset(&PendingKeydownEvent, 0, sizeof(SDL12_Event)); + SDL_FreeCursor(CurrentCursor12); VideoModes = NULL; VideoModesCount = 0; + + AllowThreadedDraws = SDL_FALSE; + AllowThreadedPumps = SDL_FALSE; + + if (EventQueueMutex) { + SDL20_DestroyMutex(EventQueueMutex); + EventQueueMutex = NULL; + } + + /* Shutdown the fake event thread. */ + EventThreadEnabled = SDL_FALSE; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_QuitSubSystem(Uint32 sdl12flags) { Uint32 sdl20flags, extraflags; - InitFlags12To20(sdl12flags, &sdl20flags, &extraflags); + + /* Some games (notably the Steam build of Multiwinia), will + * SDL_Quit(SDL_INIT_VIDEO) on resolution change, and never call + * SDL_Init() again before creating their new window. + */ + if (SDL12Compat_GetHintBoolean("SDL12COMPAT_NO_QUIT_VIDEO", SDL_FALSE)) { + sdl12flags &= ~SDL12_INIT_VIDEO; + } + InitFlags12to20(sdl12flags, &sdl20flags, &extraflags); if (extraflags & SDL12_INIT_CDROM) { - CDRomInit = 0; + QuitCDSubsystem(); + } + + if (sdl12flags & SDL12_INIT_AUDIO) { + SDL_CloseAudio(); } if (sdl12flags & SDL12_INIT_VIDEO) { Quit12Video(); } + if (sdl12flags & SDL12_INIT_JOYSTICK) { + Quit12Joystick(); + } + SDL20_QuitSubSystem(sdl20flags); if ((SDL20_WasInit(0) == 0) && (!CDRomInit)) { SDL20_Quit(); } + + InitializedSubsystems20 &= ~sdl20flags; + InitializedSubsystems20 &= ~SDL_INIT_NOPARACHUTE; /* SDL2 accepts this flag but ignores it. */ } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_Quit(void) { + SDL_bool noquitvideo = SDL12Compat_GetHintBoolean("SDL12COMPAT_NO_QUIT_VIDEO", SDL_FALSE); SDL_QuitSubSystem(SDL_WasInit(0) | SDL12_INIT_CDROM); + SDL_assert((InitializedSubsystems20 == 0) || (noquitvideo && (InitializedSubsystems20 == SDL_INIT_VIDEO))); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_Error(SDL_errorcode error) { SDL20_Error(error); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetError(const char *fmt, ...) { char ch; @@ -1711,7 +3078,7 @@ SDL_SetError(const char *fmt, ...) } } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_GetError(void) { if (SDL20_GetError == NULL) { @@ -1729,28 +3096,47 @@ GetDriverName(const char *name, char *namebuf, int maxlen) if (namebuf) { SDL20_strlcpy(namebuf, name, maxlen); return namebuf; - } else { - return name; } + return name; } return NULL; } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_AudioDriverName(char *namebuf, int maxlen) { return GetDriverName(SDL20_GetCurrentAudioDriver(), namebuf, maxlen); } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_VideoDriverName(char *namebuf, int maxlen) { - return GetDriverName(SDL20_GetCurrentVideoDriver(), namebuf, maxlen); -} +#ifdef __WINDOWS__ + const char *val = SDL12COMPAT_getenv_unsafe("SDL_VIDEODRIVER"); + if (val) { + /* give them back what they requested: */ + if (SDL20_strcmp(val, "windib") == 0 || + SDL20_strcmp(val, "directx") == 0) { + return GetDriverName(val, namebuf, maxlen); + } + } + val = SDL20_GetCurrentVideoDriver(); + if (val && SDL20_strcmp(val, "windows") == 0) { + /* Windows apps may use SDL_VideoDriverName() to check accelerated + * vs unaccelerated display by using directx and windib. + * https://github.com/libsdl-org/sdl12-compat/issues/223 */ + val = "directx"; + } + return GetDriverName(val, namebuf, maxlen); +#else + return GetDriverName(SDL20_GetCurrentVideoDriver(), namebuf, maxlen); +#endif +} -DECLSPEC int SDLCALL -SDL_PollEvent(SDL12_Event *event12) +/* you MUST hold EventQueueMutex before calling this! */ +static int +SDL_PollEvent_locked(SDL12_Event *event12) { EventQueueType *next; @@ -1774,8 +3160,25 @@ SDL_PollEvent(SDL12_Event *event12) return 1; } -DECLSPEC int SDLCALL -SDL_PushEvent(SDL12_Event *event12) +DECLSPEC12 int SDLCALL +SDL_PollEvent(SDL12_Event *event12) +{ + int retval; + + if (!EventQueueMutex) { + return 0; + } + + SDL20_LockMutex(EventQueueMutex); + retval = SDL_PollEvent_locked(event12); + SDL20_UnlockMutex(EventQueueMutex); + + return retval; +} + +/* you MUST hold EventQueueMutex before calling this! */ +static int +SDL_PushEvent_locked(SDL12_Event *event12) { EventQueueType *item = EventQueueAvailable; if (item == NULL) { @@ -1793,14 +3196,48 @@ SDL_PushEvent(SDL12_Event *event12) SDL20_memcpy(&item->event12, event12, sizeof (SDL12_Event)); + if (event12->type == SDL12_SYSWMEVENT) { /* make a copy of the data here */ + SDL20_memcpy(&item->syswm_msg, event12->syswm.msg, sizeof (SDL12_SysWMmsg)); + item->event12.syswm.msg = &item->syswm_msg; + } + return 0; } -DECLSPEC int SDLCALL -SDL_PeepEvents(SDL12_Event *events12, int numevents, SDL_eventaction action, Uint32 mask) +DECLSPEC12 int SDLCALL +SDL_PushEvent(SDL12_Event *event12) +{ + int retval; + + if (!EventQueueMutex) { + return SDL20_SetError("SDL not initialized"); + } + + SDL20_LockMutex(EventQueueMutex); + retval = SDL_PushEvent_locked(event12); + SDL20_UnlockMutex(EventQueueMutex); + + return retval; +} + + +/* you MUST hold EventQueueMutex before calling this! */ +static int +SDL_PeepEvents_locked(SDL12_Event *events12, int numevents, SDL_eventaction action, Uint32 mask) { SDL12_Event dummy_event; + /* We don't actually implement an event thread in sdl12-compat, but some + * games will only call SDL_PeepEvents(), which doesn't otherwise pump + * events, and get stuck when they've consumed all the events. + * + * Just pumping the event loop here simulates an event thread well enough + * for most things. + */ + if (EventThreadEnabled) { + SDL_PumpEvents(); + } + if (action == SDL_ADDEVENT) { int i; for (i = 0; i < numevents; i++) { @@ -1860,48 +3297,188 @@ SDL_PeepEvents(SDL12_Event *events12, int numevents, SDL_eventaction action, Uin return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL +SDL_PeepEvents(SDL12_Event *events12, int numevents, SDL_eventaction action, Uint32 mask) +{ + int retval; + + if (!EventQueueMutex) { + return SDL20_SetError("SDL not initialized"); + } + + SDL20_LockMutex(EventQueueMutex); + retval = SDL_PeepEvents_locked(events12, numevents, action, mask); + SDL20_UnlockMutex(EventQueueMutex); + + return retval; +} + +DECLSPEC12 int SDLCALL SDL_WaitEvent(SDL12_Event *event12) { - FIXME("In 1.2, this only fails (-1) if you haven't SDL_Init()'d."); + if (!EventQueueMutex) { + return SDL20_SetError("SDL not initialized"); + } + + /* the 1.2 entry point for PollEvent will grab/release the EventQueueMutex */ while (!SDL_PollEvent(event12)) { SDL20_Delay(10); } + return 1; } static SDL_bool PushEventIfNotFiltered(SDL12_Event *event12) { + SDL_bool retval = SDL_FALSE; if (event12->type != SDL12_NOEVENT) { + SDL_assert(EventQueueMutex != NULL); + SDL20_LockMutex(EventQueueMutex); if (EventStates[event12->type] != SDL_IGNORE) { if ((!EventFilter12) || (EventFilter12(event12))) { - return (SDL_PushEvent(event12) == 0)? SDL_TRUE : SDL_FALSE; + retval = (SDL_PushEvent(event12) == 0)? SDL_TRUE : SDL_FALSE; } } + SDL20_UnlockMutex(EventQueueMutex); } - return SDL_FALSE; + return retval; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_EventState(Uint8 type, int state) { + Uint8 retval = 0; /* the values of "state" match between 1.2 and 2.0 */ - const Uint8 retval = EventStates[type]; - SDL12_Event e; + if (EventQueueMutex) { + SDL12_Event e; - if (state != SDL_QUERY) { - EventStates[type] = state; - } - if (state == SDL_IGNORE) { /* drop existing events of this type. */ - while (SDL_PeepEvents(&e, 1, SDL_GETEVENT, (1< real_aspect) { + /* We want a wider aspect ratio than is available - letterbox it */ + const float scale = ((float) physical_width) / OpenGLLogicalScalingWidth; + dstrect.x = 0; + dstrect.w = physical_width; + dstrect.h = (int)SDL20_floor(OpenGLLogicalScalingHeight * scale); + dstrect.y = (physical_height - dstrect.h) / 2; + } else { + /* We want a narrower aspect ratio than is available - use side-bars */ + const float scale = ((float)physical_height) / OpenGLLogicalScalingHeight; + dstrect.y = 0; + dstrect.h = physical_height; + dstrect.w = (int)SDL20_floor(OpenGLLogicalScalingWidth * scale); + dstrect.x = (physical_width - dstrect.w) / 2; + } + + return dstrect; +} + +/* Scale a point (e.g. absolute mouse position) to the logical scaling size */ +static void +AdjustOpenGLLogicalScalingPoint(int *x, int *y) +{ + SDL_Rect viewport; + int physical_w, physical_h; + float scale_x, scale_y; + int adjusted_x, adjusted_y; + + /* Don't adjust anything if we're not using Logical Scaling */ + if (!OpenGLLogicalScalingFBO || !VideoWindow20) { + return; + } + + /* we want to scale based on the window size, which is dpi-scaled */ + SDL20_GetWindowSize(VideoWindow20, &physical_w, &physical_h); + viewport = GetOpenGLLogicalScalingViewport(physical_w, physical_h); + + scale_x = (float)OpenGLLogicalScalingWidth / viewport.w; + scale_y = (float)OpenGLLogicalScalingHeight / viewport.h; + + adjusted_x = (int) ((*x - viewport.x) * scale_x); + adjusted_y = (int) ((*y - viewport.y) * scale_y); + + /* Clamp the result to the visible window */ + *x = SDL_max(SDL_min(adjusted_x, OpenGLLogicalScalingWidth), 0); + *y = SDL_max(SDL_min(adjusted_y, OpenGLLogicalScalingHeight), 0); +} + +/* Scale a vector (e.g. relative mouse movement) to the logical scaling size */ +static void +AdjustOpenGLLogicalScalingVector(int *x, int *y, float *rx, float *ry) +{ + SDL_Rect viewport; + int physical_w, physical_h; + float scale_x, scale_y; + float float_x, float_y; + float trunc_x, trunc_y; + + /* Don't adjust anything if we're not using Logical Scaling */ + if (!OpenGLLogicalScalingFBO || !VideoWindow20) { + return; + } + + /* we want to scale based on the window size, which is dpi-scaled */ + SDL20_GetWindowSize(VideoWindow20, &physical_w, &physical_h); + viewport = GetOpenGLLogicalScalingViewport(physical_w, physical_h); + + scale_x = (float)OpenGLLogicalScalingWidth / viewport.w; + scale_y = (float)OpenGLLogicalScalingHeight / viewport.h; + + float_x = *x * scale_x + (rx ? *rx : 0.f); + float_y = *y * scale_y + (ry ? *ry : 0.f); + + trunc_x = SDL20_truncf(float_x); + trunc_y = SDL20_truncf(float_y); + + *x = (int)trunc_x; + *y = (int)trunc_y; + + if (rx) { + *rx = float_x - trunc_x; + } + if (ry) { + *ry = float_y - trunc_y; + } +} + static Uint8 MouseButtonState20to12(const Uint32 state20) { Uint8 retval = (state20 & 0x7); /* left, right, and middle will match. */ @@ -1917,7 +3494,7 @@ static Uint8 MouseButtonState20to12(const Uint32 state20) return retval; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetMouseState(int *x, int *y) { const Uint8 buttons = MouseButtonState20to12(SDL20_GetMouseState(x, y)); @@ -1926,13 +3503,13 @@ SDL_GetMouseState(int *x, int *y) return buttons; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetRelativeMouseState(int *x, int *y) { return MouseButtonState20to12(SDL20_GetRelativeMouseState(x, y)); } -DECLSPEC char * SDLCALL +DECLSPEC12 char * SDLCALL SDL_GetKeyName(SDL12Key key) { switch (key) { @@ -2184,10 +3761,13 @@ SDL_GetKeyName(SDL12Key key) static SDL12Key Keysym20to12(const SDL_Keycode keysym20) { - if (((int) keysym20) < 127) { /* (most of) low-ASCII maps directly */ + if (((int) keysym20) <= 255) { + /* (most of) low-ASCII maps directly, + * and so does the Latin-1 range (128-255) */ if (keysym20 == SDLK_PAUSE) { return SDLK12_PAUSE; - } else if (keysym20 == SDLK_CLEAR) { + } + if (keysym20 == SDLK_CLEAR) { return SDLK12_CLEAR; } return (SDL12Key) keysym20; @@ -2210,6 +3790,7 @@ Keysym20to12(const SDL_Keycode keysym20) CASEKEYSYM20TO12(RGUI, RMETA); CASEKEYSYM20TO12(LGUI, LMETA); CASEKEYSYM20TO12(PRINTSCREEN, PRINT); + CASEKEYSYM20TO12(APPLICATION, MENU); #undef CASEKEYSYM20TO12 #define CASEKEYSYM20TO12(k) case SDLK_##k: return SDLK12_##k @@ -2255,20 +3836,952 @@ Keysym20to12(const SDL_Keycode keysym20) CASEKEYSYM20TO12(LALT); CASEKEYSYM20TO12(MODE); CASEKEYSYM20TO12(HELP); - CASEKEYSYM20TO12(SYSREQ);; + CASEKEYSYM20TO12(SYSREQ); CASEKEYSYM20TO12(MENU); CASEKEYSYM20TO12(POWER); CASEKEYSYM20TO12(UNDO); #undef CASEKEYSYM20TO12 + + /* Map the "World Keys" (SDLK_WORLD_0 = 0xA0 to SDLK_WORLD_95 = 0xFF). + * In SDL2 they're the UCS-4 (32bit unicode) code for the key, + * in SDL1.2 (on X11 at least) they were the X11_KeySym & 0xFF, + * if (X11_KeySym >> 8)) was either 0 to 8 or 0x0A or 0x0C or 0x0E + * So map all those used UCS-4 codes to the corresponding SDLK12_WORLD_* codes + * Note that the Latin-1 range (keysym20 <= 255) is already handled at the top of this function + * Luckily X11's keysymdef.h lists both the values of the constants and their UCS4-value, like + * #define XK_Aogonek 0x01a1 / * U+0104 LATIN CAPITAL LETTER A WITH OGONEK * / + * So I'm using that as a reference for our mappings, which only use + * the lowest byte of the XK_* value, because of X11_KeySym & 0xFF in SDL1.2 + * + * case UCS4_code: return (SDL12Key)lowest_byte_of_corresponding_X11_KeySym; */ + + /* Latin-2 */ + case 0x0104: return (SDL12Key)0xa1; + case 0x02D8: return (SDL12Key)0xa2; + case 0x0141: return (SDL12Key)0xa3; + case 0x013D: return (SDL12Key)0xa5; + case 0x015A: return (SDL12Key)0xa6; + case 0x0160: return (SDL12Key)0xa9; + case 0x015E: return (SDL12Key)0xaa; + case 0x0164: return (SDL12Key)0xab; + case 0x0179: return (SDL12Key)0xac; + case 0x017D: return (SDL12Key)0xae; + case 0x017B: return (SDL12Key)0xaf; + case 0x0105: return (SDL12Key)0xb1; + case 0x02DB: return (SDL12Key)0xb2; + case 0x0142: return (SDL12Key)0xb3; + case 0x013E: return (SDL12Key)0xb5; + case 0x015B: return (SDL12Key)0xb6; + case 0x02C7: return (SDL12Key)0xb7; + case 0x0161: return (SDL12Key)0xb9; + case 0x015F: return (SDL12Key)0xba; + case 0x0165: return (SDL12Key)0xbb; + case 0x017A: return (SDL12Key)0xbc; + case 0x02DD: return (SDL12Key)0xbd; + case 0x017E: return (SDL12Key)0xbe; + case 0x017C: return (SDL12Key)0xbf; + case 0x0154: return (SDL12Key)0xc0; + case 0x0102: return (SDL12Key)0xc3; + case 0x0139: return (SDL12Key)0xc5; + case 0x0106: return (SDL12Key)0xc6; + case 0x010C: return (SDL12Key)0xc8; + case 0x0118: return (SDL12Key)0xca; + case 0x011A: return (SDL12Key)0xcc; + case 0x010E: return (SDL12Key)0xcf; + case 0x0110: return (SDL12Key)0xd0; + case 0x0143: return (SDL12Key)0xd1; + case 0x0147: return (SDL12Key)0xd2; + case 0x0150: return (SDL12Key)0xd5; + case 0x0158: return (SDL12Key)0xd8; + case 0x016E: return (SDL12Key)0xd9; + case 0x0170: return (SDL12Key)0xdb; + case 0x0162: return (SDL12Key)0xde; + case 0x0155: return (SDL12Key)0xe0; + case 0x0103: return (SDL12Key)0xe3; + case 0x013A: return (SDL12Key)0xe5; + case 0x0107: return (SDL12Key)0xe6; + case 0x010D: return (SDL12Key)0xe8; + case 0x0119: return (SDL12Key)0xea; + case 0x011B: return (SDL12Key)0xec; + case 0x010F: return (SDL12Key)0xef; + case 0x0111: return (SDL12Key)0xf0; + case 0x0144: return (SDL12Key)0xf1; + case 0x0148: return (SDL12Key)0xf2; + case 0x0151: return (SDL12Key)0xf5; + case 0x0159: return (SDL12Key)0xf8; + case 0x016F: return (SDL12Key)0xf9; + case 0x0171: return (SDL12Key)0xfb; + case 0x0163: return (SDL12Key)0xfe; + case 0x02D9: return (SDL12Key)0xff; + /* Latin-3 */ + case 0x0126: return (SDL12Key)0xa1; + case 0x0124: return (SDL12Key)0xa6; + case 0x0130: return (SDL12Key)0xa9; + case 0x011E: return (SDL12Key)0xab; + case 0x0134: return (SDL12Key)0xac; + case 0x0127: return (SDL12Key)0xb1; + case 0x0125: return (SDL12Key)0xb6; + case 0x0131: return (SDL12Key)0xb9; + case 0x011F: return (SDL12Key)0xbb; + case 0x0135: return (SDL12Key)0xbc; + case 0x010A: return (SDL12Key)0xc5; + case 0x0108: return (SDL12Key)0xc6; + case 0x0120: return (SDL12Key)0xd5; + case 0x011C: return (SDL12Key)0xd8; + case 0x016C: return (SDL12Key)0xdd; + case 0x015C: return (SDL12Key)0xde; + case 0x010B: return (SDL12Key)0xe5; + case 0x0109: return (SDL12Key)0xe6; + case 0x0121: return (SDL12Key)0xf5; + case 0x011D: return (SDL12Key)0xf8; + case 0x016D: return (SDL12Key)0xfd; + case 0x015D: return (SDL12Key)0xfe; + /* Latin 4 */ + case 0x0138: return (SDL12Key)0xa2; + case 0x0156: return (SDL12Key)0xa3; + case 0x0128: return (SDL12Key)0xa5; + case 0x013B: return (SDL12Key)0xa6; + case 0x0112: return (SDL12Key)0xaa; + case 0x0122: return (SDL12Key)0xab; + case 0x0166: return (SDL12Key)0xac; + case 0x0157: return (SDL12Key)0xb3; + case 0x0129: return (SDL12Key)0xb5; + case 0x013C: return (SDL12Key)0xb6; + case 0x0113: return (SDL12Key)0xba; + case 0x0123: return (SDL12Key)0xbb; + case 0x0167: return (SDL12Key)0xbc; + case 0x014A: return (SDL12Key)0xbd; + case 0x014B: return (SDL12Key)0xbf; + case 0x0100: return (SDL12Key)0xc0; + case 0x012E: return (SDL12Key)0xc7; + case 0x0116: return (SDL12Key)0xcc; + case 0x012A: return (SDL12Key)0xcf; + case 0x0145: return (SDL12Key)0xd1; + case 0x014C: return (SDL12Key)0xd2; + case 0x0136: return (SDL12Key)0xd3; + case 0x0172: return (SDL12Key)0xd9; + case 0x0168: return (SDL12Key)0xdd; + case 0x016A: return (SDL12Key)0xde; + case 0x0101: return (SDL12Key)0xe0; + case 0x012F: return (SDL12Key)0xe7; + case 0x0117: return (SDL12Key)0xec; + case 0x012B: return (SDL12Key)0xef; + case 0x0146: return (SDL12Key)0xf1; + case 0x014D: return (SDL12Key)0xf2; + case 0x0137: return (SDL12Key)0xf3; + case 0x0173: return (SDL12Key)0xf9; + case 0x0169: return (SDL12Key)0xfd; + case 0x016B: return (SDL12Key)0xfe; + /* Katakana */ + case 0x203E: return (SDL12Key)0x7e; + case 0x3002: return (SDL12Key)0xa1; + case 0x300C: return (SDL12Key)0xa2; + case 0x300D: return (SDL12Key)0xa3; + case 0x3001: return (SDL12Key)0xa4; + case 0x30FB: return (SDL12Key)0xa5; + case 0x30F2: return (SDL12Key)0xa6; + case 0x30A1: return (SDL12Key)0xa7; + case 0x30A3: return (SDL12Key)0xa8; + case 0x30A5: return (SDL12Key)0xa9; + case 0x30A7: return (SDL12Key)0xaa; + case 0x30A9: return (SDL12Key)0xab; + case 0x30E3: return (SDL12Key)0xac; + case 0x30E5: return (SDL12Key)0xad; + case 0x30E7: return (SDL12Key)0xae; + case 0x30C3: return (SDL12Key)0xaf; + case 0x30FC: return (SDL12Key)0xb0; + case 0x30A2: return (SDL12Key)0xb1; + case 0x30A4: return (SDL12Key)0xb2; + case 0x30A6: return (SDL12Key)0xb3; + case 0x30A8: return (SDL12Key)0xb4; + case 0x30AA: return (SDL12Key)0xb5; + case 0x30AB: return (SDL12Key)0xb6; + case 0x30AD: return (SDL12Key)0xb7; + case 0x30AF: return (SDL12Key)0xb8; + case 0x30B1: return (SDL12Key)0xb9; + case 0x30B3: return (SDL12Key)0xba; + case 0x30B5: return (SDL12Key)0xbb; + case 0x30B7: return (SDL12Key)0xbc; + case 0x30B9: return (SDL12Key)0xbd; + case 0x30BB: return (SDL12Key)0xbe; + case 0x30BD: return (SDL12Key)0xbf; + case 0x30BF: return (SDL12Key)0xc0; + case 0x30C1: return (SDL12Key)0xc1; + case 0x30C4: return (SDL12Key)0xc2; + case 0x30C6: return (SDL12Key)0xc3; + case 0x30C8: return (SDL12Key)0xc4; + case 0x30CA: return (SDL12Key)0xc5; + case 0x30CB: return (SDL12Key)0xc6; + case 0x30CC: return (SDL12Key)0xc7; + case 0x30CD: return (SDL12Key)0xc8; + case 0x30CE: return (SDL12Key)0xc9; + case 0x30CF: return (SDL12Key)0xca; + case 0x30D2: return (SDL12Key)0xcb; + case 0x30D5: return (SDL12Key)0xcc; + case 0x30D8: return (SDL12Key)0xcd; + case 0x30DB: return (SDL12Key)0xce; + case 0x30DE: return (SDL12Key)0xcf; + case 0x30DF: return (SDL12Key)0xd0; + case 0x30E0: return (SDL12Key)0xd1; + case 0x30E1: return (SDL12Key)0xd2; + case 0x30E2: return (SDL12Key)0xd3; + case 0x30E4: return (SDL12Key)0xd4; + case 0x30E6: return (SDL12Key)0xd5; + case 0x30E8: return (SDL12Key)0xd6; + case 0x30E9: return (SDL12Key)0xd7; + case 0x30EA: return (SDL12Key)0xd8; + case 0x30EB: return (SDL12Key)0xd9; + case 0x30EC: return (SDL12Key)0xda; + case 0x30ED: return (SDL12Key)0xdb; + case 0x30EF: return (SDL12Key)0xdc; + case 0x30F3: return (SDL12Key)0xdd; + case 0x309B: return (SDL12Key)0xde; + case 0x309C: return (SDL12Key)0xdf; + /* Arabic */ + case 0x060C: return (SDL12Key)0xac; + case 0x061B: return (SDL12Key)0xbb; + case 0x061F: return (SDL12Key)0xbf; + case 0x0621: return (SDL12Key)0xc1; + case 0x0622: return (SDL12Key)0xc2; + case 0x0623: return (SDL12Key)0xc3; + case 0x0624: return (SDL12Key)0xc4; + case 0x0625: return (SDL12Key)0xc5; + case 0x0626: return (SDL12Key)0xc6; + case 0x0627: return (SDL12Key)0xc7; + case 0x0628: return (SDL12Key)0xc8; + case 0x0629: return (SDL12Key)0xc9; + case 0x062A: return (SDL12Key)0xca; + case 0x062B: return (SDL12Key)0xcb; + case 0x062C: return (SDL12Key)0xcc; + case 0x062D: return (SDL12Key)0xcd; + case 0x062E: return (SDL12Key)0xce; + case 0x062F: return (SDL12Key)0xcf; + case 0x0630: return (SDL12Key)0xd0; + case 0x0631: return (SDL12Key)0xd1; + case 0x0632: return (SDL12Key)0xd2; + case 0x0633: return (SDL12Key)0xd3; + case 0x0634: return (SDL12Key)0xd4; + case 0x0635: return (SDL12Key)0xd5; + case 0x0636: return (SDL12Key)0xd6; + case 0x0637: return (SDL12Key)0xd7; + case 0x0638: return (SDL12Key)0xd8; + case 0x0639: return (SDL12Key)0xd9; + case 0x063A: return (SDL12Key)0xda; + case 0x0640: return (SDL12Key)0xe0; + case 0x0641: return (SDL12Key)0xe1; + case 0x0642: return (SDL12Key)0xe2; + case 0x0643: return (SDL12Key)0xe3; + case 0x0644: return (SDL12Key)0xe4; + case 0x0645: return (SDL12Key)0xe5; + case 0x0646: return (SDL12Key)0xe6; + case 0x0647: return (SDL12Key)0xe7; + case 0x0648: return (SDL12Key)0xe8; + case 0x0649: return (SDL12Key)0xe9; + case 0x064A: return (SDL12Key)0xea; + case 0x064B: return (SDL12Key)0xeb; + case 0x064C: return (SDL12Key)0xec; + case 0x064D: return (SDL12Key)0xed; + case 0x064E: return (SDL12Key)0xee; + case 0x064F: return (SDL12Key)0xef; + case 0x0650: return (SDL12Key)0xf0; + case 0x0651: return (SDL12Key)0xf1; + case 0x0652: return (SDL12Key)0xf2; + /* Cyrillic */ + case 0x0452: return (SDL12Key)0xa1; + case 0x0453: return (SDL12Key)0xa2; + case 0x0451: return (SDL12Key)0xa3; + case 0x0454: return (SDL12Key)0xa4; + case 0x0455: return (SDL12Key)0xa5; + case 0x0456: return (SDL12Key)0xa6; + case 0x0457: return (SDL12Key)0xa7; + case 0x0458: return (SDL12Key)0xa8; + case 0x0459: return (SDL12Key)0xa9; + case 0x045A: return (SDL12Key)0xaa; + case 0x045B: return (SDL12Key)0xab; + case 0x045C: return (SDL12Key)0xac; + case 0x0491: return (SDL12Key)0xad; + case 0x045E: return (SDL12Key)0xae; + case 0x045F: return (SDL12Key)0xaf; + case 0x2116: return (SDL12Key)0xb0; + case 0x0402: return (SDL12Key)0xb1; + case 0x0403: return (SDL12Key)0xb2; + case 0x0401: return (SDL12Key)0xb3; + case 0x0404: return (SDL12Key)0xb4; + case 0x0405: return (SDL12Key)0xb5; + case 0x0406: return (SDL12Key)0xb6; + case 0x0407: return (SDL12Key)0xb7; + case 0x0408: return (SDL12Key)0xb8; + case 0x0409: return (SDL12Key)0xb9; + case 0x040A: return (SDL12Key)0xba; + case 0x040B: return (SDL12Key)0xbb; + case 0x040C: return (SDL12Key)0xbc; + case 0x0490: return (SDL12Key)0xbd; + case 0x040E: return (SDL12Key)0xbe; + case 0x040F: return (SDL12Key)0xbf; + case 0x044E: return (SDL12Key)0xc0; + case 0x0430: return (SDL12Key)0xc1; + case 0x0431: return (SDL12Key)0xc2; + case 0x0446: return (SDL12Key)0xc3; + case 0x0434: return (SDL12Key)0xc4; + case 0x0435: return (SDL12Key)0xc5; + case 0x0444: return (SDL12Key)0xc6; + case 0x0433: return (SDL12Key)0xc7; + case 0x0445: return (SDL12Key)0xc8; + case 0x0438: return (SDL12Key)0xc9; + case 0x0439: return (SDL12Key)0xca; + case 0x043A: return (SDL12Key)0xcb; + case 0x043B: return (SDL12Key)0xcc; + case 0x043C: return (SDL12Key)0xcd; + case 0x043D: return (SDL12Key)0xce; + case 0x043E: return (SDL12Key)0xcf; + case 0x043F: return (SDL12Key)0xd0; + case 0x044F: return (SDL12Key)0xd1; + case 0x0440: return (SDL12Key)0xd2; + case 0x0441: return (SDL12Key)0xd3; + case 0x0442: return (SDL12Key)0xd4; + case 0x0443: return (SDL12Key)0xd5; + case 0x0436: return (SDL12Key)0xd6; + case 0x0432: return (SDL12Key)0xd7; + case 0x044C: return (SDL12Key)0xd8; + case 0x044B: return (SDL12Key)0xd9; + case 0x0437: return (SDL12Key)0xda; + case 0x0448: return (SDL12Key)0xdb; + case 0x044D: return (SDL12Key)0xdc; + case 0x0449: return (SDL12Key)0xdd; + case 0x0447: return (SDL12Key)0xde; + case 0x044A: return (SDL12Key)0xdf; + case 0x042E: return (SDL12Key)0xe0; + case 0x0410: return (SDL12Key)0xe1; + case 0x0411: return (SDL12Key)0xe2; + case 0x0426: return (SDL12Key)0xe3; + case 0x0414: return (SDL12Key)0xe4; + case 0x0415: return (SDL12Key)0xe5; + case 0x0424: return (SDL12Key)0xe6; + case 0x0413: return (SDL12Key)0xe7; + case 0x0425: return (SDL12Key)0xe8; + case 0x0418: return (SDL12Key)0xe9; + case 0x0419: return (SDL12Key)0xea; + case 0x041A: return (SDL12Key)0xeb; + case 0x041B: return (SDL12Key)0xec; + case 0x041C: return (SDL12Key)0xed; + case 0x041D: return (SDL12Key)0xee; + case 0x041E: return (SDL12Key)0xef; + case 0x041F: return (SDL12Key)0xf0; + case 0x042F: return (SDL12Key)0xf1; + case 0x0420: return (SDL12Key)0xf2; + case 0x0421: return (SDL12Key)0xf3; + case 0x0422: return (SDL12Key)0xf4; + case 0x0423: return (SDL12Key)0xf5; + case 0x0416: return (SDL12Key)0xf6; + case 0x0412: return (SDL12Key)0xf7; + case 0x042C: return (SDL12Key)0xf8; + case 0x042B: return (SDL12Key)0xf9; + case 0x0417: return (SDL12Key)0xfa; + case 0x0428: return (SDL12Key)0xfb; + case 0x042D: return (SDL12Key)0xfc; + case 0x0429: return (SDL12Key)0xfd; + case 0x0427: return (SDL12Key)0xfe; + case 0x042A: return (SDL12Key)0xff; + /* Greek */ + case 0x0386: return (SDL12Key)0xa1; + case 0x0388: return (SDL12Key)0xa2; + case 0x0389: return (SDL12Key)0xa3; + case 0x038A: return (SDL12Key)0xa4; + case 0x03AA: return (SDL12Key)0xa5; + case 0x038C: return (SDL12Key)0xa7; + case 0x038E: return (SDL12Key)0xa8; + case 0x03AB: return (SDL12Key)0xa9; + case 0x038F: return (SDL12Key)0xab; + case 0x0385: return (SDL12Key)0xae; + case 0x2015: return (SDL12Key)0xaf; + case 0x03AC: return (SDL12Key)0xb1; + case 0x03AD: return (SDL12Key)0xb2; + case 0x03AE: return (SDL12Key)0xb3; + case 0x03AF: return (SDL12Key)0xb4; + case 0x03CA: return (SDL12Key)0xb5; + case 0x0390: return (SDL12Key)0xb6; + case 0x03CC: return (SDL12Key)0xb7; + case 0x03CD: return (SDL12Key)0xb8; + case 0x03CB: return (SDL12Key)0xb9; + case 0x03B0: return (SDL12Key)0xba; + case 0x03CE: return (SDL12Key)0xbb; + case 0x0391: return (SDL12Key)0xc1; + case 0x0392: return (SDL12Key)0xc2; + case 0x0393: return (SDL12Key)0xc3; + case 0x0394: return (SDL12Key)0xc4; + case 0x0395: return (SDL12Key)0xc5; + case 0x0396: return (SDL12Key)0xc6; + case 0x0397: return (SDL12Key)0xc7; + case 0x0398: return (SDL12Key)0xc8; + case 0x0399: return (SDL12Key)0xc9; + case 0x039A: return (SDL12Key)0xca; + case 0x039B: return (SDL12Key)0xcb; + case 0x039C: return (SDL12Key)0xcc; + case 0x039D: return (SDL12Key)0xcd; + case 0x039E: return (SDL12Key)0xce; + case 0x039F: return (SDL12Key)0xcf; + case 0x03A0: return (SDL12Key)0xd0; + case 0x03A1: return (SDL12Key)0xd1; + case 0x03A3: return (SDL12Key)0xd2; + case 0x03A4: return (SDL12Key)0xd4; + case 0x03A5: return (SDL12Key)0xd5; + case 0x03A6: return (SDL12Key)0xd6; + case 0x03A7: return (SDL12Key)0xd7; + case 0x03A8: return (SDL12Key)0xd8; + case 0x03A9: return (SDL12Key)0xd9; + case 0x03B1: return (SDL12Key)0xe1; + case 0x03B2: return (SDL12Key)0xe2; + case 0x03B3: return (SDL12Key)0xe3; + case 0x03B4: return (SDL12Key)0xe4; + case 0x03B5: return (SDL12Key)0xe5; + case 0x03B6: return (SDL12Key)0xe6; + case 0x03B7: return (SDL12Key)0xe7; + case 0x03B8: return (SDL12Key)0xe8; + case 0x03B9: return (SDL12Key)0xe9; + case 0x03BA: return (SDL12Key)0xea; + case 0x03BB: return (SDL12Key)0xeb; + case 0x03BC: return (SDL12Key)0xec; + case 0x03BD: return (SDL12Key)0xed; + case 0x03BE: return (SDL12Key)0xee; + case 0x03BF: return (SDL12Key)0xef; + case 0x03C0: return (SDL12Key)0xf0; + case 0x03C1: return (SDL12Key)0xf1; + case 0x03C3: return (SDL12Key)0xf2; + case 0x03C2: return (SDL12Key)0xf3; + case 0x03C4: return (SDL12Key)0xf4; + case 0x03C5: return (SDL12Key)0xf5; + case 0x03C6: return (SDL12Key)0xf6; + case 0x03C7: return (SDL12Key)0xf7; + case 0x03C8: return (SDL12Key)0xf8; + case 0x03C9: return (SDL12Key)0xf9; + /* Technical */ + case 0x23B7: return (SDL12Key)0xa1; + case 0x250C: return (SDL12Key)0xa2; + case 0x2500: return (SDL12Key)0xa3; + case 0x2320: return (SDL12Key)0xa4; + case 0x2321: return (SDL12Key)0xa5; + case 0x2502: return (SDL12Key)0xa6; + case 0x23A1: return (SDL12Key)0xa7; + case 0x23A3: return (SDL12Key)0xa8; + case 0x23A4: return (SDL12Key)0xa9; + case 0x23A6: return (SDL12Key)0xaa; + case 0x239B: return (SDL12Key)0xab; + case 0x239D: return (SDL12Key)0xac; + case 0x239E: return (SDL12Key)0xad; + case 0x23A0: return (SDL12Key)0xae; + case 0x23A8: return (SDL12Key)0xaf; + case 0x23AC: return (SDL12Key)0xb0; + case 0x2264: return (SDL12Key)0xbc; + case 0x2260: return (SDL12Key)0xbd; + case 0x2265: return (SDL12Key)0xbe; + case 0x222B: return (SDL12Key)0xbf; + case 0x2234: return (SDL12Key)0xc0; + case 0x221D: return (SDL12Key)0xc1; + case 0x221E: return (SDL12Key)0xc2; + case 0x2207: return (SDL12Key)0xc5; + case 0x223C: return (SDL12Key)0xc8; + case 0x2243: return (SDL12Key)0xc9; + case 0x21D4: return (SDL12Key)0xcd; + case 0x21D2: return (SDL12Key)0xce; + case 0x2261: return (SDL12Key)0xcf; + case 0x221A: return (SDL12Key)0xd6; + case 0x2282: return (SDL12Key)0xda; + case 0x2283: return (SDL12Key)0xdb; + case 0x2229: return (SDL12Key)0xdc; + case 0x222A: return (SDL12Key)0xdd; + case 0x2227: return (SDL12Key)0xde; + case 0x2228: return (SDL12Key)0xdf; + case 0x2202: return (SDL12Key)0xef; + case 0x0192: return (SDL12Key)0xf6; + case 0x2190: return (SDL12Key)0xfb; + case 0x2191: return (SDL12Key)0xfc; + case 0x2192: return (SDL12Key)0xfd; + case 0x2193: return (SDL12Key)0xfe; + /* Publishing */ + case 0x2003: return (SDL12Key)0xa1; + case 0x2002: return (SDL12Key)0xa2; + case 0x2004: return (SDL12Key)0xa3; + case 0x2005: return (SDL12Key)0xa4; + case 0x2007: return (SDL12Key)0xa5; + case 0x2008: return (SDL12Key)0xa6; + case 0x2009: return (SDL12Key)0xa7; + case 0x200A: return (SDL12Key)0xa8; + case 0x2014: return (SDL12Key)0xa9; + case 0x2013: return (SDL12Key)0xaa; + case 0x2423: return (SDL12Key)0xac; + case 0x2026: return (SDL12Key)0xae; + case 0x2025: return (SDL12Key)0xaf; + case 0x2153: return (SDL12Key)0xb0; + case 0x2154: return (SDL12Key)0xb1; + case 0x2155: return (SDL12Key)0xb2; + case 0x2156: return (SDL12Key)0xb3; + case 0x2157: return (SDL12Key)0xb4; + case 0x2158: return (SDL12Key)0xb5; + case 0x2159: return (SDL12Key)0xb6; + case 0x215A: return (SDL12Key)0xb7; + case 0x2105: return (SDL12Key)0xb8; + case 0x2012: return (SDL12Key)0xbb; + case 0x27E8: return (SDL12Key)0xbc; + case 0x002E: return (SDL12Key)0xbd; + case 0x27E9: return (SDL12Key)0xbe; + case 0x215B: return (SDL12Key)0xc3; + case 0x215C: return (SDL12Key)0xc4; + case 0x215D: return (SDL12Key)0xc5; + case 0x215E: return (SDL12Key)0xc6; + case 0x2122: return (SDL12Key)0xc9; + case 0x2613: return (SDL12Key)0xca; + case 0x25C1: return (SDL12Key)0xcc; + case 0x25B7: return (SDL12Key)0xcd; + case 0x25CB: return (SDL12Key)0xce; + case 0x25AF: return (SDL12Key)0xcf; + case 0x2018: return (SDL12Key)0xd0; + case 0x2019: return (SDL12Key)0xd1; + case 0x201C: return (SDL12Key)0xd2; + case 0x201D: return (SDL12Key)0xd3; + case 0x211E: return (SDL12Key)0xd4; + case 0x2030: return (SDL12Key)0xd5; + case 0x2032: return (SDL12Key)0xd6; + case 0x2033: return (SDL12Key)0xd7; + case 0x271D: return (SDL12Key)0xd9; + case 0x25AC: return (SDL12Key)0xdb; + case 0x25C0: return (SDL12Key)0xdc; + case 0x25B6: return (SDL12Key)0xdd; + case 0x25CF: return (SDL12Key)0xde; + case 0x25AE: return (SDL12Key)0xdf; + case 0x25E6: return (SDL12Key)0xe0; + case 0x25AB: return (SDL12Key)0xe1; + case 0x25AD: return (SDL12Key)0xe2; + case 0x25B3: return (SDL12Key)0xe3; + case 0x25BD: return (SDL12Key)0xe4; + case 0x2606: return (SDL12Key)0xe5; + case 0x2022: return (SDL12Key)0xe6; + case 0x25AA: return (SDL12Key)0xe7; + case 0x25B2: return (SDL12Key)0xe8; + case 0x25BC: return (SDL12Key)0xe9; + case 0x261C: return (SDL12Key)0xea; + case 0x261E: return (SDL12Key)0xeb; + case 0x2663: return (SDL12Key)0xec; + case 0x2666: return (SDL12Key)0xed; + case 0x2665: return (SDL12Key)0xee; + case 0x2720: return (SDL12Key)0xf0; + case 0x2020: return (SDL12Key)0xf1; + case 0x2021: return (SDL12Key)0xf2; + case 0x2713: return (SDL12Key)0xf3; + case 0x2717: return (SDL12Key)0xf4; + case 0x266F: return (SDL12Key)0xf5; + case 0x266D: return (SDL12Key)0xf6; + case 0x2642: return (SDL12Key)0xf7; + case 0x2640: return (SDL12Key)0xf8; + case 0x260E: return (SDL12Key)0xf9; + case 0x2315: return (SDL12Key)0xfa; + case 0x2117: return (SDL12Key)0xfb; + case 0x2038: return (SDL12Key)0xfc; + case 0x201A: return (SDL12Key)0xfd; + case 0x201E: return (SDL12Key)0xfe; + /* Hebrew */ + case 0x2017: return (SDL12Key)0xdf; + case 0x05D0: return (SDL12Key)0xe0; + case 0x05D1: return (SDL12Key)0xe1; + case 0x05D2: return (SDL12Key)0xe2; + case 0x05D3: return (SDL12Key)0xe3; + case 0x05D4: return (SDL12Key)0xe4; + case 0x05D5: return (SDL12Key)0xe5; + case 0x05D6: return (SDL12Key)0xe6; + case 0x05D7: return (SDL12Key)0xe7; + case 0x05D8: return (SDL12Key)0xe8; + case 0x05D9: return (SDL12Key)0xe9; + case 0x05DA: return (SDL12Key)0xea; + case 0x05DB: return (SDL12Key)0xeb; + case 0x05DC: return (SDL12Key)0xec; + case 0x05DD: return (SDL12Key)0xed; + case 0x05DE: return (SDL12Key)0xee; + case 0x05DF: return (SDL12Key)0xef; + case 0x05E0: return (SDL12Key)0xf0; + case 0x05E1: return (SDL12Key)0xf1; + case 0x05E2: return (SDL12Key)0xf2; + case 0x05E3: return (SDL12Key)0xf3; + case 0x05E4: return (SDL12Key)0xf4; + case 0x05E5: return (SDL12Key)0xf5; + case 0x05E6: return (SDL12Key)0xf6; + case 0x05E7: return (SDL12Key)0xf7; + case 0x05E8: return (SDL12Key)0xf8; + case 0x05E9: return (SDL12Key)0xf9; + case 0x05EA: return (SDL12Key)0xfa; + /* Thai */ + case 0x0E01: return (SDL12Key)0xa1; + case 0x0E02: return (SDL12Key)0xa2; + case 0x0E03: return (SDL12Key)0xa3; + case 0x0E04: return (SDL12Key)0xa4; + case 0x0E05: return (SDL12Key)0xa5; + case 0x0E06: return (SDL12Key)0xa6; + case 0x0E07: return (SDL12Key)0xa7; + case 0x0E08: return (SDL12Key)0xa8; + case 0x0E09: return (SDL12Key)0xa9; + case 0x0E0A: return (SDL12Key)0xaa; + case 0x0E0B: return (SDL12Key)0xab; + case 0x0E0C: return (SDL12Key)0xac; + case 0x0E0D: return (SDL12Key)0xad; + case 0x0E0E: return (SDL12Key)0xae; + case 0x0E0F: return (SDL12Key)0xaf; + case 0x0E10: return (SDL12Key)0xb0; + case 0x0E11: return (SDL12Key)0xb1; + case 0x0E12: return (SDL12Key)0xb2; + case 0x0E13: return (SDL12Key)0xb3; + case 0x0E14: return (SDL12Key)0xb4; + case 0x0E15: return (SDL12Key)0xb5; + case 0x0E16: return (SDL12Key)0xb6; + case 0x0E17: return (SDL12Key)0xb7; + case 0x0E18: return (SDL12Key)0xb8; + case 0x0E19: return (SDL12Key)0xb9; + case 0x0E1A: return (SDL12Key)0xba; + case 0x0E1B: return (SDL12Key)0xbb; + case 0x0E1C: return (SDL12Key)0xbc; + case 0x0E1D: return (SDL12Key)0xbd; + case 0x0E1E: return (SDL12Key)0xbe; + case 0x0E1F: return (SDL12Key)0xbf; + case 0x0E20: return (SDL12Key)0xc0; + case 0x0E21: return (SDL12Key)0xc1; + case 0x0E22: return (SDL12Key)0xc2; + case 0x0E23: return (SDL12Key)0xc3; + case 0x0E24: return (SDL12Key)0xc4; + case 0x0E25: return (SDL12Key)0xc5; + case 0x0E26: return (SDL12Key)0xc6; + case 0x0E27: return (SDL12Key)0xc7; + case 0x0E28: return (SDL12Key)0xc8; + case 0x0E29: return (SDL12Key)0xc9; + case 0x0E2A: return (SDL12Key)0xca; + case 0x0E2B: return (SDL12Key)0xcb; + case 0x0E2C: return (SDL12Key)0xcc; + case 0x0E2D: return (SDL12Key)0xcd; + case 0x0E2E: return (SDL12Key)0xce; + case 0x0E2F: return (SDL12Key)0xcf; + case 0x0E30: return (SDL12Key)0xd0; + case 0x0E31: return (SDL12Key)0xd1; + case 0x0E32: return (SDL12Key)0xd2; + case 0x0E33: return (SDL12Key)0xd3; + case 0x0E34: return (SDL12Key)0xd4; + case 0x0E35: return (SDL12Key)0xd5; + case 0x0E36: return (SDL12Key)0xd6; + case 0x0E37: return (SDL12Key)0xd7; + case 0x0E38: return (SDL12Key)0xd8; + case 0x0E39: return (SDL12Key)0xd9; + case 0x0E3A: return (SDL12Key)0xda; + case 0x0E3F: return (SDL12Key)0xdf; + case 0x0E40: return (SDL12Key)0xe0; + case 0x0E41: return (SDL12Key)0xe1; + case 0x0E42: return (SDL12Key)0xe2; + case 0x0E43: return (SDL12Key)0xe3; + case 0x0E44: return (SDL12Key)0xe4; + case 0x0E45: return (SDL12Key)0xe5; + case 0x0E46: return (SDL12Key)0xe6; + case 0x0E47: return (SDL12Key)0xe7; + case 0x0E48: return (SDL12Key)0xe8; + case 0x0E49: return (SDL12Key)0xe9; + case 0x0E4A: return (SDL12Key)0xea; + case 0x0E4B: return (SDL12Key)0xeb; + case 0x0E4C: return (SDL12Key)0xec; + case 0x0E4D: return (SDL12Key)0xed; + case 0x0E50: return (SDL12Key)0xf0; + case 0x0E51: return (SDL12Key)0xf1; + case 0x0E52: return (SDL12Key)0xf2; + case 0x0E53: return (SDL12Key)0xf3; + case 0x0E54: return (SDL12Key)0xf4; + case 0x0E55: return (SDL12Key)0xf5; + case 0x0E56: return (SDL12Key)0xf6; + case 0x0E57: return (SDL12Key)0xf7; + case 0x0E58: return (SDL12Key)0xf8; + case 0x0E59: return (SDL12Key)0xf9; + /* end of SDLK_WORLD_ keys based on Latin-* or similar High-ASCII charsets + * and the low byte of their corresponding X11 XK_* KeySyms */ + default: break; + } + + return SDLK12_UNKNOWN; +} +static SDL12Key +Scancode20toKeysym12(const SDL_Scancode scancode20) +{ + switch (scancode20) { + #define CASESCANCODE20TOKEY12(s20, k12) case SDL_SCANCODE_##s20: return SDLK12_##k12 + CASESCANCODE20TOKEY12(A,a); + CASESCANCODE20TOKEY12(B,b); + CASESCANCODE20TOKEY12(C,c); + CASESCANCODE20TOKEY12(D,d); + CASESCANCODE20TOKEY12(E,e); + CASESCANCODE20TOKEY12(F,f); + CASESCANCODE20TOKEY12(G,g); + CASESCANCODE20TOKEY12(H,h); + CASESCANCODE20TOKEY12(I,i); + CASESCANCODE20TOKEY12(J,j); + CASESCANCODE20TOKEY12(K,k); + CASESCANCODE20TOKEY12(L,l); + CASESCANCODE20TOKEY12(M,m); + CASESCANCODE20TOKEY12(N,n); + CASESCANCODE20TOKEY12(O,o); + CASESCANCODE20TOKEY12(P,p); + CASESCANCODE20TOKEY12(Q,q); + CASESCANCODE20TOKEY12(R,r); + CASESCANCODE20TOKEY12(S,s); + CASESCANCODE20TOKEY12(T,t); + CASESCANCODE20TOKEY12(U,u); + CASESCANCODE20TOKEY12(V,v); + CASESCANCODE20TOKEY12(W,w); + CASESCANCODE20TOKEY12(X,x); + CASESCANCODE20TOKEY12(Y,y); + CASESCANCODE20TOKEY12(Z,z); + CASESCANCODE20TOKEY12(1,1); + CASESCANCODE20TOKEY12(2,2); + CASESCANCODE20TOKEY12(3,3); + CASESCANCODE20TOKEY12(4,4); + CASESCANCODE20TOKEY12(5,5); + CASESCANCODE20TOKEY12(6,6); + CASESCANCODE20TOKEY12(7,7); + CASESCANCODE20TOKEY12(8,8); + CASESCANCODE20TOKEY12(9,9); + CASESCANCODE20TOKEY12(0,0); + CASESCANCODE20TOKEY12(RETURN,RETURN); + CASESCANCODE20TOKEY12(ESCAPE,ESCAPE); + CASESCANCODE20TOKEY12(BACKSPACE,BACKSPACE); + CASESCANCODE20TOKEY12(TAB,TAB); + CASESCANCODE20TOKEY12(SPACE,SPACE); + CASESCANCODE20TOKEY12(MINUS,MINUS); + CASESCANCODE20TOKEY12(EQUALS,EQUALS); + CASESCANCODE20TOKEY12(LEFTBRACKET,LEFTBRACKET); + CASESCANCODE20TOKEY12(RIGHTBRACKET,RIGHTBRACKET); + CASESCANCODE20TOKEY12(BACKSLASH,BACKSLASH); + CASESCANCODE20TOKEY12(NONUSHASH,HASH); + CASESCANCODE20TOKEY12(SEMICOLON,SEMICOLON); + CASESCANCODE20TOKEY12(APOSTROPHE,QUOTE); + CASESCANCODE20TOKEY12(GRAVE,BACKQUOTE); + CASESCANCODE20TOKEY12(COMMA,COMMA); + CASESCANCODE20TOKEY12(PERIOD,PERIOD); + CASESCANCODE20TOKEY12(SLASH,SLASH); + CASESCANCODE20TOKEY12(CAPSLOCK,CAPSLOCK); + CASESCANCODE20TOKEY12(F1,F1); + CASESCANCODE20TOKEY12(F2,F2); + CASESCANCODE20TOKEY12(F3,F3); + CASESCANCODE20TOKEY12(F4,F4); + CASESCANCODE20TOKEY12(F5,F5); + CASESCANCODE20TOKEY12(F6,F6); + CASESCANCODE20TOKEY12(F7,F7); + CASESCANCODE20TOKEY12(F8,F8); + CASESCANCODE20TOKEY12(F9,F9); + CASESCANCODE20TOKEY12(F10,F10); + CASESCANCODE20TOKEY12(F11,F11); + CASESCANCODE20TOKEY12(F12,F12); + CASESCANCODE20TOKEY12(PRINTSCREEN,PRINT); + CASESCANCODE20TOKEY12(SCROLLLOCK,SCROLLOCK); + CASESCANCODE20TOKEY12(PAUSE,PAUSE); + CASESCANCODE20TOKEY12(INSERT,INSERT); + CASESCANCODE20TOKEY12(HOME,HOME); + CASESCANCODE20TOKEY12(PAGEUP,PAGEUP); + CASESCANCODE20TOKEY12(DELETE,DELETE); + CASESCANCODE20TOKEY12(END,END); + CASESCANCODE20TOKEY12(PAGEDOWN,PAGEDOWN); + CASESCANCODE20TOKEY12(RIGHT,RIGHT); + CASESCANCODE20TOKEY12(LEFT,LEFT); + CASESCANCODE20TOKEY12(DOWN,DOWN); + CASESCANCODE20TOKEY12(UP,UP); + CASESCANCODE20TOKEY12(NUMLOCKCLEAR,NUMLOCK); + + CASESCANCODE20TOKEY12(KP_DIVIDE,KP_DIVIDE); + CASESCANCODE20TOKEY12(KP_MULTIPLY,KP_MULTIPLY); + CASESCANCODE20TOKEY12(KP_MINUS,KP_MINUS); + CASESCANCODE20TOKEY12(KP_PLUS,KP_PLUS); + CASESCANCODE20TOKEY12(KP_ENTER,KP_ENTER); + CASESCANCODE20TOKEY12(KP_1,KP1); + CASESCANCODE20TOKEY12(KP_2,KP2); + CASESCANCODE20TOKEY12(KP_3,KP3); + CASESCANCODE20TOKEY12(KP_4,KP4); + CASESCANCODE20TOKEY12(KP_5,KP5); + CASESCANCODE20TOKEY12(KP_6,KP6); + CASESCANCODE20TOKEY12(KP_7,KP7); + CASESCANCODE20TOKEY12(KP_8,KP8); + CASESCANCODE20TOKEY12(KP_9,KP9); + CASESCANCODE20TOKEY12(KP_0,KP0); + + CASESCANCODE20TOKEY12(NONUSBACKSLASH,BACKSLASH); + /* In theory, this could be MENU, or COMPOSE, or neither, but on my machine, it's MENU. */ + CASESCANCODE20TOKEY12(APPLICATION,MENU); + CASESCANCODE20TOKEY12(POWER,POWER); + CASESCANCODE20TOKEY12(F13,F13); + CASESCANCODE20TOKEY12(F14,F14); + CASESCANCODE20TOKEY12(F15,F15); + CASESCANCODE20TOKEY12(KP_EQUALS,KP_EQUALS); + /* SDL 1.2 doesn't support F16..F21 */ + /* Nor SDL_SCANCODE_EXECUTE */ + CASESCANCODE20TOKEY12(HELP,HELP); + CASESCANCODE20TOKEY12(MENU,MENU); + /* The next several scancodes don't have equivalents, until... */ + CASESCANCODE20TOKEY12(SYSREQ,SYSREQ); + CASESCANCODE20TOKEY12(CLEAR,CLEAR); + /* Skip some more... */ + CASESCANCODE20TOKEY12(LCTRL,LCTRL); + CASESCANCODE20TOKEY12(LSHIFT,LSHIFT); + CASESCANCODE20TOKEY12(LALT,LALT); +#ifdef __MACOSX__ + CASESCANCODE20TOKEY12(LGUI,LMETA); +#else + CASESCANCODE20TOKEY12(LGUI,LSUPER); +#endif + CASESCANCODE20TOKEY12(RCTRL,RCTRL); + CASESCANCODE20TOKEY12(RSHIFT,RSHIFT); + CASESCANCODE20TOKEY12(RALT,RALT); +#ifdef __MACOSX__ + CASESCANCODE20TOKEY12(RGUI,RMETA); +#else + CASESCANCODE20TOKEY12(RGUI,RSUPER); +#endif + + CASESCANCODE20TOKEY12(MODE,MODE); + #undef CASESCANCODE20TOKEY12 default: break; } - FIXME("nothing maps to SDLK12_COMPOSE, SDLK12_BREAK, or SDLK12_EURO ...?"); - FIXME("map some of the SDLK12_WORLD keys"); return SDLK12_UNKNOWN; } -DECLSPEC Uint8 * SDLCALL +static Uint8 +Scancode20to12(SDL_Scancode sc) +{ + /* SDL 1.2 scancodes are the actual raw scancodes (for the most part), and + so differ wildly between different systems. Fortunately, this means + they're rarely used, and often have fallbacks. Here, we support them + for three systems: Win32, Mac OS X, and a synthesized pseudo-Linux that + should work. + Windows scancodes are bascially just Linux ones - 8. OS X has a totally + different set of scancodes from everyone else. Linux's scancodes change + depending on what driver you're using, but only really for a few keys. + Since there are applications (DOSBox) which look this up and behave + accordingly, but have fallbacks, those keys have scancodes of 0 here, + to trigger the fallbacks. */ + switch(sc) { +#if defined(_WIN32) +#define CASESCANCODE20TO12(sc20, sc12, sc12mac) case SDL_SCANCODE_##sc20: return (sc12 ? (sc12 - 8) : 0) +#elif defined(__MACOSX__) +#define CASESCANCODE20TO12(sc20, sc12, sc12mac) case SDL_SCANCODE_##sc20: return sc12mac +#else +#define CASESCANCODE20TO12(sc20, sc12, sc12mac) case SDL_SCANCODE_##sc20: return sc12 +#endif + CASESCANCODE20TO12(0, 0x13, 0x1D); + CASESCANCODE20TO12(1, 0x0A, 0x12); + CASESCANCODE20TO12(2, 0x0B, 0x13); + CASESCANCODE20TO12(3, 0x0C, 0x14); + CASESCANCODE20TO12(4, 0x0D, 0x15); + CASESCANCODE20TO12(5, 0x0E, 0x17); + CASESCANCODE20TO12(6, 0x0F, 0x16); + CASESCANCODE20TO12(7, 0x10, 0x1A); + CASESCANCODE20TO12(8, 0x11, 0x1C); + CASESCANCODE20TO12(9, 0x12, 0x19); + CASESCANCODE20TO12(A, 0x26, 0x00); + CASESCANCODE20TO12(APOSTROPHE, 0x30, 0x27); + CASESCANCODE20TO12(APPLICATION, 0x65, 0x00); + CASESCANCODE20TO12(B, 0x38, 0x0B); + CASESCANCODE20TO12(BACKSLASH, 0x33, 0x2A); + CASESCANCODE20TO12(BACKSPACE, 0x16, 0x33); + CASESCANCODE20TO12(C, 0x36, 0x08); + CASESCANCODE20TO12(CAPSLOCK, 0x42, 0x00); + CASESCANCODE20TO12(COMMA, 0x3B, 0x2B); + CASESCANCODE20TO12(D, 0x28, 0x02); + CASESCANCODE20TO12(DELETE, 0x00, 0x75); + CASESCANCODE20TO12(DOWN, 0x00, 0x7D); + CASESCANCODE20TO12(E, 0x1A, 0x0E); + CASESCANCODE20TO12(END, 0x00, 0x77); + CASESCANCODE20TO12(EQUALS, 0x15, 0x18); + CASESCANCODE20TO12(ESCAPE, 0x09, 0x35); + CASESCANCODE20TO12(F, 0x29, 0x03); + CASESCANCODE20TO12(F1, 0x43, 0x7A); + CASESCANCODE20TO12(F10, 0x4C, 0x6E); + CASESCANCODE20TO12(F11, 0x5F, 0x67); + CASESCANCODE20TO12(F12, 0x60, 0x6F); + CASESCANCODE20TO12(F2, 0x44, 0x78); + CASESCANCODE20TO12(F3, 0x45, 0x63); + CASESCANCODE20TO12(F4, 0x46, 0x76); + CASESCANCODE20TO12(F5, 0x47, 0x60); + CASESCANCODE20TO12(F6, 0x48, 0x61); + CASESCANCODE20TO12(F7, 0x49, 0x62); + CASESCANCODE20TO12(F8, 0x4A, 0x64); + CASESCANCODE20TO12(F9, 0x4B, 0x65); + CASESCANCODE20TO12(G, 0x2A, 0x05); + CASESCANCODE20TO12(GRAVE, 0x31, 0x32); /* Note: the mac scancode might not be 100% correct, see below */ + CASESCANCODE20TO12(H, 0x2B, 0x04); + CASESCANCODE20TO12(HOME, 0x00, 0x73); + CASESCANCODE20TO12(I, 0x1F, 0x22); + CASESCANCODE20TO12(INSERT, 0x00, 0x72); + CASESCANCODE20TO12(J, 0x2C, 0x26); + CASESCANCODE20TO12(K, 0x2D, 0x28); + CASESCANCODE20TO12(KP_0, 0x5A, 0x52); + CASESCANCODE20TO12(KP_1, 0x57, 0x53); + CASESCANCODE20TO12(KP_2, 0x58, 0x54); + CASESCANCODE20TO12(KP_3, 0x59, 0x55); + CASESCANCODE20TO12(KP_4, 0x53, 0x56); + CASESCANCODE20TO12(KP_5, 0x54, 0x57); + CASESCANCODE20TO12(KP_6, 0x55, 0x58); + CASESCANCODE20TO12(KP_7, 0x4F, 0x59); + CASESCANCODE20TO12(KP_8, 0x50, 0x5B); + CASESCANCODE20TO12(KP_9, 0x51, 0x5C); + CASESCANCODE20TO12(KP_DIVIDE, 0x00, 0x4B); + CASESCANCODE20TO12(KP_ENTER, 0x00, 0x4C); + CASESCANCODE20TO12(KP_EQUALS, 0x00, 0x51); + CASESCANCODE20TO12(KP_MINUS, 0x52, 0x4E); + CASESCANCODE20TO12(KP_MULTIPLY, 0x3F, 0x43); + CASESCANCODE20TO12(KP_PERIOD, 0x5B, 0x41); + CASESCANCODE20TO12(KP_PLUS, 0x56, 0x45); + CASESCANCODE20TO12(L, 0x2E, 0x25); + CASESCANCODE20TO12(LALT, 0x40, 0x00); + CASESCANCODE20TO12(LCTRL, 0x25, 0x00); + CASESCANCODE20TO12(LEFT, 0x00, 0x7B); + CASESCANCODE20TO12(LEFTBRACKET, 0x22, 0x21); + CASESCANCODE20TO12(LGUI, 0x85, 0x00); + CASESCANCODE20TO12(LSHIFT, 0x32, 0x00); + CASESCANCODE20TO12(M, 0x3A, 0x2E); + CASESCANCODE20TO12(MENU, 0x65, 0x00); + CASESCANCODE20TO12(MINUS, 0x14, 0x1B); + CASESCANCODE20TO12(N, 0x39, 0x2D); + /* On Macs with ANSI layout, 0x32 is SDL_SCANCODE_GRAVE and _NONUSBACKSLASH doesn't exist. + * On Macs with ISO layout, 0x32 is _NONUSBACKSLASH and 0x0A is the key at the position of _GRAVE.. + * Probably it's best to keep _GRAVE at 0x32 and use 0x0A for _NONUSBACKSLASH instead, + * so at least it has a unique scancode at all. */ + CASESCANCODE20TO12(NONUSBACKSLASH, 0x5E, 0x0A); + CASESCANCODE20TO12(NUMLOCKCLEAR, 0x4D, 0x47); + CASESCANCODE20TO12(O, 0x20, 0x1F); + CASESCANCODE20TO12(P, 0x21, 0x23); + CASESCANCODE20TO12(PAGEDOWN, 0x00, 0x79); + CASESCANCODE20TO12(PAGEUP, 0x00, 0x74); + CASESCANCODE20TO12(PERIOD, 0x3C, 0x2F); + CASESCANCODE20TO12(PRINTSCREEN, 0x6B, 0x6B); + CASESCANCODE20TO12(Q, 0x18, 0x0C); + CASESCANCODE20TO12(R, 0x1B, 0x0F); + CASESCANCODE20TO12(RETURN, 0x24, 0x24); + CASESCANCODE20TO12(RALT, 0x40, 0x00); + CASESCANCODE20TO12(RCTRL, 0x25, 0x00); + CASESCANCODE20TO12(RGUI, 0x86, 0x00); + CASESCANCODE20TO12(RIGHT, 0x00, 0x7C); + CASESCANCODE20TO12(RIGHTBRACKET, 0x23, 0x1E); + CASESCANCODE20TO12(RSHIFT, 0x3E, 0x00); + CASESCANCODE20TO12(S, 0x27, 0x01); + CASESCANCODE20TO12(SCROLLLOCK, 0x4E, 0x71); + CASESCANCODE20TO12(SEMICOLON, 0x2F, 0x29); + CASESCANCODE20TO12(SLASH, 0x3D, 0x2C); + CASESCANCODE20TO12(SPACE, 0x41, 0x31); + CASESCANCODE20TO12(T, 0x1C, 0x11); + CASESCANCODE20TO12(TAB, 0x17, 0x30); + CASESCANCODE20TO12(U, 0x1E, 0x20); + CASESCANCODE20TO12(UP, 0x00, 0x7E); + CASESCANCODE20TO12(V, 0x37, 0x09); + CASESCANCODE20TO12(W, 0x19, 0x0D); + CASESCANCODE20TO12(X, 0x35, 0x07); + CASESCANCODE20TO12(Y, 0x1D, 0x10); + CASESCANCODE20TO12(Z, 0x34, 0x06); +#undef CASESCANCODE20TO12 + default: + /* If we don't know it, return 0, which is "unknown". + It's also "a" on Mac OS X, but SDL 1.2 uses it as "unknown", too. */ + return 0; + } +} + +DECLSPEC12 Uint8 * SDLCALL SDL_GetKeyState(int *numkeys) { if (numkeys) { @@ -2292,6 +4805,27 @@ static int DecodeUTF8Char(char **ptr) return value; } +static int IsRepeatable(SDL12Key key) +{ + switch (key) { + case SDLK12_UNKNOWN: + case SDLK12_NUMLOCK: + case SDLK12_CAPSLOCK: + case SDLK12_LCTRL: + case SDLK12_RCTRL: + case SDLK12_LSHIFT: + case SDLK12_RSHIFT: + case SDLK12_LALT: + case SDLK12_RALT: + case SDLK12_LMETA: + case SDLK12_RMETA: + case SDLK12_MODE: + return 0; + default: + return 1; + } +} + /* Add the pending KEYDOWN event to the EventQueue, possibly with 'unicode' set * Returns 1 if there was a pending event. */ static int FlushPendingKeydownEvent(Uint32 unicode) @@ -2302,8 +4836,15 @@ static int FlushPendingKeydownEvent(Uint32 unicode) PendingKeydownEvent.key.keysym.unicode = unicode; PushEventIfNotFiltered(&PendingKeydownEvent); + + if (KeyRepeatDelay && IsRepeatable(PendingKeydownEvent.key.keysym.sym)) { + SDL20_memcpy(&KeyRepeatEvent, &PendingKeydownEvent, sizeof (SDL12_Event)); + /* SDL 1.2 waits for the delay, and then a full interval past that before the first repeat is reported. */ + KeyRepeatNextTicks = SDL20_GetTicks() + KeyRepeatDelay + KeyRepeatInterval; + } + /* Reset the event. */ - SDL20_memset(&PendingKeydownEvent, 0, sizeof(SDL12_Event)); + SDL20_memset(&PendingKeydownEvent, 0, sizeof (SDL12_Event)); return 1; } @@ -2312,6 +4853,7 @@ static int SDLCALL EventFilter20to12(void *data, SDL_Event *event20) { SDL12_Event event12; + SDL12_SysWMmsg msg; SDL_assert(data == NULL); /* currently unused. */ @@ -2323,37 +4865,50 @@ EventFilter20to12(void *data, SDL_Event *event20) break; case SDL_WINDOWEVENT: + if (!VideoWindow20) { + break; /* no window? No event. */ + } + switch (event20->window.event) { - case SDL_WINDOWEVENT_CLOSE: - event12.type = SDL12_QUIT; - break; + /* don't send an SDL12_QUIT event for SDL_WINDOWEVENT_CLOSE; + we only ever have a single window, so an SDL_QUIT will be + coming from SDL2 next anyhow, so just send that on. */ case SDL_WINDOWEVENT_SHOWN: case SDL_WINDOWEVENT_EXPOSED: - event12.type = SDL12_VIDEOEXPOSE; + /* drop these during window creation (see issue #229) */ + if (!SetVideoModeInProgress) { + event12.type = SDL12_VIDEOEXPOSE; + } break; - case SDL_WINDOWEVENT_RESIZED: - case SDL_WINDOWEVENT_SIZE_CHANGED: - FIXME("what's the difference between RESIZED and SIZE_CHANGED?"); + case SDL_WINDOWEVENT_RESIZED: { + /* don't generate a VIDEORESIZE event based on SIZE_CHANGED + events: the recommended way to handle VIDEORESIZE is + with a new SDL_SetVideoMode() call, and creating a new + window generates a SIZE_CHANGED event, which leads to an + infinite loop. */ /* don't report VIDEORESIZE if we're fullscreen-desktop; we're doing logical scaling and as far as the app is concerned the window doesn't change. */ - if (!VideoWindow20) { - FIXME("we should probably drop a lot of these events."); - break; /* there's no window? Drop this event. */ - } else { - const Uint32 flags = SDL20_GetWindowFlags(VideoWindow20); - if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { - break; - } + const Uint32 flags = SDL20_GetWindowFlags(VideoWindow20); + if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { + break; } - event12.type = SDL12_VIDEORESIZE; - event12.resize.w = event20->window.data1; - event12.resize.h = event20->window.data2; + if (ProcessingModalLoop) { + PendingResizeEvent.type = SDL12_VIDEORESIZE; + PendingResizeEvent.resize.w = event20->window.data1; + PendingResizeEvent.resize.h = event20->window.data2; + HasPendingResizeEvent = SDL_TRUE; + } else { + event12.type = SDL12_VIDEORESIZE; + event12.resize.w = event20->window.data1; + event12.resize.h = event20->window.data2; + } break; + } case SDL_WINDOWEVENT_MINIMIZED: event12.type = SDL12_ACTIVEEVENT; @@ -2393,26 +4948,80 @@ EventFilter20to12(void *data, SDL_Event *event20) } break; - /* !!! FIXME: this is sort of a mess to convert. */ - case SDL_SYSWMEVENT: FIXME("write me"); return 1; + case SDL_SYSWMEVENT: + #if defined(SDL_VIDEO_DRIVER_WINDOWS) + switch (event20->syswm.msg->msg.win.msg) { + case WM_ENTERSIZEMOVE: + case WM_ENTERMENULOOP: + ++ProcessingModalLoop; + break; + case WM_EXITSIZEMOVE: + case WM_EXITMENULOOP: + --ProcessingModalLoop; + if (ProcessingModalLoop == 0 && HasPendingResizeEvent) { + PushEventIfNotFiltered(&PendingResizeEvent); + HasPendingResizeEvent = SDL_FALSE; + } + break; + default: + break; + } + #endif + + if (!SupportSysWM) { + return 1; + } + + #if defined(SDL_VIDEO_DRIVER_WINDOWS) + SDL_assert(event20->syswm.msg->subsystem == SDL_SYSWM_WINDOWS); + msg.hwnd = event20->syswm.msg->msg.win.hwnd; + msg.msg = event20->syswm.msg->msg.win.msg; + msg.wParam = event20->syswm.msg->msg.win.wParam; + msg.lParam = event20->syswm.msg->msg.win.lParam; + #elif defined(SDL_VIDEO_DRIVER_X11) + SDL_assert(event20->syswm.msg->subsystem == SDL_SYSWM_X11); + msg.subsystem = SDL12_SYSWM_X11; + SDL20_memcpy(&msg.event.xevent, &event20->syswm.msg->msg.x11.event, sizeof (XEvent)); + #else + SDL_assert(!"should have been caught by !SupportsSysWM test"); + #endif + + SDL20_memcpy(&msg.version, SDL_Linked_Version(), sizeof (msg.version)); + event12.type = SDL12_SYSWMEVENT; + event12.syswm.msg = &msg; /* this is stack-allocated, but we copy and update the pointer later. */ + break; case SDL_KEYUP: if (event20->key.repeat) { return 1; /* ignore 2.0-style key repeat events */ } - event12.key.keysym.sym = Keysym20to12(event20->key.keysym.sym); - if (event12.key.keysym.sym == SDLK12_UNKNOWN) { - return 1; /* drop it if we can't map it */ + + if (TranslateKeyboardLayout) { + event12.key.keysym.sym = Keysym20to12(event20->key.keysym.sym); + } else { + event12.key.keysym.sym = Scancode20toKeysym12(event20->key.keysym.scancode); + } + + if (event12.key.keysym.sym == SDLK12_CAPSLOCK || + event12.key.keysym.sym == SDLK12_NUMLOCK) { + /* SDL 1.2 only sends capslock and numlock key events on keydown */ + return 1; + } + + if (KeyRepeatNextTicks) { + SDL_assert(KeyRepeatEvent.type == SDL12_KEYDOWN); + if (KeyRepeatEvent.key.keysym.sym == event12.key.keysym.sym) { + KeyRepeatNextTicks = 0; + } } KeyState[event12.key.keysym.sym] = event20->key.state; - event12.type = (event20->type == SDL_KEYDOWN) ? SDL12_KEYDOWN : SDL12_KEYUP; + event12.type = SDL12_KEYUP; event12.key.which = 0; event12.key.state = event20->key.state; - FIXME("SDL1.2 and SDL2.0 scancodes are incompatible"); /* turns out that some apps actually made use of the hardware scancodes (checking for platform beforehand) */ - event12.key.keysym.scancode = 0; + event12.key.keysym.scancode = Scancode20to12(event20->key.keysym.scancode); event12.key.keysym.mod = event20->key.keysym.mod; /* these match up between 1.2 and 2.0! */ event12.key.keysym.unicode = 0; @@ -2426,22 +5035,79 @@ EventFilter20to12(void *data, SDL_Event *event20) return 1; /* ignore 2.0-style key repeat events */ } - PendingKeydownEvent.key.keysym.sym = Keysym20to12(event20->key.keysym.sym); - if (PendingKeydownEvent.key.keysym.sym == SDLK12_UNKNOWN) { - return 1; /* drop it if we can't map it */ + if (TranslateKeyboardLayout) { + event12.key.keysym.sym = Keysym20to12(event20->key.keysym.sym); + } else { + event12.key.keysym.sym = Scancode20toKeysym12(event20->key.keysym.scancode); + } + + if (event12.key.keysym.sym == SDLK12_CAPSLOCK || + event12.key.keysym.sym == SDLK12_NUMLOCK) { + /* SDL 1.2 toggles capslock and numlock on keypress */ + if (KeyState[event12.key.keysym.sym]) { + KeyState[event12.key.keysym.sym] = SDL_RELEASED; + + event12.type = SDL12_KEYUP; + event12.key.which = 0; + event12.key.state = SDL_RELEASED; + event12.key.keysym.scancode = Scancode20to12(event20->key.keysym.scancode); + event12.key.keysym.mod = event20->key.keysym.mod; + event12.key.keysym.unicode = 0; + if (event12.key.keysym.sym == SDLK12_CAPSLOCK) { + event12.key.keysym.mod &= ~KMOD12_CAPS; + } else if (event12.key.keysym.sym == SDLK12_NUMLOCK) { + event12.key.keysym.mod &= ~KMOD12_NUM; + } + break; + } } - KeyState[PendingKeydownEvent.key.keysym.sym] = event20->key.state; + KeyState[event12.key.keysym.sym] = event20->key.state; - PendingKeydownEvent.type = (event20->type == SDL_KEYDOWN) ? SDL12_KEYDOWN : SDL12_KEYUP; + PendingKeydownEvent.type = SDL12_KEYDOWN; PendingKeydownEvent.key.which = 0; PendingKeydownEvent.key.state = event20->key.state; - FIXME("SDL1.2 and SDL2.0 scancodes are incompatible"); /* turns out that some apps actually made use of the hardware scancodes (checking for platform beforehand) */ - PendingKeydownEvent.key.keysym.scancode = 0; + PendingKeydownEvent.key.keysym.scancode = Scancode20to12(event20->key.keysym.scancode); + PendingKeydownEvent.key.keysym.sym = event12.key.keysym.sym; PendingKeydownEvent.key.keysym.mod = event20->key.keysym.mod; /* these match up between 1.2 and 2.0! */ PendingKeydownEvent.key.keysym.unicode = 0; + /* SDL 1.2 did not include modifiers in the keys that changed them */ + if (PendingKeydownEvent.key.keysym.mod) { + switch (PendingKeydownEvent.key.keysym.sym) { + case SDLK12_LCTRL: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_LCTRL; + break; + case SDLK12_RCTRL: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_RCTRL; + break; + case SDLK12_LSHIFT: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_LSHIFT; + break; + case SDLK12_RSHIFT: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_RSHIFT; + break; + case SDLK12_LALT: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_LALT; + break; + case SDLK12_RALT: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_RALT; + break; + case SDLK12_LMETA: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_LMETA; + break; + case SDLK12_RMETA: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_RMETA; + break; + case SDLK12_MODE: + PendingKeydownEvent.key.keysym.mod &= ~KMOD12_MODE; + break; + default: + break; + } + } + /* If Unicode is not enabled, flush all KEYDOWN events immediately. */ if (!EnabledUnicode) { FlushPendingKeydownEvent(0); @@ -2449,8 +5115,7 @@ EventFilter20to12(void *data, SDL_Event *event20) } /* some programs rely on unicode values for these control characters */ - switch (PendingKeydownEvent.key.keysym.sym) - { + switch (PendingKeydownEvent.key.keysym.sym) { case SDLK12_BACKSPACE: FlushPendingKeydownEvent('\b'); break; @@ -2469,7 +5134,29 @@ EventFilter20to12(void *data, SDL_Event *event20) FlushPendingKeydownEvent(0x1B); /* '\e' */ break; default: - /* not a supported control character */ + /* not a supported control character + when CTRL is pressed, text events aren't sent so use fallback for unicode */ + if (event20->key.keysym.mod & KMOD_CTRL) { + switch (PendingKeydownEvent.key.keysym.sym) { + case SDLK12_UNKNOWN: + FlushPendingKeydownEvent(0); + break; + case SDLK12_SPACE: + FlushPendingKeydownEvent(' '); + break; + case SDLK12_DELETE: + FlushPendingKeydownEvent('\x7F'); + break; + default: + if (event20->key.keysym.sym & SDLK_SCANCODE_MASK) { + /* This key has no associated unicode text */ + FlushPendingKeydownEvent(0); + } else { + FlushPendingKeydownEvent(event20->key.keysym.sym); + } + break; + } + } break; } @@ -2478,42 +5165,31 @@ EventFilter20to12(void *data, SDL_Event *event20) case SDL_TEXTEDITING: return 1; case SDL_TEXTINPUT: { char *text = event20->text.text; - int codePoint; - while ((codePoint = DecodeUTF8Char(&text)) != 0) { - if (codePoint > 0xFFFF) { - /* We need to send a UTF-16 surrogate pair. */ - Uint16 firstChar = ((codePoint - 0x10000) >> 10) + 0xD800; - Uint16 secondChar = ((codePoint - 0x10000) & 0x3FF) + 0xDC00; - event12.type = SDL12_KEYDOWN; - event12.key.state = SDL12_PRESSED; - event12.key.keysym.scancode = 0; - event12.key.keysym.sym = SDLK12_UNKNOWN; - event12.key.keysym.unicode = firstChar; - if (!FlushPendingKeydownEvent(firstChar)) { - PushEventIfNotFiltered(&event12); - } - event12.key.keysym.unicode = secondChar; - PushEventIfNotFiltered(&event12); - } else { - if (!FlushPendingKeydownEvent(codePoint)) { - event12.type = SDL12_KEYDOWN; - event12.key.state = SDL12_PRESSED; - event12.key.keysym.scancode = 0; - event12.key.keysym.sym = SDLK12_UNKNOWN; - event12.key.keysym.unicode = codePoint; - PushEventIfNotFiltered(&event12); - } - } - } - } - return 1; + const int codePoint = DecodeUTF8Char(&text); + FlushPendingKeydownEvent(codePoint); + return 1; + } case SDL_MOUSEMOTION: + if (!VideoSurface12 || !VideoSurface12->surface20) { + return 1; /* we don't have a screen surface yet? Don't send this on to the app. */ + } + event12.type = SDL12_MOUSEMOTION; event12.motion.which = (Uint8) event20->motion.which; event12.motion.state = event20->motion.state; + AdjustOpenGLLogicalScalingPoint(&event20->motion.x, &event20->motion.y); + /* Clamp the absolute position to the window dimensions. */ + event20->motion.x = SDL_max(SDL_min(event20->motion.x, VideoSurface12->w), 0); + event20->motion.y = SDL_max(SDL_min(event20->motion.y, VideoSurface12->h), 0); event12.motion.x = (Uint16) event20->motion.x; event12.motion.y = (Uint16) event20->motion.y; + if (UseMouseRelativeScaling) { + AdjustOpenGLLogicalScalingVector(&event20->motion.xrel, + &event20->motion.yrel, + &MouseRelativeRemainder.x, + &MouseRelativeRemainder.y); + } event12.motion.xrel = (Sint16) event20->motion.xrel; event12.motion.yrel = (Sint16) event20->motion.yrel; if (MouseInputIsRelative) { @@ -2525,6 +5201,7 @@ EventFilter20to12(void *data, SDL_Event *event20) } else if (MousePosition.axis >= VideoSurface12->dim) { \ MousePosition.axis = (VideoSurface12->dim - 1); \ } \ + event12.motion.axis = MousePosition.axis; \ } ADJUST_RELATIVE(x, xrel, w); ADJUST_RELATIVE(y, yrel, h); @@ -2543,8 +5220,15 @@ EventFilter20to12(void *data, SDL_Event *event20) event12.button.button += 2; /* SDL_BUTTON_X1/2 */ } event12.button.state = event20->button.state; - event12.button.x = (Uint16) event20->button.x; - event12.button.y = (Uint16) event20->button.y; + if (MouseInputIsRelative) { + /* If we're using relative mouse input, we need to use our "fake" position. */ + event12.button.x = MousePosition.x; + event12.button.y = MousePosition.y; + } else { + AdjustOpenGLLogicalScalingPoint(&event20->button.x, &event20->button.y); + event12.button.x = (Uint16) event20->button.x; + event12.button.y = (Uint16) event20->button.y; + } break; case SDL_MOUSEBUTTONUP: @@ -2555,8 +5239,15 @@ EventFilter20to12(void *data, SDL_Event *event20) event12.button.button += 2; /* SDL_BUTTON_X1/2 */ } event12.button.state = event20->button.state; - event12.button.x = (Uint16) event20->button.x; - event12.button.y = (Uint16) event20->button.y; + if (MouseInputIsRelative) { + /* If we're using relative mouse input, we need to use our "fake" position. */ + event12.button.x = MousePosition.x; + event12.button.y = MousePosition.y; + } else { + AdjustOpenGLLogicalScalingPoint(&event20->button.x, &event20->button.y); + event12.button.x = (Uint16) event20->button.x; + event12.button.y = (Uint16) event20->button.y; + } break; case SDL_MOUSEWHEEL: @@ -2567,8 +5258,8 @@ EventFilter20to12(void *data, SDL_Event *event20) event12.button.which = (Uint8) event20->wheel.which; event12.button.button = (event20->wheel.y > 0) ? 4 : 5; /* wheelup is 4, down is 5. */ event12.button.state = SDL_PRESSED; - event12.button.x = 0; - event12.button.y = 0; + event12.button.x = MousePosition.x; + event12.button.y = MousePosition.y; PushEventIfNotFiltered(&event12); event12.type = SDL12_MOUSEBUTTONUP; /* immediately release mouse "button" at the end of this switch. */ @@ -2576,47 +5267,84 @@ EventFilter20to12(void *data, SDL_Event *event20) break; case SDL_JOYAXISMOTION: - event12.type = SDL12_JOYAXISMOTION; - event12.jaxis.which = (Uint8) event20->jaxis.which; - event12.jaxis.axis = event20->jaxis.axis; - event12.jaxis.value = event20->jaxis.value; + if (!JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->jaxis.which); + if (which != -1) { + event12.type = SDL12_JOYAXISMOTION; + event12.jaxis.which = (Uint8) which; + event12.jaxis.axis = event20->jaxis.axis; + event12.jaxis.value = event20->jaxis.value; + } + } break; case SDL_JOYBALLMOTION: - event12.type = SDL12_JOYBALLMOTION; - event12.jball.which = (Uint8) event20->jball.which; - event12.jball.ball = event20->jball.ball; - event12.jball.xrel = event20->jball.xrel; - event12.jball.yrel = event20->jball.yrel; + if (!JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->jball.which); + if (which != -1) { + event12.type = SDL12_JOYBALLMOTION; + event12.jball.which = (Uint8) which; + event12.jball.ball = event20->jball.ball; + event12.jball.xrel = event20->jball.xrel; + event12.jball.yrel = event20->jball.yrel; + } + } break; case SDL_JOYHATMOTION: - event12.type = SDL12_JOYHATMOTION; - event12.jhat.which = (Uint8) event20->jhat.which; - event12.jhat.hat = event20->jhat.hat; - event12.jhat.value = event20->jhat.value; + if (!JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->jhat.which); + if (which != -1) { + event12.type = SDL12_JOYHATMOTION; + event12.jhat.which = (Uint8) which; + event12.jhat.hat = event20->jhat.hat; + event12.jhat.value = event20->jhat.value; + } + } break; case SDL_JOYBUTTONDOWN: - event12.type = SDL12_JOYBUTTONDOWN; - event12.jbutton.which = (Uint8) event20->jbutton.which; - event12.jbutton.button = event20->jbutton.button; - event12.jbutton.state = event20->jbutton.state; + case SDL_JOYBUTTONUP: + if (!JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->jbutton.which); + if (which != -1) { + event12.type = (event20->jbutton.state) ? SDL12_JOYBUTTONDOWN : SDL12_JOYBUTTONUP; + event12.jbutton.which = (Uint8) which; + event12.jbutton.button = event20->jbutton.button; + event12.jbutton.state = event20->jbutton.state; + } + } break; - case SDL_JOYBUTTONUP: - event12.type = SDL12_JOYBUTTONUP; - event12.jbutton.which = (Uint8) event20->jbutton.which; - event12.jbutton.button = event20->jbutton.button; - event12.jbutton.state = event20->jbutton.state; + case SDL_CONTROLLERAXISMOTION: + if (JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->caxis.which); + if (which != -1) { + event12.type = SDL12_JOYAXISMOTION; + event12.jaxis.which = (Uint8) which; + event12.jaxis.axis = event20->caxis.axis; + event12.jaxis.value = event20->caxis.value; + } + } break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + if (JoysticksAreGameControllers) { + const int which = FindJoystick12IndexByInstanceId(event20->cbutton.which); + if (which != -1) { + event12.type = (event20->cbutton.state) ? SDL12_JOYBUTTONDOWN : SDL12_JOYBUTTONUP; + event12.jbutton.which = (Uint8) which; + event12.jbutton.button = event20->cbutton.button; + event12.jbutton.state = event20->cbutton.state; + } + } + break; + + /* case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: - case SDL_CONTROLLERAXISMOTION: - case SDL_CONTROLLERBUTTONDOWN: - case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMAPPED: @@ -2645,14 +5373,14 @@ EventFilter20to12(void *data, SDL_Event *event20) return 1; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetEventFilter(SDL12_EventFilter filter12) { /* We always have a filter installed, but will call the app's too. */ EventFilter12 = filter12; } -DECLSPEC SDL12_EventFilter SDLCALL +DECLSPEC12 SDL12_EventFilter SDLCALL SDL_GetEventFilter(void) { return EventFilter12; @@ -2679,25 +5407,21 @@ Rect12to20(const SDL12_Rect *rect12, SDL_Rect *rect20) return rect20; } -static SDL12_Surface * -Surface20to12(SDL_Surface *surface20) +static SDL_bool +Surface20to12InPlace(SDL_Surface *surface20, + SDL12_Surface *surface12) { - SDL_BlendMode blendmode; - SDL12_Surface *surface12 = NULL; + SDL_BlendMode blendmode = SDL_BLENDMODE_NONE; SDL12_Palette *palette12 = NULL; SDL12_PixelFormat *format12 = NULL; Uint32 flags = 0; if (!surface20) { - return NULL; - } else if (surface20->pitch > 65535) { - SDL20_SetError("Pitch is too large"); /* can't fit to 16-bits */ - return NULL; + return SDL_FALSE; } - - surface12 = (SDL12_Surface *) SDL20_malloc(sizeof (SDL12_Surface)); - if (!surface12) { - goto failed; + if (surface20->pitch > 65535) { + SDL20_SetError("Pitch is too large"); /* can't fit to 16-bits */ + return SDL_FALSE; } if (surface20->format->palette) { @@ -2736,23 +5460,16 @@ Surface20to12(SDL_Surface *surface20) format12->Bmask = surface20->format->Bmask; format12->Amask = surface20->format->Amask; - if (SDL20_HasColorKey(surface20)) { - if (SDL20_GetColorKey(surface20, &format12->colorkey) < 0) { - format12->colorkey = 0; - } else { - surface12->flags |= SDL12_SRCCOLORKEY; - } + if (SDL20_GetColorKey(surface20, &format12->colorkey) < 0) { + format12->colorkey = 0; + } else { + surface12->flags |= SDL12_SRCCOLORKEY; } if (SDL20_GetSurfaceAlphaMod(surface20, &format12->alpha) < 0) { format12->alpha = 255; } - blendmode = SDL_BLENDMODE_NONE; - if ((SDL20_GetSurfaceBlendMode(surface20, &blendmode) == 0) && (blendmode == SDL_BLENDMODE_BLEND)) { - surface12->flags |= SDL12_SRCALPHA; - } - SDL20_zerop(surface12); flags = surface20->flags; flags &= ~SDL_SIMD_ALIGNED; /* we don't need to map this to 1.2 */ @@ -2763,6 +5480,10 @@ Surface20to12(SDL_Surface *surface20) #undef MAPSURFACEFLAGS SDL_assert(flags == 0); /* non-zero if there's a flag we didn't map. */ + if ((SDL20_GetSurfaceBlendMode(surface20, &blendmode) == 0) && (blendmode == SDL_BLENDMODE_BLEND)) { + surface12->flags |= SDL12_SRCALPHA; + } + surface12->format = format12; surface12->w = surface20->w; surface12->h = surface20->h; @@ -2773,20 +5494,40 @@ Surface20to12(SDL_Surface *surface20) Rect20to12(&surface20->clip_rect, &surface12->clip_rect); surface12->refcount = surface20->refcount; - return surface12; + return SDL_TRUE; failed: - SDL20_free(surface12); SDL20_free(palette12); SDL20_free(format12); - return NULL; + return SDL_FALSE; +} + +static SDL12_Surface * +Surface20to12(SDL_Surface *surface20) +{ + SDL12_Surface *surface12 = NULL; + + surface12 = (SDL12_Surface *) SDL20_malloc(sizeof (SDL12_Surface)); + if (!surface12) { + goto failed; + } + + SDL20_zerop(surface12); + if (!Surface20to12InPlace(surface20, surface12)) { + goto failed; + } + + return surface12; + +failed: + SDL20_free(surface12); + return NULL; } static void SetPalette12ForMasks(SDL12_Surface *surface12, const Uint32 Rmask, const Uint32 Gmask, const Uint32 Bmask) { SDL12_PixelFormat *format12; - SDL_PixelFormat * format20; SDL_Color *color; int i, ncolors; @@ -2844,24 +5585,15 @@ SetPalette12ForMasks(SDL12_Surface *surface12, const Uint32 Rmask, const Uint32 color->a = 255; } - format20 = surface12->surface20->format; - #define UPDATEFMT20(t) \ - format20->t##mask = format12->t##mask; \ - format20->t##loss = format12->t##loss; \ - format20->t##shift = format12->t##shift; - UPDATEFMT20(R); - UPDATEFMT20(G); - UPDATEFMT20(B); - UPDATEFMT20(A); - #undef UPDATEFMT20 } } -DECLSPEC SDL12_Surface * SDLCALL -SDL_CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +static SDL_Surface * +CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { SDL_Surface *surface20; - SDL12_Surface *surface12; + + (void)flags12; /* SDL 1.2 checks this. */ if ((width >= 16384) || (height >= 65536)) { @@ -2869,31 +5601,80 @@ SDL_CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rm return NULL; } - if (depth == 8) { /* don't pass masks to SDL2 for 8-bit surfaces, it'll cause problems. */ + /* !!! FIXME: this isn't strictly correct, but SDL2 doesn't support + !!! FIXME: arbitrary depths smaller than 8 bits, and this lets at + !!! FIXME: least one game (rockdodger) function correctly. */ + if (depth < 8 && depth != 1 && depth != 4) { + if (WantDebugLogging) { + SDL20_Log("This app is creating an %d-bit SDL_Surface, but we are bumping it to 8-bits. If you see rendering issues, please report them!", depth); + } + depth = 8; + } + + if (depth <= 8) { /* don't pass masks to SDL2 for <= 8-bit surfaces, it'll cause problems. */ surface20 = SDL20_CreateRGBSurface(0, width, height, depth, 0, 0, 0, 0); } else { surface20 = SDL20_CreateRGBSurface(0, width, height, depth, Rmask, Gmask, Bmask, Amask); } - surface12 = Surface20to12(surface20); - if (!surface12) { - SDL20_FreeSurface(surface20); - return NULL; + /* SDL 1.2 would make a surface from almost any masks, even if it doesn't + make sense; specifically, it will make a surface if a color mask is + bogus. Sometimes this even worked because it would eventually land in + a generic blitter that just copied data blindly. SDL2 wants more strict + pixel formats, so try to detect this case and try again with a standard + format. */ + if ((surface20 == NULL) && (depth >= 16) && (SDL20_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask) == SDL_PIXELFORMAT_UNKNOWN)) { + /* I have no illusions this is correct, it just works for the known problem cases so far. */ + if (depth == 16) { + Rmask = SDL_SwapLE32(0x0000F800); + Gmask = SDL_SwapLE32(0x000007E0); + Bmask = SDL_SwapLE32(0x0000001F); + Amask = 0; + } else { + Rmask = SDL_SwapLE32(0x000000FF); + Gmask = SDL_SwapLE32(0x0000FF00); + Bmask = SDL_SwapLE32(0x00FF0000); + Amask = SDL_SwapLE32(Amask ? 0xFF000000 : 0x00000000); + } + surface20 = SDL20_CreateRGBSurface(0, width, height, depth, Rmask, Gmask, Bmask, Amask); } - SDL_assert((surface12->flags & ~(SDL12_SRCCOLORKEY|SDL12_SRCALPHA)) == 0); /* shouldn't have prealloc, rleaccel, or dontfree. */ + return surface20; +} +static void +Surface12SetMasks(SDL12_Surface *surface12, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ SetPalette12ForMasks(surface12, Rmask, Gmask, Bmask); - if (flags12 & SDL12_SRCALPHA) { + if (Amask != 0) { surface12->flags |= SDL12_SRCALPHA; - SDL20_SetSurfaceBlendMode(surface20, SDL_BLENDMODE_BLEND); + SDL20_SetSurfaceBlendMode(surface12->surface20, SDL_BLENDMODE_BLEND); + } +} + +DECLSPEC12 SDL12_Surface * SDLCALL +SDL_CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ + SDL12_Surface *surface12; + SDL_Surface *surface20; + + surface20 = CreateRGBSurface(flags12, width, height, depth, Rmask, Gmask, Bmask, Amask); + if (!surface20) { + return NULL; + } + surface12 = Surface20to12(surface20); + if (!surface12) { + SDL20_FreeSurface(surface20); + return NULL; } + SDL_assert(!(width && height) || ((surface12->flags & ~(SDL12_SRCCOLORKEY|SDL12_SRCALPHA))) == 0); /* shouldn't have prealloc, rleaccel, or dontfree. */ + Surface12SetMasks(surface12, Rmask, Gmask, Bmask, Amask); return surface12; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { SDL_Surface *surface20; @@ -2918,25 +5699,26 @@ SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pit SDL_assert((surface12->flags & ~(SDL12_SRCCOLORKEY|SDL12_SRCALPHA)) == SDL12_PREALLOC); /* should _only_ have prealloc. */ + /* TODO: Is it correct that this always ignored Amask, or should it be + * using Surface12SetMasks which takes Amask into account? */ SetPalette12ForMasks(surface12, Rmask, Gmask, Bmask); return surface12; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeSurface(SDL12_Surface *surface12) { if (surface12 && (surface12 != VideoSurface12)) { - SDL20_FreeSurface(surface12->surface20); - if (surface12->format) { - SDL20_free(surface12->format->palette); - SDL20_free(surface12->format); - } + surface12->refcount--; + if (surface12->refcount) + return; + FreeSurfaceContents(surface12); SDL20_free(surface12); } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetClipRect(SDL12_Surface *surface12, SDL12_Rect *rect) { if (surface12 && rect) { @@ -2944,7 +5726,7 @@ SDL_GetClipRect(SDL12_Surface *surface12, SDL12_Rect *rect) } } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_SetClipRect(SDL12_Surface *surface12, const SDL12_Rect *rect12) { SDL_bool retval = SDL_FALSE; @@ -2957,7 +5739,7 @@ SDL_SetClipRect(SDL12_Surface *surface12, const SDL12_Rect *rect12) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_FillRect(SDL12_Surface *dst, SDL12_Rect *dstrect12, Uint32 color) { SDL_Rect dstrect20; @@ -2972,7 +5754,7 @@ SDL_FillRect(SDL12_Surface *dst, SDL12_Rect *dstrect12, Uint32 color) return retval; } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_MapRGB(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b) { /* This is probably way slower than apps expect. */ @@ -2981,7 +5763,7 @@ SDL_MapRGB(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b) return SDL20_MapRGB(PixelFormat12to20(&format20, &palette20, format12), r, g, b); } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_MapRGBA(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { /* This is probably way slower than apps expect. */ @@ -2990,7 +5772,7 @@ SDL_MapRGBA(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b, Uint8 return SDL20_MapRGBA(PixelFormat12to20(&format20, &palette20, format12), r, g, b, a); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetRGB(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, Uint8 *b) { /* This is probably way slower than apps expect. */ @@ -2999,7 +5781,7 @@ SDL_GetRGB(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, SDL20_GetRGB(pixel, PixelFormat12to20(&format20, &palette20, format12), r, g, b); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetRGBA(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) { /* This is probably way slower than apps expect. */ @@ -3008,48 +5790,59 @@ SDL_GetRGBA(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, SDL20_GetRGBA(pixel, PixelFormat12to20(&format20, &palette20, format12), r, g, b, a); } -DECLSPEC const SDL12_VideoInfo * SDLCALL +DECLSPEC12 const SDL12_VideoInfo * SDLCALL SDL_GetVideoInfo(void) { return VideoInfo12.vfmt ? &VideoInfo12 : NULL; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_VideoModeOK(int width, int height, int bpp, Uint32 sdl12flags) { - int i, nummodes, actual_bpp = 0; + int i, j, actual_bpp = 0; if (!SDL20_WasInit(SDL_INIT_VIDEO)) { return 0; } + /* if the 1.2 video backend could center a surface in a larger mode, it + would accept the size. Since we scale things, we will, too, even + without an exact width/height match. */ + if (!(sdl12flags & SDL12_FULLSCREEN)) { SDL_DisplayMode mode; SDL20_GetDesktopDisplayMode(VideoDisplayIndex, &mode); - return SDL_BITSPERPIXEL(mode.format); - } - - nummodes = SDL20_GetNumDisplayModes(VideoDisplayIndex); - for (i = 0; i < nummodes; ++i) { - SDL_DisplayMode mode; - SDL20_GetDisplayMode(VideoDisplayIndex, i, &mode); - if (!mode.w || !mode.h || (width == mode.w && height == mode.h)) { - if (!mode.format) { - return bpp; - } - if (SDL_BITSPERPIXEL(mode.format) >= (Uint32) bpp) { - actual_bpp = SDL_BITSPERPIXEL(mode.format); + if ((mode.w >= width) && (mode.h >= height)) { + actual_bpp = SDL_BITSPERPIXEL(mode.format); + } + } else { + for (i = 0; i < VideoModesCount; ++i) { + VideoModeList *vmode = &VideoModes[i]; + for (j = 0; j < vmode->nummodes; ++j) { + if (vmode->modeslist12[j].w >= width && vmode->modeslist12[j].h >= height) { + if (!vmode->format) { + return bpp; + } + if (SDL_BITSPERPIXEL(vmode->format) == 24 && bpp == 32) { + actual_bpp = 32; + } else if (SDL_BITSPERPIXEL(vmode->format) >= (Uint32) bpp) { + actual_bpp = SDL_BITSPERPIXEL(vmode->format); + } + } } } } - return actual_bpp; + + return (actual_bpp == 24) ? 32 : actual_bpp; } -DECLSPEC SDL12_Rect ** SDLCALL +DECLSPEC12 SDL12_Rect ** SDLCALL SDL_ListModes(const SDL12_PixelFormat *format12, Uint32 flags) { + VideoModeList *best_modes = NULL; Uint32 bpp; int i; + SDL_bool windowed_mode_list = SDL12Compat_GetHintBoolean("SDL12COMPAT_WINDOWED_MODE_LIST", SDL_FALSE); if (!SDL20_WasInit(SDL_INIT_VIDEO)) { SDL20_SetError("Video subsystem not initialized"); @@ -3061,8 +5854,12 @@ SDL_ListModes(const SDL12_PixelFormat *format12, Uint32 flags) return NULL; } - if (!(flags & SDL12_FULLSCREEN)) { - return (SDL12_Rect **) (-1); /* any resolution is fine. */ + if (IsDummyVideo) { + return (SDL12_Rect **) -1; /* 1.2's dummy driver always returns -1, and it's useful to special-case that. */ + } + + if (!(flags & SDL12_FULLSCREEN) && !windowed_mode_list) { + return (SDL12_Rect **) -1; /* any resolution is fine. */ } if (format12 && (format12 != VideoInfo12.vfmt)) { @@ -3076,15 +5873,28 @@ SDL_ListModes(const SDL12_PixelFormat *format12, Uint32 flags) if (SDL_BITSPERPIXEL(modes->format) == bpp) { return modes->modes12; } + if (SDL_BITSPERPIXEL(modes->format) == 24 && bpp == 32) { + best_modes = modes; + } else if (SDL_BITSPERPIXEL(modes->format) > bpp) { + if (!best_modes || SDL_BITSPERPIXEL(modes->format) > SDL_BITSPERPIXEL(best_modes->format)) { + best_modes = modes; + } + } } - SDL20_SetError("No modes support requested pixel format"); - return NULL; + if (!best_modes) { + SDL20_SetError("No modes support requested pixel format"); + return NULL; + } + return best_modes->modes12; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeCursor(SDL12_Cursor *cursor12) { + if (cursor12 == CurrentCursor12) { + CurrentCursor12 = NULL; + } if (cursor12) { if (cursor12->wm_cursor) { SDL20_FreeCursor(cursor12->wm_cursor); @@ -3095,7 +5905,7 @@ SDL_FreeCursor(SDL12_Cursor *cursor12) } } -DECLSPEC SDL12_Cursor * SDLCALL +DECLSPEC12 SDL12_Cursor * SDLCALL SDL_CreateCursor(Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y) { const size_t datasize = h * (w / 8); @@ -3144,16 +5954,20 @@ SDL_CreateCursor(Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y) return NULL; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetCursor(SDL12_Cursor *cursor) { CurrentCursor12 = cursor; SDL20_SetCursor(cursor ? cursor->wm_cursor : NULL); } -DECLSPEC SDL12_Cursor * SDLCALL +DECLSPEC12 SDL12_Cursor * SDLCALL SDL_GetCursor(void) { + if (!CurrentCursor12) { + CurrentCursor12 = SDL_CreateCursor(default_cdata, default_cmask, + DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY); + } return CurrentCursor12; } @@ -3161,8 +5975,8 @@ static void GetEnvironmentWindowPosition(int *x, int *y) { int display = VideoDisplayIndex; - const char *window = SDL20_getenv("SDL_VIDEO_WINDOW_POS"); - const char *center = SDL20_getenv("SDL_VIDEO_CENTERED"); + const char *window = SDL12COMPAT_getenv_unsafe("SDL_VIDEO_WINDOW_POS"); + const char *center = SDL12COMPAT_getenv_unsafe("SDL_VIDEO_CENTERED"); if (window) { if (SDL20_strcmp(window, "center") == 0) { center = window; @@ -3174,36 +5988,21 @@ GetEnvironmentWindowPosition(int *x, int *y) if (center) { *x = SDL_WINDOWPOS_CENTERED_DISPLAY(display); *y = SDL_WINDOWPOS_CENTERED_DISPLAY(display); - } -} - -#if 0 /* unused, yet. */ -static void -SetupScreenSaver(const int flags12) -{ - const char *env; - SDL_bool allow_screensaver; - - /* Allow environment override of screensaver disable */ - env = SDL20_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); - if (env) { - allow_screensaver = SDL20_atoi(env) ? SDL_TRUE : SDL_FALSE; - } else if (flags12 & SDL12_FULLSCREEN) { - allow_screensaver = SDL_FALSE; - } else { - allow_screensaver = SDL_TRUE; - } - if (allow_screensaver) { - SDL20_EnableScreenSaver(); } else { - SDL20_DisableScreenSaver(); + *x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display); + *y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(display); } } -#endif static SDL12_Surface * EndVidModeCreate(void) { + QueuedOverlayItem *overlay; + + if (OpenGLBlitTexture) { + OpenGLFuncs.glDeleteTextures(1, &OpenGLBlitTexture); + OpenGLBlitTexture = 0; + } if (VideoTexture20) { SDL20_DestroyTexture(VideoTexture20); VideoTexture20 = NULL; @@ -3212,6 +6011,10 @@ EndVidModeCreate(void) SDL20_DestroyRenderer(VideoRenderer20); VideoRenderer20 = NULL; } + if (VideoRendererLock) { + SDL20_DestroyMutex(VideoRendererLock); + VideoRendererLock = NULL; + } if (VideoGLContext20) { SDL20_GL_MakeCurrent(NULL, NULL); SDL20_GL_DeleteContext(VideoGLContext20); @@ -3228,38 +6031,66 @@ EndVidModeCreate(void) if (VideoSurface12) { SDL20_free(VideoSurface12->pixels); VideoSurface12->pixels = NULL; - SDL_FreeSurface(VideoSurface12); - VideoSurface12 = NULL; + FreeSurfaceContents(VideoSurface12); } if (VideoConvertSurface20) { SDL20_FreeSurface(VideoConvertSurface20); VideoConvertSurface20 = NULL; } - SDL_zero(OpenGLFuncs); + SDL20_zero(OpenGLFuncs); + OpenGLBlitLockCount = 0; OpenGLLogicalScalingWidth = 0; OpenGLLogicalScalingHeight = 0; OpenGLLogicalScalingFBO = 0; OpenGLLogicalScalingColor = 0; OpenGLLogicalScalingDepth = 0; + OpenGLLogicalScalingMultisampleFBO = 0; + OpenGLLogicalScalingMultisampleColor = 0; + OpenGLLogicalScalingMultisampleDepth = 0; MouseInputIsRelative = SDL_FALSE; MousePosition.x = 0; MousePosition.y = 0; + overlay = QueuedDisplayOverlays.next; + while (overlay != NULL) { + QueuedOverlayItem *next = overlay->next; + SDL_free(overlay); + overlay = next; + } + QueuedDisplayOverlays.next = NULL; + QueuedDisplayOverlaysTail = &QueuedDisplayOverlays; + + VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE; + SetVideoModeThread = 0; + + CurrentRefreshRate = SDL12_REFRESH_DEFAULT; + return NULL; } - -static SDL12_Surface * -CreateSurface12WithFormat(const int w, const int h, const Uint32 fmt) +/* Essentially the same as SDL_CreateRGBSurface, but in-place */ +static void +CreateVideoSurface(const Uint32 fmt) { Uint32 rmask, gmask, bmask, amask; int bpp; + SDL_Surface *surface20; + if (!SDL20_PixelFormatEnumToMasks(fmt, &bpp, &rmask, &gmask, &bmask, &amask)) { - return NULL; + return; + } + + SDL20_zerop(VideoSurface12); + surface20 = CreateRGBSurface(0, 0, 0, bpp, rmask, gmask, bmask, amask); + + if (!Surface20to12InPlace(surface20, VideoSurface12)) { + FreeSurfaceContents(VideoSurface12); + return; } - return SDL_CreateRGBSurface(0, w, h, bpp, rmask, gmask, bmask, amask); + + Surface12SetMasks(VideoSurface12, rmask, gmask, bmask, amask); } static SDL_Surface * @@ -3284,7 +6115,7 @@ LoadOpenGLFunctions(void) int major = 0, minor = 0; /* load core functions so we can guess about a few other things. */ - SDL_zero(OpenGLFuncs); + SDL20_zero(OpenGLFuncs); OpenGLFuncs.SUPPORTS_Core = SDL_TRUE; #define OPENGL_SYM(ext,rc,fn,params,args,ret) OpenGLFuncs.fn = \ (OpenGLFuncs.SUPPORTS_##ext)? (openglfn_##fn##_t)SDL20_GL_GetProcAddress(#fn) : NULL; @@ -3304,6 +6135,10 @@ LoadOpenGLFunctions(void) OpenGLFuncs.SUPPORTS_GL_ARB_framebuffer_object = SDL_TRUE; } + if (major >= 2) { + OpenGLFuncs.SUPPORTS_GL_ARB_texture_non_power_of_two = SDL_TRUE; /* core since 2.0 */ + } + /* load everything we can. */ #define OPENGL_SYM(ext,rc,fn,params,args,ret) OpenGLFuncs.fn = \ (OpenGLFuncs.SUPPORTS_##ext)? (openglfn_##fn##_t)SDL20_GL_GetProcAddress(#fn) : NULL; @@ -3311,7 +6146,7 @@ LoadOpenGLFunctions(void) } static void -ResolveFauxBackbufferMSAA() +ResolveFauxBackbufferMSAA(void) { const GLboolean has_scissor = OpenGLFuncs.glIsEnabled(GL_SCISSOR_TEST); @@ -3428,26 +6263,58 @@ glCopyTexSubImage3D_shim_for_scaling(GLenum target, GLint level, GLint xoffset, static SDL_bool InitializeOpenGLScaling(const int w, const int h) { + int alpha_size = 0; + int depth_size = 0; + int stencil_size = 0; + + SDL_assert(VideoWindow20 != NULL); + + /* Support the MOUSE_RELATIVE_SCALING hint from SDL 2.0 for OpenGL scaling. */ + UseMouseRelativeScaling = SDL12Compat_GetHintBoolean("SDL_MOUSE_RELATIVE_SCALING", SDL_TRUE); + if (!OpenGLFuncs.SUPPORTS_GL_ARB_framebuffer_object) { return SDL_FALSE; /* no FBOs, no scaling. */ } + OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, 0); OpenGLFuncs.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); SDL20_GL_SwapWindow(VideoWindow20); - OpenGLFuncs.glGenFramebuffers(1, &OpenGLLogicalScalingFBO); + SDL20_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha_size); + SDL20_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth_size); + SDL20_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_size); + + if (!OpenGLLogicalScalingFBO) { + OpenGLFuncs.glGenFramebuffers(1, &OpenGLLogicalScalingFBO); + } + + if (!OpenGLLogicalScalingColor) { + OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingColor); + } + + if (!OpenGLLogicalScalingDepth) { + OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingDepth); + } + OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, OpenGLLogicalScalingFBO); - OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingColor); OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingColor); - OpenGLFuncs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, OpenGLLogicalScalingSamples, GL_RGB8, w, h); + OpenGLFuncs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, OpenGLLogicalScalingSamples, (alpha_size > 0) ? GL_RGBA8 : GL_RGB8, w, h); OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, OpenGLLogicalScalingColor); - OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingDepth); - OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingDepth); - OpenGLFuncs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, OpenGLLogicalScalingSamples, GL_DEPTH24_STENCIL8, w, h); - OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingDepth); + + if (depth_size || stencil_size) { + OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingDepth); + OpenGLFuncs.glRenderbufferStorageMultisample(GL_RENDERBUFFER, OpenGLLogicalScalingSamples, GL_DEPTH24_STENCIL8, w, h); + if (depth_size) { + OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingDepth); + } + if (stencil_size) { + OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingDepth); + } + } + OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, 0); - if ( (OpenGLFuncs.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) || OpenGLFuncs.glGetError() ) { + if ((OpenGLFuncs.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) || OpenGLFuncs.glGetError()) { OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, 0); OpenGLFuncs.glDeleteRenderbuffers(1, &OpenGLLogicalScalingColor); OpenGLFuncs.glDeleteRenderbuffers(1, &OpenGLLogicalScalingDepth); @@ -3457,19 +6324,35 @@ InitializeOpenGLScaling(const int w, const int h) } if (OpenGLLogicalScalingSamples) { - OpenGLFuncs.glGenFramebuffers(1, &OpenGLLogicalScalingMultisampleFBO); + if (!OpenGLLogicalScalingMultisampleFBO) { + OpenGLFuncs.glGenFramebuffers(1, &OpenGLLogicalScalingMultisampleFBO); + } + if (!OpenGLLogicalScalingMultisampleColor) { + OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingMultisampleColor); + } + if (!OpenGLLogicalScalingMultisampleDepth) { + OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingMultisampleDepth); + } + OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, OpenGLLogicalScalingMultisampleFBO); - OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingMultisampleColor); OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleColor); - OpenGLFuncs.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, w, h); + OpenGLFuncs.glRenderbufferStorage(GL_RENDERBUFFER, (alpha_size > 0) ? GL_RGBA8 : GL_RGB8, w, h); OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleColor); - OpenGLFuncs.glGenRenderbuffers(1, &OpenGLLogicalScalingMultisampleDepth); - OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleDepth); - OpenGLFuncs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); /* !!! FIXME: is an extension (or core 3.0) */ - OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleDepth); + + if (depth_size || stencil_size) { + OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleDepth); + OpenGLFuncs.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); + if (depth_size) { + OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleDepth); + } + if (stencil_size) { + OpenGLFuncs.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, OpenGLLogicalScalingMultisampleDepth); + } + } + OpenGLFuncs.glBindRenderbuffer(GL_RENDERBUFFER, 0); - if ( (OpenGLFuncs.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) || OpenGLFuncs.glGetError() ) { + if ((OpenGLFuncs.glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) || OpenGLFuncs.glGetError()) { OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, 0); OpenGLFuncs.glDeleteRenderbuffers(1, &OpenGLLogicalScalingMultisampleColor); OpenGLFuncs.glDeleteRenderbuffers(1, &OpenGLLogicalScalingMultisampleDepth); @@ -3494,39 +6377,107 @@ InitializeOpenGLScaling(const int w, const int h) } -static void HandleInputGrab(SDL12_GrabMode mode); +/* The idea here is that SDL's OpenGL-based renderers always notice if + they aren't using the correct context and attempt to set the correct + context before doing any work, but (at least for X11, and probably + other platforms), the current context is thread-local, and _it's an + error to set a context current if it's already current on another thread_. + So we try to catch every place we call into the renderer and end that + work with a call to this function, which will reset the GL context to NULL, + so if an app tries to render from a background thread, the GL renderer + will be able to set the context, do it's work, and then we reset it right + after. Without this, we either need all apps to render exclusively on + the main thread or fail to draw at all. -DECLSPEC SDL12_Surface * SDLCALL -SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) + This feels risky, but it's better than the alternative! + + Also, the renderer API isn't thread safe in general, so wrapping it + in a mutex is necessary anyhow. */ + +static SDL_Renderer * +LockVideoRenderer(void) +{ + SDL20_LockMutex(VideoRendererLock); + return VideoRenderer20; +} + +static void +UnlockVideoRenderer(void) +{ + if ((VideoRenderer20 != NULL) && (SDL20_GL_GetCurrentContext() != NULL)) { + SDL20_GL_MakeCurrent(NULL, NULL); + } + SDL20_UnlockMutex(VideoRendererLock); +} + +static void UpdateInputGrab(void); + +static SDL_bool ShouldUseOpenGL(void) +{ + /* Create an OpenGL window if the default renderer is OpenGL */ + SDL_RendererInfo info; + if (SDL20_GetRenderDriverInfo(0, &info) == 0) { + if (SDL20_strncmp(info.name, "opengl", 6) == 0) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static SDL12_Surface * +SetVideoModeImpl(int width, int height, int bpp, Uint32 flags12) { SDL_DisplayMode dmode; Uint32 fullscreen_flags20 = 0; Uint32 appfmt; + const char *vsync_env = SDL12Compat_GetHint("SDL12COMPAT_SYNC_TO_VBLANK"); + float window_size_scaling = SDL12Compat_GetHintFloat("SDL12COMPAT_WINDOW_SCALING", 1.0f); + int max_bpp = SDL12Compat_GetHintInt("SDL12COMPAT_MAX_BPP", 32); SDL_bool use_gl_scaling = SDL_FALSE; - const char *env; - SDL_bool use_highdpi; + SDL_bool use_highdpi = SDL_TRUE; + SDL_bool fix_bordless_fs_win = SDL_TRUE; + int scaled_width = width; + int scaled_height = height; + const char *fromwin_env = NULL; + int gl_max_fps; + SDL_bool force_display_mode = SDL_FALSE; + VideoSurface12 = &VideoSurface12Location; - env = SDL20_getenv("SDL12COMPAT_HIGHDPI"); - use_highdpi = (!env || SDL20_atoi(env)) ? SDL_TRUE : SDL_FALSE; - - FIXME("Should we offer scaling for windowed modes, too?"); if (flags12 & SDL12_OPENGL) { - /* !!! FIXME: the reason we have a toggle to prevent this is because an app might use + /* For now we default GL scaling to ENABLED. If an app breaks or is linked directly + to glBindFramebuffer, they'll need to turn it off with this environment variable. + + The reason we have a toggle to prevent this is because an app might use FBOs directly, and will cause this to break if they bind Framebuffer 0 instead of our render target. If we can fool them into calling a fake glBindFramebuffer that binds our logical FBO instead of the window framebuffer, we can probably work with these apps, too. That's easy from SDL_GL_GetProcAddress, but we - maybe need to export the symbol from here too, for those that link against - OpenGL directly. UT2004 is known to use FBOs with SDL 1.2, and I assume - idTech 4 games (Doom 3, Quake 4, Prey) do as well. */ - env = SDL20_getenv("SDL12COMPAT_OPENGL_SCALING"); - - /* for now we default GL scaling to ENABLED. If an app breaks or is linked directly - to glBindFramebuffer, they'll need to turn it off with this environment variable */ - use_gl_scaling = (!env || SDL20_atoi(env)) ? SDL_TRUE : SDL_FALSE; + would need to export the symbol from here too, for those that link against + OpenGL directly, and we don't want to risk that. + + UT2004 is known to use FBOs with SDL 1.2, and I assume idTech 4 games (Doom 3, + Quake 4, Prey) do as well. */ + use_gl_scaling = WantOpenGLScaling; + + /* default use_highdpi to false for OpenGL windows when not using + OpenGL scaling as legacy OpenGL applications are unlikely to support + high-DPI setups properly. (They often use the window size to determine + the resolution in some or all parts of their code.) Because OpenGL scaling + is never used for windows, it is always false there. */ + use_highdpi = (flags12 & SDL12_FULLSCREEN) ? use_gl_scaling : SDL_FALSE; + + gl_max_fps = SDL12Compat_GetHintInt("SDL12COMPAT_MAX_FPS", 0); + if (gl_max_fps != 0) { + OpenGLBuffersSwapTickInterval = 1000.f / gl_max_fps; + OpenGLBuffersLastSwapTicks = SDL20_GetTicks(); + } } - FIXME("currently ignores SDL_WINDOWID, which we could use with SDL_CreateWindowFrom ...?"); + use_highdpi = SDL12Compat_GetHintBoolean("SDL12COMPAT_HIGHDPI", use_highdpi); + + fix_bordless_fs_win = SDL12Compat_GetHintBoolean("SDL12COMPAT_FIX_BORDERLESS_FS_WIN", fix_bordless_fs_win); + + ForceGLSwapBufferContext = SDL12Compat_GetHintBoolean("SDL12COMPAT_FORCE_GL_SWAPBUFFER_CONTEXT", SDL_FALSE); flags12 &= ~SDL12_HWACCEL; /* just in case - https://github.com/libsdl-org/SDL-1.2/issues/817 */ @@ -3540,21 +6491,12 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) } } - if ((flags12 & SDL12_OPENGLBLIT) == SDL12_OPENGLBLIT) { - FIXME("No OPENGLBLIT support at the moment"); - SDL20_SetError("SDL_OPENGLBLIT is (currently) unsupported"); - return NULL; - } - - FIXME("handle SDL_ANYFORMAT"); - if ((width < 0) || (height < 0)) { SDL20_SetError("Invalid width or height"); return NULL; } - FIXME("There's an environment variable to choose a display"); - if (SDL20_GetCurrentDisplayMode(0, &dmode) < 0) { + if (SDL20_GetCurrentDisplayMode(VideoDisplayIndex, &dmode) < 0) { return NULL; } @@ -3567,42 +6509,75 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) } if (bpp == 0) { + flags12 |= SDL12_ANYFORMAT; bpp = SDL_BITSPERPIXEL(dmode.format); + /* keep this simple: we aren't handling palettes here, so for < 16-bit + formats, give them 16-bit and we'll convert later. Nothing in SDL 1.2 will + handle > 32 bits, so clamp there, too. AND ALSO, most apps will handle 32-bits + but not 24, so force around that...so basically, you can have 16 or 32 bit. */ + bpp = (bpp <= 16) ? 16 : max_bpp; } - switch (bpp) { - case 8: appfmt = SDL_PIXELFORMAT_INDEX8; break; - case 16: appfmt = SDL_PIXELFORMAT_RGB565; FIXME("bgr instead of rgb?"); break; - case 24: appfmt = SDL_PIXELFORMAT_RGB24; FIXME("bgr instead of rgb?"); break; - case 32: appfmt = SDL_PIXELFORMAT_XRGB8888; FIXME("bgr instead of rgb?"); break; - default: SDL20_SetError("Unsupported bits-per-pixel"); return NULL; + if ((bpp != 8) && (bpp != 16) && (bpp != 24) && (bpp != 32)) { + SDL20_SetError("Invalid bits per pixel (range is {8...32})"); + return NULL; } - SDL_assert((VideoSurface12 != NULL) == (VideoWindow20 != NULL)); + appfmt = BPPToPixelFormat(bpp); - FIXME("don't do anything if the window's dimensions, etc haven't changed."); - FIXME("we need to preserve VideoSurface12 (but not its pixels), I think..."); + SDL_assert((VideoSurface12->surface20 != NULL) == (VideoWindow20 != NULL)); - if (VideoSurface12 && ((VideoSurface12->flags & SDL12_OPENGL) != (flags12 & SDL12_OPENGL)) ) { + if (VideoSurface12->surface20 && ((VideoSurface12->flags & SDL12_OPENGL) != (flags12 & SDL12_OPENGL))) { EndVidModeCreate(); /* rebuild the window if moving to/from a GL context */ - } else if (VideoSurface12 && (VideoSurface12->surface20->format->format != appfmt)) { - EndVidModeCreate(); /* rebuild the window if changing pixel format */ - } else if (VideoSurface12 && (VideoSurface12->w != width || VideoSurface12->h != height) && ((flags12 & SDL12_FULLSCREEN) == 0)) { - EndVidModeCreate(); /* rebuild the window if window size changed and not in full screen */ - } else if (VideoGLContext20) { - /* SDL 1.2 (infuriatingly!) destroys the GL context on each resize, so we will too */ - SDL20_GL_MakeCurrent(NULL, NULL); - SDL20_GL_DeleteContext(VideoGLContext20); - VideoGLContext20 = NULL; - SDL_zero(OpenGLFuncs); - OpenGLLogicalScalingWidth = 0; - OpenGLLogicalScalingHeight = 0; - OpenGLLogicalScalingFBO = 0; - OpenGLLogicalScalingColor = 0; - OpenGLLogicalScalingDepth = 0; - OpenGLLogicalScalingMultisampleFBO = 0; - OpenGLLogicalScalingMultisampleColor = 0; - OpenGLLogicalScalingMultisampleDepth = 0; + } else if ((flags12 & SDL12_OPENGL) && VideoSurface12->surface20 && (VideoSurface12->surface20->format->format != appfmt)) { + EndVidModeCreate(); /* rebuild the window if changing pixel format on an OpenGL surface */ + } else if (DesiredRefreshRate != CurrentRefreshRate) { + EndVidModeCreate(); /* rebuild the window if changing refresh rate */ + } else { + /* SDL 1.2 (infuriatingly!) destroys the window (and GL context!) on each resize in some cases, on various platforms. Try to match that. */ + #ifdef __WINDOWS__ + /* The windx5 driver _always_ destroyed the window, unconditionally, but the default (windib) did not, so match windib. + * windib: keep if: + * - window already exists + * - BitsPerPixel hasn't changed (in OpenGL mode; for software we don't care about the format change). + * - none of the window flags (except SDL_ANYFORMAT) have changed + * - window is already SDL_OPENGL. + * - window is not a fullscreen window. + */ + const Uint32 important_flags = ~(SDL12_PREALLOC | SDL12_ANYFORMAT); + const SDL_bool recreate_window = ( + ((VideoSurface12->flags & important_flags) != (flags12 & important_flags)) || + ((flags12 & SDL12_OPENGL) && (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp))) || + ((flags12 & SDL12_FULLSCREEN) == SDL12_FULLSCREEN) + ) ? SDL_TRUE : SDL_FALSE; + #elif defined(__APPLE__) + const SDL_bool recreate_window = SDL_TRUE; /* macOS ("quartz" backend) unconditionally destroys the GL context */ + #elif defined(__HAIKU__) + const SDL_bool recreate_window = SDL_FALSE; /* BeOS and Haiku ("bwindow" backend) unconditionally keeps the GL context */ + #elif defined(__LINUX__) || defined(unix) || defined(__unix__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(sun) + /* The x11 backend does in _some_ cases, and since Linux software depends on that, even though Wayland and + * such wasn't a thing at the time, we treat everything that looks a little like Unix this way. + * x11: keep if: + * - window already exists + * - window is already SDL_OPENGL. + * - new flags also want SDL_OPENGL. + * - BitsPerPixel hasn't changed (in OpenGL mode; for software we don't care about the format change). + * - SDL_NOFRAME hasn't changed + */ + const SDL_bool recreate_window = ( + ((VideoSurface12->flags & SDL12_OPENGL) != (flags12 & SDL12_OPENGL)) || + ((flags12 & SDL12_OPENGL) && (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp))) || + ((VideoSurface12->flags & SDL12_NOFRAME) != (flags12 & SDL12_NOFRAME)) + ) ? SDL_TRUE : SDL_FALSE; + #else + const SDL_bool recreate_window = SDL_TRUE; /* everywhere else: nuke it from orbit. Oh well. */ + #endif + + if (recreate_window) { + EndVidModeCreate(); /* rebuild the window if we can't resize it. */ + } else if (!VideoSurface12->format || (VideoSurface12->format->BitsPerPixel != bpp)) { + FreeSurfaceContents(VideoSurface12); /* hollow out the 1.2 surface if the fomat changes, even if the window survives...so palettes, etc, get recreated. */ + } } if (flags12 & SDL12_FULLSCREEN) { @@ -3610,14 +6585,52 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) GPU, so use FULLSCREEN_DESKTOP and logical scaling there. If possible, we'll do this with OpenGL, too, but we might not be able to. */ - if (use_gl_scaling || ((flags12 & SDL12_OPENGL) == 0) || ((dmode.w == width) && (dmode.h == height))) { + if ((use_gl_scaling || ((flags12 & SDL12_OPENGL) == 0) || ((dmode.w == width) && (dmode.h == height))) && (DesiredRefreshRate == SDL12_REFRESH_DEFAULT)) { fullscreen_flags20 |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else { fullscreen_flags20 |= SDL_WINDOW_FULLSCREEN; + if (DesiredRefreshRate != SDL12_REFRESH_DEFAULT) { + force_display_mode = SDL_TRUE; + } } + } else if (fix_bordless_fs_win && (flags12 & SDL12_NOFRAME) && (width == dmode.w) && (height == dmode.h)) { + /* app appears to be trying to do a "borderless fullscreen windowed" mode, so just bump + it to FULLSCREEN_DESKTOP so it cooperates with various display managers + (into a fullscreen space on macOS, hide the Dock on Gnome, etc). */ + fullscreen_flags20 |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + + fromwin_env = SDL12COMPAT_getenv_unsafe("SDL_WINDOWID"); + + if (fromwin_env) { + window_size_scaling = 1.0f; /* don't scale for external windows */ + } else if (window_size_scaling <= 0.0f) { + window_size_scaling = 1.0f; /* bogus value, reset to default */ + } else if (flags12 & SDL12_RESIZABLE) { + window_size_scaling = 1.0f; /* assume that resizable windows are already prepared to handle whatever without scaling. */ + } else if ((fullscreen_flags20 & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) { + window_size_scaling = 1.0f; /* setting the window to fullscreen or fullscreen_desktop? Don't scale it. */ + } else if ((flags12 & SDL12_OPENGL) && !use_gl_scaling) { + window_size_scaling = 1.0f; /* OpenGL but not doing GL scaling? Don't allow window size scaling. */ + } else { + scaled_width = (int) (window_size_scaling * width); + scaled_height = (int) (window_size_scaling * height); } - if (!VideoWindow20) { /* create it */ + if (fromwin_env) { + char *endp = NULL; + const Uint64 windowid = SDL_strtoull(fromwin_env, &endp, 0); + if ((*fromwin_env == '\0') || (*endp != '\0')) { + SDL20_SetError("Invalid SDL_WINDOWID"); + return EndVidModeCreate(); + } else { + EndVidModeCreate(); + VideoWindow20 = SDL20_CreateWindowFrom((void *) (size_t) (windowid)); + if (!VideoWindow20) { + return EndVidModeCreate(); + } + } + } else if (!VideoWindow20) { /* create it */ int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED; Uint32 flags20 = fullscreen_flags20; if (flags12 & SDL12_OPENGL) { flags20 |= SDL_WINDOW_OPENGL; } @@ -3625,6 +6638,10 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) if (flags12 & SDL12_NOFRAME) { flags20 |= SDL_WINDOW_BORDERLESS; } if (use_highdpi) { flags20 |= SDL_WINDOW_ALLOW_HIGHDPI; } + if (!(flags20 & SDL_WINDOW_OPENGL) && ShouldUseOpenGL()) { + flags20 |= SDL_WINDOW_OPENGL; + } + /* most platforms didn't check these environment variables, but the major ones did (x11, windib, quartz), so we'll just offer it everywhere. */ GetEnvironmentWindowPosition(&x, &y); @@ -3635,25 +6652,47 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) SDL20_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, OpenGLLogicalScalingSamples); } - VideoWindow20 = SDL20_CreateWindow(WindowTitle, x, y, width, height, flags20); + VideoWindow20 = SDL20_CreateWindow(WindowTitle, x, y, scaled_width, scaled_height, flags20); + if (!VideoWindow20 && (flags20 & SDL_WINDOW_OPENGL) && !(flags12 & SDL12_OPENGL)) { + /* OpenGL might not be installed, try again without that flag */ + flags20 &= ~SDL_WINDOW_OPENGL; + VideoWindow20 = SDL20_CreateWindow(WindowTitle, x, y, scaled_width, scaled_height, flags20); + } if (!VideoWindow20) { return EndVidModeCreate(); } if (VideoIcon20) { SDL20_SetWindowIcon(VideoWindow20, VideoIcon20); } + + if (force_display_mode) { + SDL_DisplayMode desired_mode, closest_mode; + SDL20_zero(desired_mode); + SDL20_zero(closest_mode); + desired_mode.w = scaled_width; + desired_mode.h = scaled_height; + desired_mode.refresh_rate = DesiredRefreshRate; + if (!SDL20_GetClosestDisplayMode(SDL20_GetWindowDisplayIndex(VideoWindow20), &desired_mode, &closest_mode)) { + return EndVidModeCreate(); + } else if (SDL20_SetWindowDisplayMode(VideoWindow20, &closest_mode) < 0) { + return EndVidModeCreate(); + } + CurrentRefreshRate = DesiredRefreshRate; + } } else { /* resize it */ - SDL20_SetWindowSize(VideoWindow20, width, height); + SDL20_SetWindowSize(VideoWindow20, scaled_width, scaled_height); SDL20_SetWindowFullscreen(VideoWindow20, fullscreen_flags20); + /* This second SetWindowSize is a workaround for an SDL2 bug, see https://github.com/libsdl-org/sdl12-compat/issues/148 */ + SDL20_SetWindowSize(VideoWindow20, scaled_width, scaled_height); SDL20_SetWindowBordered(VideoWindow20, (flags12 & SDL12_NOFRAME) ? SDL_FALSE : SDL_TRUE); SDL20_SetWindowResizable(VideoWindow20, (flags12 & SDL12_RESIZABLE) ? SDL_TRUE : SDL_FALSE); } - if (VideoSurface12) { + if (VideoSurface12->surface20) { SDL20_free(VideoSurface12->pixels); } else { - VideoSurface12 = CreateSurface12WithFormat(0, 0, appfmt); - if (!VideoSurface12) { + CreateVideoSurface(appfmt); + if (!VideoSurface12->surface20) { return EndVidModeCreate(); } } @@ -3672,40 +6711,88 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) VideoSurface12->flags &= ~SDL12_FULLSCREEN; } + if (flags12 & SDL12_RESIZABLE) { + VideoSurface12->flags |= SDL12_RESIZABLE; + } else { + VideoSurface12->flags &= ~SDL12_RESIZABLE; + } + if (flags12 & SDL12_OPENGL) { SDL_assert(!VideoTexture20); /* either a new window or we destroyed all this */ SDL_assert(!VideoRenderer20); - FIXME("Should we force a compatibility context here?"); - VideoGLContext20 = SDL20_GL_CreateContext(VideoWindow20); + if (!VideoGLContext20) { - return EndVidModeCreate(); + VideoGLContext20 = SDL20_GL_CreateContext(VideoWindow20); + if (!VideoGLContext20) { + return EndVidModeCreate(); + } + LoadOpenGLFunctions(); } VideoSurface12->flags |= SDL12_OPENGL; - LoadOpenGLFunctions(); - /* Try to set up a logical scaling */ if (use_gl_scaling) { if (!InitializeOpenGLScaling(width, height)) { + const SDL_bool was_fullscreen = ((fullscreen_flags20 & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) ? SDL_TRUE : SDL_FALSE; + window_size_scaling = 1.0f; use_gl_scaling = SDL_FALSE; fullscreen_flags20 &= ~SDL_WINDOW_FULLSCREEN_DESKTOP; SDL20_SetWindowFullscreen(VideoWindow20, fullscreen_flags20); - SDL20_SetWindowSize(VideoWindow20, width, height); - fullscreen_flags20 |= SDL_WINDOW_FULLSCREEN; - SDL20_SetWindowFullscreen(VideoWindow20, fullscreen_flags20); + SDL20_SetWindowSize(VideoWindow20, width, height); /* not scaled_width, scaled_height */ + if (was_fullscreen) { + fullscreen_flags20 |= SDL_WINDOW_FULLSCREEN; + SDL20_SetWindowFullscreen(VideoWindow20, fullscreen_flags20); + } + } + } + + if ((flags12 & SDL12_OPENGLBLIT) == SDL12_OPENGLBLIT) { + const int pixsize = VideoSurface12->format->BytesPerPixel; + const GLenum glfmt = (pixsize == 4) ? GL_RGBA : GL_RGB; + const GLenum gltype = (pixsize == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_5_6_5; + + if (!OpenGLFuncs.SUPPORTS_GL_ARB_texture_non_power_of_two) { + SDL20_SetError("Your OpenGL drivers don't support NPOT textures for SDL_OPENGLBLIT; please upgrade."); + return EndVidModeCreate(); + } + + if (!OpenGLBlitTexture) { + OpenGLFuncs.glGenTextures(1, &OpenGLBlitTexture); } + OpenGLFuncs.glBindTexture(GL_TEXTURE_2D, OpenGLBlitTexture); + OpenGLFuncs.glTexImage2D(GL_TEXTURE_2D, 0, (pixsize == 4) ? GL_RGBA : GL_RGB, VideoSurface12->w, VideoSurface12->h, 0, glfmt, gltype, NULL); + + VideoSurface12->surface20->pixels = SDL20_malloc(height * VideoSurface12->pitch); + VideoSurface12->pixels = VideoSurface12->surface20->pixels; + if (!VideoSurface12->pixels) { + SDL20_OutOfMemory(); + return EndVidModeCreate(); + } + SDL20_memset(VideoSurface12->pixels, 0xFF, height * VideoSurface12->pitch); /* SDL 1.2 default OPENGLBLIT surface to full intensity */ + VideoSurface12->flags |= SDL12_OPENGLBLIT; + } + + if (vsync_env) { + SDL20_GL_SetSwapInterval(SDL20_atoi(vsync_env)); + } else { + SDL20_GL_SetSwapInterval(SwapInterval); } } else { /* always use a renderer for non-OpenGL windows. */ - const char *vsync_env = SDL20_getenv("SDL12COMPAT_SYNC_TO_VBLANK"); const char *old_scale_quality = SDL20_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); - const char *scale_method_env = SDL20_getenv("SDL12COMPAT_SCALE_METHOD"); const SDL_bool want_vsync = (vsync_env && SDL20_atoi(vsync_env)) ? SDL_TRUE : SDL_FALSE; - const SDL_bool want_nearest = (scale_method_env && !SDL20_strcmp(scale_method_env, "nearest"))? SDL_TRUE : SDL_FALSE; SDL_RendererInfo rinfo; SDL_assert(!VideoGLContext20); /* either a new window or we destroyed all this */ + + if (!VideoRendererLock) { + VideoRendererLock = SDL20_CreateMutex(); + if (!VideoRendererLock) { + return EndVidModeCreate(); + } + } + if (!VideoRenderer20 && want_vsync) { VideoRenderer20 = SDL20_CreateRenderer(VideoWindow20, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC); } @@ -3733,20 +6820,28 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) return EndVidModeCreate(); } - if (VideoTexture20) { - SDL20_DestroyTexture(VideoTexture20); + if (!VideoTexture20) { + SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, WantScaleMethodNearest ? "0" : "1"); + VideoTexture20 = SDL20_CreateTexture(VideoRenderer20, rinfo.texture_formats[0], SDL_TEXTUREACCESS_STREAMING, width, height); + SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_scale_quality); + if (!VideoTexture20) { + return EndVidModeCreate(); + } } - if (VideoConvertSurface20) { - SDL20_FreeSurface(VideoConvertSurface20); - VideoConvertSurface20 = NULL; + /* clear the texture for (re)use */ + { + SDL_Surface *surface = NULL; + if (SDL20_LockTextureToSurface(VideoTexture20, NULL, &surface) == 0) { + SDL20_FillRect(surface, NULL, SDL20_MapRGB(surface->format, 0, 0, 0)); + SDL20_UnlockTexture(VideoTexture20); + } } - SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, want_nearest?"0":"1"); - VideoTexture20 = SDL20_CreateTexture(VideoRenderer20, rinfo.texture_formats[0], SDL_TEXTUREACCESS_STREAMING, width, height); - SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_scale_quality); - if (!VideoTexture20) { - return EndVidModeCreate(); + /* don't need conversion, or need to change the conversion surface's format? Nuke the existing surface (and maybe rebuild it later). */ + if (VideoConvertSurface20 && ((rinfo.texture_formats[0] == appfmt) || (rinfo.texture_formats[0] != VideoConvertSurface20->format->format))) { + SDL20_FreeSurface(VideoConvertSurface20); + VideoConvertSurface20 = NULL; } if (rinfo.texture_formats[0] != appfmt) { @@ -3758,7 +6853,7 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) } VideoSurface12->flags &= ~SDL12_OPENGL; - VideoSurface12->surface20->pixels = SDL20_malloc(height * VideoSurface12->pitch); + VideoSurface12->surface20->pixels = SDL20_calloc(height, VideoSurface12->pitch); VideoSurface12->pixels = VideoSurface12->surface20->pixels; if (!VideoSurface12->pixels) { SDL20_OutOfMemory(); @@ -3785,53 +6880,72 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) } } + SetVideoModeThread = SDL20_ThreadID(); + VideoSurfacePresentTicks = 0; + VideoSurfaceLastPresentTicks = 0; + VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE; + SDL20_RaiseWindow(VideoWindow20); - /* SDL 1.2 always grabbed input if the video mode was fullscreen. */ - if (VideoSurface12->flags & SDL12_FULLSCREEN) { - HandleInputGrab(SDL12_GRAB_ON); - } + UpdateInputGrab(); - FIXME("setup screen saver"); + if ((flags12 & SDL12_OPENGL) == 0) { + /* see notes above these functions about GL context resetting. Force a lock/unlock here to set that up. */ + LockVideoRenderer(); + UnlockVideoRenderer(); + } - VideoSurfacePresentTicks = 0; - VideoSurfaceLastPresentTicks = 0; + SDL_PumpEvents(); /* run this once at startup. */ return VideoSurface12; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL +SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) +{ + SDL12_Surface *retval; + SetVideoModeInProgress = SDL_TRUE; + retval = SetVideoModeImpl(width, height, bpp, flags12); + SetVideoModeInProgress = SDL_FALSE; + return retval; +} + +/* SDL_SetRefreshRate was never in an real SDL-1.2 release, but apparently StepMania was maintaining a fork with this API for literally years. */ +DECLSPEC12 void SDLCALL +SDL_SetRefreshRate(int rate) +{ + DesiredRefreshRate = (rate >= 0) ? rate : 0; /* takes effect on next SDL_SetVideoMode call. */ +} + +DECLSPEC12 SDL12_Surface * SDLCALL SDL_GetVideoSurface(void) { return VideoSurface12; } static int -SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, Uint8 **retval) +SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, SDL_Rect *dstrect20, Uint8 **retval) { /* The 1.2 docs say this: * RGBA->RGBA: * SDL_SRCALPHA set: - * alpha-blend (using the source alpha channel) the RGB values; - * leave destination alpha untouched. [Note: is this correct?] + * alpha-blend (using the source alpha channel) the RGB values; + * leave destination alpha untouched. [Note: is this correct?] * * In SDL2, we change the destination alpha. We have to save it off in this case, which sucks. */ Uint8 *dstalpha = NULL; - const SDL_bool save_dstalpha = ((src12->flags & SDL12_SRCALPHA) && dst12->format->Amask && ((src12->format->alpha != 255) || src12->format->Amask)) ? SDL_TRUE : SDL_FALSE; - - FIXME("This should only save the dst rect in use"); - - if (save_dstalpha) { - Uint8 *dptr; - int x, y; - - const int w = dst12->w; - const int h = dst12->h; + const SDL_bool save_dstalpha = (PreserveDestinationAlpha && (src12->flags & SDL12_SRCALPHA) && dst12->format->Amask && ((src12->format->alpha != 255) || src12->format->Amask)) ? SDL_TRUE : SDL_FALSE; + if (save_dstalpha && (dstrect20->w > 0) && (dstrect20->h > 0)) { const Uint32 amask = dst12->format->Amask; const Uint32 ashift = dst12->format->Ashift; const Uint16 pitch = dst12->pitch; + Uint8 *dptr; + int x, y, w, h; + + w = dstrect20->w; + h = dstrect20->h; dstalpha = (Uint8 *) SDL20_malloc(w * h); if (!dstalpha) { @@ -3840,11 +6954,9 @@ SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, Uint8 **retval) } dptr = dstalpha; - if ((amask == 0xFF) || (amask == 0xFF00) || (amask == 0xFF0000) ||(amask == 0xFF000000)) { - FIXME("this could be SIMD'd"); - } if (dst12->format->BytesPerPixel == 2) { const Uint16 *sptr = (const Uint16 *) dst12->pixels; + sptr += ((dst12->pitch / 2) * dstrect20->y) + dstrect20->x; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *(dptr++) = (Uint8) ((sptr[x] & amask) >> ashift); @@ -3853,6 +6965,7 @@ SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, Uint8 **retval) } } else if (dst12->format->BytesPerPixel == 4) { const Uint32 *sptr = (const Uint32 *) dst12->pixels; + sptr += ((dst12->pitch / 4) * dstrect20->y) + dstrect20->x; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *(dptr++) = (Uint8) ((sptr[x] & amask) >> ashift); @@ -3860,7 +6973,7 @@ SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, Uint8 **retval) sptr = (Uint32 *) (((Uint8 *) sptr) + pitch); } } else { - FIXME("Unhandled dest alpha"); + SDL_assert(!"Unhandled dest alpha"); } } @@ -3869,23 +6982,21 @@ SaveDestAlpha(SDL12_Surface *src12, SDL12_Surface *dst12, Uint8 **retval) } static void -RestoreDestAlpha(SDL12_Surface *dst12, Uint8 *dstalpha) +RestoreDestAlpha(SDL12_Surface *dst12, Uint8 *dstalpha, const SDL_Rect *dstrect20) { if (dstalpha) { - int x, y; - - const int w = dst12->w; - const int h = dst12->h; const Uint8 *sptr = dstalpha; const Uint32 amask = dst12->format->Amask; const Uint32 ashift = dst12->format->Ashift; const Uint16 pitch = dst12->pitch; + int x, y, w, h; + + w = dstrect20->w; + h = dstrect20->h; - if ((amask == 0xFF) || (amask == 0xFF00) || (amask == 0xFF0000) ||(amask == 0xFF000000)) { - FIXME("this could be SIMD'd"); - } if (dst12->format->BytesPerPixel == 2) { Uint16 *dptr = (Uint16 *) dst12->pixels; + dptr += ((dst12->pitch / 2) * dstrect20->y) + dstrect20->x; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { dptr[x] = (Uint16) ((dptr[x] & ~amask) | ((((Uint16) *(sptr++)) << ashift) & amask)); @@ -3894,6 +7005,7 @@ RestoreDestAlpha(SDL12_Surface *dst12, Uint8 *dstalpha) } } else if (dst12->format->BytesPerPixel == 4) { Uint32 *dptr = (Uint32 *) dst12->pixels; + dptr += ((dst12->pitch / 4) * dstrect20->y) + dstrect20->x; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { dptr[x] = (dptr[x] & ~amask) | ((((Uint32) *(sptr++)) << ashift) & amask); @@ -3901,22 +7013,50 @@ RestoreDestAlpha(SDL12_Surface *dst12, Uint8 *dstalpha) dptr = (Uint32 *) (((Uint8 *) dptr) + pitch); } } else { - FIXME("Unhandled dest alpha"); + SDL_assert(!"Unhandled dest alpha"); } SDL20_free(dstalpha); } } -DECLSPEC int SDLCALL -SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) +static void +PrepBlitDestRect(SDL_Rect *dstrect20, SDL12_Surface *dst12, const SDL12_Rect *dstrect12) { - Uint8 *dstalpha; + /* dstrect12 w and h is ignored, SDL 1.2 only cares about position. */ + dstrect20->w = dst12->w; + dstrect20->h = dst12->h; + + if (dstrect12) { + SDL_Rect fulldstrect20; + fulldstrect20.x = fulldstrect20.y = 0; + fulldstrect20.w = dst12->w; + fulldstrect20.h = dst12->h; + dstrect20->x = dstrect12->x; + dstrect20->y = dstrect12->y; + SDL20_IntersectRect(&fulldstrect20, dstrect20, dstrect20); + } else { + dstrect20->x = 0; + dstrect20->y = 0; + } +} + +DECLSPEC12 int SDLCALL +SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) +{ + Uint8 *dstalpha; SDL_Rect srcrect20, dstrect20; int retval; if ((src12 == NULL) || (dst12 == NULL)) { return SDL20_SetError("SDL_UpperBlit: passed a NULL surface"); - } else if (SaveDestAlpha(src12, dst12, &dstalpha) < 0) { + } + if ((src12->pixels == NULL) || (dst12->pixels == NULL)) { + return SDL20_SetError("SDL_UpperBlit: passed a surface with NULL pixels"); + } + + PrepBlitDestRect(&dstrect20, dst12, dstrect12); + + if (SaveDestAlpha(src12, dst12, &dstrect20, &dstalpha) < 0) { return -1; } @@ -3925,7 +7065,7 @@ SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, dst12->surface20, dstrect12 ? Rect12to20(dstrect12, &dstrect20) : NULL); - RestoreDestAlpha(dst12, dstalpha); + RestoreDestAlpha(dst12, dstalpha, &dstrect20); if (dstrect12) { Rect20to12(&dstrect20, dstrect12); @@ -3934,14 +7074,16 @@ SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LowerBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) { Uint8 *dstalpha; SDL_Rect srcrect20, dstrect20; int retval; - if (SaveDestAlpha(src12, dst12, &dstalpha) < 0) { + PrepBlitDestRect(&dstrect20, dst12, dstrect12); + + if (SaveDestAlpha(src12, dst12, &dstrect20, &dstalpha) < 0) { return -1; } @@ -3950,7 +7092,7 @@ SDL_LowerBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, dst12->surface20, dstrect12 ? Rect12to20(dstrect12, &dstrect20) : NULL); - RestoreDestAlpha(dst12, dstalpha); + RestoreDestAlpha(dst12, dstalpha, &dstrect20); if (srcrect12) { Rect20to12(&srcrect20, srcrect12); @@ -3963,7 +7105,7 @@ SDL_LowerBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SoftStretch(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) { SDL_Rect srcrect20, dstrect20; @@ -3973,7 +7115,7 @@ SDL_SoftStretch(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst1 dstrect12 ? Rect12to20(dstrect12, &dstrect20) : NULL); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetAlpha(SDL12_Surface *surface12, Uint32 flags12, Uint8 value) { /* note that SDL 1.2 does not check if surface12 is NULL before dereferencing it either */ @@ -4003,24 +7145,53 @@ SDL_SetAlpha(SDL12_Surface *surface12, Uint32 flags12, Uint8 value) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LockSurface(SDL12_Surface *surface12) { - const int retval = SDL20_LockSurface(surface12->surface20); - surface12->pixels = surface12->surface20->pixels; - surface12->pitch = surface12->surface20->pitch; + int retval = 0; + /* just pretend to lock for the screen surface, but ignore it. */ + if (surface12 != VideoSurface12) { + retval = SDL20_LockSurface(surface12->surface20); + surface12->pixels = surface12->surface20->pixels; + surface12->pitch = surface12->surface20->pitch; + } return retval; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UnlockSurface(SDL12_Surface *surface12) { - SDL20_UnlockSurface(surface12->surface20); - surface12->pixels = surface12->surface20->pixels; - surface12->pitch = surface12->surface20->pitch; + /* just pretend to lock for the screen surface, but ignore it. */ + if (surface12 != VideoSurface12) { + SDL20_UnlockSurface(surface12->surface20); + surface12->pixels = surface12->surface20->pixels; + surface12->pitch = surface12->surface20->pitch; + } +} + +DECLSPEC12 int SDLCALL +SDL_SetColorKey(SDL12_Surface *surface12, Uint32 flag12, Uint32 key) +{ + const SDL_bool addkey = (flag12 & SDL12_SRCCOLORKEY) ? SDL_TRUE : SDL_FALSE; + const int retval = SDL20_SetColorKey(surface12->surface20, addkey, key); + if (SDL20_GetColorKey(surface12->surface20, &surface12->format->colorkey) < 0) { + surface12->format->colorkey = 0; + } + + if (addkey) { + surface12->flags |= SDL12_SRCCOLORKEY; + /* you could set a color key on a 1.2 surface that had an alpha channel, but it would be ignored during blits. */ + if (surface12->format->Amask) { + SDL20_SetColorKey(surface12->surface20, SDL_FALSE, key); + } + } else { + surface12->flags &= ~SDL12_SRCCOLORKEY; + } + + return retval; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_ConvertSurface(SDL12_Surface *src12, const SDL12_PixelFormat *format12, Uint32 flags12) { Uint32 flags20 = 0; @@ -4038,20 +7209,25 @@ SDL_ConvertSurface(SDL12_Surface *src12, const SDL12_PixelFormat *format12, Uint if (!retval) { SDL20_FreeSurface(surface20); } else { - if (flags12 & SDL12_SRCALPHA) { + if (retval->format->Amask != 0) { SDL20_SetSurfaceBlendMode(surface20, SDL_BLENDMODE_BLEND); retval->flags |= SDL12_SRCALPHA; } + if (flags12 & SDL12_SRCCOLORKEY) { + Uint8 r, g, b, a; + SDL20_GetRGBA(src12->format->colorkey, src12->surface20->format, &r, &g, &b, &a); + SDL_SetColorKey(retval, SDL12_SRCCOLORKEY, SDL20_MapRGBA(retval->surface20->format, r, g, b, a)); + } } } return retval; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_DisplayFormat(SDL12_Surface *surface12) { const Uint32 flags = surface12->flags & (SDL12_SRCCOLORKEY|SDL12_SRCALPHA|SDL12_RLEACCELOK); - if (!VideoSurface12) { + if (!VideoSurface12 || !VideoSurface12->surface20) { SDL20_SetError("No video mode has been set"); return NULL; } @@ -4059,7 +7235,7 @@ SDL_DisplayFormat(SDL12_Surface *surface12) return SDL_ConvertSurface(surface12, VideoSurface12->format, flags); } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_DisplayFormatAlpha(SDL12_Surface *surface12) { const Uint32 flags = surface12->flags & (SDL12_SRCALPHA|SDL12_RLEACCELOK); @@ -4067,13 +7243,13 @@ SDL_DisplayFormatAlpha(SDL12_Surface *surface12) SDL_PixelFormat *fmt20 = NULL; SDL12_PixelFormat fmt12; - if (!VideoSurface12) { + if (!VideoSurface12 || !VideoSurface12->surface20) { SDL20_SetError("No video mode has been set"); return NULL; } /* we only allow a few formats for the screen surface, and this is the appropriate alpha format for all of them. */ - fmt20 = SDL20_AllocFormat(SDL_PIXELFORMAT_ARGB8888); FIXME("bgr instead of rgb?"); + fmt20 = SDL20_AllocFormat(SDL_PIXELFORMAT_ARGB8888); if (!fmt20) { return NULL; } @@ -4086,20 +7262,50 @@ SDL_DisplayFormatAlpha(SDL12_Surface *surface12) static void PresentScreen(void) { - SDL20_RenderClear(VideoRenderer20); - SDL20_RenderCopy(VideoRenderer20, VideoTexture20, NULL, NULL); + QueuedOverlayItem *overlay; + SDL_Renderer *renderer = LockVideoRenderer(); + + if (!renderer) { + return; + } + + /* We don't actually implement an event thread in sdl12-compat, but some + * games will only call SDL_PeepEvents(), which doesn't otherwise pump + * events, and get stuck when they've consumed all the events. + * + * Just pumping the event loop here simulates an event thread well enough + * for most things. + */ + if (EventThreadEnabled) { + SDL_PumpEvents(); + } + + SDL20_RenderClear(renderer); + SDL20_RenderCopy(renderer, VideoTexture20, NULL, NULL); /* Render any pending YUV overlay over the surface texture. */ - if (QueuedDisplayOverlay12) { - SDL12_YUVData *hwdata = (SDL12_YUVData *) QueuedDisplayOverlay12->hwdata; - SDL_Rect dstrect20; - SDL20_RenderCopy(VideoRenderer20, hwdata->texture20, NULL, Rect12to20(&QueuedDisplayOverlayDstRect12, &dstrect20)); - QueuedDisplayOverlay12 = NULL; + overlay = QueuedDisplayOverlays.next; + if (overlay) { + while (overlay != NULL) { + QueuedOverlayItem *next = overlay->next; + if (overlay->overlay12) { + SDL12_YUVData *hwdata = (SDL12_YUVData *) overlay->overlay12->hwdata; + SDL_Rect dstrect20; + SDL20_RenderCopy(renderer, hwdata->texture20, NULL, Rect12to20(&overlay->dstrect12, &dstrect20)); + } + SDL_free(overlay); + overlay = next; + } + QueuedDisplayOverlays.next = NULL; + QueuedDisplayOverlaysTail = &QueuedDisplayOverlays; } - SDL20_RenderPresent(VideoRenderer20); + SDL20_RenderPresent(renderer); + VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE; VideoSurfaceLastPresentTicks = SDL20_GetTicks(); VideoSurfacePresentTicks = 0; + + UnlockVideoRenderer(); } static void @@ -4123,96 +7329,258 @@ UpdateRect12to20(SDL12_Surface *surface12, const SDL12_Rect *rect12, SDL_Rect *r } } -DECLSPEC void SDLCALL +/* For manual throttling of screen updates. */ +static int +GetDesiredMillisecondsPerFrame(void) +{ + SDL_DisplayMode mode; + if (VideoSurface12->flags & SDL12_FULLSCREEN) { + SDL_assert(VideoWindow20 != NULL); + if (SDL20_GetWindowDisplayMode(VideoWindow20, &mode) == 0) { + if (mode.refresh_rate) { + return 1000 / mode.refresh_rate; + } + } + } else if (SDL20_GetCurrentDisplayMode(VideoDisplayIndex, &mode) == 0) { + /* If we're windowed, assume we're on the default screen. */ + if (mode.refresh_rate) { + return 1000 / mode.refresh_rate; + } + } + return 15; +} + +/* SDL_OPENGLBLIT support APIs. https://discourse.libsdl.org/t/ogl-and-sdl/2775/3 */ +DECLSPEC12 void SDLCALL +SDL_GL_Lock(void) +{ + if (!OpenGLBlitTexture) { + return; + } + + if (++OpenGLBlitLockCount == 1) { + OpenGLFuncs.glPushAttrib(GL_ALL_ATTRIB_BITS); + OpenGLFuncs.glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + OpenGLFuncs.glEnable(GL_TEXTURE_2D); + OpenGLFuncs.glEnable(GL_BLEND); + OpenGLFuncs.glDisable(GL_FOG); + OpenGLFuncs.glDisable(GL_ALPHA_TEST); + OpenGLFuncs.glDisable(GL_DEPTH_TEST); + OpenGLFuncs.glDisable(GL_SCISSOR_TEST); + OpenGLFuncs.glDisable(GL_STENCIL_TEST); + OpenGLFuncs.glDisable(GL_CULL_FACE); + + OpenGLFuncs.glBindTexture(GL_TEXTURE_2D, OpenGLBlitTexture); + OpenGLFuncs.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + OpenGLFuncs.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + OpenGLFuncs.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + OpenGLFuncs.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + OpenGLFuncs.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + OpenGLFuncs.glPixelStorei(GL_UNPACK_ROW_LENGTH, VideoSurface12->pitch / VideoSurface12->format->BytesPerPixel); + OpenGLFuncs.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + OpenGLFuncs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + OpenGLFuncs.glViewport(0, 0, VideoSurface12->w, VideoSurface12->h); + OpenGLFuncs.glMatrixMode(GL_PROJECTION); + OpenGLFuncs.glPushMatrix(); + OpenGLFuncs.glLoadIdentity(); + + OpenGLFuncs.glOrtho(0.0, (GLdouble) VideoSurface12->w, (GLdouble) VideoSurface12->h, 0.0, 0.0, 1.0); + + OpenGLFuncs.glMatrixMode(GL_MODELVIEW); + OpenGLFuncs.glPushMatrix(); + OpenGLFuncs.glLoadIdentity(); + } +} + +DECLSPEC12 void SDLCALL +SDL_GL_UpdateRects(int numrects, SDL12_Rect *rects12) +{ + if (OpenGLBlitTexture) { + const int srcpitch = VideoSurface12->pitch; + const int pixsize = VideoSurface12->format->BytesPerPixel; + const GLenum glfmt = (pixsize == 4) ? GL_RGBA : GL_RGB; + const GLenum gltype = (pixsize == 4) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT_5_6_5; + SDL_Rect surfacerect20; + int i; + + surfacerect20.x = surfacerect20.y = 0; + surfacerect20.w = VideoSurface12->w; + surfacerect20.h = VideoSurface12->h; + + for (i = 0; i < numrects; i++) { + SDL_Rect rect20; + SDL_Rect intersected20; + Uint8 *src; + + SDL20_IntersectRect(Rect12to20(&rects12[i], &rect20), &surfacerect20, &intersected20); + + src = (((Uint8 *) VideoSurface12->pixels) + (intersected20.y * srcpitch)) + (intersected20.x * pixsize); + OpenGLFuncs.glTexSubImage2D(GL_TEXTURE_2D, 0, intersected20.x, intersected20.y, intersected20.w, intersected20.h, glfmt, gltype, src); + + OpenGLFuncs.glBegin(GL_TRIANGLE_STRIP); + { + const GLfloat tex_x1 = ((GLfloat) intersected20.x) / ((GLfloat) VideoSurface12->w); + const GLfloat tex_y1 = ((GLfloat) intersected20.y) / ((GLfloat) VideoSurface12->h); + const GLfloat tex_x2 = tex_x1 + ((GLfloat) intersected20.w) / ((GLfloat) VideoSurface12->w); + const GLfloat tex_y2 = tex_y1 + ((GLfloat) intersected20.h) / ((GLfloat) VideoSurface12->h); + const GLint vert_x1 = (GLint) intersected20.x; + const GLint vert_y1 = (GLint) intersected20.y; + const GLint vert_x2 = vert_x1 + (GLint) intersected20.w; + const GLint vert_y2 = vert_y1 + (GLint) intersected20.h; + OpenGLFuncs.glTexCoord2f(tex_x1, tex_y1); + OpenGLFuncs.glVertex2i(vert_x1, vert_y1); + OpenGLFuncs.glTexCoord2f(tex_x2, tex_y1); + OpenGLFuncs.glVertex2i(vert_x2, vert_y1); + OpenGLFuncs.glTexCoord2f(tex_x1, tex_y2); + OpenGLFuncs.glVertex2i(vert_x1, vert_y2); + OpenGLFuncs.glTexCoord2f(tex_x2, tex_y2); + OpenGLFuncs.glVertex2i(vert_x2, vert_y2); + } + OpenGLFuncs.glEnd(); + } + } +} + + +DECLSPEC12 void SDLCALL +SDL_GL_Unlock(void) +{ + if (OpenGLBlitTexture) { + if (OpenGLBlitLockCount > 0) { + if (--OpenGLBlitLockCount == 0) { + OpenGLFuncs.glPopMatrix(); + OpenGLFuncs.glMatrixMode(GL_PROJECTION); + OpenGLFuncs.glPopMatrix(); + OpenGLFuncs.glPopClientAttrib(); + OpenGLFuncs.glPopAttrib(); + } + } + } +} + + +DECLSPEC12 void SDLCALL SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12) { + const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread) ? SDL_TRUE : SDL_FALSE; + /* strangely, SDL 1.2 doesn't check if surface12 is NULL before touching it */ /* (UpdateRect, singular, does...) */ + + if ((surface12 == VideoSurface12) && ((surface12->flags & SDL12_OPENGLBLIT) == SDL12_OPENGLBLIT)) { + SDL_GL_Lock(); + SDL_GL_UpdateRects(numrects, rects12); + SDL_GL_Unlock(); + return; + } + if (surface12->flags & SDL12_OPENGL) { SDL20_SetError("Use SDL_GL_SwapBuffers() on OpenGL surfaces"); return; } /* everything else is marked SDL12_DOUBLEBUF and SHOULD BE a no-op here, - * but in practice most apps never got a double-buffered surface and - * don't handle it correctly, so we have to work around it. */ + * but in practice most apps never got a double-buffered surface and + * don't handle it correctly, so we have to work around it. */ if (surface12 == VideoSurface12) { + const SDL_bool upload_later = (!ThisIsSetVideoModeThread && !AllowThreadedDraws) ? SDL_TRUE : SDL_FALSE; + SDL_Palette *logicalPal = surface12->surface20->format->palette; + const int pixsize = surface12->format->BytesPerPixel; + const int srcpitch = surface12->pitch; SDL_bool whole_screen = SDL_FALSE; - SDL_Rect rect20; + SDL_Renderer *renderer = NULL; void *pixels = NULL; + SDL_Rect rect20; int pitch = 0; - int i; + int i, j; - if (SDL20_LockTexture(VideoTexture20, NULL, &pixels, &pitch) < 0) { - return; /* oh well */ + if (!upload_later) { + renderer = LockVideoRenderer(); /* must own the renderer before locking the texture! */ } - if (VideoConvertSurface20) { - SDL_Palette *logicalPal = surface12->surface20->format->palette; - surface12->surface20->format->palette = VideoPhysicalPalette20; - VideoConvertSurface20->pixels = pixels; - VideoConvertSurface20->pitch = pitch; - for (i = 0; i < numrects; i++) { - UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen); - if (rect20.w && rect20.h) { - SDL20_UpperBlit(VideoSurface12->surface20, &rect20, VideoConvertSurface20, &rect20); - } + for (i = 0; i < numrects; i++) { + UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen); + + if (!renderer) { + continue; } - VideoConvertSurface20->pixels = NULL; - VideoConvertSurface20->pitch = 0; - surface12->surface20->format->palette = logicalPal; - } else { - const int srcpitch = surface12->pitch; - const int pixsize = surface12->format->BytesPerPixel; - for (i = 0; i < numrects; i++) { - UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen); - if (rect20.w && rect20.h) { - const int cpy = rect20.w * pixsize; - const int h = surface12->h; - char *dst = (((char *) pixels) + (rect20.y * pitch)) + (rect20.x * pixsize); - char *src = (((char *) surface12->pixels) + (rect20.y * srcpitch)) + (rect20.x * pixsize); - int j = 0; - for (; j < h; j++) { - SDL20_memcpy(dst, src, cpy); - src += srcpitch; - dst += pitch; - } + if (!rect20.w || !rect20.h) { + continue; + } + if (SDL20_LockTexture(VideoTexture20, &rect20, &pixels, &pitch) < 0) { + continue; /* oh well */ + } + + if (VideoConvertSurface20) { + SDL_Rect dstrect20; /* pretend that the subregion is just the top left of the convert surface. */ + dstrect20.x = dstrect20.y = 0; + dstrect20.w = rect20.w; + dstrect20.h = rect20.h; + surface12->surface20->format->palette = VideoPhysicalPalette20; + VideoConvertSurface20->pixels = pixels; + VideoConvertSurface20->pitch = pitch; + VideoConvertSurface20->w = rect20.w; + VideoConvertSurface20->h = rect20.h; + SDL20_UpperBlit(VideoSurface12->surface20, &rect20, VideoConvertSurface20, &dstrect20); + } else { + const int cpy = rect20.w * pixsize; + char *dst = (char *) pixels; + const Uint8 *src = (((Uint8 *) surface12->pixels) + (rect20.y * srcpitch)) + (rect20.x * pixsize); + for (j = 0; j < rect20.h; j++) { + SDL20_memcpy(dst, src, cpy); + src += srcpitch; + dst += pitch; } } + + SDL20_UnlockTexture(VideoTexture20); } - SDL20_UnlockTexture(VideoTexture20); + if (VideoConvertSurface20) { /* reset some state we messed with */ + surface12->surface20->format->palette = logicalPal; + VideoConvertSurface20->pixels = NULL; + VideoConvertSurface20->pitch = 0; + VideoConvertSurface20->w = VideoSurface12->w; + VideoConvertSurface20->h = VideoSurface12->h; + } - if (whole_screen) { + if (upload_later) { + VideoSurfaceUpdatedInBackgroundThread = SDL_TRUE; + VideoSurfacePresentTicks = whole_screen ? 1 : VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame(); /* flip it later (or as soon as the main thread can). */ + } else if (whole_screen) { PresentScreen(); /* flip it now. */ } else { - FIXME("Don't hardcode 15, do this based on display refresh rate."); - FIXME("Maybe just flip it immediately in PumpEvents if this flag is set, instead?"); - VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + 15; /* flip it later. */ + if (!VideoSurfacePresentTicks) { + VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame(); /* flip it later. */ + } else if (SDL_TICKS_PASSED(SDL20_GetTicks(), VideoSurfacePresentTicks)) { + PresentScreen(); + } + } + + if (renderer) { + UnlockVideoRenderer(); } } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UpdateRect(SDL12_Surface *screen12, Sint32 x, Sint32 y, Uint32 w, Uint32 h) { if (screen12) { SDL12_Rect rect12; rect12.x = (Sint16) x; rect12.y = (Sint16) y; - rect12.w = (Uint16) (w ? w : screen12->w); - rect12.h = (Uint16) (h ? h : screen12->h); + rect12.w = (Uint16) (w ? w : (unsigned)screen12->w); + rect12.h = (Uint16) (h ? h : (unsigned)screen12->h); SDL_UpdateRects(screen12, 1, &rect12); } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_Flip(SDL12_Surface *surface12) { - if (surface12->flags & SDL12_OPENGL) { - return SDL20_SetError("Use SDL_GL_SwapBuffers() on OpenGL surfaces"); - } - if (surface12 == VideoSurface12) { SDL_UpdateRect(surface12, 0, 0, 0, 0); /* update the whole screen and present now. */ } @@ -4220,18 +7588,57 @@ SDL_Flip(SDL12_Surface *surface12) return 0; } -DECLSPEC void SDLCALL +static void +HandleKeyRepeat(void) +{ + /* SDL 1.2 is a little weird about key repeat; it manages it itself + (so we can't use SDL2's key repeat), it only runs during PumpEvents + (so no AddTimer or separate thread to manage it), and it cares about + the last key pressed (so if a key is repeating and you press a new + one down, it stops repeating the first key). */ + if (KeyRepeatNextTicks) { + const Uint32 now = SDL20_GetTicks(); + SDL_assert(KeyRepeatEvent.type == SDL12_KEYDOWN); + if (SDL_TICKS_PASSED(now, KeyRepeatNextTicks)) { + /* these repeat from the current time in SDL 1.2, not consistently! */ + KeyRepeatNextTicks = now + KeyRepeatInterval; + PushEventIfNotFiltered(&KeyRepeatEvent); + } + } +} + +DECLSPEC12 void SDLCALL SDL_PumpEvents(void) { + const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread) ? SDL_TRUE : SDL_FALSE; SDL_Event e; + static SDL_bool InPumpEvents = SDL_FALSE; + + if (!ThisIsSetVideoModeThread && !AllowThreadedPumps) { + return; + } + + if (InPumpEvents) + return; + InPumpEvents = SDL_TRUE; /* If the app is doing dirty rectangles, we set a flag and present the - * screen surface when they pump for new events if we're close to 60Hz, - * which we consider a sign that they are done rendering for the current - * frame and it would make sense to send it to the screen. */ + * screen surface when they pump for new events if we're close to 60Hz, + * which we consider a sign that they are done rendering for the current + * frame and it would make sense to send it to the screen. */ + if (VideoSurfacePresentTicks && SDL_TICKS_PASSED(SDL20_GetTicks(), VideoSurfacePresentTicks)) { - PresentScreen(); + if (VideoSurfaceUpdatedInBackgroundThread) { + SDL_Flip(VideoSurface12); /* this will update the texture and present. */ + } else { + PresentScreen(); + } } + + if (EventQueueMutex) { + SDL20_LockMutex(EventQueueMutex); + } + while (SDL20_PollEvent(&e)) { /* spin to drain the SDL2 event queue. */ } /* If there's a pending KEYDOWN event, and we haven't got a TEXTINPUT @@ -4239,9 +7646,16 @@ SDL_PumpEvents(void) if (PendingKeydownEvent.type == SDL12_KEYDOWN) { FlushPendingKeydownEvent(0); } + + HandleKeyRepeat(); /* deal with SDL 1.2-style key repeat... */ + + if (EventQueueMutex) { + SDL20_UnlockMutex(EventQueueMutex); + } + InPumpEvents = SDL_FALSE; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_SetCaption(const char *title, const char *icon) { if (WindowTitle) { @@ -4257,7 +7671,7 @@ SDL_WM_SetCaption(const char *title, const char *icon) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_GetCaption(const char **title, const char **icon) { if (title) { @@ -4268,7 +7682,7 @@ SDL_WM_GetCaption(const char **title, const char **icon) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) { SDL_BlendMode oldmode; @@ -4277,8 +7691,8 @@ SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) int bpp; int ret; - if (VideoWindow20) { - SDL20_SetWindowIcon(VideoWindow20, icon12->surface20); + /* Make sure we actually have an icon to set */ + if (!icon12) { return; } @@ -4304,7 +7718,6 @@ SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) const int h = icon12->h; const int mpitch = (w + 7) / 8; Uint32 *ptr = (Uint32 *) icon20->pixels; - int x, y; SDL_assert(icon20->format->BytesPerPixel == 4); @@ -4327,16 +7740,22 @@ SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) SDL20_FreeSurface(VideoIcon20); VideoIcon20 = icon20; } + + if (VideoWindow20) { + SDL20_SetWindowIcon(VideoWindow20, VideoIcon20); + } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_WM_IconifyWindow(void) { - SDL20_MinimizeWindow(VideoWindow20); + if (VideoWindow20) { + SDL20_MinimizeWindow(VideoWindow20); + } return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_WM_ToggleFullScreen(SDL12_Surface *surface) { int retval = 0; @@ -4359,8 +7778,12 @@ SDL_WM_ToggleFullScreen(SDL12_Surface *surface) VideoSurface12->flags |= SDL12_FULLSCREEN; } } - if (retval && VideoRenderer20) { - SDL20_RenderSetLogicalSize(VideoRenderer20, VideoSurface12->w, VideoSurface12->h); + if (retval) { + SDL_Renderer *renderer = LockVideoRenderer(); + if (renderer) { + SDL20_RenderSetLogicalSize(renderer, VideoSurface12->w, VideoSurface12->h); + UnlockVideoRenderer(); + } } } return retval; @@ -4372,105 +7795,117 @@ UpdateRelativeMouseMode(void) /* in SDL 1.2, hiding+grabbing the cursor was like SDL2's relative mouse mode. */ if (VideoWindow20) { const SDL_bool enable = (VideoWindowGrabbed && VideoCursorHidden) ? SDL_TRUE : SDL_FALSE; - if (MouseInputIsRelative != enable) { - MouseInputIsRelative = enable; - if (MouseInputIsRelative) { - /* reset position, we'll have to track it ourselves in SDL_MOUSEMOTION events, since 1.2 - * would give you window coordinates, even in relative mode. */ - SDL20_GetMouseState(&MousePosition.x, &MousePosition.y); - } - SDL20_SetRelativeMouseMode(MouseInputIsRelative); - } + MouseInputIsRelative = enable; + SDL20_SetRelativeMouseMode(MouseInputIsRelative); } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_ShowCursor(int toggle) { const int retval = VideoCursorHidden ? 0 : 1; if (toggle >= 0) { const SDL_bool wanthide = (toggle == 0) ? SDL_TRUE : SDL_FALSE; - if (VideoCursorHidden != wanthide) { - SDL20_ShowCursor(wanthide ? 0 : 1); - VideoCursorHidden = wanthide; - UpdateRelativeMouseMode(); - } + SDL20_ShowCursor(wanthide ? 0 : 1); + VideoCursorHidden = wanthide; + UpdateRelativeMouseMode(); } return retval; } static void -HandleInputGrab(SDL12_GrabMode mode) +UpdateInputGrab(void) { /* SDL 1.2 always grabbed input if the video mode was fullscreen. */ - const SDL_bool isfullscreen = (VideoSurface12 && (VideoSurface12->flags & SDL12_FULLSCREEN)) ? SDL_TRUE : SDL_FALSE; - const SDL_bool wantgrab = (isfullscreen || (mode == SDL12_GRAB_ON)) ? SDL_TRUE : SDL_FALSE; - if (VideoWindowGrabbed != wantgrab) { + if (VideoWindow20) { + const SDL_bool isfullscreen = (VideoSurface12 && VideoSurface12->surface20 && (VideoSurface12->flags & SDL12_FULLSCREEN)) ? SDL_TRUE : SDL_FALSE; + const SDL_bool wantgrab = (VideoWindowGrabWanted || isfullscreen) ? SDL_TRUE : SDL_FALSE; SDL20_SetWindowGrab(VideoWindow20, wantgrab); VideoWindowGrabbed = wantgrab; UpdateRelativeMouseMode(); } } -DECLSPEC SDL12_GrabMode SDLCALL +DECLSPEC12 SDL12_GrabMode SDLCALL SDL_WM_GrabInput(SDL12_GrabMode mode) { if (mode != SDL12_GRAB_QUERY) { - HandleInputGrab(mode); + VideoWindowGrabWanted = (mode == SDL12_GRAB_ON) ? SDL_TRUE : SDL_FALSE; + UpdateInputGrab(); } - return VideoWindowGrabbed ? SDL12_GRAB_ON : SDL12_GRAB_OFF; + return VideoWindowGrabWanted ? SDL12_GRAB_ON : SDL12_GRAB_OFF; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WarpMouse(Uint16 x, Uint16 y) { if (MouseInputIsRelative) { /* we have to track this ourselves, in case app calls SDL_GetMouseState(). */ MousePosition.x = (int) x; MousePosition.y = (int) y; + } else if (x == MousePosition.x && y == MousePosition.y) { + /* There's no movement needed, just generate an internal event */ + SDL12_Event event; + event.type = SDL12_MOUSEMOTION; + event.motion.which = 0; + event.motion.state = SDL_GetMouseState(NULL, NULL); + event.motion.x = x; + event.motion.y = y; + event.motion.xrel = 0; + event.motion.yrel = 0; + PushEventIfNotFiltered(&event); } else { - SDL20_WarpMouseInWindow(VideoWindow20, x, y); + if (VideoWindow20) { + SDL_Rect viewport; + float scale_x, scale_y; + + if (OpenGLLogicalScalingFBO) { + int physical_w, physical_h; + + /* we want to scale into the window size, which is dpi-scaled */ + SDL20_GetWindowSize(VideoWindow20, &physical_w, &physical_h); + + viewport = GetOpenGLLogicalScalingViewport(physical_w, physical_h); + + scale_x = (float)viewport.w / OpenGLLogicalScalingWidth; + scale_y = (float)viewport.h / OpenGLLogicalScalingHeight; + } else { + SDL20_RenderGetViewport(VideoRenderer20, &viewport); + SDL20_RenderGetScale(VideoRenderer20, &scale_x, &scale_y); + viewport.x = (int)SDL20_lroundf(viewport.x * scale_x); + viewport.y = (int)SDL20_lroundf(viewport.y * scale_y); + viewport.w = (int)SDL20_lroundf(viewport.w * scale_x); + viewport.h = (int)SDL20_lroundf(viewport.h * scale_y); + } + + x = (int)SDL20_lroundf(viewport.x + (x * scale_x)); + y = (int)SDL20_lroundf(viewport.y + (y * scale_y)); + + SDL20_WarpMouseInWindow(VideoWindow20, x, y); + } } } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetAppState(void) { Uint8 state12 = 0; - Uint32 flags20 = 0; - - flags20 = SDL20_GetWindowFlags(VideoWindow20); - if ((flags20 & SDL_WINDOW_SHOWN) && !(flags20 & SDL_WINDOW_MINIMIZED)) { - state12 |= SDL12_APPACTIVE; - } - if (flags20 & SDL_WINDOW_INPUT_FOCUS) { - state12 |= SDL12_APPINPUTFOCUS; - } - if (flags20 & SDL_WINDOW_MOUSE_FOCUS) { - state12 |= SDL12_APPMOUSEFOCUS; + if (VideoWindow20) { + const Uint32 flags20 = SDL20_GetWindowFlags(VideoWindow20); + if ((flags20 & SDL_WINDOW_SHOWN) && !(flags20 & SDL_WINDOW_MINIMIZED)) { + state12 |= SDL12_APPACTIVE; + } + if (flags20 & SDL_WINDOW_INPUT_FOCUS) { + state12 |= SDL12_APPINPUTFOCUS; + } + if (flags20 & SDL_WINDOW_MOUSE_FOCUS) { + state12 |= SDL12_APPMOUSEFOCUS; + } } return state12; } -DECLSPEC int SDLCALL -SDL_SetColorKey(SDL12_Surface *surface12, Uint32 flag12, Uint32 key) -{ - const SDL_bool addkey = (flag12 & SDL12_SRCCOLORKEY) ? SDL_TRUE : SDL_FALSE; - const int retval = SDL20_SetColorKey(surface12->surface20, addkey, key); - if (SDL20_GetColorKey(surface12->surface20, &surface12->format->colorkey) < 0) { - surface12->format->colorkey = 0; - } - - if (addkey) { - surface12->flags |= SDL12_SRCCOLORKEY; - } else { - surface12->flags &= ~SDL12_SRCCOLORKEY; - } - - return retval; -} - -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, int firstcolor, int ncolors) { @@ -4498,11 +7933,12 @@ SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, /* we need to force the "unused" field to 255, since it's "alpha" in SDL2. */ opaquecolors = (SDL_Color *) SDL20_malloc(sizeof (SDL_Color) * ncolors); if (!opaquecolors) { - return SDL20_OutOfMemory(); + SDL20_OutOfMemory(); + return 0; } /* don't SDL_memcpy in case the 'a' field is uninitialized and upsets - * memory tools like Valgrind. */ + * memory tools like Valgrind. */ for (i = 0; i < ncolors; i++) { opaquecolors[i].r = colors[i].r; opaquecolors[i].g = colors[i].g; @@ -4510,17 +7946,17 @@ SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, opaquecolors[i].a = 255; } - retval = 0; + retval = 1; /* "The return value is 1 if all colours could be set as requested, and 0 otherwise." */ if (flags & SDL12_LOGPAL) { if (SDL20_SetPaletteColors(palette20, opaquecolors, firstcolor, ncolors) < 0) { - retval = -1; + retval = 0; } } if ((flags & SDL12_PHYSPAL) && (surface12 == VideoSurface12) && VideoPhysicalPalette20) { if (SDL20_SetPaletteColors(VideoPhysicalPalette20, opaquecolors, firstcolor, ncolors) < 0) { - retval = -1; + retval = 0; } } @@ -4536,24 +7972,112 @@ SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetColors(SDL12_Surface *surface12, const SDL_Color * colors, int firstcolor, int ncolors) { return SDL_SetPalette(surface12, SDL12_LOGPAL | SDL12_PHYSPAL, colors, firstcolor, ncolors); } -DECLSPEC int SDLCALL -SDL_GetWMInfo(SDL_SysWMinfo * info) + +#if defined(SDL_VIDEO_DRIVER_X11) +/* In 1.2, these would lock the event thread (if you _used_ the event thread), and call XSync(SDL_Display, False) before unlocking */ +static void x11_lock_display(void) {} +static void x11_unlock_display(void) {} +#endif + +DECLSPEC12 int SDLCALL +SDL_GetWMInfo(SDL12_SysWMinfo *info12) +{ + SDL_SysWMinfo info20; + SDL_bool temp_window = SDL_FALSE; + SDL_Window *win20 = VideoWindow20; + int rc; + + if (info12->version.major > 1) { + SDL20_SetError("Requested version is unsupported"); + return 0; /* some programs only test against 0, not -1 */ + } + if (!SupportSysWM) { + SDL20_SetError("No SysWM support available"); + return 0; /* some programs only test against 0, not -1 */ + } + + if (win20 == NULL) { + /* It was legal to call SDL_GetWMInfo without SDL_SetVideoMode() on X11 and Windows (and others...?) in 1.2. */ + win20 = SDL20_CreateWindow("SDL_GetWMInfo support window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 128, 128, SDL_WINDOW_HIDDEN); + if (!win20) { + return 0; + } + temp_window = SDL_TRUE; + } + + SDL20_zero(info20); + + /* SDL2, before the version scheme change, would fail if the requested version wasn't + 2.0.x, so if the SDL2 is from before this was fixed, we need to lie about the + version, and assume it will work out. */ + if (LinkedSDL2VersionInt >= SDL_VERSIONNUM(2,24,0)) { + SDL_VERSION(&info20.version); + } else { + info20.version.major = 2; + info20.version.minor = 0; + info20.version.patch = 22; + } + + rc = SDL20_GetWindowWMInfo(win20, &info20); + + if (temp_window) { + SDL20_DestroyWindow(win20); + } + + if (!rc) { + return 0; /* some programs only test against 0, not -1 */ + } + +#if defined(_WIN32) + if (info20.subsystem == SDL_SYSWM_WINDOWS) { + info12->window = temp_window ? 0 : info20.info.win.window; + if (SDL_VERSIONNUM(info12->version.major, info12->version.minor, info12->version.patch) >= SDL_VERSIONNUM(1, 2, 5)) { + info12->hglrc = (HGLRC) VideoGLContext20; + } + } else { + SDL20_SetError("No SysWM information available"); + return 0; /* some programs only test against 0, not -1 */ + } +#elif defined(SDL_VIDEO_DRIVER_X11) + if (info20.subsystem == SDL_SYSWM_X11) { + info12->subsystem = SDL12_SYSWM_X11; + info12->info.x11.display = info20.info.x11.display; + info12->info.x11.window = temp_window ? 0 : info20.info.x11.window; + if (SDL_VERSIONNUM(info12->version.major, info12->version.minor, info12->version.patch) >= SDL_VERSIONNUM(1, 0, 2)) { + /* While these don't exist in SDL2, some programs expect to get a valid window anyway. */ + info12->info.x11.fswindow = info12->info.x11.window; + info12->info.x11.wmwindow = info12->info.x11.window; + } + if (SDL_VERSIONNUM(info12->version.major, info12->version.minor, info12->version.patch) >= SDL_VERSIONNUM(1, 2, 12)) { + info12->info.x11.gfxdisplay = info20.info.x11.display; /* shrug */ + } + info12->info.x11.lock_func = x11_lock_display; /* just no-ops for now */ + info12->info.x11.unlock_func = x11_unlock_display; + } else { + SDL20_SetError("No SysWM information available"); + return 0; /* some programs only test against 0, not -1 */ + } +#else + info12->data = 0; /* shrug */ +#endif + + return 1; +} + +DECLSPEC12 SDL_Window * SDLCALL +SDL12COMPAT_GetWindow(void) { - /*return SDL20_GetWindowWMInfo(VideoWindow20, info);*/ - FIXME("write me"); - (void)info; - SDL20_Unsupported(); - return 0; /* some programs only test against 0, not -1 */ + return VideoWindow20; } -DECLSPEC SDL12_Overlay * SDLCALL +DECLSPEC12 SDL12_Overlay * SDLCALL SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) { /* SDL 1.2 has you pass the screen surface in here, but it doesn't check that it's _actually_ the screen surface, @@ -4562,8 +8086,10 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) rendering anyhow, we always make an SDL_Texture in here and draw over the screen pixels, in hopes that the GPU gives us a boost here. */ + const char *old_scale_quality = SDL20_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); SDL12_Overlay *retval = NULL; SDL12_YUVData *hwdata = NULL; + SDL_Renderer *renderer = NULL; Uint32 format20 = 0; if (display12 != VideoSurface12) { /* SDL 1.2 doesn't check this, but it seems irresponsible not to. */ @@ -4576,8 +8102,6 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) return NULL; } - SDL_assert(VideoRenderer20 != NULL); - switch (format12) { #define SUPPORTED_YUV_FORMAT(x) case SDL12_##x##_OVERLAY: format20 = SDL_PIXELFORMAT_##x; break SUPPORTED_YUV_FORMAT(YV12); @@ -4615,7 +8139,14 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) hwdata->pitches[0] = w * 2; } - hwdata->texture20 = SDL20_CreateTexture(VideoRenderer20, format20, SDL_TEXTUREACCESS_STREAMING, w, h); + renderer = LockVideoRenderer(); + + SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + hwdata->texture20 = SDL20_CreateTexture(renderer, format20, SDL_TEXTUREACCESS_STREAMING, w, h); + SDL20_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_scale_quality); + + UnlockVideoRenderer(); + if (!hwdata->texture20) { SDL20_free(hwdata->pixelbuf); SDL20_free(retval); @@ -4630,10 +8161,14 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) retval->hw_overlay = 1; retval->pitches = hwdata->pitches; + /* Some programs (e.g. mplayer) access pixels without locking. */ + retval->pixels = hwdata->pixels; + hwdata->dirty = SDL_TRUE; + return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LockYUVOverlay(SDL12_Overlay *overlay12) { SDL12_YUVData *hwdata; @@ -4656,19 +8191,44 @@ SDL_LockYUVOverlay(SDL12_Overlay *overlay12) return 0; /* success */ } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12) { + QueuedOverlayItem *overlay; SDL12_YUVData *hwdata; + SDL_Renderer *renderer; + const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread) ? SDL_TRUE : SDL_FALSE; if (!overlay12) { return SDL20_InvalidParamError("overlay"); - } else if (!dstrect12) { + } + if (!dstrect12) { return SDL20_InvalidParamError("dstrect"); - } else if (!VideoRenderer20) { + } + if ((renderer = LockVideoRenderer()) == NULL) { return SDL20_SetError("No software screen surface available"); } + for (overlay = QueuedDisplayOverlays.next; overlay != NULL; overlay = overlay->next) { + if (overlay->overlay12 == overlay12) { /* trying to draw the same overlay twice in one frame? Dump the current surface and overlays to the screen now. */ + /* Force an update of the screen. */ + if (ThisIsSetVideoModeThread) { + if (VideoSurfaceUpdatedInBackgroundThread) { + SDL_Flip(VideoSurface12); /* this will update the texture and present. */ + } else if (VideoSurfacePresentTicks) { + PresentScreen(); + } + } + + break; + } + } + + if ((overlay = (QueuedOverlayItem *) SDL_malloc(sizeof (QueuedOverlayItem))) == NULL) { + UnlockVideoRenderer(); + return SDL20_OutOfMemory(); + } + hwdata = (SDL12_YUVData *) overlay12->hwdata; /* Upload contents if we've been locked, even if we're _still_ locked, to @@ -4699,34 +8259,60 @@ SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12) /* The app may or may not SDL_Flip() after this...queue this to render on the next present, and start a timer going to force a present, in case they don't. */ - FIXME("is it legal to display multiple yuv overlays?"); /* if so, this will need to be a list instead of a single pointer. */ - QueuedDisplayOverlay12 = overlay12; - SDL20_memcpy(&QueuedDisplayOverlayDstRect12, dstrect12, sizeof (SDL12_Rect)); - VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + 15; /* flip it later. */ + + overlay->overlay12 = overlay12; + SDL20_memcpy(&overlay->dstrect12, dstrect12, sizeof (SDL12_Rect)); + overlay->next = NULL; + + SDL_assert(QueuedDisplayOverlaysTail != NULL); + SDL_assert(QueuedDisplayOverlaysTail->next == NULL); + QueuedDisplayOverlaysTail->next = overlay; + QueuedDisplayOverlaysTail = overlay; + + if (!VideoSurfacePresentTicks) { + VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame(); /* flip it later. */ + } + + UnlockVideoRenderer(); return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UnlockYUVOverlay(SDL12_Overlay *overlay12) { - if (overlay12) { + /* MLT SDL1 consumer uses locks, but accesses pixels pointer outside the lock, so don't null the pixels pointer. */ + /*if (overlay12) { overlay12->pixels = NULL; - } + }*/ + (void)overlay12; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeYUVOverlay(SDL12_Overlay *overlay12) { if (overlay12) { + SDL_Renderer *renderer = LockVideoRenderer(); SDL12_YUVData *hwdata = (SDL12_YUVData *) overlay12->hwdata; - SDL20_DestroyTexture(hwdata->texture20); - SDL20_free(hwdata->pixelbuf); - SDL20_free(overlay12); - } + QueuedOverlayItem *overlay = QueuedDisplayOverlays.next; + while (overlay != NULL) { + if (overlay->overlay12 == overlay12) { + overlay->overlay12 = NULL; /* don't try to draw this later. */ + } + overlay = overlay->next; + } + + if (renderer) { + SDL20_DestroyTexture(hwdata->texture20); + UnlockVideoRenderer(); + } + + SDL20_free(hwdata->pixelbuf); + SDL20_free(overlay12); + } } -DECLSPEC void * SDLCALL +DECLSPEC12 void * SDLCALL SDL_GL_GetProcAddress(const char *sym) { /* see comments on glBindFramebuffer_shim_for_scaling for explanation */ @@ -4756,10 +8342,15 @@ SDL_GL_GetProcAddress(const char *sym) if ((SDL20_strcmp(sym, "glCopyTexSubImage3D") == 0)) { return (void *) glCopyTexSubImage3D_shim_for_scaling; } + + /* this function is specific to the shim library */ + if ((SDL20_strcmp(sym, "SDL12COMPAT_GetWindow") == 0)) { + return (void *) SDL12COMPAT_GetWindow; + } return SDL20_GL_GetProcAddress(sym); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_LoadLibrary(const char *libname) { /* SDL 1.2 would unload the previous library if one was loaded. SDL2 @@ -4790,7 +8381,7 @@ SDL_GL_LoadLibrary(const char *libname) } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_SetAttribute(SDL12_GLattr attr, int value) { if (attr >= SDL12_GL_MAX_ATTRIBUTE) { @@ -4800,22 +8391,23 @@ SDL_GL_SetAttribute(SDL12_GLattr attr, int value) /* swap control was moved out of this API, everything else lines up. */ if (attr == SDL12_GL_SWAP_CONTROL) { SwapInterval = value; - FIXME("Actually set swap interval somewhere"); return 0; } - else if (attr == SDL12_GL_MULTISAMPLESAMPLES) { + if (attr == SDL12_GL_MULTISAMPLESAMPLES) { OpenGLLogicalScalingSamples = value; return 0; } - else if (attr == SDL12_GL_MULTISAMPLEBUFFERS) { + if (attr == SDL12_GL_MULTISAMPLEBUFFERS) { return 0; } return SDL20_GL_SetAttribute((SDL_GLattr) attr, value); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_GetAttribute(SDL12_GLattr attr, int* value) { + int retval; + if (attr >= SDL12_GL_MAX_ATTRIBUTE) { return SDL20_SetError("Unknown GL attribute"); } @@ -4824,58 +8416,56 @@ SDL_GL_GetAttribute(SDL12_GLattr attr, int* value) *value = SDL20_GL_GetSwapInterval(); return 0; } - else if (attr == SDL12_GL_MULTISAMPLESAMPLES) { + if (attr == SDL12_GL_MULTISAMPLESAMPLES) { *value = OpenGLLogicalScalingSamples; return 0; - } - else if (attr == SDL12_GL_MULTISAMPLEBUFFERS) { + } + if (attr == SDL12_GL_MULTISAMPLEBUFFERS) { *value = (OpenGLLogicalScalingSamples) ? 1 : 0; return 0; - } - return SDL20_GL_GetAttribute((SDL_GLattr) attr, value); + } + + /* SDL2 has a bug where FBO 0 must be bound or SDL20_GL_GetAttribute gives incorrect information. + See https://github.com/libsdl-org/sdl12-compat/issues/150 */ + if (OpenGLCurrentDrawFBO == 0) { + retval = SDL20_GL_GetAttribute((SDL_GLattr) attr, value); + } else { + SDL_assert(OpenGLFuncs.glBindFramebuffer != NULL); + OpenGLFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + retval = SDL20_GL_GetAttribute((SDL_GLattr) attr, value); + OpenGLFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OpenGLCurrentDrawFBO); + } + + return retval; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_SwapBuffers(void) { if (VideoWindow20) { + if (OpenGLBuffersSwapTickInterval != 0.f) { + const Uint32 tickDelta = SDL20_GetTicks() - OpenGLBuffersLastSwapTicks; + if (tickDelta < OpenGLBuffersSwapTickInterval) { + SDL20_Delay((int)(OpenGLBuffersSwapTickInterval + 0.5f) - tickDelta); + } + } + /* Some applications, e.g. Awesomenauts, play with glXMakeCurrent() behind our backs and break SwapBuffers() */ + if (ForceGLSwapBufferContext) { + SDL20_GL_MakeCurrent(VideoWindow20, VideoGLContext20); + } + if (OpenGLLogicalScalingFBO != 0) { const GLboolean has_scissor = OpenGLFuncs.glIsEnabled(GL_SCISSOR_TEST); - const char *scale_method_env = SDL20_getenv("SDL12COMPAT_SCALE_METHOD"); - const SDL_bool want_nearest = (scale_method_env && !SDL20_strcmp(scale_method_env, "nearest"))? SDL_TRUE : SDL_FALSE; + int physical_w, physical_h; GLfloat clearcolor[4]; - float want_aspect, real_aspect; - int drawablew, drawableh; SDL_Rect dstrect; - SDL20_GL_GetDrawableSize(VideoWindow20, &drawablew, &drawableh); - OpenGLFuncs.glGetFloatv(GL_COLOR_CLEAR_VALUE, clearcolor); + /* use the drawable size, which is != window size for HIGHDPI systems */ + SDL20_GL_GetDrawableSize(VideoWindow20, &physical_w, &physical_h); + dstrect = GetOpenGLLogicalScalingViewport(physical_w, physical_h); - want_aspect = ((float) OpenGLLogicalScalingWidth) / ((float) OpenGLLogicalScalingHeight); - real_aspect = ((float) drawablew) / ((float) drawableh); - - if (SDL20_fabsf(want_aspect-real_aspect) < 0.0001f) { - /* The aspect ratios are the same, just scale appropriately */ - dstrect.x = 0; - dstrect.y = 0; - dstrect.w = drawablew; - dstrect.h = drawableh; - } else if (want_aspect > real_aspect) { - /* We want a wider aspect ratio than is available - letterbox it */ - const float scale = ((float) drawablew) / OpenGLLogicalScalingWidth; - dstrect.x = 0; - dstrect.w = drawablew; - dstrect.h = (int)SDL20_floorf(OpenGLLogicalScalingHeight * scale); - dstrect.y = (drawableh - dstrect.h) / 2; - } else { - /* We want a narrower aspect ratio than is available - use side-bars */ - const float scale = ((float)drawableh) / OpenGLLogicalScalingHeight; - dstrect.y = 0; - dstrect.h = drawableh; - dstrect.w = (int)SDL20_floorf(OpenGLLogicalScalingWidth * scale); - dstrect.x = (drawablew - dstrect.w) / 2; - } + OpenGLFuncs.glGetFloatv(GL_COLOR_CLEAR_VALUE, clearcolor); if (has_scissor) { OpenGLFuncs.glDisable(GL_SCISSOR_TEST); /* scissor test affects framebuffer_blit */ @@ -4897,7 +8487,7 @@ SDL_GL_SwapBuffers(void) OpenGLFuncs.glClear(GL_COLOR_BUFFER_BIT); OpenGLFuncs.glBlitFramebuffer(0, 0, OpenGLLogicalScalingWidth, OpenGLLogicalScalingHeight, dstrect.x, dstrect.y, dstrect.x + dstrect.w, dstrect.y + dstrect.h, - GL_COLOR_BUFFER_BIT, want_nearest?GL_NEAREST:GL_LINEAR); + GL_COLOR_BUFFER_BIT, WantScaleMethodNearest ? GL_NEAREST : GL_LINEAR); OpenGLFuncs.glBindFramebuffer(GL_FRAMEBUFFER, 0); SDL20_GL_SwapWindow(VideoWindow20); OpenGLFuncs.glClearColor(clearcolor[0], clearcolor[1], clearcolor[2], clearcolor[3]); @@ -4909,10 +8499,14 @@ SDL_GL_SwapBuffers(void) } else { SDL20_GL_SwapWindow(VideoWindow20); } + + if (OpenGLBuffersSwapTickInterval != 0.f) { + OpenGLBuffersLastSwapTicks = SDL20_GetTicks(); + } } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetGamma(float red, float green, float blue) { Uint16 red_ramp[256]; @@ -4932,51 +8526,58 @@ SDL_SetGamma(float red, float green, float blue) } else { SDL20_CalculateGammaRamp(blue, blue_ramp); } + return SDL20_SetWindowGammaRamp(VideoWindow20, red_ramp, green_ramp, blue_ramp); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetGammaRamp(const Uint16 *red, const Uint16 *green, const Uint16 *blue) { return SDL20_SetWindowGammaRamp(VideoWindow20, red, green, blue); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GetGammaRamp(Uint16 *red, Uint16 *green, Uint16 *blue) { return SDL20_GetWindowGammaRamp(VideoWindow20, red, green, blue); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_EnableKeyRepeat(int delay, int interval) { - FIXME("write me"); - (void) delay; - (void) interval; + if ((delay < 0) || (interval < 0)) { + return SDL20_SetError("Invalid key repeat values"); + } + + KeyRepeatEvent.type = SDL12_NOEVENT; + KeyRepeatNextTicks = 0; + KeyRepeatDelay = (Uint32) delay; + KeyRepeatInterval = (Uint32) interval; return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetKeyRepeat(int *delay, int *interval) { - FIXME("write me"); if (delay) { - *delay = SDL12_DEFAULT_REPEAT_DELAY; + *delay = (int) KeyRepeatDelay; } if (interval) { - *interval = SDL12_DEFAULT_REPEAT_INTERVAL; + *interval = (int) KeyRepeatInterval; } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_EnableUNICODE(int enable) { - int old = EnabledUnicode; - EnabledUnicode = enable; - if (enable) { - SDL20_StartTextInput(); - } else { - SDL20_StopTextInput(); + const int old = EnabledUnicode; + if (enable >= 0) { + EnabledUnicode = enable; + if (enable) { + SDL20_StartTextInput(); + } else { + SDL20_StopTextInput(); + } } return old; } @@ -4995,7 +8596,7 @@ SetTimerCallback12(Uint32 interval, void* param) return RoundTimerTo12Resolution(((SDL12_TimerCallback)param)(interval)); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetTimer(Uint32 interval, SDL12_TimerCallback callback) { static SDL_TimerID compat_timer; @@ -5015,7 +8616,29 @@ SDL_SetTimer(Uint32 interval, SDL12_TimerCallback callback) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 void SDLCALL +SDL_Delay(Uint32 ticks) +{ + /* In case there's a loading screen from a background thread and the main thread is waiting... */ + const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread) ? SDL_TRUE : SDL_FALSE; + if (ThisIsSetVideoModeThread) { + if (VideoSurfaceUpdatedInBackgroundThread) { + SDL_Flip(VideoSurface12); /* this will update the texture and present. */ + } else if (VideoSurfacePresentTicks) { + PresentScreen(); + } + } + + SDL20_Delay(ticks); +} + +DECLSPEC12 char * SDLCALL +SDL_getenv(const char *name) +{ + return SDL12COMPAT_getenv_unsafe(name); +} + +DECLSPEC12 int SDLCALL SDL_putenv(const char *_var) { char *ptr = NULL; @@ -5031,72 +8654,12 @@ SDL_putenv(const char *_var) } *ptr = '\0'; /* split the string into name and value. */ - SDL20_setenv(var, ptr + 1, 1); + SDL12COMPAT_setenv_unsafe(var, ptr + 1); SDL20_free(var); return 0; } -/* CD-ROM support is gone from SDL 2.0, so just have stubs that fail. */ - -typedef void *SDL12_CD; /* close enough. :) */ -typedef int SDL12_CDstatus; /* close enough. :) */ - -DECLSPEC int SDLCALL -SDL_CDNumDrives(void) -{ - FIXME("should return -1 without SDL_INIT_CDROM"); - return 0; -} - -DECLSPEC const char *SDLCALL SDL_CDName(int drive) { - SDL20_Unsupported(); - (void)drive; - return NULL; -} -DECLSPEC SDL12_CD *SDLCALL SDL_CDOpen(int drive) { - SDL20_Unsupported(); - (void)drive; - return NULL; -} -DECLSPEC SDL12_CDstatus SDLCALL SDL_CDStatus(SDL12_CD *cdrom) { - (void) cdrom; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDPlayTracks(SDL12_CD *cdrom, int start_track, int start_frame, int ntracks, int nframes) { - (void) cdrom; - (void) start_track; - (void) start_frame; - (void) ntracks; - (void) nframes; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDPlay(SDL12_CD *cdrom, int start, int length) { - (void) cdrom; - (void) start; - (void) length; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDPause(SDL12_CD *cdrom) { - (void) cdrom; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDResume(SDL12_CD *cdrom) { - (void) cdrom; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDStop(SDL12_CD *cdrom) { - (void) cdrom; - return SDL20_Unsupported(); -} -DECLSPEC int SDLCALL SDL_CDEject(SDL12_CD *cdrom) { - (void) cdrom; - return SDL20_Unsupported(); -} -DECLSPEC void SDLCALL SDL_CDClose(SDL12_CD *cdrom) { - (void) cdrom; -} - #if (defined(_WIN32) || defined(__OS2__)) && !defined(SDL_PASSED_BEGINTHREAD_ENDTHREAD) #error SDL_PASSED_BEGINTHREAD_ENDTHREAD not defined #endif @@ -5112,20 +8675,20 @@ DECLSPEC void SDLCALL SDL_CDClose(SDL12_CD *cdrom) { * each other. * * Therefore, we have to do the following trick below. */ -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) { return SDL20_CreateThread(fn, NULL, data, NULL, NULL); } #else -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) { return SDL20_CreateThread(fn, NULL, data, pfnBeginThread, pfnEndThread); } #endif #else -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) { return SDL20_CreateThread(fn, NULL, data); @@ -5134,28 +8697,28 @@ SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) /* These two will truncate the returned value on LP64 systems, * a shortcoming of SDL-1.2. */ -DECLSPEC Uint32 SDLCALL SDL_ThreadID(void) +DECLSPEC12 Uint32 SDLCALL SDL_ThreadID(void) { return SDL20_ThreadID(); } -DECLSPEC Uint32 SDLCALL SDL_GetThreadID(SDL_Thread *thread) +DECLSPEC12 Uint32 SDLCALL SDL_GetThreadID(SDL_Thread *thread) { return SDL20_GetThreadID(thread); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_mutexP(SDL_mutex *mutex) { return SDL20_LockMutex(mutex); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_mutexV(SDL_mutex *mutex) { return SDL20_UnlockMutex(mutex); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_KillThread(SDL_Thread *thread) { (void)thread; @@ -5163,18 +8726,6 @@ SDL_KillThread(SDL_Thread *thread) "This program should be fixed. No thread was actually harmed.\n"); } -typedef struct SDL12_TimerID_Data -{ - SDL_TimerID timer_id; - SDL12_NewTimerCallback callback; - void *param; -} SDL12_TimerID_Data; - -/* This changed from an opaque pointer to an int in 2.0. */ -typedef SDL12_TimerID_Data *SDL12_TimerID; -SDL_COMPILE_TIME_ASSERT(timer, sizeof(SDL12_TimerID) >= sizeof(SDL_TimerID)); - - static Uint32 SDLCALL AddTimerCallback12(Uint32 interval, void *param) { @@ -5182,7 +8733,7 @@ AddTimerCallback12(Uint32 interval, void *param) return RoundTimerTo12Resolution(data->callback(interval, data->param)); } -DECLSPEC SDL12_TimerID SDLCALL +DECLSPEC12 SDL12_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL12_NewTimerCallback callback, void *param) { SDL12_TimerID data = (SDL12_TimerID) SDL20_malloc(sizeof (SDL12_TimerID_Data)); @@ -5201,18 +8752,65 @@ SDL_AddTimer(Uint32 interval, SDL12_NewTimerCallback callback, void *param) return NULL; } + if (EventQueueMutex) { + SDL20_LockMutex(EventQueueMutex); + } + + data->prev = NULL; + data->next = AddedTimers; + if (AddedTimers) { + AddedTimers->prev = data; + } + AddedTimers = data; + + if (EventQueueMutex) { + SDL20_UnlockMutex(EventQueueMutex); + } + return data; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_RemoveTimer(SDL12_TimerID data) { - /* !!! FIXME: 1.2 will safely return SDL_FALSE if this is a - * bogus timer. This code will dereference a bogus pointer. */ - const SDL_bool retval = SDL20_RemoveTimer(data->timer_id); - if (retval) { - SDL20_free(data); + SDL_bool retval = SDL_FALSE; + if (data) { + /* SDL 1.2 would make sure the pointer was valid and return false instead of crashing, so we check that too. */ + SDL12_TimerID i; + + if (EventQueueMutex) { + SDL20_LockMutex(EventQueueMutex); + } + + for (i = AddedTimers; i != NULL; i = i->next) { + if (i == data) { + break; + } + } + + if (i != NULL) { /* this is valid. */ + if (data->prev) { + data->prev->next = data->next; + } + if (data->next) { + data->next->prev = data->prev; + } + if (data == AddedTimers) { + AddedTimers = data->next; + } + retval = SDL_TRUE; + SDL20_RemoveTimer(data->timer_id); + } + + if (EventQueueMutex) { + SDL20_UnlockMutex(EventQueueMutex); + } + + if (retval) { + SDL20_free(data); + } } + return retval; } @@ -5228,7 +8826,7 @@ typedef struct SDL12_RWops { } SDL12_RWops; -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_AllocRW(void) { SDL12_RWops *rwops = (SDL12_RWops *) SDL20_malloc(sizeof (SDL12_RWops)); @@ -5238,7 +8836,7 @@ SDL_AllocRW(void) return rwops; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeRW(SDL12_RWops *rwops12) { SDL20_free(rwops12); @@ -5300,7 +8898,7 @@ RWops20to12(SDL_RWops *rwops20) return rwops12; } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromFile(const char *file, const char *mode) { if (!file || !*file || !mode || !*mode) { @@ -5310,30 +8908,32 @@ SDL_RWFromFile(const char *file, const char *mode) return RWops20to12(SDL20_RWFromFile(file, mode)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromFP(void *io, int autoclose) { - return RWops20to12(SDL20_RWFromFP(io, autoclose)); + return RWops20to12(SDL20_RWFromFP(io, autoclose ? SDL_TRUE : SDL_FALSE)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromMem(void *mem, int size) { return RWops20to12(SDL20_RWFromMem(mem, size)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromConstMem(const void *mem, int size) { return RWops20to12(SDL20_RWFromConstMem(mem, size)); } -#define READ_AND_BYTESWAP(endian, bits) \ - DECLSPEC Uint##bits SDLCALL SDL_Read##endian##bits(SDL12_RWops *rwops12) { \ - Uint##bits val; rwops12->read(rwops12, &val, sizeof (val), 1); \ - return SDL_Swap##endian##bits(val); \ - } - +#define READ_AND_BYTESWAP(endian, bits) \ +DECLSPEC12 Uint##bits SDLCALL \ +SDL_Read##endian##bits(SDL12_RWops *rwops12) \ +{ \ + Uint##bits val; \ + rwops12->read(rwops12, &val, sizeof (val), 1); \ + return SDL_Swap##endian##bits(val); \ +} READ_AND_BYTESWAP(LE,16) READ_AND_BYTESWAP(BE,16) READ_AND_BYTESWAP(LE,32) @@ -5342,11 +8942,13 @@ READ_AND_BYTESWAP(LE,64) READ_AND_BYTESWAP(BE,64) #undef READ_AND_BYTESWAP -#define BYTESWAP_AND_WRITE(endian, bits) \ - DECLSPEC int SDLCALL SDL_Write##endian##bits(SDL12_RWops *rwops12, Uint##bits val) { \ - val = SDL_Swap##endian##bits(val); \ - return rwops12->write(rwops12, &val, sizeof (val), 1); \ - } +#define BYTESWAP_AND_WRITE(endian, bits) \ +DECLSPEC12 int SDLCALL \ +SDL_Write##endian##bits(SDL12_RWops *rwops12, Uint##bits val) \ +{ \ + val = SDL_Swap##endian##bits(val); \ + return rwops12->write(rwops12, &val, sizeof (val), 1); \ +} BYTESWAP_AND_WRITE(LE,16) BYTESWAP_AND_WRITE(BE,16) BYTESWAP_AND_WRITE(LE,32) @@ -5360,7 +8962,7 @@ static Sint64 SDLCALL RWops12to20_size(struct SDL_RWops *rwops20) { SDL12_RWops *rwops12 = (SDL12_RWops *) rwops20->hidden.unknown.data1; - int size = (int) ((size_t) rwops20->hidden.unknown.data2); + int size = (int) ((intptr_t) rwops20->hidden.unknown.data2); int pos; if (size != -1) { @@ -5371,9 +8973,9 @@ RWops12to20_size(struct SDL_RWops *rwops20) if (pos == -1) { return SDL20_Error(SDL_EFSEEK); } - size = (Sint64) rwops12->seek(rwops12, 0, RW_SEEK_END); + size = rwops12->seek(rwops12, 0, RW_SEEK_END); rwops12->seek(rwops12, pos, RW_SEEK_SET); - rwops20->hidden.unknown.data2 = (void *) ((size_t) size); + rwops20->hidden.unknown.data2 = (void *) ((intptr_t) size); return size; } @@ -5438,7 +9040,7 @@ RWops12to20(SDL12_RWops *rwops12) SDL20_zerop(rwops20); rwops20->type = rwops12->type; rwops20->hidden.unknown.data1 = rwops12; - rwops20->hidden.unknown.data2 = (void *) ((size_t) -1); /* cached size of stream */ + rwops20->hidden.unknown.data2 = (void *) ((intptr_t) -1); /* cached size of stream */ rwops20->size = RWops12to20_size; rwops20->seek = RWops12to20_seek; rwops20->read = RWops12to20_read; @@ -5447,7 +9049,7 @@ RWops12to20(SDL12_RWops *rwops12) return rwops20; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_LoadBMP_RW(SDL12_RWops *rwops12, int freerwops12) { SDL_RWops *rwops20 = RWops12to20(rwops12); @@ -5462,24 +9064,47 @@ SDL_LoadBMP_RW(SDL12_RWops *rwops12, int freerwops12) return surface12; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SaveBMP_RW(SDL12_Surface *surface12, SDL12_RWops *rwops12, int freerwops12) { SDL_RWops *rwops20 = RWops12to20(rwops12); const int retval = SDL20_SaveBMP_RW(surface12->surface20, rwops20, freerwops12); - FIXME("wrap surface"); if (!freerwops12) { /* free our wrapper if SDL2 didn't close it. */ SDL20_FreeRW(rwops20); } return retval; } -DECLSPEC SDL_AudioSpec * SDLCALL +DECLSPEC12 SDL_AudioSpec * SDLCALL SDL_LoadWAV_RW(SDL12_RWops *rwops12, int freerwops12, SDL_AudioSpec *spec, Uint8 **buf, Uint32 *len) { SDL_RWops *rwops20 = RWops12to20(rwops12); - SDL_AudioSpec *retval = SDL20_LoadWAV_RW(rwops20, freerwops12, spec, buf, len); + SDL_AudioSpec *retval = NULL; + + *buf = NULL; + + if (!rwops20) { + return NULL; + } + + /* SDL2's LoadWAV requires a seekable stream, but SDL 1.2 didn't, + so if the stream appears unseekable, try to load it into a + memory rwops that we _can_ seek in */ + if (rwops20->seek(rwops20, 0, RW_SEEK_CUR) != -1) { /* if seekable */ + retval = SDL20_LoadWAV_RW(rwops20, freerwops12, spec, buf, len); + } else { + size_t datasize = 0; + void *buffer = SDL20_LoadFile_RW(rwops20, &datasize, freerwops12); + if (buffer) { + SDL_RWops *memrwops20 = SDL20_RWFromConstMem(buffer, (int) datasize); + if (memrwops20) { + retval = SDL20_LoadWAV_RW(memrwops20, 1, spec, buf, len); + } + SDL_free(buffer); + } + } + if (retval && retval->format & 0x20) { SDL20_SetError("Unsupported 32-bit PCM data format"); SDL20_FreeWAV(*buf); @@ -5492,128 +9117,1338 @@ SDL_LoadWAV_RW(SDL12_RWops *rwops12, int freerwops12, return retval; } + +/* CD-ROM API! + We don't support physical CD drives in sdl12-compat. In modern times, it's + hard to find discs at all, let alone discs with audio tracks. Drives are + also getting scarce, and ones that are plugged into the sound output + hardware moreso. With this in mind, sdl12-compat can be instructed to + point to a filesystem directory full .mp3 files, and will pretend this is + an audio CD-ROM, and will decode these files and mix them into an audio + stream as if they were playing from a disc. */ + +#if defined(_MSC_VER) && defined(_M_IX86) +#include "x86_msvc.h" +#endif + +#define CDAUDIO_FPS 75 /* CD audio frames per second. */ + +/* public domain, single-header MP3 decoder for fake CD-ROM audio support! */ +#define DR_MP3_IMPLEMENTATION +#if defined(__GNUC__) && (__GNUC__ >= 4) && \ + !(defined(_WIN32) || defined(__EMX__)) +#define DRMP3_API __attribute__((visibility("hidden"))) +#elif defined(__APPLE__) +#define DRMP3_API __private_extern__ +#else +#define DRMP3_API /* just in case */ +#endif +#define DR_MP3_NO_STDIO 1 +#define DR_MP3_NO_S16 1 +#define DR_MP3_FLOAT_OUTPUT 1 +#define DR_MP3_NO_FULL_READ 1 +#define DRMP3_ASSERT(x) SDL_assert((x)) +#define DRMP3_MALLOC(sz) SDL20_malloc((sz)) +#define DRMP3_REALLOC(p, sz) SDL20_realloc((p), (sz)) +#define DRMP3_FREE(p) SDL20_free((p)) +#define DRMP3_COPY_MEMORY(dst, src, sz) SDL20_memcpy((dst), (src), (sz)) +#define DRMP3_MOVE_MEMORY(dst, src, sz) SDL20_memmove((dst), (src), (sz)) +#define DRMP3_ZERO_MEMORY(p, sz) SDL20_memset((p), 0, (sz)) + +#include "dr_mp3.h" + +static SDL_INLINE Sint64 SDLCALL SDL20_RWseek(SDL_RWops *ctx, Sint64 ofs, int whence) { + return ctx->seek(ctx, ofs, whence); +} +static SDL_INLINE Sint64 SDLCALL SDL20_RWtell(SDL_RWops *ctx) { + return ctx->seek(ctx, 0, RW_SEEK_CUR); +} +static SDL_INLINE size_t SDLCALL SDL20_RWread(SDL_RWops *ctx, void *ptr, size_t size, size_t n) { + return ctx->read(ctx, ptr, size, n); +} +static SDL_INLINE int SDLCALL SDL20_RWclose(SDL_RWops *ctx) { + return ctx->close(ctx); +} + +static size_t +mp3_sdlrwops_read(void *data, void *buf, size_t bytesToRead) +{ + return SDL20_RWread((SDL_RWops *) data, buf, 1, bytesToRead); +} + +static drmp3_bool32 +mp3_sdlrwops_seek(void *data, int offset, drmp3_seek_origin origin) +{ + int whence; + switch (origin) { + case DRMP3_SEEK_SET: + whence = RW_SEEK_SET; + break; + case DRMP3_SEEK_CUR: + whence = RW_SEEK_CUR; + break; + case DRMP3_SEEK_END: + whence = RW_SEEK_END; + break; + default: + return DRMP3_FALSE; + } + return (SDL20_RWseek((SDL_RWops *) data, offset, whence) == -1) ? DRMP3_FALSE : DRMP3_TRUE; +} + +static drmp3_bool32 +mp3_sdlrwops_tell(void *data, drmp3_int64 *pos) +{ + *pos = SDL20_RWtell((SDL_RWops *) data); + return (*pos != -1) ? DRMP3_TRUE : DRMP3_FALSE; +} + +static SDL_bool OpenSDL2AudioDevice(SDL_AudioSpec *want); +static int CloseSDL2AudioDevice(void); +static SDL_bool ResetAudioStream(SDL_AudioStream **_stream, SDL_AudioSpec *spec, const SDL_AudioSpec *to, const SDL_AudioFormat fromfmt, const Uint8 fromchannels, const int fromfreq); + typedef struct { - void (SDLCALL *app_callback)(void *userdata, Uint8 *stream, int len); - void *app_userdata; - Uint8 silence; + SDL_AudioSpec device_format; + + SDL_bool app_callback_opened; + SDL_AudioSpec app_callback_format; + SDL_AudioStream *app_callback_stream; + + SDL_bool cdrom_opened; + SDL_AudioSpec cdrom_format; + SDL_AudioStream *cdrom_stream; + + SDL12_CDstatus cdrom_status; + int cdrom_pcm_frames_written; + int cdrom_cur_track; + int cdrom_cur_frame; + int cdrom_stop_ntracks; + int cdrom_stop_nframes; + drmp3 cdrom_mp3; + + Uint8 *mix_buffer; + size_t mixbuflen; } AudioCallbackWrapperData; static AudioCallbackWrapperData *audio_cbdata = NULL; +static SDL_atomic_t audio_callback_paused; -static void SDLCALL -AudioCallbackWrapper(void *userdata, Uint8 *stream, int len) + +static void +FreeMp3(drmp3 *mp3) { - AudioCallbackWrapperData *data = (AudioCallbackWrapperData *) userdata; - SDL20_memset(stream, data->silence, len); /* SDL2 doesn't clear the stream before calling in here, but 1.2 expects it. */ - data->app_callback(data->app_userdata, stream, len); + SDL_RWops *rw = (SDL_RWops *) mp3->pUserData; + if (rw) { + drmp3_uninit(mp3); + mp3->pUserData = NULL; + SDL20_RWclose(rw); + } } -DECLSPEC int SDLCALL -SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained) +static SDL_bool +CDSubsystemIsInitialized(void) { - AudioCallbackWrapperData *data; - int retval; - - /* SDL2 uses a NULL callback to mean "we plan to use SDL_QueueAudio()" */ - if (want && (want->callback == NULL)) { - return SDL20_SetError("Callback can't be NULL"); + if (!CDRomInit) { + SDL20_SetError("CD-ROM subsystem not initialized"); + return SDL_FALSE; } + return SDL_TRUE; +} - data = (AudioCallbackWrapperData *) SDL20_calloc(1, sizeof (AudioCallbackWrapperData)); - if (!data) { - return SDL20_OutOfMemory(); +/* This never reports failure; if there's a problem, we report zero drives found. */ +static void +InitializeCDSubsystem(void) +{ + const char *cdpath; + + if (CDRomInit) { + return; } - data->app_callback = want->callback; - data->app_userdata = want->userdata; - want->callback = AudioCallbackWrapper; - want->userdata = data; - /* to avoid receiving a possible incompatible configuration - * from SDL2, always pass NULL as the 'obtained' pointer. */ - FIXME("Respect 1.2 environment variables for defining format here."); - if (!want->format) { - want->format = AUDIO_S16SYS; + + cdpath = SDL12Compat_GetHint("SDL12COMPAT_FAKE_CDROM_PATH"); + if (cdpath) { + CDRomPath = SDL_strdup(cdpath); } - if (!want->freq) { - want->freq = 22050; - want->samples = 0; + + CDRomInit = SDL_TRUE; +} + +static void +QuitCDSubsystem(void) +{ + if (!CDRomInit) { + return; } - if (!want->channels) { - want->channels = 2; + SDL_free(CDRomPath); + CDRomPath = NULL; + CDRomInit = SDL_FALSE; +} + +DECLSPEC12 int SDLCALL +SDL_CDNumDrives(void) +{ + if (!CDSubsystemIsInitialized()) { + return -1; } - if (!want->samples) { - Uint32 samp = (want->freq / 1000) * 46; /* ~46 ms */ - Uint32 pow2 = 1; - while (pow2 < samp) pow2 <<= 1; - want->samples = pow2; - } - retval = SDL20_OpenAudio(want, NULL); - want->callback = data->app_callback; - want->userdata = data->app_userdata; - if (retval < 0) { - SDL20_free(data); - } else { - data->silence = want->silence; - SDL_assert(audio_cbdata==NULL); - audio_cbdata = data; - if (obtained) { - SDL20_memcpy(obtained, want, sizeof (SDL_AudioSpec)); + + if (!CDRomPath) { + static SDL_bool warned_once = SDL_FALSE; + if (!warned_once) { + warned_once = SDL_TRUE; + SDL20_Log("This app is looking for CD-ROM drives, but no path was specified"); + SDL20_Log("Set the SDL12COMPAT_FAKE_CDROM_PATH environment variable to a directory"); + SDL20_Log("of MP3 files named trackXX.mp3 where XX is a track number in two digits"); + SDL20_Log("from 01 to 99"); } } - return retval; + return CDRomPath ? 1 : 0; } -DECLSPEC void SDLCALL -SDL_CloseAudio(void) +static SDL_bool +ValidCDDriveIndex(const int drive) { - SDL20_CloseAudio(); - SDL20_free(audio_cbdata); - audio_cbdata = NULL; -} + if (!CDSubsystemIsInitialized()) { + return SDL_FALSE; + } + if (!CDRomPath || (drive != 0)) { + SDL20_SetError("Invalid CD-ROM drive index"); + return SDL_FALSE; + } -/* !!! FIXME: these are just stubs for now, but Sam thinks that maybe these -were added at Loki for Heavy Gear 2's UI. They just make GL calls. */ -DECLSPEC void SDLCALL -SDL_GL_Lock(void) -{ - FIXME("write me"); + return SDL_TRUE; } -DECLSPEC void SDLCALL -SDL_GL_UpdateRects(int numrects, SDL12_Rect *rects) +DECLSPEC12 const char * SDLCALL +SDL_CDName(int drive) { - (void) numrects; - (void) rects; - FIXME("write me"); + return ValidCDDriveIndex(drive) ? CDRomPath : NULL; } -DECLSPEC void SDLCALL -SDL_GL_Unlock(void) +DECLSPEC12 SDL12_CD * SDLCALL +SDL_CDOpen(int drive) { - FIXME("write me"); -} + SDL12_CD *retval; + size_t alloclen; + char *fullpath; + drmp3 *mp3 = NULL; + Uint32 total_track_offset = 0; + SDL_bool has_audio = SDL_FALSE; + if (!ValidCDDriveIndex(drive)) { + return NULL; + } -/* SDL_GL_DisableContext and SDL_GL_EnableContext_Thread are not real SDL 1.2 - APIs, but some idTech4 games shipped with a custom SDL 1.2 build that added - these functions, to let them make a GL context current on a background thread, - so we supply them as well to be binary compatible for those games. */ + retval = (SDL12_CD *) SDL20_calloc(1, sizeof(SDL12_CD)); + if (!retval) { + SDL20_OutOfMemory(); + return NULL; + } -DECLSPEC void SDLCALL -SDL_GL_DisableContext(void) -{ - SDL20_GL_MakeCurrent(NULL, NULL); + alloclen = SDL20_strlen(CDRomPath) + 32; + fullpath = (char *) SDL20_malloc(alloclen); + if (fullpath == NULL) { + SDL20_free(retval); + SDL20_OutOfMemory(); + return NULL; + } + + mp3 = (drmp3 *) SDL20_malloc(sizeof (drmp3)); + if (!mp3) { + SDL20_free(fullpath); + SDL20_free(retval); + SDL20_OutOfMemory(); + return NULL; + } + + /* We would do a proper enumeration of this directory, but that + would need platform-specific code that SDL2 doesn't offer. + readdir() is surprisingly hard to do without a bunch of different + platform backends! We just open files until we fail to do so, + and then stop. */ + for (;;) { + SDL_RWops *rw; + drmp3_uint64 pcmframes; + drmp3_uint32 samplerate; + SDL12_CDtrack *track; + SDL_bool fake_data_track = SDL_FALSE; + int c; + char c0, c1; + + c = retval->numtracks + 1; + c0 = c / 10 + '0'; + c1 = c % 10 + '0'; + + /* note that fake data track files just need to exist, they can be empty files. */ + SDL20_snprintf(fullpath, alloclen, "%s%strack%c%c.dat", CDRomPath, DIRSEP, c0, c1); + rw = SDL20_RWFromFile(fullpath, "rb"); + if (rw) { /* fake data track. */ + fake_data_track = SDL_TRUE; + SDL20_RWclose(rw); + rw = NULL; + } else { + SDL20_snprintf(fullpath, alloclen, "%s%strack%c%c.mp3", CDRomPath, DIRSEP, c0, c1); + rw = SDL20_RWFromFile(fullpath, "rb"); + /* if there isn't a track 1 specified, pretend it's a data track, which matches most games' needs. */ + if (!rw && (c == 1)) { + fake_data_track = SDL_TRUE; + } + } + + if (!rw && !fake_data_track) { + break; /* ok, we're done looking for more. */ + } + track = &retval->track[retval->numtracks]; + if (!fake_data_track) { + SDL_assert(rw != NULL); + if (!drmp3_init(mp3, mp3_sdlrwops_read, mp3_sdlrwops_seek, mp3_sdlrwops_tell, NULL, rw, NULL)) { + SDL20_RWclose(rw); + rw = NULL; + fake_data_track = SDL_TRUE; /* congratulations, bogus or unsupported MP3, you just became data! */ + } else { + pcmframes = drmp3_get_pcm_frame_count(mp3); + samplerate = mp3->sampleRate; + FreeMp3(mp3); + rw = NULL; + + track->id = retval->numtracks; + track->type = 0; /* audio track. Data tracks are 4. */ + track->length = (Uint32) ((((double) pcmframes) / ((double) samplerate)) * CDAUDIO_FPS); + track->offset = total_track_offset; + total_track_offset += track->length; + + has_audio = SDL_TRUE; + } + } + + SDL_assert(rw == NULL); /* we should have dealt with this in all paths. */ + + if (fake_data_track) { + track->type = 4; /* data track. E.g.: quake's audio starts at track 2. */ + } + + retval->numtracks++; + + if (retval->numtracks == 99) { + break; /* max tracks you can have on an audio CD. */ + } + } + + if (!has_audio) { + retval->numtracks = 0; /* data-only */ + } + SDL20_free(mp3); + SDL20_free(fullpath); + + retval->id = 1; /* just to be non-zero, I guess. */ + retval->status = (retval->numtracks > 0) ? SDL12_CD_STOPPED : SDL12_CD_TRAYEMPTY; + + if (retval->numtracks > 0) { + SDL_AudioSpec want; + SDL20_zero(want); + want.freq = 44100; + want.format = AUDIO_F32SYS; + want.channels = 2; + want.samples = 4096; + + if (!OpenSDL2AudioDevice(&want)) { + retval->numtracks = 0; + retval->status = SDL12_CD_TRAYEMPTY; + } else { + /* Device is locked now, even if was opened and playing before. Set up some things. */ + SDL20_memcpy(&audio_cbdata->cdrom_format, &want, sizeof (SDL_AudioSpec)); + audio_cbdata->cdrom_opened = SDL_TRUE; + audio_cbdata->cdrom_status = SDL12_CD_STOPPED; + audio_cbdata->cdrom_pcm_frames_written = 0; + audio_cbdata->cdrom_cur_track = 0; + audio_cbdata->cdrom_cur_frame = 0; + SDL20_UnlockAudio(); + } + } + + CDRomDevice = retval; /* NULL API args use the last opened device. */ + + return retval; } -DECLSPEC void SDLCALL -SDL_GL_EnableContext_Thread(void) +static SDL12_CD * +ValidCDDevice(SDL12_CD *cdrom) { - const SDL_bool enable = (VideoGLContext20 && VideoWindow20)? SDL_TRUE : SDL_FALSE; - SDL20_GL_MakeCurrent(enable ? VideoWindow20 : NULL, enable ? VideoGLContext20 : NULL); -} + if (!CDSubsystemIsInitialized()) { + return NULL; + } else if (!cdrom) { + if (!CDRomDevice) { + SDL20_SetError("CD-ROM not opened"); + } else { + cdrom = CDRomDevice; + } + } + return cdrom; +} + + +DECLSPEC12 SDL12_CDstatus SDLCALL +SDL_CDStatus(SDL12_CD *cdrom) +{ + SDL12_CDstatus retval; + + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return SDL12_CD_ERROR; + } + + SDL20_LockAudio(); /* we update this during the audio callback. */ + if (audio_cbdata) { + cdrom->status = audio_cbdata->cdrom_status; + cdrom->cur_track = audio_cbdata->cdrom_cur_track; + cdrom->cur_frame = audio_cbdata->cdrom_cur_frame; + } + retval = cdrom->status; + SDL20_UnlockAudio(); + + return retval; +} + +static SDL_bool +LoadCDTrack(const int tracknum, drmp3 *mp3) +{ + const SDL_AudioSpec *have = &audio_cbdata->device_format; + SDL_RWops *rw = NULL; + const size_t alloclen = SDL20_strlen(CDRomPath) + 32; + char *fullpath = (char *) SDL_malloc(alloclen); + const int c = tracknum + 1; + char c0, c1; + + if (!fullpath) { + return SDL_FALSE; + } + + c0 = c / 10 + '0'; + c1 = c % 10 + '0'; + SDL20_snprintf(fullpath, alloclen, "%s%strack%c%c.mp3", CDRomPath, DIRSEP, c0, c1); + rw = SDL20_RWFromFile(fullpath, "rb"); + SDL20_free(fullpath); + + if (!rw) { + return SDL_FALSE; + } + + if (!drmp3_init(mp3, mp3_sdlrwops_read, mp3_sdlrwops_seek, mp3_sdlrwops_tell, NULL, rw, NULL)) { + SDL20_RWclose(rw); + return SDL_FALSE; + } + + if (!ResetAudioStream(&audio_cbdata->cdrom_stream, &audio_cbdata->cdrom_format, have, AUDIO_F32SYS, mp3->channels, mp3->sampleRate)) { + FreeMp3(mp3); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static int +StartCDAudioPlaying(SDL12_CD *cdrom, const int start_track, const int start_frame, const int ntracks, const int nframes) +{ + drmp3 *mp3 = (drmp3 *) SDL20_malloc(sizeof (drmp3)); + const SDL_bool loaded = mp3 ? LoadCDTrack(start_track, mp3) : SDL_FALSE; + const SDL_bool seeking = (loaded && (start_frame > 0))? SDL_TRUE : SDL_FALSE; + const drmp3_uint32 pcm_frame = seeking ? (Uint32) ((start_frame / 75.0) * (Sint32)mp3->sampleRate) : 0; + + if (!mp3) { + return SDL20_OutOfMemory(); + } + + if (seeking) { /* do seeking before handing off to the audio thread. */ + drmp3_seek_to_pcm_frame(mp3, pcm_frame); + } + + SDL20_LockAudio(); + if (audio_cbdata) { + cdrom->status = audio_cbdata->cdrom_status = loaded ? SDL12_CD_PLAYING : SDL12_CD_TRAYEMPTY; + audio_cbdata->cdrom_pcm_frames_written = (int) pcm_frame; + audio_cbdata->cdrom_cur_track = start_track; + audio_cbdata->cdrom_cur_frame = start_frame; + audio_cbdata->cdrom_stop_ntracks = ntracks; + audio_cbdata->cdrom_stop_nframes = nframes; + FreeMp3(&audio_cbdata->cdrom_mp3); + if (loaded) { + SDL20_memcpy(&audio_cbdata->cdrom_mp3, mp3, sizeof (drmp3)); + } + } + SDL20_UnlockAudio(); + + SDL20_free(mp3); + + return loaded ? 0 : SDL20_SetError("Failed to start CD track"); +} + + +DECLSPEC12 int SDLCALL +SDL_CDPlayTracks(SDL12_CD *cdrom, int start_track, int start_frame, int ntracks, int nframes) +{ + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + if (cdrom->status == SDL12_CD_TRAYEMPTY) { + return SDL20_SetError("Tray empty"); + } + if ((start_track < 0) || (start_track >= cdrom->numtracks)) { + return SDL20_SetError("Invalid start track"); + } + if ((start_frame < 0) || (((Uint32) start_frame) >= cdrom->track[start_track].length)) { + return SDL20_SetError("Invalid start frame"); + } + if ((ntracks < 0) || ((start_track + ntracks) >= cdrom->numtracks)) { + return SDL20_SetError("Invalid number of tracks"); + } + if ((nframes < 0) || (((Uint32) nframes) >= cdrom->track[start_track + ntracks].length)) { + return SDL20_SetError("Invalid number of frames"); + } + + if (!ntracks && !nframes) { + ntracks = cdrom->numtracks - start_track; + nframes = cdrom->track[cdrom->numtracks - 1].length; + } + + return StartCDAudioPlaying(cdrom, start_track, start_frame, ntracks, nframes); +} + +DECLSPEC12 int SDLCALL +SDL_CDPlay(SDL12_CD *cdrom, int start, int length) +{ + const Uint32 ui32start = (Uint32) start; + Uint32 remain = (Uint32) length; + int start_track = -1; + int start_frame = -1; + int ntracks = -1; + int nframes = -1; + int i; + + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + if (cdrom->status == SDL12_CD_TRAYEMPTY) { + return SDL20_SetError("Tray empty"); + } + if (start < 0) { + return SDL20_SetError("Invalid start"); + } + if (length < 0) { + return SDL20_SetError("Invalid length"); + } + + for (i = 0; i < cdrom->numtracks; i++) { + if ((ui32start >= cdrom->track[i].offset) && (ui32start < (cdrom->track[i].offset + cdrom->track[i].length))) { + start_track = i; + break; + } + } + + if (start_track == -1) { + return SDL20_SetError("Invalid start"); + } + + start_frame = start - cdrom->track[start_track].offset; + + if (remain < (cdrom->track[start_track].length - start_frame)) { + ntracks = 0; + nframes = remain; + remain = 0; + } else { + remain -= (cdrom->track[start_track].length - start_frame); + for (i = start_track + 1; i < cdrom->numtracks; i++) { + if (remain < cdrom->track[i].length) { + ntracks = i - start_track; + nframes = remain; + remain = 0; + break; + } + remain -= cdrom->track[i].length; + } + } + + if (remain) { + ntracks = (cdrom->numtracks - start_track) - 1; + nframes = cdrom->track[cdrom->numtracks - 1].length; + } + + return StartCDAudioPlaying(cdrom, start_track, start_frame, ntracks, nframes); +} + +DECLSPEC12 int SDLCALL +SDL_CDPause(SDL12_CD *cdrom) +{ + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + if (cdrom->status == SDL12_CD_TRAYEMPTY) { + return SDL20_SetError("Tray empty"); + } + + SDL20_LockAudio(); + if (audio_cbdata) { + if (audio_cbdata->cdrom_status == SDL12_CD_PLAYING) { + audio_cbdata->cdrom_status = SDL12_CD_PAUSED; + } + cdrom->status = audio_cbdata->cdrom_status; + } + SDL20_UnlockAudio(); + return 0; +} + +DECLSPEC12 int SDLCALL +SDL_CDResume(SDL12_CD *cdrom) +{ + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + if (cdrom->status == SDL12_CD_TRAYEMPTY) { + return SDL20_SetError("Tray empty"); + } + + SDL20_LockAudio(); + if (audio_cbdata) { + if (audio_cbdata->cdrom_status == SDL12_CD_PAUSED) { + audio_cbdata->cdrom_status = SDL12_CD_PLAYING; + } + cdrom->status = audio_cbdata->cdrom_status; + } + SDL20_UnlockAudio(); + return 0; +} + + +DECLSPEC12 int SDLCALL +SDL_CDStop(SDL12_CD *cdrom) +{ + SDL_RWops *oldrw = NULL; + + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + + SDL20_LockAudio(); + if (audio_cbdata) { + if ((audio_cbdata->cdrom_status == SDL12_CD_PLAYING) || (audio_cbdata->cdrom_status == SDL12_CD_PAUSED)) { + audio_cbdata->cdrom_status = SDL12_CD_STOPPED; + FreeMp3(&audio_cbdata->cdrom_mp3); + } + cdrom->status = audio_cbdata->cdrom_status; + } + SDL20_UnlockAudio(); + + if (oldrw) { + SDL20_RWclose(oldrw); + } + return 0; +} + +DECLSPEC12 int SDLCALL +SDL_CDEject(SDL12_CD *cdrom) +{ + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return -1; + } + + SDL20_LockAudio(); + if (audio_cbdata) { + audio_cbdata->cdrom_status = SDL12_CD_TRAYEMPTY; + FreeMp3(&audio_cbdata->cdrom_mp3); + } + cdrom->status = SDL12_CD_TRAYEMPTY; + SDL20_UnlockAudio(); + return 0; +} + +DECLSPEC12 void SDLCALL +SDL_CDClose(SDL12_CD *cdrom) +{ + if ((cdrom = ValidCDDevice(cdrom)) == NULL) { + return; + } + + SDL20_LockAudio(); + if (audio_cbdata) { + audio_cbdata->cdrom_status = SDL12_CD_STOPPED; + audio_cbdata->cdrom_opened = SDL_FALSE; + } + SDL20_UnlockAudio(); + + if (audio_cbdata) { + FreeMp3(&audio_cbdata->cdrom_mp3); + SDL20_FreeAudioStream(audio_cbdata->cdrom_stream); + audio_cbdata->cdrom_stream = NULL; + } + + CloseSDL2AudioDevice(); + + if (cdrom == CDRomDevice) { + CDRomDevice = NULL; + } + SDL20_free(cdrom); +} + + +static void +FakeCdRomAudioCallback(AudioCallbackWrapperData *data, Uint8 *stream, int len, const SDL_bool must_mix) +{ + Uint32 total_available, available = 0; + Uint32 channels, want_frames; + + if (data->cdrom_status != SDL12_CD_PLAYING) { + if (!must_mix) { + SDL20_memset(stream, data->device_format.silence, len); + } + return; + } + + SDL_assert((data->cdrom_status == SDL12_CD_PLAYING) && (data->cdrom_mp3.pUserData != NULL)); + + channels = data->cdrom_format.channels; + want_frames = data->cdrom_format.samples / channels; + + while ((!data->cdrom_mp3.atEnd) && (SDL20_AudioStreamAvailable(data->cdrom_stream) < len)) { + const Uint32 frames_read = (Uint32) drmp3_read_pcm_frames_f32(&data->cdrom_mp3, want_frames, (float *) data->mix_buffer); + const Uint32 bytes_read = frames_read * channels * sizeof (float); + SDL_assert(bytes_read <= data->cdrom_format.size); + if ((!bytes_read) || (SDL20_AudioStreamPut(data->cdrom_stream, data->mix_buffer, bytes_read) == -1)) { /* probably out of memory if failed */ + data->cdrom_mp3.atEnd = DRMP3_TRUE; /* force this to fail from now on */ + SDL20_AudioStreamFlush(data->cdrom_stream); /* make sure all we've put is available to get. */ + break; + } + } + + total_available = SDL20_AudioStreamAvailable(data->cdrom_stream); + available = total_available; + if (((Uint32) len) < available) { + available = (Uint32) len; + } + + if (available > 0) { + if (!must_mix) { + SDL20_AudioStreamGet(data->cdrom_stream, stream, available); + } else { + SDL20_AudioStreamGet(data->cdrom_stream, data->mix_buffer, available); + SDL20_MixAudioFormat(stream, data->mix_buffer, audio_cbdata->device_format.format, available, SDL_MIX_MAXVOLUME); + } + + data->cdrom_pcm_frames_written += (int) ((available / ((double) SDL_AUDIO_BITSIZE(data->device_format.format) / 8.0)) / data->device_format.channels); + data->cdrom_cur_frame = (int) ((((double)data->cdrom_pcm_frames_written) / ((double)data->device_format.freq)) * CDAUDIO_FPS); + if (data->cdrom_stop_ntracks == 0) { + if (data->cdrom_cur_frame >= data->cdrom_stop_nframes) { + data->cdrom_mp3.atEnd = DRMP3_TRUE; /* played all that was requested! */ + } + } + } + + if ((total_available == 0) && (data->cdrom_mp3.atEnd)) { /* mp3 is done for whatever reason */ + SDL_bool silence = ((!must_mix) && (available < ((Uint32) len))) ? SDL_TRUE : SDL_FALSE; /* silence any section we couldn't provide */ + + FreeMp3(&data->cdrom_mp3); + + if (data->cdrom_stop_ntracks > 0) { + data->cdrom_stop_ntracks--; + data->cdrom_pcm_frames_written = 0; + data->cdrom_cur_frame = 0; + + if (data->cdrom_status == SDL12_CD_PLAYING) { /* go on to next track? */ + const SDL_bool loaded = LoadCDTrack(++data->cdrom_cur_track, &data->cdrom_mp3); + if (!loaded) { + data->cdrom_status = SDL12_CD_STOPPED; + } else { /* let new track fill out rest of callback. */ + if (available < ((Uint32) len)) { + FakeCdRomAudioCallback(data, stream + available, len - available, must_mix); + silence = SDL_FALSE; + } + } + } + } else { + data->cdrom_status = SDL12_CD_STOPPED; /* played all that was requested! */ + } + + if (silence) { + SDL20_memset(stream + available, data->device_format.silence, len - available); + } + } +} + + +static void SDLCALL +AudioCallbackWrapper(void *userdata, Uint8 *stream, int len) +{ + AudioCallbackWrapperData *data = (AudioCallbackWrapperData *) userdata; + SDL_bool must_mix = SDL_FALSE; + + if (data->app_callback_opened && !SDL20_AtomicGet(&audio_callback_paused)) { + while (SDL20_AudioStreamAvailable(data->app_callback_stream) < len) { + SDL20_memset(data->mix_buffer, data->app_callback_format.silence, data->app_callback_format.size); /* SDL2 doesn't clear the stream before calling in here, but 1.2 expects it. */ + data->app_callback_format.callback(data->app_callback_format.userdata, data->mix_buffer, data->app_callback_format.size); + if (SDL20_AudioStreamPut(data->app_callback_stream, data->mix_buffer, data->app_callback_format.size) == -1) { /* probably out of memory if failed */ + break; /* this will make the AudioStreamGet call fail. */ + } + } + if (SDL20_AudioStreamGet(data->app_callback_stream, stream, len) != len) { + SDL20_memset(stream, data->device_format.silence, len); + } else { + must_mix = SDL_TRUE; + } + } + + FakeCdRomAudioCallback(data, stream, len, must_mix); +} + + +static SDL_bool +ResetAudioStream(SDL_AudioStream **_stream, SDL_AudioSpec *spec, const SDL_AudioSpec *to, const SDL_AudioFormat fromfmt, const Uint8 fromchannels, const int fromfreq) +{ + if ((!*_stream) || (spec->channels != fromchannels) || (spec->format != fromfmt) || (spec->freq != fromfreq)) { + SDL20_FreeAudioStream(*_stream); + *_stream = SDL20_NewAudioStream(fromfmt, fromchannels, fromfreq, to->format, to->channels, to->freq); + if (!*_stream) { + return SDL_FALSE; + } + + spec->channels = fromchannels; + spec->format = fromfmt; + spec->freq = fromfreq; + spec->size = spec->samples * spec->channels * (SDL_AUDIO_BITSIZE(spec->format) / 8); + + if (audio_cbdata->mixbuflen < spec->size) { + void *ptr = SDL20_realloc(audio_cbdata->mix_buffer, spec->size); + if (!ptr) { + SDL20_FreeAudioStream(*_stream); + *_stream = NULL; + SDL20_OutOfMemory(); + return SDL_FALSE; + } + audio_cbdata->mixbuflen = spec->size; + audio_cbdata->mix_buffer = (Uint8 *) ptr; + } + } + return SDL_TRUE; +} + +static SDL_bool +OpenSDL2AudioDevice(SDL_AudioSpec *appwant) +{ + SDL_AudioSpec devwant; + + /* note that 0x80 isn't perfect silence for U16 formats, but we only have one byte that is used for memset() calls, so it has to do. SDL2 has the same bug. */ + appwant->silence = SDL_AUDIO_ISSIGNED(appwant->format) ? 0x00 : 0x80; + appwant->size = appwant->samples * appwant->channels * (SDL_AUDIO_BITSIZE(appwant->format) / 8); + + if (audio_cbdata != NULL) { /* device is already open. */ + SDL20_LockAudio(); /* Device is already at acceptable parameters, just pause it for further setup by caller. */ + return SDL_TRUE; + } + + audio_cbdata = (AudioCallbackWrapperData *) SDL20_calloc(1, sizeof (AudioCallbackWrapperData)); + if (!audio_cbdata) { + SDL20_OutOfMemory(); + return SDL_FALSE; + } + + /* Two things use the audio device: the app, through 1.2's SDL_OpenAudio, + and the fake CD-ROM device. Either can open the device, and both write + to SDL_AudioStreams to buffer and convert data. We open the device + in a format that accommodates both inputs. + + In case the app asks for something really low-quality--an old game + playing 8-bit mono audio at 8000Hz or whatever--we force the audio + hardware to something better, in case the app _also_ wants to play + CD audio, so we can get good quality out of that without reopening + the device. We have to be able to buffer and convert between the + app's needs and SDL2's anyhow, so this isn't a big deal. If they + ask for better than CD quality, we'll allow it and upsample any + CD audio that is played. + + If the CD-ROM is opened first, and the app wants better-than-CD + quality later, there's not much we can do, it'll have to + downsample, but I suspect this is rare, and the audio will + still be good enough. */ + + SDL20_memcpy(&devwant, appwant, sizeof (SDL_AudioSpec)); + devwant.callback = AudioCallbackWrapper; + devwant.userdata = audio_cbdata; + devwant.freq = SDL_max(devwant.freq, 44100); + devwant.channels = SDL_max(devwant.channels, 2); + if (SDL_AUDIO_BITSIZE(devwant.format) < 16) { + devwant.format = AUDIO_S16SYS; + } + + if (SDL20_OpenAudio(&devwant, &audio_cbdata->device_format) == -1) { + SDL_free(audio_cbdata); + audio_cbdata = NULL; + return SDL_FALSE; + } + + SDL20_LockAudio(); + SDL20_PauseAudio(0); /* always unpause, but caller will unlock after finalizing setup. */ + + return SDL_TRUE; +} + +static int +CloseSDL2AudioDevice(void) +{ + SDL_bool close_sdl2_device; + + SDL20_LockAudio(); + close_sdl2_device = (audio_cbdata && !audio_cbdata->app_callback_opened && !audio_cbdata->cdrom_opened) ? SDL_TRUE : SDL_FALSE; + SDL20_UnlockAudio(); + + if (close_sdl2_device) { + SDL20_CloseAudio(); + SDL20_FreeAudioStream(audio_cbdata->app_callback_stream); + SDL20_FreeAudioStream(audio_cbdata->cdrom_stream); + SDL20_free(audio_cbdata->mix_buffer); + SDL20_free(audio_cbdata); + audio_cbdata = NULL; + } + + return -1; +} + + +DECLSPEC12 int SDLCALL +SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained) +{ + SDL_bool already_opened; + + /* SDL_OpenAudio() will init the subsystem for you if necessary, yuck. */ + if ((InitializedSubsystems20 & SDL_INIT_AUDIO) != SDL_INIT_AUDIO) { + if (SDL_InitSubSystem(SDL12_INIT_AUDIO) < 0) { + return -1; + } + } + + /* SDL2 uses a NULL callback to mean "we plan to use SDL_QueueAudio()" */ + if (want && (want->callback == NULL)) { + return SDL20_SetError("Callback can't be NULL"); + } + + SDL20_LockAudio(); + already_opened = (audio_cbdata && audio_cbdata->app_callback_opened) ? SDL_TRUE : SDL_FALSE; + SDL20_UnlockAudio(); + if (already_opened) { + return SDL20_SetError("Audio device already opened"); + } + + if (!want->format) { + const char *env = SDL12COMPAT_getenv_unsafe("SDL_AUDIO_FORMAT"); /* SDL 1.2 checks this. */ + if (env != NULL) { + if (SDL20_strcmp(env, "U8") == 0) { want->format = AUDIO_U8; } + else if (SDL20_strcmp(env, "S8") == 0) { want->format = AUDIO_S8; } + else if (SDL20_strcmp(env, "U16") == 0) { want->format = AUDIO_U16SYS; } + else if (SDL20_strcmp(env, "S16") == 0) { want->format = AUDIO_S16SYS; } + else if (SDL20_strcmp(env, "U16LSB") == 0) { want->format = AUDIO_U16LSB; } + else if (SDL20_strcmp(env, "S16LSB") == 0) { want->format = AUDIO_S16LSB; } + else if (SDL20_strcmp(env, "U16MSB") == 0) { want->format = AUDIO_U16MSB; } + else if (SDL20_strcmp(env, "S16MSB") == 0) { want->format = AUDIO_S16MSB; } + else if (SDL20_strcmp(env, "U16SYS") == 0) { want->format = AUDIO_U16SYS; } + else if (SDL20_strcmp(env, "S16SYS") == 0) { want->format = AUDIO_S16SYS; } + } + if (!want->format) { + want->format = AUDIO_S16SYS; + } + } + + if (!want->freq) { + const char *env = SDL12COMPAT_getenv_unsafe("SDL_AUDIO_FREQUENCY"); /* SDL 1.2 checks this. */ + if (env != NULL) { + want->freq = SDL20_atoi(env); + } + if (!want->freq) { + want->freq = 22050; + } + want->samples = 0; + } + + if (!want->channels) { + const char *env = SDL12COMPAT_getenv_unsafe("SDL_AUDIO_CHANNELS"); /* SDL 1.2 checks this. */ + if (env != NULL) { + want->channels = SDL20_atoi(env); + } + if (!want->channels) { + want->channels = 2; + } + } + + if (!want->samples) { + const char *env = SDL12COMPAT_getenv_unsafe("SDL_AUDIO_SAMPLES"); /* SDL 1.2 checks this. */ + if (env != NULL) { + want->samples = SDL20_atoi(env); + } + if (!want->samples) { + const Uint32 samp = (want->freq / 1000) * 46; /* ~46 ms */ + Uint32 pow2 = 1; + while (pow2 < samp) { + pow2 <<= 1; + } + want->samples = pow2; + } + } + + /* the app always passes callback data through an SDL_AudioStream, since it + has to share with the fake CD-ROM support. This also avoids the risk of + getting an incompatible device configuration from SDL2. As such, + the app always gets the format it requests. */ + if (!OpenSDL2AudioDevice(want)) { + return -1; + } + + /* Device is locked now, unconditionally. Set up some things. */ + + if (obtained) { /* the app always gets the format it requests */ + SDL20_memcpy(obtained, want, sizeof (SDL_AudioSpec)); + } + + SDL20_memcpy(&audio_cbdata->app_callback_format, want, sizeof (SDL_AudioSpec)); + SDL20_AtomicSet(&audio_callback_paused, SDL_TRUE); /* app callback always starts paused after open. */ + + SDL_assert(audio_cbdata->app_callback_stream == NULL); + if (!ResetAudioStream(&audio_cbdata->app_callback_stream, &audio_cbdata->app_callback_format, &audio_cbdata->device_format, want->format, want->channels, want->freq)) { + SDL20_UnlockAudio(); /* make sure CD audio doesn't hang if it's playing. */ + return CloseSDL2AudioDevice(); /* will stay open if CD audio is still playing, cleans up otherwise. */ + } + + audio_cbdata->app_callback_opened = SDL_TRUE; + + SDL20_UnlockAudio(); /* we're off and going. */ + + return 0; +} + +DECLSPEC12 void SDLCALL +SDL_PauseAudio(int pause_on) +{ + SDL20_AtomicSet(&audio_callback_paused, pause_on ? SDL_TRUE : SDL_FALSE); +} + +DECLSPEC12 SDL_AudioStatus SDLCALL +SDL_GetAudioStatus(void) +{ + SDL_AudioStatus retval = SDL_AUDIO_STOPPED; + SDL20_LockAudio(); + if (audio_cbdata && audio_cbdata->app_callback_opened) { + retval = SDL20_AtomicGet(&audio_callback_paused) ? SDL_AUDIO_PAUSED : SDL_AUDIO_PLAYING; + } + SDL20_UnlockAudio(); + return retval; +} + +DECLSPEC12 void SDLCALL +SDL_MixAudio(Uint8 *dst, const Uint8 *src, Uint32 len, int volume) +{ + SDL_AudioFormat fmt; + + if (volume == 0) { + return; /* nothing to do. */ + } + + /* in 1.2, if not the subsystem isn't initialized _at all_, it forces + format to AUDIO_S16. If it's initialized but the device isn't opened, + you get a format of zero and it returns an error without mixing + anything. */ + SDL20_LockAudio(); + if ((InitializedSubsystems20 & SDL_INIT_AUDIO) != SDL_INIT_AUDIO) { + fmt = AUDIO_S16; /* to quote 1.2: "HACK HACK HACK" */ + } else if (!audio_cbdata || !audio_cbdata->app_callback_opened) { + fmt = 0; /* this will fail, but drop the lock first. */ + } else { + fmt = audio_cbdata->app_callback_format.format; + } + SDL20_UnlockAudio(); + + if (fmt == 0) { + SDL_SetError("SDL_MixAudio(): unknown audio format"); /* this is the exact error 1.2 reports for this. */ + } else { + SDL20_MixAudioFormat(dst, src, fmt, len, volume); + } +} + + +DECLSPEC12 void SDLCALL +SDL_CloseAudio(void) +{ + SDL20_LockAudio(); + if (audio_cbdata) { + audio_cbdata->app_callback_opened = SDL_FALSE; + SDL20_FreeAudioStream(audio_cbdata->app_callback_stream); + audio_cbdata->app_callback_stream = NULL; + } + SDL20_UnlockAudio(); + + CloseSDL2AudioDevice(); +} + +static SDL_AudioCVT * +AudioCVT12to20(const SDL12_AudioCVT *cvt12, SDL_AudioCVT *cvt20) +{ + SDL20_zerop(cvt20); + cvt20->needed = cvt12->needed; + cvt20->src_format = cvt12->src_format; + cvt20->dst_format = cvt12->dst_format; + cvt20->rate_incr = cvt12->rate_incr; + cvt20->buf = cvt12->buf; + cvt20->len = cvt12->len; + cvt20->len_cvt = cvt12->len_cvt; + cvt20->len_mult = cvt12->len_mult; + cvt20->len_ratio = cvt12->len_ratio; + SDL20_memcpy(cvt20->filters, cvt12->filters, sizeof (cvt12->filters)); + cvt20->filter_index = cvt12->filter_index; + return cvt20; +} + +static SDL12_AudioCVT * +AudioCVT20to12(const SDL_AudioCVT *cvt20, SDL12_AudioCVT *cvt12) +{ + SDL20_zerop(cvt12); + cvt12->needed = cvt20->needed; + cvt12->src_format = cvt20->src_format; + cvt12->dst_format = cvt20->dst_format; + cvt12->rate_incr = cvt20->rate_incr; + cvt12->buf = cvt20->buf; + cvt12->len = cvt20->len; + cvt12->len_cvt = cvt20->len_cvt; + cvt12->len_mult = cvt20->len_mult; + cvt12->len_ratio = cvt20->len_ratio; + SDL20_memcpy(cvt12->filters, cvt20->filters, sizeof (cvt20->filters)); + cvt12->filter_index = cvt20->filter_index; + return cvt12; +} + +static void SDLCALL +CompatibilityCVT_RunStream(SDL12_AudioCVT *cvt12, Uint16 format) +{ + const size_t channel_mash = (size_t) cvt12->filters[SDL_arraysize(cvt12->filters) - 1]; + const Uint8 src_channels = (Uint8) (channel_mash & 0xFF); + const Uint8 dst_channels = (Uint8) ((channel_mash >> 8) & 0xFF); + + /* use an audiostream, so we can allocate a dynamic buffer for the work, even if the app screwed up their allocation. */ + SDL_AudioStream *stream = SDL20_NewAudioStream(format, src_channels, 44100, cvt12->dst_format, dst_channels, 44100); /* don't resample here! */ + if (stream == NULL) { + return; /* oh well. */ + } + + if ((SDL20_AudioStreamPut(stream, cvt12->buf, cvt12->len_cvt) == -1) || (SDL20_AudioStreamFlush(stream) == -1)) { /* probably out of memory if failed. */ + SDL20_FreeAudioStream(stream); + return; /* oh well. */ + } + + cvt12->len_cvt = SDL20_AudioStreamAvailable(stream); + SDL20_AudioStreamGet(stream, cvt12->buf, cvt12->len_cvt); + SDL20_FreeAudioStream(stream); + + if (cvt12->filters[++cvt12->filter_index]) { + cvt12->filters[cvt12->filter_index](cvt12, cvt12->dst_format); + } +} + +/* this is an extremely low-quality resampler: it only doubles or halves the + sample rate and offers no interpolation...but it's also how SDL 1.2 did it. + Notably: it can resample in-place, so this avoids bugs in apps that don't + set up SDL_AudioCVT's buffer correctly, or expect this weird 1.2 behavior + for whatever reason. */ +static void SDLCALL +CompatibilityCVT_Resampler(SDL12_AudioCVT *cvt12, Uint16 format) +{ + const int bitsize = (int) SDL_AUDIO_BITSIZE(format); + int i; + + SDL_assert((bitsize == 8) || (bitsize == 16)); /* there were no 32-bit audio types in 1.2. */ + + if (cvt12->rate_incr < 1.0) { /* upsampling */ + /*printf("2x Upsampling!\n");*/ + #define DO_RESAMPLE(typ) \ + const typ *src = (const typ *) (cvt12->buf + cvt12->len_cvt); \ + typ *dst = (typ *) (cvt12->buf + (cvt12->len_cvt * 2)); \ + for (i = cvt12->len_cvt / sizeof (typ); i; i--) { \ + const typ sample = *(--src); \ + dst -= 2; \ + dst[0] = dst[1] = sample; \ + } + if (bitsize == 8) { + DO_RESAMPLE(Uint8); + } else if (bitsize == 16) { + DO_RESAMPLE(Uint16); + } + #undef DO_RESAMPLE + cvt12->len_cvt *= 2; + } else { /* downsampling. */ + /*printf("2x Downsampling!\n");*/ + #define DO_RESAMPLE(typ) \ + const typ *src = (const typ *) cvt12->buf; \ + typ *dst = (typ *) cvt12->buf; \ + for (i = cvt12->len_cvt / (sizeof (typ) * 2); i; i--, src += 2) { \ + *(dst++) = *src; \ + } + if (bitsize == 8) { + DO_RESAMPLE(Uint8); + } else if (bitsize == 16) { + DO_RESAMPLE(Uint16); + } + #undef DO_RESAMPLE + cvt12->len_cvt /= 2; + } + + if (cvt12->filters[++cvt12->filter_index]) { + cvt12->filters[cvt12->filter_index](cvt12, format); + } +} + +DECLSPEC12 int SDLCALL +SDL_BuildAudioCVT(SDL12_AudioCVT *cvt12, Uint16 src_format, Uint8 src_channels, int src_rate, Uint16 dst_format, Uint8 dst_channels, int dst_rate) +{ + int retval = 0; + + SDL20_zerop(cvt12); /* SDL 1.2 derefences cvt12 without checking for NULL */ + + if (!WantCompatibilityAudioCVT) { + SDL_AudioCVT cvt20; + retval = SDL20_BuildAudioCVT(&cvt20, src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); + AudioCVT20to12(&cvt20, cvt12); + } else { + const size_t channel_mash = ((size_t) src_channels) | (((size_t) dst_channels) << 8); + + if ((src_format == dst_format) && (src_channels == dst_channels) && (src_rate == dst_rate)) { + return 0; /* no conversion needed. */ + } + + cvt12->needed = 1; + cvt12->len_mult = 1; + cvt12->len_ratio = 1.0; + cvt12->src_format = src_format; + cvt12->dst_format = dst_format; + + if ((src_format != dst_format) || (src_channels != dst_channels)) { + if (src_format != dst_format) { + /* there are only 8 and 16 bit formats in SDL 1.2 */ + if (SDL_AUDIO_BITSIZE(src_format) < SDL_AUDIO_BITSIZE(dst_format)) { + cvt12->len_mult *= 2; + cvt12->len_ratio *= 2.0; + } else { + cvt12->len_ratio /= 2.0; + } + } + + /* SDL 1.2 only supported 1, 2, 4, and 6 channels, and would fail to convert between 4 and 6 at all. :O */ + if (src_channels < dst_channels) { + const int diff = (int) (dst_channels / src_channels); + cvt12->len_mult *= diff; + cvt12->len_ratio *= (double) diff; + } else if (src_channels > dst_channels) { + const int diff = (int) (src_channels / dst_channels); + cvt12->len_ratio /= (double) diff; + } + + cvt12->filters[cvt12->filter_index++] = CompatibilityCVT_RunStream; + cvt12->filters[SDL_arraysize(cvt12->filters) - 1] = (SDL12_AudioCVTFilter) channel_mash; /* cheat by hiding this info at end of array. */ + } + + if (src_rate != dst_rate) { + Uint32 hi_rate = src_rate; + Uint32 lo_rate = dst_rate; + int len_mult = 1; + double len_ratio = 0.5; + + if (src_rate < dst_rate) { /* flip everything. */ + hi_rate = dst_rate; + lo_rate = src_rate; + len_mult = 2; + len_ratio = 2.0; + } + + while (((lo_rate * 2) / 100) <= (hi_rate / 100)) { /* this is what SDL 1.2 does. *shrug* */ + if (cvt12->filter_index >= (int)(SDL_arraysize(cvt12->filters) - 2)) { + return SDL20_SetError("Too many conversion filters needed"); + } + cvt12->filters[cvt12->filter_index++] = CompatibilityCVT_Resampler; + cvt12->len_mult *= len_mult; + lo_rate *= 2; + cvt12->len_ratio *= len_ratio; + } + + cvt12->rate_incr = ((double) src_rate) / ((double) dst_rate); + } + + retval = 1; /* conversion definitely needed. */ + } + + return retval; +} + +DECLSPEC12 int SDLCALL +SDL_ConvertAudio(SDL12_AudioCVT *cvt12) +{ + int retval = 0; + + if (!cvt12->buf) { /* neither SDL 1.2 nor 2.0 makes sure cvt12 isn't NULL here. :/ */ + retval = SDL20_SetError("No buffer allocated for conversion"); + } else if (!WantCompatibilityAudioCVT) { + SDL_AudioCVT cvt20; + retval = SDL20_ConvertAudio(AudioCVT12to20(cvt12, &cvt20)); + AudioCVT20to12(&cvt20, cvt12); + } else { + cvt12->len_cvt = cvt12->len; + cvt12->filter_index = 0; + if (cvt12->filters[0]) { + cvt12->filters[0](cvt12, cvt12->src_format); + } + } + + return retval; +} + + +/* SDL_GL_DisableContext and SDL_GL_EnableContext_Thread are not real SDL 1.2 + APIs, but some idTech4 games shipped with a custom SDL 1.2 build that added + these functions, to let them make a GL context current on a background thread, + so we supply them as well to be binary compatible for those games. */ + +DECLSPEC12 void SDLCALL +SDL_GL_DisableContext(void) +{ + SDL20_GL_MakeCurrent(NULL, NULL); +} + +DECLSPEC12 void SDLCALL +SDL_GL_EnableContext_Thread(void) +{ + const SDL_bool enable = (VideoGLContext20 && VideoWindow20) ? SDL_TRUE : SDL_FALSE; + SDL20_GL_MakeCurrent(enable ? VideoWindow20 : NULL, enable ? VideoGLContext20 : NULL); +} + + +/* X11_KeyToUnicode is an internal function in the SDL 1.2 x11 backend that some Linux + software (older versions of the Torque Engine, for example) would call directly, so + we're supplying an extremely naive implementation here. Apps using this should be + fixed if possible, and this implementation is generally incorrect but hopefully + enough to get apps to limp along. + As this isn't X11-specific, we supply it globally, so x11 binaries can transition + to Wayland, and if there's some wildly-misbuilt win32 software, they can call it + too. :) */ +#if !(defined(_WIN32) || defined(__OS2__)) /* #if defined(__unix__) || defined(__APPLE__) ?? */ +DECLSPEC12 Uint16 SDLCALL +X11_KeyToUnicode(SDL12Key key, SDL12Mod mod) +{ + if (((int) key) >= 127) { + return 0; + } + if ((key >= SDLK12_a) && (key <= SDLK12_z)) { + const int shifted = ((mod & (KMOD12_LSHIFT|KMOD12_RSHIFT)) != 0) ? 1 : 0; + int capital = ((mod & KMOD12_CAPS) != 0) ? 1 : 0; + if (shifted) { + capital = !capital; + } + return (Uint16) ((capital ? 'A' : 'a') + (key - SDLK12_a)); + } + + return (Uint16) key; +} +#endif #ifdef __cplusplus } diff --git a/src/SDL12_compat_objc.m b/src/SDL12_compat_objc.m index 5c9031425..80906e710 100644 --- a/src/SDL12_compat_objc.m +++ b/src/SDL12_compat_objc.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,10 +21,7 @@ /* This file contains some macOS-specific support code */ -#define __BUILDING_SDL12_COMPAT__ 1 -#include "SDL.h" - -#ifdef __MACOSX__ +#ifdef __APPLE__ #include #if __GNUC__ >= 4 @@ -49,6 +46,8 @@ SDL12_PRIVATE void sdl12_compat_macos_init(void) SDL12_PRIVATE void error_dialog(const char *errorMsg) { + NSAlert *alert; + if (NSApp == nil) { ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); @@ -58,12 +57,26 @@ SDL12_PRIVATE void error_dialog(const char *errorMsg) } [NSApp activateIgnoringOtherApps:YES]; - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert = [[[NSAlert alloc] init] autorelease]; alert.alertStyle = NSAlertStyleCritical; alert.messageText = @"Fatal error! Cannot continue!"; alert.informativeText = [NSString stringWithCString:errorMsg encoding:NSASCIIStringEncoding]; [alert runModal]; } + +SDL12_PRIVATE void SDL12COMPAT_NSLog(const char *prefix, const char *text) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; { + NSString *nsText = [NSString stringWithUTF8String:text]; + if (prefix && *prefix) { + NSString *nsPrefix = [NSString stringWithUTF8String:prefix]; + NSLog(@"%@%@", nsPrefix, nsText); + } else { + NSLog(@"%@", nsText); + } + } + [pool drain]; +} #endif /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/SDL20_include_wrapper.h b/src/SDL20_include_wrapper.h index f9d6c35e1..e53cc8094 100644 --- a/src/SDL20_include_wrapper.h +++ b/src/SDL20_include_wrapper.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -28,93 +28,907 @@ #ifndef _INCL_SDL20_INCLUDE_WRAPPER_H_ #define _INCL_SDL20_INCLUDE_WRAPPER_H_ -#define SDL_GetVersion IGNORE_THIS_VERSION_OF_SDL_GetVersion +#define SDL_SetError IGNORE_THIS_VERSION_OF_SDL_SetError #define SDL_Log IGNORE_THIS_VERSION_OF_SDL_Log +#define SDL_LogVerbose IGNORE_THIS_VERSION_OF_SDL_LogVerbose +#define SDL_LogDebug IGNORE_THIS_VERSION_OF_SDL_LogDebug +#define SDL_LogInfo IGNORE_THIS_VERSION_OF_SDL_LogInfo +#define SDL_LogWarn IGNORE_THIS_VERSION_OF_SDL_LogWarn +#define SDL_LogError IGNORE_THIS_VERSION_OF_SDL_LogError +#define SDL_LogCritical IGNORE_THIS_VERSION_OF_SDL_LogCritical +#define SDL_LogMessage IGNORE_THIS_VERSION_OF_SDL_LogMessage +#define SDL_sscanf IGNORE_THIS_VERSION_OF_SDL_sscanf +#define SDL_snprintf IGNORE_THIS_VERSION_OF_SDL_snprintf +#define SDL_CreateThread IGNORE_THIS_VERSION_OF_SDL_CreateThread +#define SDL_RWFromFP IGNORE_THIS_VERSION_OF_SDL_RWFromFP +#define SDL_RegisterApp IGNORE_THIS_VERSION_OF_SDL_RegisterApp +#define SDL_UnregisterApp IGNORE_THIS_VERSION_OF_SDL_UnregisterApp +#define SDL_Direct3D9GetAdapterIndex IGNORE_THIS_VERSION_OF_SDL_Direct3D9GetAdapterIndex +#define SDL_RenderGetD3D9Device IGNORE_THIS_VERSION_OF_SDL_RenderGetD3D9Device +#define SDL_iPhoneSetAnimationCallback IGNORE_THIS_VERSION_OF_SDL_iPhoneSetAnimationCallback +#define SDL_iPhoneSetEventPump IGNORE_THIS_VERSION_OF_SDL_iPhoneSetEventPump +#define SDL_AndroidGetJNIEnv IGNORE_THIS_VERSION_OF_SDL_AndroidGetJNIEnv +#define SDL_AndroidGetActivity IGNORE_THIS_VERSION_OF_SDL_AndroidGetActivity +#define SDL_AndroidGetInternalStoragePath IGNORE_THIS_VERSION_OF_SDL_AndroidGetInternalStoragePath +#define SDL_AndroidGetExternalStorageState IGNORE_THIS_VERSION_OF_SDL_AndroidGetExternalStorageState +#define SDL_AndroidGetExternalStoragePath IGNORE_THIS_VERSION_OF_SDL_AndroidGetExternalStoragePath +#define SDL_Init IGNORE_THIS_VERSION_OF_SDL_Init +#define SDL_InitSubSystem IGNORE_THIS_VERSION_OF_SDL_InitSubSystem +#define SDL_QuitSubSystem IGNORE_THIS_VERSION_OF_SDL_QuitSubSystem +#define SDL_WasInit IGNORE_THIS_VERSION_OF_SDL_WasInit +#define SDL_Quit IGNORE_THIS_VERSION_OF_SDL_Quit #define SDL_ReportAssertion IGNORE_THIS_VERSION_OF_SDL_ReportAssertion +#define SDL_SetAssertionHandler IGNORE_THIS_VERSION_OF_SDL_SetAssertionHandler +#define SDL_GetAssertionReport IGNORE_THIS_VERSION_OF_SDL_GetAssertionReport +#define SDL_ResetAssertionReport IGNORE_THIS_VERSION_OF_SDL_ResetAssertionReport +#define SDL_AtomicTryLock IGNORE_THIS_VERSION_OF_SDL_AtomicTryLock +#define SDL_AtomicLock IGNORE_THIS_VERSION_OF_SDL_AtomicLock +#define SDL_AtomicUnlock IGNORE_THIS_VERSION_OF_SDL_AtomicUnlock +#define SDL_AtomicCAS IGNORE_THIS_VERSION_OF_SDL_AtomicCAS +#define SDL_AtomicSet IGNORE_THIS_VERSION_OF_SDL_AtomicSet +#define SDL_AtomicGet IGNORE_THIS_VERSION_OF_SDL_AtomicGet +#define SDL_AtomicAdd IGNORE_THIS_VERSION_OF_SDL_AtomicAdd +#define SDL_AtomicCASPtr IGNORE_THIS_VERSION_OF_SDL_AtomicCASPtr +#define SDL_AtomicSetPtr IGNORE_THIS_VERSION_OF_SDL_AtomicSetPtr +#define SDL_AtomicGetPtr IGNORE_THIS_VERSION_OF_SDL_AtomicGetPtr +#define SDL_GetNumAudioDrivers IGNORE_THIS_VERSION_OF_SDL_GetNumAudioDrivers +#define SDL_GetAudioDriver IGNORE_THIS_VERSION_OF_SDL_GetAudioDriver +#define SDL_AudioInit IGNORE_THIS_VERSION_OF_SDL_AudioInit +#define SDL_AudioQuit IGNORE_THIS_VERSION_OF_SDL_AudioQuit +#define SDL_GetCurrentAudioDriver IGNORE_THIS_VERSION_OF_SDL_GetCurrentAudioDriver +#define SDL_OpenAudio IGNORE_THIS_VERSION_OF_SDL_OpenAudio +#define SDL_GetNumAudioDevices IGNORE_THIS_VERSION_OF_SDL_GetNumAudioDevices +#define SDL_GetAudioDeviceName IGNORE_THIS_VERSION_OF_SDL_GetAudioDeviceName +#define SDL_OpenAudioDevice IGNORE_THIS_VERSION_OF_SDL_OpenAudioDevice +#define SDL_GetAudioStatus IGNORE_THIS_VERSION_OF_SDL_GetAudioStatus +#define SDL_GetAudioDeviceStatus IGNORE_THIS_VERSION_OF_SDL_GetAudioDeviceStatus +#define SDL_PauseAudio IGNORE_THIS_VERSION_OF_SDL_PauseAudio +#define SDL_PauseAudioDevice IGNORE_THIS_VERSION_OF_SDL_PauseAudioDevice +#define SDL_LoadWAV_RW IGNORE_THIS_VERSION_OF_SDL_LoadWAV_RW +#define SDL_FreeWAV IGNORE_THIS_VERSION_OF_SDL_FreeWAV +#define SDL_BuildAudioCVT IGNORE_THIS_VERSION_OF_SDL_BuildAudioCVT +#define SDL_ConvertAudio IGNORE_THIS_VERSION_OF_SDL_ConvertAudio +#define SDL_MixAudio IGNORE_THIS_VERSION_OF_SDL_MixAudio +#define SDL_MixAudioFormat IGNORE_THIS_VERSION_OF_SDL_MixAudioFormat +#define SDL_LockAudio IGNORE_THIS_VERSION_OF_SDL_LockAudio +#define SDL_LockAudioDevice IGNORE_THIS_VERSION_OF_SDL_LockAudioDevice +#define SDL_UnlockAudio IGNORE_THIS_VERSION_OF_SDL_UnlockAudio +#define SDL_UnlockAudioDevice IGNORE_THIS_VERSION_OF_SDL_UnlockAudioDevice +#define SDL_CloseAudio IGNORE_THIS_VERSION_OF_SDL_CloseAudio +#define SDL_CloseAudioDevice IGNORE_THIS_VERSION_OF_SDL_CloseAudioDevice +#define SDL_SetClipboardText IGNORE_THIS_VERSION_OF_SDL_SetClipboardText +#define SDL_GetClipboardText IGNORE_THIS_VERSION_OF_SDL_GetClipboardText +#define SDL_HasClipboardText IGNORE_THIS_VERSION_OF_SDL_HasClipboardText +#define SDL_GetCPUCount IGNORE_THIS_VERSION_OF_SDL_GetCPUCount +#define SDL_GetCPUCacheLineSize IGNORE_THIS_VERSION_OF_SDL_GetCPUCacheLineSize +#define SDL_HasRDTSC IGNORE_THIS_VERSION_OF_SDL_HasRDTSC +#define SDL_HasAltiVec IGNORE_THIS_VERSION_OF_SDL_HasAltiVec +#define SDL_HasMMX IGNORE_THIS_VERSION_OF_SDL_HasMMX +#define SDL_Has3DNow IGNORE_THIS_VERSION_OF_SDL_Has3DNow +#define SDL_HasSSE IGNORE_THIS_VERSION_OF_SDL_HasSSE +#define SDL_HasSSE2 IGNORE_THIS_VERSION_OF_SDL_HasSSE2 +#define SDL_HasSSE3 IGNORE_THIS_VERSION_OF_SDL_HasSSE3 +#define SDL_HasSSE41 IGNORE_THIS_VERSION_OF_SDL_HasSSE41 +#define SDL_HasSSE42 IGNORE_THIS_VERSION_OF_SDL_HasSSE42 +#define SDL_GetSystemRAM IGNORE_THIS_VERSION_OF_SDL_GetSystemRAM +#define SDL_GetError IGNORE_THIS_VERSION_OF_SDL_GetError +#define SDL_ClearError IGNORE_THIS_VERSION_OF_SDL_ClearError #define SDL_Error IGNORE_THIS_VERSION_OF_SDL_Error -#define SDL_SetError IGNORE_THIS_VERSION_OF_SDL_SetError -#define SDL_PollEvent IGNORE_THIS_VERSION_OF_SDL_PollEvent -#define SDL_PushEvent IGNORE_THIS_VERSION_OF_SDL_PushEvent -#define SDL_EventState IGNORE_THIS_VERSION_OF_SDL_EventState +#define SDL_PumpEvents IGNORE_THIS_VERSION_OF_SDL_PumpEvents #define SDL_PeepEvents IGNORE_THIS_VERSION_OF_SDL_PeepEvents +#define SDL_HasEvent IGNORE_THIS_VERSION_OF_SDL_HasEvent +#define SDL_HasEvents IGNORE_THIS_VERSION_OF_SDL_HasEvents +#define SDL_FlushEvent IGNORE_THIS_VERSION_OF_SDL_FlushEvent +#define SDL_FlushEvents IGNORE_THIS_VERSION_OF_SDL_FlushEvents +#define SDL_PollEvent IGNORE_THIS_VERSION_OF_SDL_PollEvent #define SDL_WaitEvent IGNORE_THIS_VERSION_OF_SDL_WaitEvent +#define SDL_WaitEventTimeout IGNORE_THIS_VERSION_OF_SDL_WaitEventTimeout +#define SDL_PushEvent IGNORE_THIS_VERSION_OF_SDL_PushEvent #define SDL_SetEventFilter IGNORE_THIS_VERSION_OF_SDL_SetEventFilter #define SDL_GetEventFilter IGNORE_THIS_VERSION_OF_SDL_GetEventFilter -#define SDL_CreateRGBSurface IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurface -#define SDL_CreateRGBSurfaceFrom IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurfaceFrom -#define SDL_FreeSurface IGNORE_THIS_VERSION_OF_SDL_FreeSurface -#define SDL_SetClipRect IGNORE_THIS_VERSION_OF_SDL_SetClipRect -#define SDL_GetClipRect IGNORE_THIS_VERSION_OF_SDL_GetClipRect -#define SDL_FillRect IGNORE_THIS_VERSION_OF_SDL_FillRect -#define SDL_GetRGB IGNORE_THIS_VERSION_OF_SDL_GetRGB -#define SDL_GetRGBA IGNORE_THIS_VERSION_OF_SDL_GetRGBA -#define SDL_MapRGB IGNORE_THIS_VERSION_OF_SDL_MapRGB -#define SDL_MapRGBA IGNORE_THIS_VERSION_OF_SDL_MapRGBA +#define SDL_AddEventWatch IGNORE_THIS_VERSION_OF_SDL_AddEventWatch +#define SDL_DelEventWatch IGNORE_THIS_VERSION_OF_SDL_DelEventWatch +#define SDL_FilterEvents IGNORE_THIS_VERSION_OF_SDL_FilterEvents +#define SDL_EventState IGNORE_THIS_VERSION_OF_SDL_EventState +#define SDL_RegisterEvents IGNORE_THIS_VERSION_OF_SDL_RegisterEvents +#define SDL_GetBasePath IGNORE_THIS_VERSION_OF_SDL_GetBasePath +#define SDL_GetPrefPath IGNORE_THIS_VERSION_OF_SDL_GetPrefPath +#define SDL_GameControllerAddMapping IGNORE_THIS_VERSION_OF_SDL_GameControllerAddMapping +#define SDL_GameControllerMappingForGUID IGNORE_THIS_VERSION_OF_SDL_GameControllerMappingForGUID +#define SDL_GameControllerMapping IGNORE_THIS_VERSION_OF_SDL_GameControllerMapping +#define SDL_IsGameController IGNORE_THIS_VERSION_OF_SDL_IsGameController +#define SDL_GameControllerNameForIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerNameForIndex +#define SDL_GameControllerOpen IGNORE_THIS_VERSION_OF_SDL_GameControllerOpen +#define SDL_GameControllerName IGNORE_THIS_VERSION_OF_SDL_GameControllerName +#define SDL_GameControllerGetAttached IGNORE_THIS_VERSION_OF_SDL_GameControllerGetAttached +#define SDL_GameControllerGetJoystick IGNORE_THIS_VERSION_OF_SDL_GameControllerGetJoystick +#define SDL_GameControllerEventState IGNORE_THIS_VERSION_OF_SDL_GameControllerEventState +#define SDL_GameControllerUpdate IGNORE_THIS_VERSION_OF_SDL_GameControllerUpdate +#define SDL_GameControllerGetAxisFromString IGNORE_THIS_VERSION_OF_SDL_GameControllerGetAxisFromString +#define SDL_GameControllerGetStringForAxis IGNORE_THIS_VERSION_OF_SDL_GameControllerGetStringForAxis +#define SDL_GameControllerGetBindForAxis IGNORE_THIS_VERSION_OF_SDL_GameControllerGetBindForAxis +#define SDL_GameControllerGetAxis IGNORE_THIS_VERSION_OF_SDL_GameControllerGetAxis +#define SDL_GameControllerGetButtonFromString IGNORE_THIS_VERSION_OF_SDL_GameControllerGetButtonFromString +#define SDL_GameControllerGetStringForButton IGNORE_THIS_VERSION_OF_SDL_GameControllerGetStringForButton +#define SDL_GameControllerGetBindForButton IGNORE_THIS_VERSION_OF_SDL_GameControllerGetBindForButton +#define SDL_GameControllerGetButton IGNORE_THIS_VERSION_OF_SDL_GameControllerGetButton +#define SDL_GameControllerClose IGNORE_THIS_VERSION_OF_SDL_GameControllerClose +#define SDL_RecordGesture IGNORE_THIS_VERSION_OF_SDL_RecordGesture +#define SDL_SaveAllDollarTemplates IGNORE_THIS_VERSION_OF_SDL_SaveAllDollarTemplates +#define SDL_SaveDollarTemplate IGNORE_THIS_VERSION_OF_SDL_SaveDollarTemplate +#define SDL_LoadDollarTemplates IGNORE_THIS_VERSION_OF_SDL_LoadDollarTemplates +#define SDL_NumHaptics IGNORE_THIS_VERSION_OF_SDL_NumHaptics +#define SDL_HapticName IGNORE_THIS_VERSION_OF_SDL_HapticName +#define SDL_HapticOpen IGNORE_THIS_VERSION_OF_SDL_HapticOpen +#define SDL_HapticOpened IGNORE_THIS_VERSION_OF_SDL_HapticOpened +#define SDL_HapticIndex IGNORE_THIS_VERSION_OF_SDL_HapticIndex +#define SDL_MouseIsHaptic IGNORE_THIS_VERSION_OF_SDL_MouseIsHaptic +#define SDL_HapticOpenFromMouse IGNORE_THIS_VERSION_OF_SDL_HapticOpenFromMouse +#define SDL_JoystickIsHaptic IGNORE_THIS_VERSION_OF_SDL_JoystickIsHaptic +#define SDL_HapticOpenFromJoystick IGNORE_THIS_VERSION_OF_SDL_HapticOpenFromJoystick +#define SDL_HapticClose IGNORE_THIS_VERSION_OF_SDL_HapticClose +#define SDL_HapticNumEffects IGNORE_THIS_VERSION_OF_SDL_HapticNumEffects +#define SDL_HapticNumEffectsPlaying IGNORE_THIS_VERSION_OF_SDL_HapticNumEffectsPlaying +#define SDL_HapticQuery IGNORE_THIS_VERSION_OF_SDL_HapticQuery +#define SDL_HapticNumAxes IGNORE_THIS_VERSION_OF_SDL_HapticNumAxes +#define SDL_HapticEffectSupported IGNORE_THIS_VERSION_OF_SDL_HapticEffectSupported +#define SDL_HapticNewEffect IGNORE_THIS_VERSION_OF_SDL_HapticNewEffect +#define SDL_HapticUpdateEffect IGNORE_THIS_VERSION_OF_SDL_HapticUpdateEffect +#define SDL_HapticRunEffect IGNORE_THIS_VERSION_OF_SDL_HapticRunEffect +#define SDL_HapticStopEffect IGNORE_THIS_VERSION_OF_SDL_HapticStopEffect +#define SDL_HapticDestroyEffect IGNORE_THIS_VERSION_OF_SDL_HapticDestroyEffect +#define SDL_HapticGetEffectStatus IGNORE_THIS_VERSION_OF_SDL_HapticGetEffectStatus +#define SDL_HapticSetGain IGNORE_THIS_VERSION_OF_SDL_HapticSetGain +#define SDL_HapticSetAutocenter IGNORE_THIS_VERSION_OF_SDL_HapticSetAutocenter +#define SDL_HapticPause IGNORE_THIS_VERSION_OF_SDL_HapticPause +#define SDL_HapticUnpause IGNORE_THIS_VERSION_OF_SDL_HapticUnpause +#define SDL_HapticStopAll IGNORE_THIS_VERSION_OF_SDL_HapticStopAll +#define SDL_HapticRumbleSupported IGNORE_THIS_VERSION_OF_SDL_HapticRumbleSupported +#define SDL_HapticRumbleInit IGNORE_THIS_VERSION_OF_SDL_HapticRumbleInit +#define SDL_HapticRumblePlay IGNORE_THIS_VERSION_OF_SDL_HapticRumblePlay +#define SDL_HapticRumbleStop IGNORE_THIS_VERSION_OF_SDL_HapticRumbleStop +#define SDL_SetHintWithPriority IGNORE_THIS_VERSION_OF_SDL_SetHintWithPriority +#define SDL_SetHint IGNORE_THIS_VERSION_OF_SDL_SetHint +#define SDL_GetHint IGNORE_THIS_VERSION_OF_SDL_GetHint +#define SDL_AddHintCallback IGNORE_THIS_VERSION_OF_SDL_AddHintCallback +#define SDL_DelHintCallback IGNORE_THIS_VERSION_OF_SDL_DelHintCallback +#define SDL_ClearHints IGNORE_THIS_VERSION_OF_SDL_ClearHints +#define SDL_NumJoysticks IGNORE_THIS_VERSION_OF_SDL_NumJoysticks +#define SDL_JoystickNameForIndex IGNORE_THIS_VERSION_OF_SDL_JoystickNameForIndex +#define SDL_JoystickOpen IGNORE_THIS_VERSION_OF_SDL_JoystickOpen +#define SDL_JoystickName IGNORE_THIS_VERSION_OF_SDL_JoystickName +#define SDL_JoystickGetDeviceGUID IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceGUID +#define SDL_JoystickGetGUID IGNORE_THIS_VERSION_OF_SDL_JoystickGetGUID +#define SDL_JoystickGetGUIDString IGNORE_THIS_VERSION_OF_SDL_JoystickGetGUIDString +#define SDL_JoystickGetGUIDFromString IGNORE_THIS_VERSION_OF_SDL_JoystickGetGUIDFromString +#define SDL_JoystickGetAttached IGNORE_THIS_VERSION_OF_SDL_JoystickGetAttached +#define SDL_JoystickInstanceID IGNORE_THIS_VERSION_OF_SDL_JoystickInstanceID +#define SDL_JoystickNumAxes IGNORE_THIS_VERSION_OF_SDL_JoystickNumAxes +#define SDL_JoystickNumBalls IGNORE_THIS_VERSION_OF_SDL_JoystickNumBalls +#define SDL_JoystickNumHats IGNORE_THIS_VERSION_OF_SDL_JoystickNumHats +#define SDL_JoystickNumButtons IGNORE_THIS_VERSION_OF_SDL_JoystickNumButtons +#define SDL_JoystickUpdate IGNORE_THIS_VERSION_OF_SDL_JoystickUpdate +#define SDL_JoystickEventState IGNORE_THIS_VERSION_OF_SDL_JoystickEventState +#define SDL_JoystickGetAxis IGNORE_THIS_VERSION_OF_SDL_JoystickGetAxis +#define SDL_JoystickGetHat IGNORE_THIS_VERSION_OF_SDL_JoystickGetHat +#define SDL_JoystickGetBall IGNORE_THIS_VERSION_OF_SDL_JoystickGetBall +#define SDL_JoystickGetButton IGNORE_THIS_VERSION_OF_SDL_JoystickGetButton +#define SDL_JoystickClose IGNORE_THIS_VERSION_OF_SDL_JoystickClose +#define SDL_GetKeyboardFocus IGNORE_THIS_VERSION_OF_SDL_GetKeyboardFocus +#define SDL_GetKeyboardState IGNORE_THIS_VERSION_OF_SDL_GetKeyboardState +#define SDL_GetModState IGNORE_THIS_VERSION_OF_SDL_GetModState +#define SDL_SetModState IGNORE_THIS_VERSION_OF_SDL_SetModState +#define SDL_GetKeyFromScancode IGNORE_THIS_VERSION_OF_SDL_GetKeyFromScancode +#define SDL_GetScancodeFromKey IGNORE_THIS_VERSION_OF_SDL_GetScancodeFromKey +#define SDL_GetScancodeName IGNORE_THIS_VERSION_OF_SDL_GetScancodeName +#define SDL_GetScancodeFromName IGNORE_THIS_VERSION_OF_SDL_GetScancodeFromName +#define SDL_GetKeyName IGNORE_THIS_VERSION_OF_SDL_GetKeyName +#define SDL_GetKeyFromName IGNORE_THIS_VERSION_OF_SDL_GetKeyFromName +#define SDL_StartTextInput IGNORE_THIS_VERSION_OF_SDL_StartTextInput +#define SDL_IsTextInputActive IGNORE_THIS_VERSION_OF_SDL_IsTextInputActive +#define SDL_StopTextInput IGNORE_THIS_VERSION_OF_SDL_StopTextInput +#define SDL_SetTextInputRect IGNORE_THIS_VERSION_OF_SDL_SetTextInputRect +#define SDL_HasScreenKeyboardSupport IGNORE_THIS_VERSION_OF_SDL_HasScreenKeyboardSupport +#define SDL_IsScreenKeyboardShown IGNORE_THIS_VERSION_OF_SDL_IsScreenKeyboardShown +#define SDL_LoadObject IGNORE_THIS_VERSION_OF_SDL_LoadObject +#define SDL_LoadFunction IGNORE_THIS_VERSION_OF_SDL_LoadFunction +#define SDL_UnloadObject IGNORE_THIS_VERSION_OF_SDL_UnloadObject +#define SDL_LogSetAllPriority IGNORE_THIS_VERSION_OF_SDL_LogSetAllPriority +#define SDL_LogSetPriority IGNORE_THIS_VERSION_OF_SDL_LogSetPriority +#define SDL_LogGetPriority IGNORE_THIS_VERSION_OF_SDL_LogGetPriority +#define SDL_LogResetPriorities IGNORE_THIS_VERSION_OF_SDL_LogResetPriorities +#define SDL_LogMessageV IGNORE_THIS_VERSION_OF_SDL_LogMessageV +#define SDL_LogGetOutputFunction IGNORE_THIS_VERSION_OF_SDL_LogGetOutputFunction +#define SDL_LogSetOutputFunction IGNORE_THIS_VERSION_OF_SDL_LogSetOutputFunction +#define SDL_SetMainReady IGNORE_THIS_VERSION_OF_SDL_SetMainReady +#define SDL_ShowMessageBox IGNORE_THIS_VERSION_OF_SDL_ShowMessageBox +#define SDL_ShowSimpleMessageBox IGNORE_THIS_VERSION_OF_SDL_ShowSimpleMessageBox +#define SDL_GetMouseFocus IGNORE_THIS_VERSION_OF_SDL_GetMouseFocus +#define SDL_GetMouseState IGNORE_THIS_VERSION_OF_SDL_GetMouseState +#define SDL_GetRelativeMouseState IGNORE_THIS_VERSION_OF_SDL_GetRelativeMouseState +#define SDL_WarpMouseInWindow IGNORE_THIS_VERSION_OF_SDL_WarpMouseInWindow +#define SDL_SetRelativeMouseMode IGNORE_THIS_VERSION_OF_SDL_SetRelativeMouseMode +#define SDL_GetRelativeMouseMode IGNORE_THIS_VERSION_OF_SDL_GetRelativeMouseMode #define SDL_CreateCursor IGNORE_THIS_VERSION_OF_SDL_CreateCursor +#define SDL_CreateColorCursor IGNORE_THIS_VERSION_OF_SDL_CreateColorCursor +#define SDL_CreateSystemCursor IGNORE_THIS_VERSION_OF_SDL_CreateSystemCursor #define SDL_SetCursor IGNORE_THIS_VERSION_OF_SDL_SetCursor #define SDL_GetCursor IGNORE_THIS_VERSION_OF_SDL_GetCursor +#define SDL_GetDefaultCursor IGNORE_THIS_VERSION_OF_SDL_GetDefaultCursor #define SDL_FreeCursor IGNORE_THIS_VERSION_OF_SDL_FreeCursor -#define SDL_UpdateRect IGNORE_THIS_VERSION_OF_SDL_UpdateRect -#define SDL_UpdateRects IGNORE_THIS_VERSION_OF_SDL_UpdateRects -#define SDL_GetMouseState IGNORE_THIS_VERSION_OF_SDL_GetMouseState -#define SDL_GetRelativeMouseState IGNORE_THIS_VERSION_OF_SDL_GetRelativeMouseState -#define SDL_GL_SetAttribute IGNORE_THIS_VERSION_OF_SDL_GL_SetAttribute -#define SDL_GL_GetAttribute IGNORE_THIS_VERSION_OF_SDL_GL_GetAttribute -#define SDL_CreateThread IGNORE_THIS_VERSION_OF_SDL_CreateThread -#define SDL_AddTimer IGNORE_THIS_VERSION_OF_SDL_AddTimer -#define SDL_RemoveTimer IGNORE_THIS_VERSION_OF_SDL_RemoveTimer -#define SDL_AllocRW IGNORE_THIS_VERSION_OF_SDL_AllocRW -#define SDL_FreeRW IGNORE_THIS_VERSION_OF_SDL_FreeRW +#define SDL_ShowCursor IGNORE_THIS_VERSION_OF_SDL_ShowCursor +#define SDL_CreateMutex IGNORE_THIS_VERSION_OF_SDL_CreateMutex +#define SDL_LockMutex IGNORE_THIS_VERSION_OF_SDL_LockMutex +#define SDL_TryLockMutex IGNORE_THIS_VERSION_OF_SDL_TryLockMutex +#define SDL_UnlockMutex IGNORE_THIS_VERSION_OF_SDL_UnlockMutex +#define SDL_DestroyMutex IGNORE_THIS_VERSION_OF_SDL_DestroyMutex +#define SDL_CreateSemaphore IGNORE_THIS_VERSION_OF_SDL_CreateSemaphore +#define SDL_DestroySemaphore IGNORE_THIS_VERSION_OF_SDL_DestroySemaphore +#define SDL_SemWait IGNORE_THIS_VERSION_OF_SDL_SemWait +#define SDL_SemTryWait IGNORE_THIS_VERSION_OF_SDL_SemTryWait +#define SDL_SemWaitTimeout IGNORE_THIS_VERSION_OF_SDL_SemWaitTimeout +#define SDL_SemPost IGNORE_THIS_VERSION_OF_SDL_SemPost +#define SDL_SemValue IGNORE_THIS_VERSION_OF_SDL_SemValue +#define SDL_CreateCond IGNORE_THIS_VERSION_OF_SDL_CreateCond +#define SDL_DestroyCond IGNORE_THIS_VERSION_OF_SDL_DestroyCond +#define SDL_CondSignal IGNORE_THIS_VERSION_OF_SDL_CondSignal +#define SDL_CondBroadcast IGNORE_THIS_VERSION_OF_SDL_CondBroadcast +#define SDL_CondWait IGNORE_THIS_VERSION_OF_SDL_CondWait +#define SDL_CondWaitTimeout IGNORE_THIS_VERSION_OF_SDL_CondWaitTimeout +#define SDL_GetPixelFormatName IGNORE_THIS_VERSION_OF_SDL_GetPixelFormatName +#define SDL_PixelFormatEnumToMasks IGNORE_THIS_VERSION_OF_SDL_PixelFormatEnumToMasks +#define SDL_MasksToPixelFormatEnum IGNORE_THIS_VERSION_OF_SDL_MasksToPixelFormatEnum +#define SDL_AllocFormat IGNORE_THIS_VERSION_OF_SDL_AllocFormat +#define SDL_FreeFormat IGNORE_THIS_VERSION_OF_SDL_FreeFormat +#define SDL_AllocPalette IGNORE_THIS_VERSION_OF_SDL_AllocPalette +#define SDL_SetPixelFormatPalette IGNORE_THIS_VERSION_OF_SDL_SetPixelFormatPalette +#define SDL_SetPaletteColors IGNORE_THIS_VERSION_OF_SDL_SetPaletteColors +#define SDL_FreePalette IGNORE_THIS_VERSION_OF_SDL_FreePalette +#define SDL_MapRGB IGNORE_THIS_VERSION_OF_SDL_MapRGB +#define SDL_MapRGBA IGNORE_THIS_VERSION_OF_SDL_MapRGBA +#define SDL_GetRGB IGNORE_THIS_VERSION_OF_SDL_GetRGB +#define SDL_GetRGBA IGNORE_THIS_VERSION_OF_SDL_GetRGBA +#define SDL_CalculateGammaRamp IGNORE_THIS_VERSION_OF_SDL_CalculateGammaRamp +#define SDL_GetPlatform IGNORE_THIS_VERSION_OF_SDL_GetPlatform +#define SDL_GetPowerInfo IGNORE_THIS_VERSION_OF_SDL_GetPowerInfo +#define SDL_HasIntersection IGNORE_THIS_VERSION_OF_SDL_HasIntersection +#define SDL_IntersectRect IGNORE_THIS_VERSION_OF_SDL_IntersectRect +#define SDL_UnionRect IGNORE_THIS_VERSION_OF_SDL_UnionRect +#define SDL_EnclosePoints IGNORE_THIS_VERSION_OF_SDL_EnclosePoints +#define SDL_IntersectRectAndLine IGNORE_THIS_VERSION_OF_SDL_IntersectRectAndLine +#define SDL_GetNumRenderDrivers IGNORE_THIS_VERSION_OF_SDL_GetNumRenderDrivers +#define SDL_GetRenderDriverInfo IGNORE_THIS_VERSION_OF_SDL_GetRenderDriverInfo +#define SDL_CreateWindowAndRenderer IGNORE_THIS_VERSION_OF_SDL_CreateWindowAndRenderer +#define SDL_CreateRenderer IGNORE_THIS_VERSION_OF_SDL_CreateRenderer +#define SDL_CreateSoftwareRenderer IGNORE_THIS_VERSION_OF_SDL_CreateSoftwareRenderer +#define SDL_GetRenderer IGNORE_THIS_VERSION_OF_SDL_GetRenderer +#define SDL_GetRendererInfo IGNORE_THIS_VERSION_OF_SDL_GetRendererInfo +#define SDL_GetRendererOutputSize IGNORE_THIS_VERSION_OF_SDL_GetRendererOutputSize +#define SDL_CreateTexture IGNORE_THIS_VERSION_OF_SDL_CreateTexture +#define SDL_CreateTextureFromSurface IGNORE_THIS_VERSION_OF_SDL_CreateTextureFromSurface +#define SDL_QueryTexture IGNORE_THIS_VERSION_OF_SDL_QueryTexture +#define SDL_SetTextureColorMod IGNORE_THIS_VERSION_OF_SDL_SetTextureColorMod +#define SDL_GetTextureColorMod IGNORE_THIS_VERSION_OF_SDL_GetTextureColorMod +#define SDL_SetTextureAlphaMod IGNORE_THIS_VERSION_OF_SDL_SetTextureAlphaMod +#define SDL_GetTextureAlphaMod IGNORE_THIS_VERSION_OF_SDL_GetTextureAlphaMod +#define SDL_SetTextureBlendMode IGNORE_THIS_VERSION_OF_SDL_SetTextureBlendMode +#define SDL_GetTextureBlendMode IGNORE_THIS_VERSION_OF_SDL_GetTextureBlendMode +#define SDL_UpdateTexture IGNORE_THIS_VERSION_OF_SDL_UpdateTexture +#define SDL_UpdateYUVTexture IGNORE_THIS_VERSION_OF_SDL_UpdateYUVTexture +#define SDL_LockTexture IGNORE_THIS_VERSION_OF_SDL_LockTexture +#define SDL_UnlockTexture IGNORE_THIS_VERSION_OF_SDL_UnlockTexture +#define SDL_RenderTargetSupported IGNORE_THIS_VERSION_OF_SDL_RenderTargetSupported +#define SDL_SetRenderTarget IGNORE_THIS_VERSION_OF_SDL_SetRenderTarget +#define SDL_GetRenderTarget IGNORE_THIS_VERSION_OF_SDL_GetRenderTarget +#define SDL_RenderSetLogicalSize IGNORE_THIS_VERSION_OF_SDL_RenderSetLogicalSize +#define SDL_RenderGetLogicalSize IGNORE_THIS_VERSION_OF_SDL_RenderGetLogicalSize +#define SDL_RenderSetViewport IGNORE_THIS_VERSION_OF_SDL_RenderSetViewport +#define SDL_RenderGetViewport IGNORE_THIS_VERSION_OF_SDL_RenderGetViewport +#define SDL_RenderSetClipRect IGNORE_THIS_VERSION_OF_SDL_RenderSetClipRect +#define SDL_RenderGetClipRect IGNORE_THIS_VERSION_OF_SDL_RenderGetClipRect +#define SDL_RenderSetScale IGNORE_THIS_VERSION_OF_SDL_RenderSetScale +#define SDL_RenderGetScale IGNORE_THIS_VERSION_OF_SDL_RenderGetScale +#define SDL_SetRenderDrawColor IGNORE_THIS_VERSION_OF_SDL_SetRenderDrawColor +#define SDL_GetRenderDrawColor IGNORE_THIS_VERSION_OF_SDL_GetRenderDrawColor +#define SDL_SetRenderDrawBlendMode IGNORE_THIS_VERSION_OF_SDL_SetRenderDrawBlendMode +#define SDL_GetRenderDrawBlendMode IGNORE_THIS_VERSION_OF_SDL_GetRenderDrawBlendMode +#define SDL_RenderClear IGNORE_THIS_VERSION_OF_SDL_RenderClear +#define SDL_RenderDrawPoint IGNORE_THIS_VERSION_OF_SDL_RenderDrawPoint +#define SDL_RenderDrawPoints IGNORE_THIS_VERSION_OF_SDL_RenderDrawPoints +#define SDL_RenderDrawLine IGNORE_THIS_VERSION_OF_SDL_RenderDrawLine +#define SDL_RenderDrawLines IGNORE_THIS_VERSION_OF_SDL_RenderDrawLines +#define SDL_RenderDrawRect IGNORE_THIS_VERSION_OF_SDL_RenderDrawRect +#define SDL_RenderDrawRects IGNORE_THIS_VERSION_OF_SDL_RenderDrawRects +#define SDL_RenderFillRect IGNORE_THIS_VERSION_OF_SDL_RenderFillRect +#define SDL_RenderFillRects IGNORE_THIS_VERSION_OF_SDL_RenderFillRects +#define SDL_RenderCopy IGNORE_THIS_VERSION_OF_SDL_RenderCopy +#define SDL_RenderCopyEx IGNORE_THIS_VERSION_OF_SDL_RenderCopyEx +#define SDL_RenderReadPixels IGNORE_THIS_VERSION_OF_SDL_RenderReadPixels +#define SDL_RenderPresent IGNORE_THIS_VERSION_OF_SDL_RenderPresent +#define SDL_DestroyTexture IGNORE_THIS_VERSION_OF_SDL_DestroyTexture +#define SDL_DestroyRenderer IGNORE_THIS_VERSION_OF_SDL_DestroyRenderer +#define SDL_GL_BindTexture IGNORE_THIS_VERSION_OF_SDL_GL_BindTexture +#define SDL_GL_UnbindTexture IGNORE_THIS_VERSION_OF_SDL_GL_UnbindTexture #define SDL_RWFromFile IGNORE_THIS_VERSION_OF_SDL_RWFromFile -#define SDL_RWFromFP IGNORE_THIS_VERSION_OF_SDL_RWFromFP #define SDL_RWFromMem IGNORE_THIS_VERSION_OF_SDL_RWFromMem #define SDL_RWFromConstMem IGNORE_THIS_VERSION_OF_SDL_RWFromConstMem +#define SDL_AllocRW IGNORE_THIS_VERSION_OF_SDL_AllocRW +#define SDL_FreeRW IGNORE_THIS_VERSION_OF_SDL_FreeRW +#define SDL_ReadU8 IGNORE_THIS_VERSION_OF_SDL_ReadU8 #define SDL_ReadLE16 IGNORE_THIS_VERSION_OF_SDL_ReadLE16 #define SDL_ReadBE16 IGNORE_THIS_VERSION_OF_SDL_ReadBE16 #define SDL_ReadLE32 IGNORE_THIS_VERSION_OF_SDL_ReadLE32 #define SDL_ReadBE32 IGNORE_THIS_VERSION_OF_SDL_ReadBE32 #define SDL_ReadLE64 IGNORE_THIS_VERSION_OF_SDL_ReadLE64 #define SDL_ReadBE64 IGNORE_THIS_VERSION_OF_SDL_ReadBE64 +#define SDL_WriteU8 IGNORE_THIS_VERSION_OF_SDL_WriteU8 #define SDL_WriteLE16 IGNORE_THIS_VERSION_OF_SDL_WriteLE16 #define SDL_WriteBE16 IGNORE_THIS_VERSION_OF_SDL_WriteBE16 #define SDL_WriteLE32 IGNORE_THIS_VERSION_OF_SDL_WriteLE32 #define SDL_WriteBE32 IGNORE_THIS_VERSION_OF_SDL_WriteBE32 #define SDL_WriteLE64 IGNORE_THIS_VERSION_OF_SDL_WriteLE64 #define SDL_WriteBE64 IGNORE_THIS_VERSION_OF_SDL_WriteBE64 -#define SDL_GetThreadID IGNORE_THIS_VERSION_OF_SDL_GetThreadID -#define SDL_ThreadID IGNORE_THIS_VERSION_OF_SDL_ThreadID -#define SDL_JoystickName IGNORE_THIS_VERSION_OF_SDL_JoystickName +#define SDL_CreateShapedWindow IGNORE_THIS_VERSION_OF_SDL_CreateShapedWindow +#define SDL_IsShapedWindow IGNORE_THIS_VERSION_OF_SDL_IsShapedWindow +#define SDL_SetWindowShape IGNORE_THIS_VERSION_OF_SDL_SetWindowShape +#define SDL_GetShapedWindowMode IGNORE_THIS_VERSION_OF_SDL_GetShapedWindowMode +#define SDL_malloc IGNORE_THIS_VERSION_OF_SDL_malloc +#define SDL_calloc IGNORE_THIS_VERSION_OF_SDL_calloc +#define SDL_realloc IGNORE_THIS_VERSION_OF_SDL_realloc +#define SDL_free IGNORE_THIS_VERSION_OF_SDL_free +#define SDL_getenv IGNORE_THIS_VERSION_OF_SDL_getenv +#define SDL_setenv IGNORE_THIS_VERSION_OF_SDL_setenv +#define SDL_qsort IGNORE_THIS_VERSION_OF_SDL_qsort +#define SDL_abs IGNORE_THIS_VERSION_OF_SDL_abs +#define SDL_isdigit IGNORE_THIS_VERSION_OF_SDL_isdigit +#define SDL_isspace IGNORE_THIS_VERSION_OF_SDL_isspace +#define SDL_toupper IGNORE_THIS_VERSION_OF_SDL_toupper +#define SDL_tolower IGNORE_THIS_VERSION_OF_SDL_tolower +#define SDL_memset IGNORE_THIS_VERSION_OF_SDL_memset +#define SDL_memcpy IGNORE_THIS_VERSION_OF_SDL_memcpy +#define SDL_memmove IGNORE_THIS_VERSION_OF_SDL_memmove +#define SDL_memcmp IGNORE_THIS_VERSION_OF_SDL_memcmp +#define SDL_wcslen IGNORE_THIS_VERSION_OF_SDL_wcslen +#define SDL_wcslcpy IGNORE_THIS_VERSION_OF_SDL_wcslcpy +#define SDL_wcslcat IGNORE_THIS_VERSION_OF_SDL_wcslcat +#define SDL_strlen IGNORE_THIS_VERSION_OF_SDL_strlen +#define SDL_strlcpy IGNORE_THIS_VERSION_OF_SDL_strlcpy +#define SDL_utf8strlcpy IGNORE_THIS_VERSION_OF_SDL_utf8strlcpy +#define SDL_strlcat IGNORE_THIS_VERSION_OF_SDL_strlcat +#define SDL_strdup IGNORE_THIS_VERSION_OF_SDL_strdup +#define SDL_strrev IGNORE_THIS_VERSION_OF_SDL_strrev +#define SDL_strupr IGNORE_THIS_VERSION_OF_SDL_strupr +#define SDL_strlwr IGNORE_THIS_VERSION_OF_SDL_strlwr +#define SDL_strchr IGNORE_THIS_VERSION_OF_SDL_strchr +#define SDL_strrchr IGNORE_THIS_VERSION_OF_SDL_strrchr +#define SDL_strstr IGNORE_THIS_VERSION_OF_SDL_strstr +#define SDL_itoa IGNORE_THIS_VERSION_OF_SDL_itoa +#define SDL_uitoa IGNORE_THIS_VERSION_OF_SDL_uitoa +#define SDL_ltoa IGNORE_THIS_VERSION_OF_SDL_ltoa +#define SDL_ultoa IGNORE_THIS_VERSION_OF_SDL_ultoa +#define SDL_lltoa IGNORE_THIS_VERSION_OF_SDL_lltoa +#define SDL_ulltoa IGNORE_THIS_VERSION_OF_SDL_ulltoa +#define SDL_atoi IGNORE_THIS_VERSION_OF_SDL_atoi +#define SDL_atof IGNORE_THIS_VERSION_OF_SDL_atof +#define SDL_strtol IGNORE_THIS_VERSION_OF_SDL_strtol +#define SDL_strtoul IGNORE_THIS_VERSION_OF_SDL_strtoul +#define SDL_strtoll IGNORE_THIS_VERSION_OF_SDL_strtoll +#define SDL_strtoull IGNORE_THIS_VERSION_OF_SDL_strtoull +#define SDL_strtod IGNORE_THIS_VERSION_OF_SDL_strtod +#define SDL_strcmp IGNORE_THIS_VERSION_OF_SDL_strcmp +#define SDL_strncmp IGNORE_THIS_VERSION_OF_SDL_strncmp +#define SDL_strcasecmp IGNORE_THIS_VERSION_OF_SDL_strcasecmp +#define SDL_strncasecmp IGNORE_THIS_VERSION_OF_SDL_strncasecmp +#define SDL_vsnprintf IGNORE_THIS_VERSION_OF_SDL_vsnprintf +#define SDL_acos IGNORE_THIS_VERSION_OF_SDL_acos +#define SDL_asin IGNORE_THIS_VERSION_OF_SDL_asin +#define SDL_atan IGNORE_THIS_VERSION_OF_SDL_atan +#define SDL_atan2 IGNORE_THIS_VERSION_OF_SDL_atan2 +#define SDL_ceil IGNORE_THIS_VERSION_OF_SDL_ceil +#define SDL_copysign IGNORE_THIS_VERSION_OF_SDL_copysign +#define SDL_cos IGNORE_THIS_VERSION_OF_SDL_cos +#define SDL_cosf IGNORE_THIS_VERSION_OF_SDL_cosf +#define SDL_fabs IGNORE_THIS_VERSION_OF_SDL_fabs +#define SDL_floor IGNORE_THIS_VERSION_OF_SDL_floor +#define SDL_log IGNORE_THIS_VERSION_OF_SDL_log +#define SDL_pow IGNORE_THIS_VERSION_OF_SDL_pow +#define SDL_scalbn IGNORE_THIS_VERSION_OF_SDL_scalbn +#define SDL_sin IGNORE_THIS_VERSION_OF_SDL_sin +#define SDL_sinf IGNORE_THIS_VERSION_OF_SDL_sinf +#define SDL_sqrt IGNORE_THIS_VERSION_OF_SDL_sqrt +#define SDL_iconv_open IGNORE_THIS_VERSION_OF_SDL_iconv_open +#define SDL_iconv_close IGNORE_THIS_VERSION_OF_SDL_iconv_close +#define SDL_iconv IGNORE_THIS_VERSION_OF_SDL_iconv +#define SDL_iconv_string IGNORE_THIS_VERSION_OF_SDL_iconv_string +#define SDL_CreateRGBSurface IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurface +#define SDL_CreateRGBSurfaceFrom IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurfaceFrom +#define SDL_FreeSurface IGNORE_THIS_VERSION_OF_SDL_FreeSurface +#define SDL_SetSurfacePalette IGNORE_THIS_VERSION_OF_SDL_SetSurfacePalette +#define SDL_LockSurface IGNORE_THIS_VERSION_OF_SDL_LockSurface +#define SDL_UnlockSurface IGNORE_THIS_VERSION_OF_SDL_UnlockSurface #define SDL_LoadBMP_RW IGNORE_THIS_VERSION_OF_SDL_LoadBMP_RW #define SDL_SaveBMP_RW IGNORE_THIS_VERSION_OF_SDL_SaveBMP_RW -#define SDL_LoadWAV_RW IGNORE_THIS_VERSION_OF_SDL_LoadWAV_RW +#define SDL_SetSurfaceRLE IGNORE_THIS_VERSION_OF_SDL_SetSurfaceRLE +#define SDL_SetColorKey IGNORE_THIS_VERSION_OF_SDL_SetColorKey +#define SDL_GetColorKey IGNORE_THIS_VERSION_OF_SDL_GetColorKey +#define SDL_SetSurfaceColorMod IGNORE_THIS_VERSION_OF_SDL_SetSurfaceColorMod +#define SDL_GetSurfaceColorMod IGNORE_THIS_VERSION_OF_SDL_GetSurfaceColorMod +#define SDL_SetSurfaceAlphaMod IGNORE_THIS_VERSION_OF_SDL_SetSurfaceAlphaMod +#define SDL_GetSurfaceAlphaMod IGNORE_THIS_VERSION_OF_SDL_GetSurfaceAlphaMod +#define SDL_SetSurfaceBlendMode IGNORE_THIS_VERSION_OF_SDL_SetSurfaceBlendMode +#define SDL_GetSurfaceBlendMode IGNORE_THIS_VERSION_OF_SDL_GetSurfaceBlendMode +#define SDL_SetClipRect IGNORE_THIS_VERSION_OF_SDL_SetClipRect +#define SDL_GetClipRect IGNORE_THIS_VERSION_OF_SDL_GetClipRect +#define SDL_ConvertSurface IGNORE_THIS_VERSION_OF_SDL_ConvertSurface +#define SDL_ConvertSurfaceFormat IGNORE_THIS_VERSION_OF_SDL_ConvertSurfaceFormat +#define SDL_ConvertPixels IGNORE_THIS_VERSION_OF_SDL_ConvertPixels +#define SDL_FillRect IGNORE_THIS_VERSION_OF_SDL_FillRect +#define SDL_FillRects IGNORE_THIS_VERSION_OF_SDL_FillRects #define SDL_UpperBlit IGNORE_THIS_VERSION_OF_SDL_UpperBlit #define SDL_LowerBlit IGNORE_THIS_VERSION_OF_SDL_LowerBlit #define SDL_SoftStretch IGNORE_THIS_VERSION_OF_SDL_SoftStretch -#define SDL_ConvertSurface IGNORE_THIS_VERSION_OF_SDL_ConvertSurface -#define SDL_SetColorKey IGNORE_THIS_VERSION_OF_SDL_SetColorKey -#define SDL_LockSurface IGNORE_THIS_VERSION_OF_SDL_LockSurface -#define SDL_UnlockSurface IGNORE_THIS_VERSION_OF_SDL_UnlockSurface -#define SDL_GetKeyName IGNORE_THIS_VERSION_OF_SDL_GetKeyName +#define SDL_UpperBlitScaled IGNORE_THIS_VERSION_OF_SDL_UpperBlitScaled +#define SDL_LowerBlitScaled IGNORE_THIS_VERSION_OF_SDL_LowerBlitScaled +#define SDL_GetWindowWMInfo IGNORE_THIS_VERSION_OF_SDL_GetWindowWMInfo +#define SDL_GetThreadName IGNORE_THIS_VERSION_OF_SDL_GetThreadName +#define SDL_ThreadID IGNORE_THIS_VERSION_OF_SDL_ThreadID +#define SDL_GetThreadID IGNORE_THIS_VERSION_OF_SDL_GetThreadID +#define SDL_SetThreadPriority IGNORE_THIS_VERSION_OF_SDL_SetThreadPriority +#define SDL_WaitThread IGNORE_THIS_VERSION_OF_SDL_WaitThread +#define SDL_DetachThread IGNORE_THIS_VERSION_OF_SDL_DetachThread +#define SDL_TLSCreate IGNORE_THIS_VERSION_OF_SDL_TLSCreate +#define SDL_TLSGet IGNORE_THIS_VERSION_OF_SDL_TLSGet +#define SDL_TLSSet IGNORE_THIS_VERSION_OF_SDL_TLSSet +#define SDL_GetTicks IGNORE_THIS_VERSION_OF_SDL_GetTicks +#define SDL_GetPerformanceCounter IGNORE_THIS_VERSION_OF_SDL_GetPerformanceCounter +#define SDL_GetPerformanceFrequency IGNORE_THIS_VERSION_OF_SDL_GetPerformanceFrequency +#define SDL_Delay IGNORE_THIS_VERSION_OF_SDL_Delay +#define SDL_AddTimer IGNORE_THIS_VERSION_OF_SDL_AddTimer +#define SDL_RemoveTimer IGNORE_THIS_VERSION_OF_SDL_RemoveTimer +#define SDL_GetNumTouchDevices IGNORE_THIS_VERSION_OF_SDL_GetNumTouchDevices +#define SDL_GetTouchDevice IGNORE_THIS_VERSION_OF_SDL_GetTouchDevice +#define SDL_GetNumTouchFingers IGNORE_THIS_VERSION_OF_SDL_GetNumTouchFingers +#define SDL_GetTouchFinger IGNORE_THIS_VERSION_OF_SDL_GetTouchFinger +#define SDL_GetVersion IGNORE_THIS_VERSION_OF_SDL_GetVersion +#define SDL_GetRevision IGNORE_THIS_VERSION_OF_SDL_GetRevision +#define SDL_GetRevisionNumber IGNORE_THIS_VERSION_OF_SDL_GetRevisionNumber +#define SDL_GetNumVideoDrivers IGNORE_THIS_VERSION_OF_SDL_GetNumVideoDrivers +#define SDL_GetVideoDriver IGNORE_THIS_VERSION_OF_SDL_GetVideoDriver #define SDL_VideoInit IGNORE_THIS_VERSION_OF_SDL_VideoInit +#define SDL_VideoQuit IGNORE_THIS_VERSION_OF_SDL_VideoQuit +#define SDL_GetCurrentVideoDriver IGNORE_THIS_VERSION_OF_SDL_GetCurrentVideoDriver +#define SDL_GetNumVideoDisplays IGNORE_THIS_VERSION_OF_SDL_GetNumVideoDisplays +#define SDL_GetDisplayName IGNORE_THIS_VERSION_OF_SDL_GetDisplayName +#define SDL_GetDisplayBounds IGNORE_THIS_VERSION_OF_SDL_GetDisplayBounds +#define SDL_GetDisplayDPI IGNORE_THIS_VERSION_OF_SDL_GetDisplayDPI +#define SDL_GetNumDisplayModes IGNORE_THIS_VERSION_OF_SDL_GetNumDisplayModes +#define SDL_GetDisplayMode IGNORE_THIS_VERSION_OF_SDL_GetDisplayMode +#define SDL_GetDesktopDisplayMode IGNORE_THIS_VERSION_OF_SDL_GetDesktopDisplayMode +#define SDL_GetCurrentDisplayMode IGNORE_THIS_VERSION_OF_SDL_GetCurrentDisplayMode +#define SDL_GetClosestDisplayMode IGNORE_THIS_VERSION_OF_SDL_GetClosestDisplayMode +#define SDL_GetWindowDisplayIndex IGNORE_THIS_VERSION_OF_SDL_GetWindowDisplayIndex +#define SDL_SetWindowDisplayMode IGNORE_THIS_VERSION_OF_SDL_SetWindowDisplayMode +#define SDL_GetWindowDisplayMode IGNORE_THIS_VERSION_OF_SDL_GetWindowDisplayMode +#define SDL_GetWindowPixelFormat IGNORE_THIS_VERSION_OF_SDL_GetWindowPixelFormat +#define SDL_CreateWindow IGNORE_THIS_VERSION_OF_SDL_CreateWindow +#define SDL_CreateWindowFrom IGNORE_THIS_VERSION_OF_SDL_CreateWindowFrom +#define SDL_GetWindowID IGNORE_THIS_VERSION_OF_SDL_GetWindowID +#define SDL_GetWindowFromID IGNORE_THIS_VERSION_OF_SDL_GetWindowFromID +#define SDL_GetWindowFlags IGNORE_THIS_VERSION_OF_SDL_GetWindowFlags +#define SDL_SetWindowTitle IGNORE_THIS_VERSION_OF_SDL_SetWindowTitle +#define SDL_GetWindowTitle IGNORE_THIS_VERSION_OF_SDL_GetWindowTitle +#define SDL_SetWindowIcon IGNORE_THIS_VERSION_OF_SDL_SetWindowIcon +#define SDL_SetWindowData IGNORE_THIS_VERSION_OF_SDL_SetWindowData +#define SDL_GetWindowData IGNORE_THIS_VERSION_OF_SDL_GetWindowData +#define SDL_SetWindowPosition IGNORE_THIS_VERSION_OF_SDL_SetWindowPosition +#define SDL_GetWindowPosition IGNORE_THIS_VERSION_OF_SDL_GetWindowPosition +#define SDL_SetWindowSize IGNORE_THIS_VERSION_OF_SDL_SetWindowSize +#define SDL_GetWindowSize IGNORE_THIS_VERSION_OF_SDL_GetWindowSize +#define SDL_SetWindowMinimumSize IGNORE_THIS_VERSION_OF_SDL_SetWindowMinimumSize +#define SDL_GetWindowMinimumSize IGNORE_THIS_VERSION_OF_SDL_GetWindowMinimumSize +#define SDL_SetWindowMaximumSize IGNORE_THIS_VERSION_OF_SDL_SetWindowMaximumSize +#define SDL_GetWindowMaximumSize IGNORE_THIS_VERSION_OF_SDL_GetWindowMaximumSize +#define SDL_SetWindowBordered IGNORE_THIS_VERSION_OF_SDL_SetWindowBordered +#define SDL_ShowWindow IGNORE_THIS_VERSION_OF_SDL_ShowWindow +#define SDL_HideWindow IGNORE_THIS_VERSION_OF_SDL_HideWindow +#define SDL_RaiseWindow IGNORE_THIS_VERSION_OF_SDL_RaiseWindow +#define SDL_MaximizeWindow IGNORE_THIS_VERSION_OF_SDL_MaximizeWindow +#define SDL_MinimizeWindow IGNORE_THIS_VERSION_OF_SDL_MinimizeWindow +#define SDL_RestoreWindow IGNORE_THIS_VERSION_OF_SDL_RestoreWindow +#define SDL_SetWindowFullscreen IGNORE_THIS_VERSION_OF_SDL_SetWindowFullscreen +#define SDL_GetWindowSurface IGNORE_THIS_VERSION_OF_SDL_GetWindowSurface +#define SDL_UpdateWindowSurface IGNORE_THIS_VERSION_OF_SDL_UpdateWindowSurface +#define SDL_UpdateWindowSurfaceRects IGNORE_THIS_VERSION_OF_SDL_UpdateWindowSurfaceRects +#define SDL_SetWindowGrab IGNORE_THIS_VERSION_OF_SDL_SetWindowGrab +#define SDL_GetWindowGrab IGNORE_THIS_VERSION_OF_SDL_GetWindowGrab +#define SDL_SetWindowBrightness IGNORE_THIS_VERSION_OF_SDL_SetWindowBrightness +#define SDL_GetWindowBrightness IGNORE_THIS_VERSION_OF_SDL_GetWindowBrightness +#define SDL_SetWindowGammaRamp IGNORE_THIS_VERSION_OF_SDL_SetWindowGammaRamp +#define SDL_GetWindowGammaRamp IGNORE_THIS_VERSION_OF_SDL_GetWindowGammaRamp +#define SDL_DestroyWindow IGNORE_THIS_VERSION_OF_SDL_DestroyWindow +#define SDL_IsScreenSaverEnabled IGNORE_THIS_VERSION_OF_SDL_IsScreenSaverEnabled +#define SDL_EnableScreenSaver IGNORE_THIS_VERSION_OF_SDL_EnableScreenSaver +#define SDL_DisableScreenSaver IGNORE_THIS_VERSION_OF_SDL_DisableScreenSaver +#define SDL_GL_LoadLibrary IGNORE_THIS_VERSION_OF_SDL_GL_LoadLibrary +#define SDL_GL_GetProcAddress IGNORE_THIS_VERSION_OF_SDL_GL_GetProcAddress +#define SDL_GL_UnloadLibrary IGNORE_THIS_VERSION_OF_SDL_GL_UnloadLibrary +#define SDL_GL_ExtensionSupported IGNORE_THIS_VERSION_OF_SDL_GL_ExtensionSupported +#define SDL_GL_SetAttribute IGNORE_THIS_VERSION_OF_SDL_GL_SetAttribute +#define SDL_GL_GetAttribute IGNORE_THIS_VERSION_OF_SDL_GL_GetAttribute +#define SDL_GL_CreateContext IGNORE_THIS_VERSION_OF_SDL_GL_CreateContext +#define SDL_GL_MakeCurrent IGNORE_THIS_VERSION_OF_SDL_GL_MakeCurrent +#define SDL_GL_GetCurrentWindow IGNORE_THIS_VERSION_OF_SDL_GL_GetCurrentWindow +#define SDL_GL_GetCurrentContext IGNORE_THIS_VERSION_OF_SDL_GL_GetCurrentContext +#define SDL_GL_GetDrawableSize IGNORE_THIS_VERSION_OF_SDL_GL_GetDrawableSize +#define SDL_GL_SetSwapInterval IGNORE_THIS_VERSION_OF_SDL_GL_SetSwapInterval +#define SDL_GL_GetSwapInterval IGNORE_THIS_VERSION_OF_SDL_GL_GetSwapInterval +#define SDL_GL_SwapWindow IGNORE_THIS_VERSION_OF_SDL_GL_SwapWindow +#define SDL_GL_DeleteContext IGNORE_THIS_VERSION_OF_SDL_GL_DeleteContext +#define SDL_vsscanf IGNORE_THIS_VERSION_OF_SDL_vsscanf +#define SDL_GameControllerAddMappingsFromRW IGNORE_THIS_VERSION_OF_SDL_GameControllerAddMappingsFromRW +#define SDL_GL_ResetAttributes IGNORE_THIS_VERSION_OF_SDL_GL_ResetAttributes +#define SDL_HasAVX IGNORE_THIS_VERSION_OF_SDL_HasAVX +#define SDL_GetDefaultAssertionHandler IGNORE_THIS_VERSION_OF_SDL_GetDefaultAssertionHandler +#define SDL_GetAssertionHandler IGNORE_THIS_VERSION_OF_SDL_GetAssertionHandler +#define SDL_DXGIGetOutputInfo IGNORE_THIS_VERSION_OF_SDL_DXGIGetOutputInfo +#define SDL_RenderIsClipEnabled IGNORE_THIS_VERSION_OF_SDL_RenderIsClipEnabled +#define SDL_WinRTRunApp IGNORE_THIS_VERSION_OF_SDL_WinRTRunApp +#define SDL_WarpMouseGlobal IGNORE_THIS_VERSION_OF_SDL_WarpMouseGlobal +#define SDL_WinRTGetFSPathUNICODE IGNORE_THIS_VERSION_OF_SDL_WinRTGetFSPathUNICODE +#define SDL_WinRTGetFSPathUTF8 IGNORE_THIS_VERSION_OF_SDL_WinRTGetFSPathUTF8 +#define SDL_sqrtf IGNORE_THIS_VERSION_OF_SDL_sqrtf +#define SDL_tan IGNORE_THIS_VERSION_OF_SDL_tan +#define SDL_tanf IGNORE_THIS_VERSION_OF_SDL_tanf +#define SDL_CaptureMouse IGNORE_THIS_VERSION_OF_SDL_CaptureMouse +#define SDL_SetWindowHitTest IGNORE_THIS_VERSION_OF_SDL_SetWindowHitTest +#define SDL_GetGlobalMouseState IGNORE_THIS_VERSION_OF_SDL_GetGlobalMouseState +#define SDL_HasAVX2 IGNORE_THIS_VERSION_OF_SDL_HasAVX2 +#define SDL_QueueAudio IGNORE_THIS_VERSION_OF_SDL_QueueAudio +#define SDL_GetQueuedAudioSize IGNORE_THIS_VERSION_OF_SDL_GetQueuedAudioSize +#define SDL_ClearQueuedAudio IGNORE_THIS_VERSION_OF_SDL_ClearQueuedAudio +#define SDL_GetGrabbedWindow IGNORE_THIS_VERSION_OF_SDL_GetGrabbedWindow +#define SDL_SetWindowsMessageHook IGNORE_THIS_VERSION_OF_SDL_SetWindowsMessageHook +#define SDL_JoystickCurrentPowerLevel IGNORE_THIS_VERSION_OF_SDL_JoystickCurrentPowerLevel +#define SDL_GameControllerFromInstanceID IGNORE_THIS_VERSION_OF_SDL_GameControllerFromInstanceID +#define SDL_JoystickFromInstanceID IGNORE_THIS_VERSION_OF_SDL_JoystickFromInstanceID +#define SDL_GetDisplayUsableBounds IGNORE_THIS_VERSION_OF_SDL_GetDisplayUsableBounds +#define SDL_GetWindowBordersSize IGNORE_THIS_VERSION_OF_SDL_GetWindowBordersSize +#define SDL_SetWindowOpacity IGNORE_THIS_VERSION_OF_SDL_SetWindowOpacity +#define SDL_GetWindowOpacity IGNORE_THIS_VERSION_OF_SDL_GetWindowOpacity +#define SDL_SetWindowInputFocus IGNORE_THIS_VERSION_OF_SDL_SetWindowInputFocus +#define SDL_SetWindowModalFor IGNORE_THIS_VERSION_OF_SDL_SetWindowModalFor +#define SDL_RenderSetIntegerScale IGNORE_THIS_VERSION_OF_SDL_RenderSetIntegerScale +#define SDL_RenderGetIntegerScale IGNORE_THIS_VERSION_OF_SDL_RenderGetIntegerScale +#define SDL_DequeueAudio IGNORE_THIS_VERSION_OF_SDL_DequeueAudio +#define SDL_SetWindowResizable IGNORE_THIS_VERSION_OF_SDL_SetWindowResizable +#define SDL_CreateRGBSurfaceWithFormat IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurfaceWithFormat +#define SDL_CreateRGBSurfaceWithFormatFrom IGNORE_THIS_VERSION_OF_SDL_CreateRGBSurfaceWithFormatFrom +#define SDL_GetHintBoolean IGNORE_THIS_VERSION_OF_SDL_GetHintBoolean +#define SDL_JoystickGetDeviceVendor IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceVendor +#define SDL_JoystickGetDeviceProduct IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceProduct +#define SDL_JoystickGetDeviceProductVersion IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceProductVersion +#define SDL_JoystickGetVendor IGNORE_THIS_VERSION_OF_SDL_JoystickGetVendor +#define SDL_JoystickGetProduct IGNORE_THIS_VERSION_OF_SDL_JoystickGetProduct +#define SDL_JoystickGetProductVersion IGNORE_THIS_VERSION_OF_SDL_JoystickGetProductVersion +#define SDL_GameControllerGetVendor IGNORE_THIS_VERSION_OF_SDL_GameControllerGetVendor +#define SDL_GameControllerGetProduct IGNORE_THIS_VERSION_OF_SDL_GameControllerGetProduct +#define SDL_GameControllerGetProductVersion IGNORE_THIS_VERSION_OF_SDL_GameControllerGetProductVersion +#define SDL_HasNEON IGNORE_THIS_VERSION_OF_SDL_HasNEON +#define SDL_GameControllerNumMappings IGNORE_THIS_VERSION_OF_SDL_GameControllerNumMappings +#define SDL_GameControllerMappingForIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerMappingForIndex +#define SDL_JoystickGetAxisInitialState IGNORE_THIS_VERSION_OF_SDL_JoystickGetAxisInitialState +#define SDL_JoystickGetDeviceType IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceType +#define SDL_JoystickGetType IGNORE_THIS_VERSION_OF_SDL_JoystickGetType +#define SDL_MemoryBarrierReleaseFunction IGNORE_THIS_VERSION_OF_SDL_MemoryBarrierReleaseFunction +#define SDL_MemoryBarrierAcquireFunction IGNORE_THIS_VERSION_OF_SDL_MemoryBarrierAcquireFunction +#define SDL_JoystickGetDeviceInstanceID IGNORE_THIS_VERSION_OF_SDL_JoystickGetDeviceInstanceID +#define SDL_utf8strlen IGNORE_THIS_VERSION_OF_SDL_utf8strlen +#define SDL_LoadFile_RW IGNORE_THIS_VERSION_OF_SDL_LoadFile_RW +#define SDL_wcscmp IGNORE_THIS_VERSION_OF_SDL_wcscmp +#define SDL_ComposeCustomBlendMode IGNORE_THIS_VERSION_OF_SDL_ComposeCustomBlendMode +#define SDL_DuplicateSurface IGNORE_THIS_VERSION_OF_SDL_DuplicateSurface +#define SDL_Vulkan_LoadLibrary IGNORE_THIS_VERSION_OF_SDL_Vulkan_LoadLibrary +#define SDL_Vulkan_GetVkGetInstanceProcAddr IGNORE_THIS_VERSION_OF_SDL_Vulkan_GetVkGetInstanceProcAddr +#define SDL_Vulkan_UnloadLibrary IGNORE_THIS_VERSION_OF_SDL_Vulkan_UnloadLibrary +#define SDL_Vulkan_GetInstanceExtensions IGNORE_THIS_VERSION_OF_SDL_Vulkan_GetInstanceExtensions +#define SDL_Vulkan_CreateSurface IGNORE_THIS_VERSION_OF_SDL_Vulkan_CreateSurface +#define SDL_Vulkan_GetDrawableSize IGNORE_THIS_VERSION_OF_SDL_Vulkan_GetDrawableSize +#define SDL_LockJoysticks IGNORE_THIS_VERSION_OF_SDL_LockJoysticks +#define SDL_UnlockJoysticks IGNORE_THIS_VERSION_OF_SDL_UnlockJoysticks +#define SDL_GetMemoryFunctions IGNORE_THIS_VERSION_OF_SDL_GetMemoryFunctions +#define SDL_SetMemoryFunctions IGNORE_THIS_VERSION_OF_SDL_SetMemoryFunctions +#define SDL_GetNumAllocations IGNORE_THIS_VERSION_OF_SDL_GetNumAllocations +#define SDL_NewAudioStream IGNORE_THIS_VERSION_OF_SDL_NewAudioStream +#define SDL_AudioStreamPut IGNORE_THIS_VERSION_OF_SDL_AudioStreamPut +#define SDL_AudioStreamGet IGNORE_THIS_VERSION_OF_SDL_AudioStreamGet +#define SDL_AudioStreamClear IGNORE_THIS_VERSION_OF_SDL_AudioStreamClear +#define SDL_AudioStreamAvailable IGNORE_THIS_VERSION_OF_SDL_AudioStreamAvailable +#define SDL_FreeAudioStream IGNORE_THIS_VERSION_OF_SDL_FreeAudioStream +#define SDL_AudioStreamFlush IGNORE_THIS_VERSION_OF_SDL_AudioStreamFlush +#define SDL_acosf IGNORE_THIS_VERSION_OF_SDL_acosf +#define SDL_asinf IGNORE_THIS_VERSION_OF_SDL_asinf +#define SDL_atanf IGNORE_THIS_VERSION_OF_SDL_atanf +#define SDL_atan2f IGNORE_THIS_VERSION_OF_SDL_atan2f +#define SDL_ceilf IGNORE_THIS_VERSION_OF_SDL_ceilf +#define SDL_copysignf IGNORE_THIS_VERSION_OF_SDL_copysignf +#define SDL_fabsf IGNORE_THIS_VERSION_OF_SDL_fabsf +#define SDL_floorf IGNORE_THIS_VERSION_OF_SDL_floorf +#define SDL_logf IGNORE_THIS_VERSION_OF_SDL_logf +#define SDL_powf IGNORE_THIS_VERSION_OF_SDL_powf +#define SDL_scalbnf IGNORE_THIS_VERSION_OF_SDL_scalbnf +#define SDL_fmod IGNORE_THIS_VERSION_OF_SDL_fmod +#define SDL_fmodf IGNORE_THIS_VERSION_OF_SDL_fmodf +#define SDL_SetYUVConversionMode IGNORE_THIS_VERSION_OF_SDL_SetYUVConversionMode +#define SDL_GetYUVConversionMode IGNORE_THIS_VERSION_OF_SDL_GetYUVConversionMode +#define SDL_GetYUVConversionModeForResolution IGNORE_THIS_VERSION_OF_SDL_GetYUVConversionModeForResolution +#define SDL_RenderGetMetalLayer IGNORE_THIS_VERSION_OF_SDL_RenderGetMetalLayer +#define SDL_RenderGetMetalCommandEncoder IGNORE_THIS_VERSION_OF_SDL_RenderGetMetalCommandEncoder +#define SDL_IsAndroidTV IGNORE_THIS_VERSION_OF_SDL_IsAndroidTV +#define SDL_WinRTGetDeviceFamily IGNORE_THIS_VERSION_OF_SDL_WinRTGetDeviceFamily +#define SDL_log10 IGNORE_THIS_VERSION_OF_SDL_log10 +#define SDL_log10f IGNORE_THIS_VERSION_OF_SDL_log10f +#define SDL_GameControllerMappingForDeviceIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerMappingForDeviceIndex +#define SDL_LinuxSetThreadPriority IGNORE_THIS_VERSION_OF_SDL_LinuxSetThreadPriority +#define SDL_HasAVX512F IGNORE_THIS_VERSION_OF_SDL_HasAVX512F +#define SDL_IsChromebook IGNORE_THIS_VERSION_OF_SDL_IsChromebook +#define SDL_IsDeXMode IGNORE_THIS_VERSION_OF_SDL_IsDeXMode +#define SDL_AndroidBackButton IGNORE_THIS_VERSION_OF_SDL_AndroidBackButton +#define SDL_exp IGNORE_THIS_VERSION_OF_SDL_exp +#define SDL_expf IGNORE_THIS_VERSION_OF_SDL_expf +#define SDL_wcsdup IGNORE_THIS_VERSION_OF_SDL_wcsdup +#define SDL_GameControllerRumble IGNORE_THIS_VERSION_OF_SDL_GameControllerRumble +#define SDL_JoystickRumble IGNORE_THIS_VERSION_OF_SDL_JoystickRumble +#define SDL_NumSensors IGNORE_THIS_VERSION_OF_SDL_NumSensors +#define SDL_SensorGetDeviceName IGNORE_THIS_VERSION_OF_SDL_SensorGetDeviceName +#define SDL_SensorGetDeviceType IGNORE_THIS_VERSION_OF_SDL_SensorGetDeviceType +#define SDL_SensorGetDeviceNonPortableType IGNORE_THIS_VERSION_OF_SDL_SensorGetDeviceNonPortableType +#define SDL_SensorGetDeviceInstanceID IGNORE_THIS_VERSION_OF_SDL_SensorGetDeviceInstanceID +#define SDL_SensorOpen IGNORE_THIS_VERSION_OF_SDL_SensorOpen +#define SDL_SensorFromInstanceID IGNORE_THIS_VERSION_OF_SDL_SensorFromInstanceID +#define SDL_SensorGetName IGNORE_THIS_VERSION_OF_SDL_SensorGetName +#define SDL_SensorGetType IGNORE_THIS_VERSION_OF_SDL_SensorGetType +#define SDL_SensorGetNonPortableType IGNORE_THIS_VERSION_OF_SDL_SensorGetNonPortableType +#define SDL_SensorGetInstanceID IGNORE_THIS_VERSION_OF_SDL_SensorGetInstanceID +#define SDL_SensorGetData IGNORE_THIS_VERSION_OF_SDL_SensorGetData +#define SDL_SensorClose IGNORE_THIS_VERSION_OF_SDL_SensorClose +#define SDL_SensorUpdate IGNORE_THIS_VERSION_OF_SDL_SensorUpdate +#define SDL_IsTablet IGNORE_THIS_VERSION_OF_SDL_IsTablet +#define SDL_GetDisplayOrientation IGNORE_THIS_VERSION_OF_SDL_GetDisplayOrientation +#define SDL_HasColorKey IGNORE_THIS_VERSION_OF_SDL_HasColorKey +#define SDL_CreateThreadWithStackSize IGNORE_THIS_VERSION_OF_SDL_CreateThreadWithStackSize +#define SDL_JoystickGetDevicePlayerIndex IGNORE_THIS_VERSION_OF_SDL_JoystickGetDevicePlayerIndex +#define SDL_JoystickGetPlayerIndex IGNORE_THIS_VERSION_OF_SDL_JoystickGetPlayerIndex +#define SDL_GameControllerGetPlayerIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerGetPlayerIndex +#define SDL_RenderFlush IGNORE_THIS_VERSION_OF_SDL_RenderFlush +#define SDL_RenderDrawPointF IGNORE_THIS_VERSION_OF_SDL_RenderDrawPointF +#define SDL_RenderDrawPointsF IGNORE_THIS_VERSION_OF_SDL_RenderDrawPointsF +#define SDL_RenderDrawLineF IGNORE_THIS_VERSION_OF_SDL_RenderDrawLineF +#define SDL_RenderDrawLinesF IGNORE_THIS_VERSION_OF_SDL_RenderDrawLinesF +#define SDL_RenderDrawRectF IGNORE_THIS_VERSION_OF_SDL_RenderDrawRectF +#define SDL_RenderDrawRectsF IGNORE_THIS_VERSION_OF_SDL_RenderDrawRectsF +#define SDL_RenderFillRectF IGNORE_THIS_VERSION_OF_SDL_RenderFillRectF +#define SDL_RenderFillRectsF IGNORE_THIS_VERSION_OF_SDL_RenderFillRectsF +#define SDL_RenderCopyF IGNORE_THIS_VERSION_OF_SDL_RenderCopyF +#define SDL_RenderCopyExF IGNORE_THIS_VERSION_OF_SDL_RenderCopyExF +#define SDL_GetTouchDeviceType IGNORE_THIS_VERSION_OF_SDL_GetTouchDeviceType +#define SDL_UIKitRunApp IGNORE_THIS_VERSION_OF_SDL_UIKitRunApp +#define SDL_SIMDGetAlignment IGNORE_THIS_VERSION_OF_SDL_SIMDGetAlignment +#define SDL_SIMDAlloc IGNORE_THIS_VERSION_OF_SDL_SIMDAlloc +#define SDL_SIMDFree IGNORE_THIS_VERSION_OF_SDL_SIMDFree +#define SDL_RWsize IGNORE_THIS_VERSION_OF_SDL_RWsize +#define SDL_RWseek IGNORE_THIS_VERSION_OF_SDL_RWseek +#define SDL_RWtell IGNORE_THIS_VERSION_OF_SDL_RWtell +#define SDL_RWread IGNORE_THIS_VERSION_OF_SDL_RWread +#define SDL_RWwrite IGNORE_THIS_VERSION_OF_SDL_RWwrite +#define SDL_RWclose IGNORE_THIS_VERSION_OF_SDL_RWclose +#define SDL_LoadFile IGNORE_THIS_VERSION_OF_SDL_LoadFile +#define SDL_Metal_CreateView IGNORE_THIS_VERSION_OF_SDL_Metal_CreateView +#define SDL_Metal_DestroyView IGNORE_THIS_VERSION_OF_SDL_Metal_DestroyView +#define SDL_LockTextureToSurface IGNORE_THIS_VERSION_OF_SDL_LockTextureToSurface +#define SDL_HasARMSIMD IGNORE_THIS_VERSION_OF_SDL_HasARMSIMD +#define SDL_strtokr IGNORE_THIS_VERSION_OF_SDL_strtokr +#define SDL_wcsstr IGNORE_THIS_VERSION_OF_SDL_wcsstr +#define SDL_wcsncmp IGNORE_THIS_VERSION_OF_SDL_wcsncmp +#define SDL_GameControllerTypeForIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerTypeForIndex +#define SDL_GameControllerGetType IGNORE_THIS_VERSION_OF_SDL_GameControllerGetType +#define SDL_GameControllerFromPlayerIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerFromPlayerIndex +#define SDL_GameControllerSetPlayerIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerSetPlayerIndex +#define SDL_JoystickFromPlayerIndex IGNORE_THIS_VERSION_OF_SDL_JoystickFromPlayerIndex +#define SDL_JoystickSetPlayerIndex IGNORE_THIS_VERSION_OF_SDL_JoystickSetPlayerIndex +#define SDL_SetTextureScaleMode IGNORE_THIS_VERSION_OF_SDL_SetTextureScaleMode +#define SDL_GetTextureScaleMode IGNORE_THIS_VERSION_OF_SDL_GetTextureScaleMode +#define SDL_OnApplicationWillTerminate IGNORE_THIS_VERSION_OF_SDL_OnApplicationWillTerminate +#define SDL_OnApplicationDidReceiveMemoryWarning IGNORE_THIS_VERSION_OF_SDL_OnApplicationDidReceiveMemoryWarning +#define SDL_OnApplicationWillResignActive IGNORE_THIS_VERSION_OF_SDL_OnApplicationWillResignActive +#define SDL_OnApplicationDidEnterBackground IGNORE_THIS_VERSION_OF_SDL_OnApplicationDidEnterBackground +#define SDL_OnApplicationWillEnterForeground IGNORE_THIS_VERSION_OF_SDL_OnApplicationWillEnterForeground +#define SDL_OnApplicationDidBecomeActive IGNORE_THIS_VERSION_OF_SDL_OnApplicationDidBecomeActive +#define SDL_OnApplicationDidChangeStatusBarOrientation IGNORE_THIS_VERSION_OF_SDL_OnApplicationDidChangeStatusBarOrientation +#define SDL_GetAndroidSDKVersion IGNORE_THIS_VERSION_OF_SDL_GetAndroidSDKVersion +#define SDL_isupper IGNORE_THIS_VERSION_OF_SDL_isupper +#define SDL_islower IGNORE_THIS_VERSION_OF_SDL_islower +#define SDL_JoystickAttachVirtual IGNORE_THIS_VERSION_OF_SDL_JoystickAttachVirtual +#define SDL_JoystickDetachVirtual IGNORE_THIS_VERSION_OF_SDL_JoystickDetachVirtual +#define SDL_JoystickIsVirtual IGNORE_THIS_VERSION_OF_SDL_JoystickIsVirtual +#define SDL_JoystickSetVirtualAxis IGNORE_THIS_VERSION_OF_SDL_JoystickSetVirtualAxis +#define SDL_JoystickSetVirtualButton IGNORE_THIS_VERSION_OF_SDL_JoystickSetVirtualButton +#define SDL_JoystickSetVirtualHat IGNORE_THIS_VERSION_OF_SDL_JoystickSetVirtualHat +#define SDL_GetErrorMsg IGNORE_THIS_VERSION_OF_SDL_GetErrorMsg +#define SDL_LockSensors IGNORE_THIS_VERSION_OF_SDL_LockSensors +#define SDL_UnlockSensors IGNORE_THIS_VERSION_OF_SDL_UnlockSensors +#define SDL_Metal_GetLayer IGNORE_THIS_VERSION_OF_SDL_Metal_GetLayer +#define SDL_Metal_GetDrawableSize IGNORE_THIS_VERSION_OF_SDL_Metal_GetDrawableSize +#define SDL_trunc IGNORE_THIS_VERSION_OF_SDL_trunc +#define SDL_truncf IGNORE_THIS_VERSION_OF_SDL_truncf +#define SDL_GetPreferredLocales IGNORE_THIS_VERSION_OF_SDL_GetPreferredLocales +#define SDL_SIMDRealloc IGNORE_THIS_VERSION_OF_SDL_SIMDRealloc +#define SDL_AndroidRequestPermission IGNORE_THIS_VERSION_OF_SDL_AndroidRequestPermission +#define SDL_OpenURL IGNORE_THIS_VERSION_OF_SDL_OpenURL +#define SDL_HasSurfaceRLE IGNORE_THIS_VERSION_OF_SDL_HasSurfaceRLE +#define SDL_GameControllerHasLED IGNORE_THIS_VERSION_OF_SDL_GameControllerHasLED +#define SDL_GameControllerSetLED IGNORE_THIS_VERSION_OF_SDL_GameControllerSetLED +#define SDL_JoystickHasLED IGNORE_THIS_VERSION_OF_SDL_JoystickHasLED +#define SDL_JoystickSetLED IGNORE_THIS_VERSION_OF_SDL_JoystickSetLED +#define SDL_GameControllerRumbleTriggers IGNORE_THIS_VERSION_OF_SDL_GameControllerRumbleTriggers +#define SDL_JoystickRumbleTriggers IGNORE_THIS_VERSION_OF_SDL_JoystickRumbleTriggers +#define SDL_GameControllerHasAxis IGNORE_THIS_VERSION_OF_SDL_GameControllerHasAxis +#define SDL_GameControllerHasButton IGNORE_THIS_VERSION_OF_SDL_GameControllerHasButton +#define SDL_GameControllerGetNumTouchpads IGNORE_THIS_VERSION_OF_SDL_GameControllerGetNumTouchpads +#define SDL_GameControllerGetNumTouchpadFingers IGNORE_THIS_VERSION_OF_SDL_GameControllerGetNumTouchpadFingers +#define SDL_GameControllerGetTouchpadFinger IGNORE_THIS_VERSION_OF_SDL_GameControllerGetTouchpadFinger +#define SDL_crc32 IGNORE_THIS_VERSION_OF_SDL_crc32 +#define SDL_GameControllerGetSerial IGNORE_THIS_VERSION_OF_SDL_GameControllerGetSerial +#define SDL_JoystickGetSerial IGNORE_THIS_VERSION_OF_SDL_JoystickGetSerial +#define SDL_GameControllerHasSensor IGNORE_THIS_VERSION_OF_SDL_GameControllerHasSensor +#define SDL_GameControllerSetSensorEnabled IGNORE_THIS_VERSION_OF_SDL_GameControllerSetSensorEnabled +#define SDL_GameControllerIsSensorEnabled IGNORE_THIS_VERSION_OF_SDL_GameControllerIsSensorEnabled +#define SDL_GameControllerGetSensorData IGNORE_THIS_VERSION_OF_SDL_GameControllerGetSensorData +#define SDL_wcscasecmp IGNORE_THIS_VERSION_OF_SDL_wcscasecmp +#define SDL_wcsncasecmp IGNORE_THIS_VERSION_OF_SDL_wcsncasecmp +#define SDL_round IGNORE_THIS_VERSION_OF_SDL_round +#define SDL_roundf IGNORE_THIS_VERSION_OF_SDL_roundf +#define SDL_lround IGNORE_THIS_VERSION_OF_SDL_lround +#define SDL_lroundf IGNORE_THIS_VERSION_OF_SDL_lroundf +#define SDL_SoftStretchLinear IGNORE_THIS_VERSION_OF_SDL_SoftStretchLinear +#define SDL_RenderGetD3D11Device IGNORE_THIS_VERSION_OF_SDL_RenderGetD3D11Device +#define SDL_UpdateNVTexture IGNORE_THIS_VERSION_OF_SDL_UpdateNVTexture +#define SDL_SetWindowKeyboardGrab IGNORE_THIS_VERSION_OF_SDL_SetWindowKeyboardGrab +#define SDL_SetWindowMouseGrab IGNORE_THIS_VERSION_OF_SDL_SetWindowMouseGrab +#define SDL_GetWindowKeyboardGrab IGNORE_THIS_VERSION_OF_SDL_GetWindowKeyboardGrab +#define SDL_GetWindowMouseGrab IGNORE_THIS_VERSION_OF_SDL_GetWindowMouseGrab +#define SDL_isalpha IGNORE_THIS_VERSION_OF_SDL_isalpha +#define SDL_isalnum IGNORE_THIS_VERSION_OF_SDL_isalnum +#define SDL_isblank IGNORE_THIS_VERSION_OF_SDL_isblank +#define SDL_iscntrl IGNORE_THIS_VERSION_OF_SDL_iscntrl +#define SDL_isxdigit IGNORE_THIS_VERSION_OF_SDL_isxdigit +#define SDL_ispunct IGNORE_THIS_VERSION_OF_SDL_ispunct +#define SDL_isprint IGNORE_THIS_VERSION_OF_SDL_isprint +#define SDL_isgraph IGNORE_THIS_VERSION_OF_SDL_isgraph +#define SDL_AndroidShowToast IGNORE_THIS_VERSION_OF_SDL_AndroidShowToast +#define SDL_GetAudioDeviceSpec IGNORE_THIS_VERSION_OF_SDL_GetAudioDeviceSpec +#define SDL_TLSCleanup IGNORE_THIS_VERSION_OF_SDL_TLSCleanup +#define SDL_SetWindowAlwaysOnTop IGNORE_THIS_VERSION_OF_SDL_SetWindowAlwaysOnTop +#define SDL_FlashWindow IGNORE_THIS_VERSION_OF_SDL_FlashWindow +#define SDL_GameControllerSendEffect IGNORE_THIS_VERSION_OF_SDL_GameControllerSendEffect +#define SDL_JoystickSendEffect IGNORE_THIS_VERSION_OF_SDL_JoystickSendEffect +#define SDL_GameControllerGetSensorDataRate IGNORE_THIS_VERSION_OF_SDL_GameControllerGetSensorDataRate +#define SDL_SetTextureUserData IGNORE_THIS_VERSION_OF_SDL_SetTextureUserData +#define SDL_GetTextureUserData IGNORE_THIS_VERSION_OF_SDL_GetTextureUserData +#define SDL_RenderGeometry IGNORE_THIS_VERSION_OF_SDL_RenderGeometry +#define SDL_RenderGeometryRaw IGNORE_THIS_VERSION_OF_SDL_RenderGeometryRaw +#define SDL_RenderSetVSync IGNORE_THIS_VERSION_OF_SDL_RenderSetVSync +#define SDL_asprintf IGNORE_THIS_VERSION_OF_SDL_asprintf +#define SDL_vasprintf IGNORE_THIS_VERSION_OF_SDL_vasprintf +#define SDL_GetWindowICCProfile IGNORE_THIS_VERSION_OF_SDL_GetWindowICCProfile +#define SDL_GetTicks64 IGNORE_THIS_VERSION_OF_SDL_GetTicks64 +#define SDL_LinuxSetThreadPriorityAndPolicy IGNORE_THIS_VERSION_OF_SDL_LinuxSetThreadPriorityAndPolicy +#define SDL_GameControllerGetAppleSFSymbolsNameForButton IGNORE_THIS_VERSION_OF_SDL_GameControllerGetAppleSFSymbolsNameForButton +#define SDL_GameControllerGetAppleSFSymbolsNameForAxis IGNORE_THIS_VERSION_OF_SDL_GameControllerGetAppleSFSymbolsNameForAxis +#define SDL_hid_init IGNORE_THIS_VERSION_OF_SDL_hid_init +#define SDL_hid_exit IGNORE_THIS_VERSION_OF_SDL_hid_exit +#define SDL_hid_device_change_count IGNORE_THIS_VERSION_OF_SDL_hid_device_change_count +#define SDL_hid_enumerate IGNORE_THIS_VERSION_OF_SDL_hid_enumerate +#define SDL_hid_free_enumeration IGNORE_THIS_VERSION_OF_SDL_hid_free_enumeration +#define SDL_hid_open IGNORE_THIS_VERSION_OF_SDL_hid_open +#define SDL_hid_open_path IGNORE_THIS_VERSION_OF_SDL_hid_open_path +#define SDL_hid_write IGNORE_THIS_VERSION_OF_SDL_hid_write +#define SDL_hid_read_timeout IGNORE_THIS_VERSION_OF_SDL_hid_read_timeout +#define SDL_hid_read IGNORE_THIS_VERSION_OF_SDL_hid_read +#define SDL_hid_set_nonblocking IGNORE_THIS_VERSION_OF_SDL_hid_set_nonblocking +#define SDL_hid_send_feature_report IGNORE_THIS_VERSION_OF_SDL_hid_send_feature_report +#define SDL_hid_get_feature_report IGNORE_THIS_VERSION_OF_SDL_hid_get_feature_report +#define SDL_hid_close IGNORE_THIS_VERSION_OF_SDL_hid_close +#define SDL_hid_get_manufacturer_string IGNORE_THIS_VERSION_OF_SDL_hid_get_manufacturer_string +#define SDL_hid_get_product_string IGNORE_THIS_VERSION_OF_SDL_hid_get_product_string +#define SDL_hid_get_serial_number_string IGNORE_THIS_VERSION_OF_SDL_hid_get_serial_number_string +#define SDL_hid_get_indexed_string IGNORE_THIS_VERSION_OF_SDL_hid_get_indexed_string +#define SDL_SetWindowMouseRect IGNORE_THIS_VERSION_OF_SDL_SetWindowMouseRect +#define SDL_GetWindowMouseRect IGNORE_THIS_VERSION_OF_SDL_GetWindowMouseRect +#define SDL_RenderWindowToLogical IGNORE_THIS_VERSION_OF_SDL_RenderWindowToLogical +#define SDL_RenderLogicalToWindow IGNORE_THIS_VERSION_OF_SDL_RenderLogicalToWindow +#define SDL_JoystickHasRumble IGNORE_THIS_VERSION_OF_SDL_JoystickHasRumble +#define SDL_JoystickHasRumbleTriggers IGNORE_THIS_VERSION_OF_SDL_JoystickHasRumbleTriggers +#define SDL_GameControllerHasRumble IGNORE_THIS_VERSION_OF_SDL_GameControllerHasRumble +#define SDL_GameControllerHasRumbleTriggers IGNORE_THIS_VERSION_OF_SDL_GameControllerHasRumbleTriggers +#define SDL_hid_ble_scan IGNORE_THIS_VERSION_OF_SDL_hid_ble_scan +#define SDL_PremultiplyAlpha IGNORE_THIS_VERSION_OF_SDL_PremultiplyAlpha +#define SDL_AndroidSendMessage IGNORE_THIS_VERSION_OF_SDL_AndroidSendMessage +#define SDL_GetTouchName IGNORE_THIS_VERSION_OF_SDL_GetTouchName +#define SDL_ClearComposition IGNORE_THIS_VERSION_OF_SDL_ClearComposition +#define SDL_IsTextInputShown IGNORE_THIS_VERSION_OF_SDL_IsTextInputShown +#define SDL_HasIntersectionF IGNORE_THIS_VERSION_OF_SDL_HasIntersectionF +#define SDL_IntersectFRect IGNORE_THIS_VERSION_OF_SDL_IntersectFRect +#define SDL_UnionFRect IGNORE_THIS_VERSION_OF_SDL_UnionFRect +#define SDL_EncloseFPoints IGNORE_THIS_VERSION_OF_SDL_EncloseFPoints +#define SDL_IntersectFRectAndLine IGNORE_THIS_VERSION_OF_SDL_IntersectFRectAndLine +#define SDL_RenderGetWindow IGNORE_THIS_VERSION_OF_SDL_RenderGetWindow +#define SDL_bsearch IGNORE_THIS_VERSION_OF_SDL_bsearch +#define SDL_GameControllerPathForIndex IGNORE_THIS_VERSION_OF_SDL_GameControllerPathForIndex +#define SDL_GameControllerPath IGNORE_THIS_VERSION_OF_SDL_GameControllerPath +#define SDL_JoystickPathForIndex IGNORE_THIS_VERSION_OF_SDL_JoystickPathForIndex +#define SDL_JoystickPath IGNORE_THIS_VERSION_OF_SDL_JoystickPath +#define SDL_JoystickAttachVirtualEx IGNORE_THIS_VERSION_OF_SDL_JoystickAttachVirtualEx +#define SDL_GameControllerGetFirmwareVersion IGNORE_THIS_VERSION_OF_SDL_GameControllerGetFirmwareVersion +#define SDL_JoystickGetFirmwareVersion IGNORE_THIS_VERSION_OF_SDL_JoystickGetFirmwareVersion +#define SDL_GUIDToString IGNORE_THIS_VERSION_OF_SDL_GUIDToString +#define SDL_GUIDFromString IGNORE_THIS_VERSION_OF_SDL_GUIDFromString +#define SDL_HasLSX IGNORE_THIS_VERSION_OF_SDL_HasLSX +#define SDL_HasLASX IGNORE_THIS_VERSION_OF_SDL_HasLASX +#define SDL_RenderGetD3D12Device IGNORE_THIS_VERSION_OF_SDL_RenderGetD3D12Device +#define SDL_utf8strnlen IGNORE_THIS_VERSION_OF_SDL_utf8strnlen +#define SDL_GDKGetTaskQueue IGNORE_THIS_VERSION_OF_SDL_GDKGetTaskQueue +#define SDL_GDKRunApp IGNORE_THIS_VERSION_OF_SDL_GDKRunApp +#define SDL_GetOriginalMemoryFunctions IGNORE_THIS_VERSION_OF_SDL_GetOriginalMemoryFunctions +#define SDL_ResetKeyboard IGNORE_THIS_VERSION_OF_SDL_ResetKeyboard +#define SDL_GetDefaultAudioInfo IGNORE_THIS_VERSION_OF_SDL_GetDefaultAudioInfo +#define SDL_GetPointDisplayIndex IGNORE_THIS_VERSION_OF_SDL_GetPointDisplayIndex +#define SDL_GetRectDisplayIndex IGNORE_THIS_VERSION_OF_SDL_GetRectDisplayIndex +#define SDL_ResetHint IGNORE_THIS_VERSION_OF_SDL_ResetHint +#define SDL_crc16 IGNORE_THIS_VERSION_OF_SDL_crc16 +#define SDL_GetWindowSizeInPixels IGNORE_THIS_VERSION_OF_SDL_GetWindowSizeInPixels +#define SDL_GetJoystickGUIDInfo IGNORE_THIS_VERSION_OF_SDL_GetJoystickGUIDInfo +#define SDL_SetPrimarySelectionText IGNORE_THIS_VERSION_OF_SDL_SetPrimarySelectionText +#define SDL_GetPrimarySelectionText IGNORE_THIS_VERSION_OF_SDL_GetPrimarySelectionText +#define SDL_HasPrimarySelectionText IGNORE_THIS_VERSION_OF_SDL_HasPrimarySelectionText +#define SDL_GameControllerGetSensorDataWithTimestamp IGNORE_THIS_VERSION_OF_SDL_GameControllerGetSensorDataWithTimestamp +#define SDL_SensorGetDataWithTimestamp IGNORE_THIS_VERSION_OF_SDL_SensorGetDataWithTimestamp +#define SDL_ResetHints IGNORE_THIS_VERSION_OF_SDL_ResetHints +#define SDL_strcasestr IGNORE_THIS_VERSION_OF_SDL_strcasestr +#define SDL_GDKSuspendComplete IGNORE_THIS_VERSION_OF_SDL_GDKSuspendComplete +#define SDL_HasWindowSurface IGNORE_THIS_VERSION_OF_SDL_HasWindowSurface +#define SDL_DestroyWindowSurface IGNORE_THIS_VERSION_OF_SDL_DestroyWindowSurface +#define SDL_GDKGetDefaultUser IGNORE_THIS_VERSION_OF_SDL_GDKGetDefaultUser +#define SDL_GameControllerGetSteamHandle IGNORE_THIS_VERSION_OF_SDL_GameControllerGetSteamHandle -#define BUILD_SDL 1 +#if defined(_WIN32) || defined(__OS2__) /* *** HACK HACK HACK: * *** Avoid including SDL_thread.h: it defines SDL_CreateThread() as a macro */ -#if defined(_WIN32) || defined(__OS2__) #define _SDL_thread_h #define SDL_thread_h_ #define SDL_PASSED_BEGINTHREAD_ENDTHREAD #endif #ifdef __OS2__ #define INCL_DOSMODULEMGR /* for Dos_LoadModule() & co. */ +#define INCL_DOSPROCESS #endif #define __BUILDING_SDL12_COMPAT__ 1 #include "SDL.h" -#include "SDL_syswm.h" /* includes windows.h for _WIN32, os2.h for __OS2__ */ +#include "SDL_syswm.h" /* includes windows.h or os2.h */ + +/* Headers from SDL2 >= 2.0.7 needed for SDL_AudioStream. */ +#if !SDL_VERSION_ATLEAST(2,0,7) +#error You need to compile against SDL >= 2.0.7 headers. +#endif -/* Missing SDL_thread.h stuff (see above): */ +#if !SDL_VERSION_ATLEAST(2,0,10) +// SDL_PixelFormatEnum was an anonymous enum before SDL 2.0.10. +// Force it to Uint32 if compiling on older headers. +typedef Uint32 SDL_PixelFormatEnum; +#endif + +/* Missing SDL_thread.h stuff (see above) */ #if defined(_WIN32) || defined(__OS2__) typedef struct SDL_Thread SDL_Thread; typedef int (SDLCALL *SDL_ThreadFunction) (void*); @@ -127,262 +941,3600 @@ typedef void (*pfnSDL_CurrentEndThread) (void); typedef UINT_PTR (__cdecl *pfnSDL_CurrentBeginThread) (void*, unsigned, unsigned (__stdcall *func)(void*), void*, unsigned, unsigned*); typedef void (__cdecl *pfnSDL_CurrentEndThread) (unsigned); -/* the following macros from Win32 SDK headers are harmful here: */ +/* the following macros from Win32 SDK headers are harmful here. */ +#undef CreateWindow #undef CreateThread #undef CreateSemaphore #undef CreateMutex #endif /* _WIN32 */ -#undef SDL_Log -#undef SDL_GetVersion -#undef SDL_ReportAssertion -#undef SDL_Error + +#ifdef SDL_SetError #undef SDL_SetError -#undef SDL_PollEvent -#undef SDL_PushEvent -#undef SDL_EventState -#undef SDL_PeepEvents -#undef SDL_WaitEvent -#undef SDL_SetEventFilter -#undef SDL_GetEventFilter -#undef SDL_CreateRGBSurface -#undef SDL_CreateRGBSurfaceFrom -#undef SDL_FreeSurface -#undef SDL_SetClipRect -#undef SDL_GetClipRect -#undef SDL_FillRect -#undef SDL_GetRGB -#undef SDL_GetRGBA -#undef SDL_MapRGB -#undef SDL_MapRGBA -#undef SDL_CreateCursor -#undef SDL_SetCursor -#undef SDL_GetCursor -#undef SDL_FreeCursor -#undef SDL_UpdateRect -#undef SDL_UpdateRects -#undef SDL_GetMouseState -#undef SDL_GetRelativeMouseState -#undef SDL_GL_SetAttribute -#undef SDL_GL_GetAttribute -#undef SDL_CreateThread -#undef SDL_AddTimer -#undef SDL_RemoveTimer -#undef SDL_AllocRW -#undef SDL_FreeRW -#undef SDL_RWFromFile -#undef SDL_RWFromFP -#undef SDL_RWFromMem -#undef SDL_RWFromConstMem -#undef SDL_ReadLE16 -#undef SDL_ReadBE16 -#undef SDL_ReadLE32 -#undef SDL_ReadBE32 -#undef SDL_ReadLE64 -#undef SDL_ReadBE64 -#undef SDL_WriteLE16 -#undef SDL_WriteBE16 -#undef SDL_WriteLE32 -#undef SDL_WriteBE32 -#undef SDL_WriteLE64 -#undef SDL_WriteBE64 -#undef SDL_GetThreadID -#undef SDL_ThreadID -#undef SDL_JoystickName -#undef SDL_LoadBMP_RW -#undef SDL_SaveBMP_RW -#undef SDL_LoadWAV_RW -#undef SDL_UpperBlit -#undef SDL_LowerBlit -#undef SDL_SoftStretch -#undef SDL_ConvertSurface -#undef SDL_SetColorKey -#undef SDL_LockSurface -#undef SDL_UnlockSurface -#undef SDL_GetKeyName -#undef SDL_VideoInit +#endif -#ifdef SDL_mutexP -#undef SDL_mutexP +#ifdef SDL_Log +#undef SDL_Log #endif -#ifdef SDL_mutexV -#undef SDL_mutexV +#ifdef SDL_LogVerbose +#undef SDL_LogVerbose #endif -#ifdef SDL_BlitSurface -#undef SDL_BlitSurface +#ifdef SDL_LogDebug +#undef SDL_LogDebug #endif -#ifdef SDL_malloc -#undef SDL_malloc +#ifdef SDL_LogInfo +#undef SDL_LogInfo #endif -#ifdef SDL_calloc -#undef SDL_calloc +#ifdef SDL_LogWarn +#undef SDL_LogWarn #endif -#ifdef SDL_realloc -#undef SDL_realloc +#ifdef SDL_LogError +#undef SDL_LogError #endif -#ifdef SDL_free -#undef SDL_free +#ifdef SDL_LogCritical +#undef SDL_LogCritical #endif -#ifdef SDL_getenv -#undef SDL_getenv +#ifdef SDL_LogMessage +#undef SDL_LogMessage #endif -#ifdef SDL_qsort -#undef SDL_qsort +#ifdef SDL_sscanf +#undef SDL_sscanf #endif -#ifdef SDL_memset -#undef SDL_memset +#ifdef SDL_snprintf +#undef SDL_snprintf #endif -#ifdef SDL_memcpy -#undef SDL_memcpy +#ifdef SDL_CreateThread +#undef SDL_CreateThread #endif -#ifdef SDL_revcpy -#undef SDL_revcpy +#ifdef SDL_RWFromFP +#undef SDL_RWFromFP #endif -#ifdef SDL_memcmp -#undef SDL_memcmp +#ifdef SDL_RegisterApp +#undef SDL_RegisterApp #endif -#ifdef SDL_strlen -#undef SDL_strlen +#ifdef SDL_UnregisterApp +#undef SDL_UnregisterApp #endif -#ifdef SDL_strlcpy -#undef SDL_strlcpy +#ifdef SDL_Direct3D9GetAdapterIndex +#undef SDL_Direct3D9GetAdapterIndex #endif -#ifdef SDL_strlcat -#undef SDL_strlcat +#ifdef SDL_RenderGetD3D9Device +#undef SDL_RenderGetD3D9Device #endif -#ifdef SDL_strdup -#undef SDL_strdup +#ifdef SDL_iPhoneSetAnimationCallback +#undef SDL_iPhoneSetAnimationCallback #endif -#ifdef SDL_strrev -#undef SDL_strrev +#ifdef SDL_iPhoneSetEventPump +#undef SDL_iPhoneSetEventPump #endif -#ifdef SDL_strupr -#undef SDL_strupr +#ifdef SDL_AndroidGetJNIEnv +#undef SDL_AndroidGetJNIEnv #endif -#ifdef SDL_strlwr -#undef SDL_strlwr +#ifdef SDL_AndroidGetActivity +#undef SDL_AndroidGetActivity #endif -#ifdef SDL_strchr -#undef SDL_strchr +#ifdef SDL_AndroidGetInternalStoragePath +#undef SDL_AndroidGetInternalStoragePath #endif -#ifdef SDL_strrchr -#undef SDL_strrchr +#ifdef SDL_AndroidGetExternalStorageState +#undef SDL_AndroidGetExternalStorageState #endif -#ifdef SDL_strstr -#undef SDL_strstr +#ifdef SDL_AndroidGetExternalStoragePath +#undef SDL_AndroidGetExternalStoragePath #endif -#ifdef SDL_ltoa -#undef SDL_ltoa +#ifdef SDL_Init +#undef SDL_Init #endif -#ifdef SDL_ultoa -#undef SDL_ultoa +#ifdef SDL_InitSubSystem +#undef SDL_InitSubSystem #endif -#ifdef SDL_strtol -#undef SDL_strtol +#ifdef SDL_QuitSubSystem +#undef SDL_QuitSubSystem #endif -#ifdef SDL_strtoul -#undef SDL_strtoul +#ifdef SDL_WasInit +#undef SDL_WasInit #endif -#ifdef SDL_lltoa -#undef SDL_lltoa +#ifdef SDL_Quit +#undef SDL_Quit #endif -#ifdef SDL_ulltoa -#undef SDL_ulltoa +#ifdef SDL_ReportAssertion +#undef SDL_ReportAssertion #endif -#ifdef SDL_strtoll -#undef SDL_strtoll +#ifdef SDL_SetAssertionHandler +#undef SDL_SetAssertionHandler #endif -#ifdef SDL_strtoull -#undef SDL_strtoull +#ifdef SDL_GetAssertionReport +#undef SDL_GetAssertionReport #endif -#ifdef SDL_strtod -#undef SDL_strtod +#ifdef SDL_ResetAssertionReport +#undef SDL_ResetAssertionReport #endif -#ifdef SDL_strcmp -#undef SDL_strcmp +#ifdef SDL_AtomicTryLock +#undef SDL_AtomicTryLock #endif -#ifdef SDL_strncmp -#undef SDL_strncmp +#ifdef SDL_AtomicLock +#undef SDL_AtomicLock #endif -#ifdef SDL_strcasecmp -#undef SDL_strcasecmp +#ifdef SDL_AtomicUnlock +#undef SDL_AtomicUnlock #endif -#ifdef SDL_strncasecmp -#undef SDL_strncasecmp +#ifdef SDL_AtomicCAS +#undef SDL_AtomicCAS #endif -#ifdef SDL_sscanf -#undef SDL_sscanf +#ifdef SDL_AtomicSet +#undef SDL_AtomicSet #endif -#ifdef SDL_snprintf -#undef SDL_snprintf +#ifdef SDL_AtomicGet +#undef SDL_AtomicGet #endif -#ifdef SDL_vsnprintf -#undef SDL_vsnprintf +#ifdef SDL_AtomicAdd +#undef SDL_AtomicAdd #endif -#ifdef SDL_iconv_open -#undef SDL_iconv_open +#ifdef SDL_AtomicCASPtr +#undef SDL_AtomicCASPtr #endif -#ifdef SDL_iconv_close -#undef SDL_iconv_close +#ifdef SDL_AtomicSetPtr +#undef SDL_AtomicSetPtr #endif -#ifdef SDL_iconv -#undef SDL_iconv +#ifdef SDL_AtomicGetPtr +#undef SDL_AtomicGetPtr #endif -#ifdef SDL_iconv_string -#undef SDL_iconv_string +#ifdef SDL_GetNumAudioDrivers +#undef SDL_GetNumAudioDrivers #endif -#ifdef SDL_atoi -#undef SDL_atoi +#ifdef SDL_GetAudioDriver +#undef SDL_GetAudioDriver #endif -#ifdef SDL_setenv -#undef SDL_setenv +#ifdef SDL_AudioInit +#undef SDL_AudioInit #endif +#ifdef SDL_AudioQuit +#undef SDL_AudioQuit #endif -/* vi: set ts=4 sw=4 expandtab: */ +#ifdef SDL_GetCurrentAudioDriver +#undef SDL_GetCurrentAudioDriver +#endif + +#ifdef SDL_OpenAudio +#undef SDL_OpenAudio +#endif + +#ifdef SDL_GetNumAudioDevices +#undef SDL_GetNumAudioDevices +#endif + +#ifdef SDL_GetAudioDeviceName +#undef SDL_GetAudioDeviceName +#endif + +#ifdef SDL_OpenAudioDevice +#undef SDL_OpenAudioDevice +#endif + +#ifdef SDL_GetAudioStatus +#undef SDL_GetAudioStatus +#endif + +#ifdef SDL_GetAudioDeviceStatus +#undef SDL_GetAudioDeviceStatus +#endif + +#ifdef SDL_PauseAudio +#undef SDL_PauseAudio +#endif + +#ifdef SDL_PauseAudioDevice +#undef SDL_PauseAudioDevice +#endif +#ifdef SDL_LoadWAV_RW +#undef SDL_LoadWAV_RW +#endif + +#ifdef SDL_FreeWAV +#undef SDL_FreeWAV +#endif + +#ifdef SDL_BuildAudioCVT +#undef SDL_BuildAudioCVT +#endif + +#ifdef SDL_ConvertAudio +#undef SDL_ConvertAudio +#endif + +#ifdef SDL_MixAudio +#undef SDL_MixAudio +#endif + +#ifdef SDL_MixAudioFormat +#undef SDL_MixAudioFormat +#endif + +#ifdef SDL_LockAudio +#undef SDL_LockAudio +#endif + +#ifdef SDL_LockAudioDevice +#undef SDL_LockAudioDevice +#endif + +#ifdef SDL_UnlockAudio +#undef SDL_UnlockAudio +#endif + +#ifdef SDL_UnlockAudioDevice +#undef SDL_UnlockAudioDevice +#endif + +#ifdef SDL_CloseAudio +#undef SDL_CloseAudio +#endif + +#ifdef SDL_CloseAudioDevice +#undef SDL_CloseAudioDevice +#endif + +#ifdef SDL_SetClipboardText +#undef SDL_SetClipboardText +#endif + +#ifdef SDL_GetClipboardText +#undef SDL_GetClipboardText +#endif + +#ifdef SDL_HasClipboardText +#undef SDL_HasClipboardText +#endif + +#ifdef SDL_GetCPUCount +#undef SDL_GetCPUCount +#endif + +#ifdef SDL_GetCPUCacheLineSize +#undef SDL_GetCPUCacheLineSize +#endif + +#ifdef SDL_HasRDTSC +#undef SDL_HasRDTSC +#endif + +#ifdef SDL_HasAltiVec +#undef SDL_HasAltiVec +#endif + +#ifdef SDL_HasMMX +#undef SDL_HasMMX +#endif + +#ifdef SDL_Has3DNow +#undef SDL_Has3DNow +#endif + +#ifdef SDL_HasSSE +#undef SDL_HasSSE +#endif + +#ifdef SDL_HasSSE2 +#undef SDL_HasSSE2 +#endif + +#ifdef SDL_HasSSE3 +#undef SDL_HasSSE3 +#endif + +#ifdef SDL_HasSSE41 +#undef SDL_HasSSE41 +#endif + +#ifdef SDL_HasSSE42 +#undef SDL_HasSSE42 +#endif + +#ifdef SDL_GetSystemRAM +#undef SDL_GetSystemRAM +#endif + +#ifdef SDL_GetError +#undef SDL_GetError +#endif + +#ifdef SDL_ClearError +#undef SDL_ClearError +#endif + +#ifdef SDL_Error +#undef SDL_Error +#endif + +#ifdef SDL_PumpEvents +#undef SDL_PumpEvents +#endif + +#ifdef SDL_PeepEvents +#undef SDL_PeepEvents +#endif + +#ifdef SDL_HasEvent +#undef SDL_HasEvent +#endif + +#ifdef SDL_HasEvents +#undef SDL_HasEvents +#endif + +#ifdef SDL_FlushEvent +#undef SDL_FlushEvent +#endif + +#ifdef SDL_FlushEvents +#undef SDL_FlushEvents +#endif + +#ifdef SDL_PollEvent +#undef SDL_PollEvent +#endif + +#ifdef SDL_WaitEvent +#undef SDL_WaitEvent +#endif + +#ifdef SDL_WaitEventTimeout +#undef SDL_WaitEventTimeout +#endif + +#ifdef SDL_PushEvent +#undef SDL_PushEvent +#endif + +#ifdef SDL_SetEventFilter +#undef SDL_SetEventFilter +#endif + +#ifdef SDL_GetEventFilter +#undef SDL_GetEventFilter +#endif + +#ifdef SDL_AddEventWatch +#undef SDL_AddEventWatch +#endif + +#ifdef SDL_DelEventWatch +#undef SDL_DelEventWatch +#endif + +#ifdef SDL_FilterEvents +#undef SDL_FilterEvents +#endif + +#ifdef SDL_EventState +#undef SDL_EventState +#endif + +#ifdef SDL_RegisterEvents +#undef SDL_RegisterEvents +#endif + +#ifdef SDL_GetBasePath +#undef SDL_GetBasePath +#endif + +#ifdef SDL_GetPrefPath +#undef SDL_GetPrefPath +#endif + +#ifdef SDL_GameControllerAddMapping +#undef SDL_GameControllerAddMapping +#endif + +#ifdef SDL_GameControllerMappingForGUID +#undef SDL_GameControllerMappingForGUID +#endif + +#ifdef SDL_GameControllerMapping +#undef SDL_GameControllerMapping +#endif + +#ifdef SDL_IsGameController +#undef SDL_IsGameController +#endif + +#ifdef SDL_GameControllerNameForIndex +#undef SDL_GameControllerNameForIndex +#endif + +#ifdef SDL_GameControllerOpen +#undef SDL_GameControllerOpen +#endif + +#ifdef SDL_GameControllerName +#undef SDL_GameControllerName +#endif + +#ifdef SDL_GameControllerGetAttached +#undef SDL_GameControllerGetAttached +#endif + +#ifdef SDL_GameControllerGetJoystick +#undef SDL_GameControllerGetJoystick +#endif + +#ifdef SDL_GameControllerEventState +#undef SDL_GameControllerEventState +#endif + +#ifdef SDL_GameControllerUpdate +#undef SDL_GameControllerUpdate +#endif + +#ifdef SDL_GameControllerGetAxisFromString +#undef SDL_GameControllerGetAxisFromString +#endif + +#ifdef SDL_GameControllerGetStringForAxis +#undef SDL_GameControllerGetStringForAxis +#endif + +#ifdef SDL_GameControllerGetBindForAxis +#undef SDL_GameControllerGetBindForAxis +#endif + +#ifdef SDL_GameControllerGetAxis +#undef SDL_GameControllerGetAxis +#endif + +#ifdef SDL_GameControllerGetButtonFromString +#undef SDL_GameControllerGetButtonFromString +#endif + +#ifdef SDL_GameControllerGetStringForButton +#undef SDL_GameControllerGetStringForButton +#endif + +#ifdef SDL_GameControllerGetBindForButton +#undef SDL_GameControllerGetBindForButton +#endif + +#ifdef SDL_GameControllerGetButton +#undef SDL_GameControllerGetButton +#endif + +#ifdef SDL_GameControllerClose +#undef SDL_GameControllerClose +#endif + +#ifdef SDL_RecordGesture +#undef SDL_RecordGesture +#endif + +#ifdef SDL_SaveAllDollarTemplates +#undef SDL_SaveAllDollarTemplates +#endif + +#ifdef SDL_SaveDollarTemplate +#undef SDL_SaveDollarTemplate +#endif + +#ifdef SDL_LoadDollarTemplates +#undef SDL_LoadDollarTemplates +#endif + +#ifdef SDL_NumHaptics +#undef SDL_NumHaptics +#endif + +#ifdef SDL_HapticName +#undef SDL_HapticName +#endif + +#ifdef SDL_HapticOpen +#undef SDL_HapticOpen +#endif + +#ifdef SDL_HapticOpened +#undef SDL_HapticOpened +#endif + +#ifdef SDL_HapticIndex +#undef SDL_HapticIndex +#endif + +#ifdef SDL_MouseIsHaptic +#undef SDL_MouseIsHaptic +#endif + +#ifdef SDL_HapticOpenFromMouse +#undef SDL_HapticOpenFromMouse +#endif + +#ifdef SDL_JoystickIsHaptic +#undef SDL_JoystickIsHaptic +#endif + +#ifdef SDL_HapticOpenFromJoystick +#undef SDL_HapticOpenFromJoystick +#endif + +#ifdef SDL_HapticClose +#undef SDL_HapticClose +#endif + +#ifdef SDL_HapticNumEffects +#undef SDL_HapticNumEffects +#endif + +#ifdef SDL_HapticNumEffectsPlaying +#undef SDL_HapticNumEffectsPlaying +#endif + +#ifdef SDL_HapticQuery +#undef SDL_HapticQuery +#endif + +#ifdef SDL_HapticNumAxes +#undef SDL_HapticNumAxes +#endif + +#ifdef SDL_HapticEffectSupported +#undef SDL_HapticEffectSupported +#endif + +#ifdef SDL_HapticNewEffect +#undef SDL_HapticNewEffect +#endif + +#ifdef SDL_HapticUpdateEffect +#undef SDL_HapticUpdateEffect +#endif + +#ifdef SDL_HapticRunEffect +#undef SDL_HapticRunEffect +#endif + +#ifdef SDL_HapticStopEffect +#undef SDL_HapticStopEffect +#endif + +#ifdef SDL_HapticDestroyEffect +#undef SDL_HapticDestroyEffect +#endif + +#ifdef SDL_HapticGetEffectStatus +#undef SDL_HapticGetEffectStatus +#endif + +#ifdef SDL_HapticSetGain +#undef SDL_HapticSetGain +#endif + +#ifdef SDL_HapticSetAutocenter +#undef SDL_HapticSetAutocenter +#endif + +#ifdef SDL_HapticPause +#undef SDL_HapticPause +#endif + +#ifdef SDL_HapticUnpause +#undef SDL_HapticUnpause +#endif + +#ifdef SDL_HapticStopAll +#undef SDL_HapticStopAll +#endif + +#ifdef SDL_HapticRumbleSupported +#undef SDL_HapticRumbleSupported +#endif + +#ifdef SDL_HapticRumbleInit +#undef SDL_HapticRumbleInit +#endif + +#ifdef SDL_HapticRumblePlay +#undef SDL_HapticRumblePlay +#endif + +#ifdef SDL_HapticRumbleStop +#undef SDL_HapticRumbleStop +#endif + +#ifdef SDL_SetHintWithPriority +#undef SDL_SetHintWithPriority +#endif + +#ifdef SDL_SetHint +#undef SDL_SetHint +#endif + +#ifdef SDL_GetHint +#undef SDL_GetHint +#endif + +#ifdef SDL_AddHintCallback +#undef SDL_AddHintCallback +#endif + +#ifdef SDL_DelHintCallback +#undef SDL_DelHintCallback +#endif + +#ifdef SDL_ClearHints +#undef SDL_ClearHints +#endif + +#ifdef SDL_NumJoysticks +#undef SDL_NumJoysticks +#endif + +#ifdef SDL_JoystickNameForIndex +#undef SDL_JoystickNameForIndex +#endif + +#ifdef SDL_JoystickOpen +#undef SDL_JoystickOpen +#endif + +#ifdef SDL_JoystickName +#undef SDL_JoystickName +#endif + +#ifdef SDL_JoystickGetDeviceGUID +#undef SDL_JoystickGetDeviceGUID +#endif + +#ifdef SDL_JoystickGetGUID +#undef SDL_JoystickGetGUID +#endif + +#ifdef SDL_JoystickGetGUIDString +#undef SDL_JoystickGetGUIDString +#endif + +#ifdef SDL_JoystickGetGUIDFromString +#undef SDL_JoystickGetGUIDFromString +#endif + +#ifdef SDL_JoystickGetAttached +#undef SDL_JoystickGetAttached +#endif + +#ifdef SDL_JoystickInstanceID +#undef SDL_JoystickInstanceID +#endif + +#ifdef SDL_JoystickNumAxes +#undef SDL_JoystickNumAxes +#endif + +#ifdef SDL_JoystickNumBalls +#undef SDL_JoystickNumBalls +#endif + +#ifdef SDL_JoystickNumHats +#undef SDL_JoystickNumHats +#endif + +#ifdef SDL_JoystickNumButtons +#undef SDL_JoystickNumButtons +#endif + +#ifdef SDL_JoystickUpdate +#undef SDL_JoystickUpdate +#endif + +#ifdef SDL_JoystickEventState +#undef SDL_JoystickEventState +#endif + +#ifdef SDL_JoystickGetAxis +#undef SDL_JoystickGetAxis +#endif + +#ifdef SDL_JoystickGetHat +#undef SDL_JoystickGetHat +#endif + +#ifdef SDL_JoystickGetBall +#undef SDL_JoystickGetBall +#endif + +#ifdef SDL_JoystickGetButton +#undef SDL_JoystickGetButton +#endif + +#ifdef SDL_JoystickClose +#undef SDL_JoystickClose +#endif + +#ifdef SDL_GetKeyboardFocus +#undef SDL_GetKeyboardFocus +#endif + +#ifdef SDL_GetKeyboardState +#undef SDL_GetKeyboardState +#endif + +#ifdef SDL_GetModState +#undef SDL_GetModState +#endif + +#ifdef SDL_SetModState +#undef SDL_SetModState +#endif + +#ifdef SDL_GetKeyFromScancode +#undef SDL_GetKeyFromScancode +#endif + +#ifdef SDL_GetScancodeFromKey +#undef SDL_GetScancodeFromKey +#endif + +#ifdef SDL_GetScancodeName +#undef SDL_GetScancodeName +#endif + +#ifdef SDL_GetScancodeFromName +#undef SDL_GetScancodeFromName +#endif + +#ifdef SDL_GetKeyName +#undef SDL_GetKeyName +#endif + +#ifdef SDL_GetKeyFromName +#undef SDL_GetKeyFromName +#endif + +#ifdef SDL_StartTextInput +#undef SDL_StartTextInput +#endif + +#ifdef SDL_IsTextInputActive +#undef SDL_IsTextInputActive +#endif + +#ifdef SDL_StopTextInput +#undef SDL_StopTextInput +#endif + +#ifdef SDL_SetTextInputRect +#undef SDL_SetTextInputRect +#endif + +#ifdef SDL_HasScreenKeyboardSupport +#undef SDL_HasScreenKeyboardSupport +#endif + +#ifdef SDL_IsScreenKeyboardShown +#undef SDL_IsScreenKeyboardShown +#endif + +#ifdef SDL_LoadObject +#undef SDL_LoadObject +#endif + +#ifdef SDL_LoadFunction +#undef SDL_LoadFunction +#endif + +#ifdef SDL_UnloadObject +#undef SDL_UnloadObject +#endif + +#ifdef SDL_LogSetAllPriority +#undef SDL_LogSetAllPriority +#endif + +#ifdef SDL_LogSetPriority +#undef SDL_LogSetPriority +#endif + +#ifdef SDL_LogGetPriority +#undef SDL_LogGetPriority +#endif + +#ifdef SDL_LogResetPriorities +#undef SDL_LogResetPriorities +#endif + +#ifdef SDL_LogMessageV +#undef SDL_LogMessageV +#endif + +#ifdef SDL_LogGetOutputFunction +#undef SDL_LogGetOutputFunction +#endif + +#ifdef SDL_LogSetOutputFunction +#undef SDL_LogSetOutputFunction +#endif + +#ifdef SDL_SetMainReady +#undef SDL_SetMainReady +#endif + +#ifdef SDL_ShowMessageBox +#undef SDL_ShowMessageBox +#endif + +#ifdef SDL_ShowSimpleMessageBox +#undef SDL_ShowSimpleMessageBox +#endif + +#ifdef SDL_GetMouseFocus +#undef SDL_GetMouseFocus +#endif + +#ifdef SDL_GetMouseState +#undef SDL_GetMouseState +#endif + +#ifdef SDL_GetRelativeMouseState +#undef SDL_GetRelativeMouseState +#endif + +#ifdef SDL_WarpMouseInWindow +#undef SDL_WarpMouseInWindow +#endif + +#ifdef SDL_SetRelativeMouseMode +#undef SDL_SetRelativeMouseMode +#endif + +#ifdef SDL_GetRelativeMouseMode +#undef SDL_GetRelativeMouseMode +#endif + +#ifdef SDL_CreateCursor +#undef SDL_CreateCursor +#endif + +#ifdef SDL_CreateColorCursor +#undef SDL_CreateColorCursor +#endif + +#ifdef SDL_CreateSystemCursor +#undef SDL_CreateSystemCursor +#endif + +#ifdef SDL_SetCursor +#undef SDL_SetCursor +#endif + +#ifdef SDL_GetCursor +#undef SDL_GetCursor +#endif + +#ifdef SDL_GetDefaultCursor +#undef SDL_GetDefaultCursor +#endif + +#ifdef SDL_FreeCursor +#undef SDL_FreeCursor +#endif + +#ifdef SDL_ShowCursor +#undef SDL_ShowCursor +#endif + +#ifdef SDL_CreateMutex +#undef SDL_CreateMutex +#endif + +#ifdef SDL_LockMutex +#undef SDL_LockMutex +#endif + +#ifdef SDL_TryLockMutex +#undef SDL_TryLockMutex +#endif + +#ifdef SDL_UnlockMutex +#undef SDL_UnlockMutex +#endif + +#ifdef SDL_DestroyMutex +#undef SDL_DestroyMutex +#endif + +#ifdef SDL_CreateSemaphore +#undef SDL_CreateSemaphore +#endif + +#ifdef SDL_DestroySemaphore +#undef SDL_DestroySemaphore +#endif + +#ifdef SDL_SemWait +#undef SDL_SemWait +#endif + +#ifdef SDL_SemTryWait +#undef SDL_SemTryWait +#endif + +#ifdef SDL_SemWaitTimeout +#undef SDL_SemWaitTimeout +#endif + +#ifdef SDL_SemPost +#undef SDL_SemPost +#endif + +#ifdef SDL_SemValue +#undef SDL_SemValue +#endif + +#ifdef SDL_CreateCond +#undef SDL_CreateCond +#endif + +#ifdef SDL_DestroyCond +#undef SDL_DestroyCond +#endif + +#ifdef SDL_CondSignal +#undef SDL_CondSignal +#endif + +#ifdef SDL_CondBroadcast +#undef SDL_CondBroadcast +#endif + +#ifdef SDL_CondWait +#undef SDL_CondWait +#endif + +#ifdef SDL_CondWaitTimeout +#undef SDL_CondWaitTimeout +#endif + +#ifdef SDL_GetPixelFormatName +#undef SDL_GetPixelFormatName +#endif + +#ifdef SDL_PixelFormatEnumToMasks +#undef SDL_PixelFormatEnumToMasks +#endif + +#ifdef SDL_MasksToPixelFormatEnum +#undef SDL_MasksToPixelFormatEnum +#endif + +#ifdef SDL_AllocFormat +#undef SDL_AllocFormat +#endif + +#ifdef SDL_FreeFormat +#undef SDL_FreeFormat +#endif + +#ifdef SDL_AllocPalette +#undef SDL_AllocPalette +#endif + +#ifdef SDL_SetPixelFormatPalette +#undef SDL_SetPixelFormatPalette +#endif + +#ifdef SDL_SetPaletteColors +#undef SDL_SetPaletteColors +#endif + +#ifdef SDL_FreePalette +#undef SDL_FreePalette +#endif + +#ifdef SDL_MapRGB +#undef SDL_MapRGB +#endif + +#ifdef SDL_MapRGBA +#undef SDL_MapRGBA +#endif + +#ifdef SDL_GetRGB +#undef SDL_GetRGB +#endif + +#ifdef SDL_GetRGBA +#undef SDL_GetRGBA +#endif + +#ifdef SDL_CalculateGammaRamp +#undef SDL_CalculateGammaRamp +#endif + +#ifdef SDL_GetPlatform +#undef SDL_GetPlatform +#endif + +#ifdef SDL_GetPowerInfo +#undef SDL_GetPowerInfo +#endif + +#ifdef SDL_HasIntersection +#undef SDL_HasIntersection +#endif + +#ifdef SDL_IntersectRect +#undef SDL_IntersectRect +#endif + +#ifdef SDL_UnionRect +#undef SDL_UnionRect +#endif + +#ifdef SDL_EnclosePoints +#undef SDL_EnclosePoints +#endif + +#ifdef SDL_IntersectRectAndLine +#undef SDL_IntersectRectAndLine +#endif + +#ifdef SDL_GetNumRenderDrivers +#undef SDL_GetNumRenderDrivers +#endif + +#ifdef SDL_GetRenderDriverInfo +#undef SDL_GetRenderDriverInfo +#endif + +#ifdef SDL_CreateWindowAndRenderer +#undef SDL_CreateWindowAndRenderer +#endif + +#ifdef SDL_CreateRenderer +#undef SDL_CreateRenderer +#endif + +#ifdef SDL_CreateSoftwareRenderer +#undef SDL_CreateSoftwareRenderer +#endif + +#ifdef SDL_GetRenderer +#undef SDL_GetRenderer +#endif + +#ifdef SDL_GetRendererInfo +#undef SDL_GetRendererInfo +#endif + +#ifdef SDL_GetRendererOutputSize +#undef SDL_GetRendererOutputSize +#endif + +#ifdef SDL_CreateTexture +#undef SDL_CreateTexture +#endif + +#ifdef SDL_CreateTextureFromSurface +#undef SDL_CreateTextureFromSurface +#endif + +#ifdef SDL_QueryTexture +#undef SDL_QueryTexture +#endif + +#ifdef SDL_SetTextureColorMod +#undef SDL_SetTextureColorMod +#endif + +#ifdef SDL_GetTextureColorMod +#undef SDL_GetTextureColorMod +#endif + +#ifdef SDL_SetTextureAlphaMod +#undef SDL_SetTextureAlphaMod +#endif + +#ifdef SDL_GetTextureAlphaMod +#undef SDL_GetTextureAlphaMod +#endif + +#ifdef SDL_SetTextureBlendMode +#undef SDL_SetTextureBlendMode +#endif + +#ifdef SDL_GetTextureBlendMode +#undef SDL_GetTextureBlendMode +#endif + +#ifdef SDL_UpdateTexture +#undef SDL_UpdateTexture +#endif + +#ifdef SDL_UpdateYUVTexture +#undef SDL_UpdateYUVTexture +#endif + +#ifdef SDL_LockTexture +#undef SDL_LockTexture +#endif + +#ifdef SDL_UnlockTexture +#undef SDL_UnlockTexture +#endif + +#ifdef SDL_RenderTargetSupported +#undef SDL_RenderTargetSupported +#endif + +#ifdef SDL_SetRenderTarget +#undef SDL_SetRenderTarget +#endif + +#ifdef SDL_GetRenderTarget +#undef SDL_GetRenderTarget +#endif + +#ifdef SDL_RenderSetLogicalSize +#undef SDL_RenderSetLogicalSize +#endif + +#ifdef SDL_RenderGetLogicalSize +#undef SDL_RenderGetLogicalSize +#endif + +#ifdef SDL_RenderSetViewport +#undef SDL_RenderSetViewport +#endif + +#ifdef SDL_RenderGetViewport +#undef SDL_RenderGetViewport +#endif + +#ifdef SDL_RenderSetClipRect +#undef SDL_RenderSetClipRect +#endif + +#ifdef SDL_RenderGetClipRect +#undef SDL_RenderGetClipRect +#endif + +#ifdef SDL_RenderSetScale +#undef SDL_RenderSetScale +#endif + +#ifdef SDL_RenderGetScale +#undef SDL_RenderGetScale +#endif + +#ifdef SDL_SetRenderDrawColor +#undef SDL_SetRenderDrawColor +#endif + +#ifdef SDL_GetRenderDrawColor +#undef SDL_GetRenderDrawColor +#endif + +#ifdef SDL_SetRenderDrawBlendMode +#undef SDL_SetRenderDrawBlendMode +#endif + +#ifdef SDL_GetRenderDrawBlendMode +#undef SDL_GetRenderDrawBlendMode +#endif + +#ifdef SDL_RenderClear +#undef SDL_RenderClear +#endif + +#ifdef SDL_RenderDrawPoint +#undef SDL_RenderDrawPoint +#endif + +#ifdef SDL_RenderDrawPoints +#undef SDL_RenderDrawPoints +#endif + +#ifdef SDL_RenderDrawLine +#undef SDL_RenderDrawLine +#endif + +#ifdef SDL_RenderDrawLines +#undef SDL_RenderDrawLines +#endif + +#ifdef SDL_RenderDrawRect +#undef SDL_RenderDrawRect +#endif + +#ifdef SDL_RenderDrawRects +#undef SDL_RenderDrawRects +#endif + +#ifdef SDL_RenderFillRect +#undef SDL_RenderFillRect +#endif + +#ifdef SDL_RenderFillRects +#undef SDL_RenderFillRects +#endif + +#ifdef SDL_RenderCopy +#undef SDL_RenderCopy +#endif + +#ifdef SDL_RenderCopyEx +#undef SDL_RenderCopyEx +#endif + +#ifdef SDL_RenderReadPixels +#undef SDL_RenderReadPixels +#endif + +#ifdef SDL_RenderPresent +#undef SDL_RenderPresent +#endif + +#ifdef SDL_DestroyTexture +#undef SDL_DestroyTexture +#endif + +#ifdef SDL_DestroyRenderer +#undef SDL_DestroyRenderer +#endif + +#ifdef SDL_GL_BindTexture +#undef SDL_GL_BindTexture +#endif + +#ifdef SDL_GL_UnbindTexture +#undef SDL_GL_UnbindTexture +#endif + +#ifdef SDL_RWFromFile +#undef SDL_RWFromFile +#endif + +#ifdef SDL_RWFromMem +#undef SDL_RWFromMem +#endif + +#ifdef SDL_RWFromConstMem +#undef SDL_RWFromConstMem +#endif + +#ifdef SDL_AllocRW +#undef SDL_AllocRW +#endif + +#ifdef SDL_FreeRW +#undef SDL_FreeRW +#endif + +#ifdef SDL_ReadU8 +#undef SDL_ReadU8 +#endif + +#ifdef SDL_ReadLE16 +#undef SDL_ReadLE16 +#endif + +#ifdef SDL_ReadBE16 +#undef SDL_ReadBE16 +#endif + +#ifdef SDL_ReadLE32 +#undef SDL_ReadLE32 +#endif + +#ifdef SDL_ReadBE32 +#undef SDL_ReadBE32 +#endif + +#ifdef SDL_ReadLE64 +#undef SDL_ReadLE64 +#endif + +#ifdef SDL_ReadBE64 +#undef SDL_ReadBE64 +#endif + +#ifdef SDL_WriteU8 +#undef SDL_WriteU8 +#endif + +#ifdef SDL_WriteLE16 +#undef SDL_WriteLE16 +#endif + +#ifdef SDL_WriteBE16 +#undef SDL_WriteBE16 +#endif + +#ifdef SDL_WriteLE32 +#undef SDL_WriteLE32 +#endif + +#ifdef SDL_WriteBE32 +#undef SDL_WriteBE32 +#endif + +#ifdef SDL_WriteLE64 +#undef SDL_WriteLE64 +#endif + +#ifdef SDL_WriteBE64 +#undef SDL_WriteBE64 +#endif + +#ifdef SDL_CreateShapedWindow +#undef SDL_CreateShapedWindow +#endif + +#ifdef SDL_IsShapedWindow +#undef SDL_IsShapedWindow +#endif + +#ifdef SDL_SetWindowShape +#undef SDL_SetWindowShape +#endif + +#ifdef SDL_GetShapedWindowMode +#undef SDL_GetShapedWindowMode +#endif + +#ifdef SDL_malloc +#undef SDL_malloc +#endif + +#ifdef SDL_calloc +#undef SDL_calloc +#endif + +#ifdef SDL_realloc +#undef SDL_realloc +#endif + +#ifdef SDL_free +#undef SDL_free +#endif + +#ifdef SDL_getenv +#undef SDL_getenv +#endif + +#ifdef SDL_setenv +#undef SDL_setenv +#endif + +#ifdef SDL_qsort +#undef SDL_qsort +#endif + +#ifdef SDL_abs +#undef SDL_abs +#endif + +#ifdef SDL_isdigit +#undef SDL_isdigit +#endif + +#ifdef SDL_isspace +#undef SDL_isspace +#endif + +#ifdef SDL_toupper +#undef SDL_toupper +#endif + +#ifdef SDL_tolower +#undef SDL_tolower +#endif + +#ifdef SDL_memset +#undef SDL_memset +#endif + +#ifdef SDL_memcpy +#undef SDL_memcpy +#endif + +#ifdef SDL_memmove +#undef SDL_memmove +#endif + +#ifdef SDL_memcmp +#undef SDL_memcmp +#endif + +#ifdef SDL_wcslen +#undef SDL_wcslen +#endif + +#ifdef SDL_wcslcpy +#undef SDL_wcslcpy +#endif + +#ifdef SDL_wcslcat +#undef SDL_wcslcat +#endif + +#ifdef SDL_strlen +#undef SDL_strlen +#endif + +#ifdef SDL_strlcpy +#undef SDL_strlcpy +#endif + +#ifdef SDL_utf8strlcpy +#undef SDL_utf8strlcpy +#endif + +#ifdef SDL_strlcat +#undef SDL_strlcat +#endif + +#ifdef SDL_strdup +#undef SDL_strdup +#endif + +#ifdef SDL_strrev +#undef SDL_strrev +#endif + +#ifdef SDL_strupr +#undef SDL_strupr +#endif + +#ifdef SDL_strlwr +#undef SDL_strlwr +#endif + +#ifdef SDL_strchr +#undef SDL_strchr +#endif + +#ifdef SDL_strrchr +#undef SDL_strrchr +#endif + +#ifdef SDL_strstr +#undef SDL_strstr +#endif + +#ifdef SDL_itoa +#undef SDL_itoa +#endif + +#ifdef SDL_uitoa +#undef SDL_uitoa +#endif + +#ifdef SDL_ltoa +#undef SDL_ltoa +#endif + +#ifdef SDL_ultoa +#undef SDL_ultoa +#endif + +#ifdef SDL_lltoa +#undef SDL_lltoa +#endif + +#ifdef SDL_ulltoa +#undef SDL_ulltoa +#endif + +#ifdef SDL_atoi +#undef SDL_atoi +#endif + +#ifdef SDL_atof +#undef SDL_atof +#endif + +#ifdef SDL_strtol +#undef SDL_strtol +#endif + +#ifdef SDL_strtoul +#undef SDL_strtoul +#endif + +#ifdef SDL_strtoll +#undef SDL_strtoll +#endif + +#ifdef SDL_strtoull +#undef SDL_strtoull +#endif + +#ifdef SDL_strtod +#undef SDL_strtod +#endif + +#ifdef SDL_strcmp +#undef SDL_strcmp +#endif + +#ifdef SDL_strncmp +#undef SDL_strncmp +#endif + +#ifdef SDL_strcasecmp +#undef SDL_strcasecmp +#endif + +#ifdef SDL_strncasecmp +#undef SDL_strncasecmp +#endif + +#ifdef SDL_vsnprintf +#undef SDL_vsnprintf +#endif + +#ifdef SDL_acos +#undef SDL_acos +#endif + +#ifdef SDL_asin +#undef SDL_asin +#endif + +#ifdef SDL_atan +#undef SDL_atan +#endif + +#ifdef SDL_atan2 +#undef SDL_atan2 +#endif + +#ifdef SDL_ceil +#undef SDL_ceil +#endif + +#ifdef SDL_copysign +#undef SDL_copysign +#endif + +#ifdef SDL_cos +#undef SDL_cos +#endif + +#ifdef SDL_cosf +#undef SDL_cosf +#endif + +#ifdef SDL_fabs +#undef SDL_fabs +#endif + +#ifdef SDL_floor +#undef SDL_floor +#endif + +#ifdef SDL_log +#undef SDL_log +#endif + +#ifdef SDL_pow +#undef SDL_pow +#endif + +#ifdef SDL_scalbn +#undef SDL_scalbn +#endif + +#ifdef SDL_sin +#undef SDL_sin +#endif + +#ifdef SDL_sinf +#undef SDL_sinf +#endif + +#ifdef SDL_sqrt +#undef SDL_sqrt +#endif + +#ifdef SDL_iconv_open +#undef SDL_iconv_open +#endif + +#ifdef SDL_iconv_close +#undef SDL_iconv_close +#endif + +#ifdef SDL_iconv +#undef SDL_iconv +#endif + +#ifdef SDL_iconv_string +#undef SDL_iconv_string +#endif + +#ifdef SDL_CreateRGBSurface +#undef SDL_CreateRGBSurface +#endif + +#ifdef SDL_CreateRGBSurfaceFrom +#undef SDL_CreateRGBSurfaceFrom +#endif + +#ifdef SDL_FreeSurface +#undef SDL_FreeSurface +#endif + +#ifdef SDL_SetSurfacePalette +#undef SDL_SetSurfacePalette +#endif + +#ifdef SDL_LockSurface +#undef SDL_LockSurface +#endif + +#ifdef SDL_UnlockSurface +#undef SDL_UnlockSurface +#endif + +#ifdef SDL_LoadBMP_RW +#undef SDL_LoadBMP_RW +#endif + +#ifdef SDL_SaveBMP_RW +#undef SDL_SaveBMP_RW +#endif + +#ifdef SDL_SetSurfaceRLE +#undef SDL_SetSurfaceRLE +#endif + +#ifdef SDL_SetColorKey +#undef SDL_SetColorKey +#endif + +#ifdef SDL_GetColorKey +#undef SDL_GetColorKey +#endif + +#ifdef SDL_SetSurfaceColorMod +#undef SDL_SetSurfaceColorMod +#endif + +#ifdef SDL_GetSurfaceColorMod +#undef SDL_GetSurfaceColorMod +#endif + +#ifdef SDL_SetSurfaceAlphaMod +#undef SDL_SetSurfaceAlphaMod +#endif + +#ifdef SDL_GetSurfaceAlphaMod +#undef SDL_GetSurfaceAlphaMod +#endif + +#ifdef SDL_SetSurfaceBlendMode +#undef SDL_SetSurfaceBlendMode +#endif + +#ifdef SDL_GetSurfaceBlendMode +#undef SDL_GetSurfaceBlendMode +#endif + +#ifdef SDL_SetClipRect +#undef SDL_SetClipRect +#endif + +#ifdef SDL_GetClipRect +#undef SDL_GetClipRect +#endif + +#ifdef SDL_ConvertSurface +#undef SDL_ConvertSurface +#endif + +#ifdef SDL_ConvertSurfaceFormat +#undef SDL_ConvertSurfaceFormat +#endif + +#ifdef SDL_ConvertPixels +#undef SDL_ConvertPixels +#endif + +#ifdef SDL_FillRect +#undef SDL_FillRect +#endif + +#ifdef SDL_FillRects +#undef SDL_FillRects +#endif + +#ifdef SDL_UpperBlit +#undef SDL_UpperBlit +#endif + +#ifdef SDL_LowerBlit +#undef SDL_LowerBlit +#endif + +#ifdef SDL_SoftStretch +#undef SDL_SoftStretch +#endif + +#ifdef SDL_UpperBlitScaled +#undef SDL_UpperBlitScaled +#endif + +#ifdef SDL_LowerBlitScaled +#undef SDL_LowerBlitScaled +#endif + +#ifdef SDL_GetWindowWMInfo +#undef SDL_GetWindowWMInfo +#endif + +#ifdef SDL_GetThreadName +#undef SDL_GetThreadName +#endif + +#ifdef SDL_ThreadID +#undef SDL_ThreadID +#endif + +#ifdef SDL_GetThreadID +#undef SDL_GetThreadID +#endif + +#ifdef SDL_SetThreadPriority +#undef SDL_SetThreadPriority +#endif + +#ifdef SDL_WaitThread +#undef SDL_WaitThread +#endif + +#ifdef SDL_DetachThread +#undef SDL_DetachThread +#endif + +#ifdef SDL_TLSCreate +#undef SDL_TLSCreate +#endif + +#ifdef SDL_TLSGet +#undef SDL_TLSGet +#endif + +#ifdef SDL_TLSSet +#undef SDL_TLSSet +#endif + +#ifdef SDL_GetTicks +#undef SDL_GetTicks +#endif + +#ifdef SDL_GetPerformanceCounter +#undef SDL_GetPerformanceCounter +#endif + +#ifdef SDL_GetPerformanceFrequency +#undef SDL_GetPerformanceFrequency +#endif + +#ifdef SDL_Delay +#undef SDL_Delay +#endif + +#ifdef SDL_AddTimer +#undef SDL_AddTimer +#endif + +#ifdef SDL_RemoveTimer +#undef SDL_RemoveTimer +#endif + +#ifdef SDL_GetNumTouchDevices +#undef SDL_GetNumTouchDevices +#endif + +#ifdef SDL_GetTouchDevice +#undef SDL_GetTouchDevice +#endif + +#ifdef SDL_GetNumTouchFingers +#undef SDL_GetNumTouchFingers +#endif + +#ifdef SDL_GetTouchFinger +#undef SDL_GetTouchFinger +#endif + +#ifdef SDL_GetVersion +#undef SDL_GetVersion +#endif + +#ifdef SDL_GetRevision +#undef SDL_GetRevision +#endif + +#ifdef SDL_GetRevisionNumber +#undef SDL_GetRevisionNumber +#endif + +#ifdef SDL_GetNumVideoDrivers +#undef SDL_GetNumVideoDrivers +#endif + +#ifdef SDL_GetVideoDriver +#undef SDL_GetVideoDriver +#endif + +#ifdef SDL_VideoInit +#undef SDL_VideoInit +#endif + +#ifdef SDL_VideoQuit +#undef SDL_VideoQuit +#endif + +#ifdef SDL_GetCurrentVideoDriver +#undef SDL_GetCurrentVideoDriver +#endif + +#ifdef SDL_GetNumVideoDisplays +#undef SDL_GetNumVideoDisplays +#endif + +#ifdef SDL_GetDisplayName +#undef SDL_GetDisplayName +#endif + +#ifdef SDL_GetDisplayBounds +#undef SDL_GetDisplayBounds +#endif + +#ifdef SDL_GetDisplayDPI +#undef SDL_GetDisplayDPI +#endif + +#ifdef SDL_GetNumDisplayModes +#undef SDL_GetNumDisplayModes +#endif + +#ifdef SDL_GetDisplayMode +#undef SDL_GetDisplayMode +#endif + +#ifdef SDL_GetDesktopDisplayMode +#undef SDL_GetDesktopDisplayMode +#endif + +#ifdef SDL_GetCurrentDisplayMode +#undef SDL_GetCurrentDisplayMode +#endif + +#ifdef SDL_GetClosestDisplayMode +#undef SDL_GetClosestDisplayMode +#endif + +#ifdef SDL_GetWindowDisplayIndex +#undef SDL_GetWindowDisplayIndex +#endif + +#ifdef SDL_SetWindowDisplayMode +#undef SDL_SetWindowDisplayMode +#endif + +#ifdef SDL_GetWindowDisplayMode +#undef SDL_GetWindowDisplayMode +#endif + +#ifdef SDL_GetWindowPixelFormat +#undef SDL_GetWindowPixelFormat +#endif + +#ifdef SDL_CreateWindow +#undef SDL_CreateWindow +#endif + +#ifdef SDL_CreateWindowFrom +#undef SDL_CreateWindowFrom +#endif + +#ifdef SDL_GetWindowID +#undef SDL_GetWindowID +#endif + +#ifdef SDL_GetWindowFromID +#undef SDL_GetWindowFromID +#endif + +#ifdef SDL_GetWindowFlags +#undef SDL_GetWindowFlags +#endif + +#ifdef SDL_SetWindowTitle +#undef SDL_SetWindowTitle +#endif + +#ifdef SDL_GetWindowTitle +#undef SDL_GetWindowTitle +#endif + +#ifdef SDL_SetWindowIcon +#undef SDL_SetWindowIcon +#endif + +#ifdef SDL_SetWindowData +#undef SDL_SetWindowData +#endif + +#ifdef SDL_GetWindowData +#undef SDL_GetWindowData +#endif + +#ifdef SDL_SetWindowPosition +#undef SDL_SetWindowPosition +#endif + +#ifdef SDL_GetWindowPosition +#undef SDL_GetWindowPosition +#endif + +#ifdef SDL_SetWindowSize +#undef SDL_SetWindowSize +#endif + +#ifdef SDL_GetWindowSize +#undef SDL_GetWindowSize +#endif + +#ifdef SDL_SetWindowMinimumSize +#undef SDL_SetWindowMinimumSize +#endif + +#ifdef SDL_GetWindowMinimumSize +#undef SDL_GetWindowMinimumSize +#endif + +#ifdef SDL_SetWindowMaximumSize +#undef SDL_SetWindowMaximumSize +#endif + +#ifdef SDL_GetWindowMaximumSize +#undef SDL_GetWindowMaximumSize +#endif + +#ifdef SDL_SetWindowBordered +#undef SDL_SetWindowBordered +#endif + +#ifdef SDL_ShowWindow +#undef SDL_ShowWindow +#endif + +#ifdef SDL_HideWindow +#undef SDL_HideWindow +#endif + +#ifdef SDL_RaiseWindow +#undef SDL_RaiseWindow +#endif + +#ifdef SDL_MaximizeWindow +#undef SDL_MaximizeWindow +#endif + +#ifdef SDL_MinimizeWindow +#undef SDL_MinimizeWindow +#endif + +#ifdef SDL_RestoreWindow +#undef SDL_RestoreWindow +#endif + +#ifdef SDL_SetWindowFullscreen +#undef SDL_SetWindowFullscreen +#endif + +#ifdef SDL_GetWindowSurface +#undef SDL_GetWindowSurface +#endif + +#ifdef SDL_UpdateWindowSurface +#undef SDL_UpdateWindowSurface +#endif + +#ifdef SDL_UpdateWindowSurfaceRects +#undef SDL_UpdateWindowSurfaceRects +#endif + +#ifdef SDL_SetWindowGrab +#undef SDL_SetWindowGrab +#endif + +#ifdef SDL_GetWindowGrab +#undef SDL_GetWindowGrab +#endif + +#ifdef SDL_SetWindowBrightness +#undef SDL_SetWindowBrightness +#endif + +#ifdef SDL_GetWindowBrightness +#undef SDL_GetWindowBrightness +#endif + +#ifdef SDL_SetWindowGammaRamp +#undef SDL_SetWindowGammaRamp +#endif + +#ifdef SDL_GetWindowGammaRamp +#undef SDL_GetWindowGammaRamp +#endif + +#ifdef SDL_DestroyWindow +#undef SDL_DestroyWindow +#endif + +#ifdef SDL_IsScreenSaverEnabled +#undef SDL_IsScreenSaverEnabled +#endif + +#ifdef SDL_EnableScreenSaver +#undef SDL_EnableScreenSaver +#endif + +#ifdef SDL_DisableScreenSaver +#undef SDL_DisableScreenSaver +#endif + +#ifdef SDL_GL_LoadLibrary +#undef SDL_GL_LoadLibrary +#endif + +#ifdef SDL_GL_GetProcAddress +#undef SDL_GL_GetProcAddress +#endif + +#ifdef SDL_GL_UnloadLibrary +#undef SDL_GL_UnloadLibrary +#endif + +#ifdef SDL_GL_ExtensionSupported +#undef SDL_GL_ExtensionSupported +#endif + +#ifdef SDL_GL_SetAttribute +#undef SDL_GL_SetAttribute +#endif + +#ifdef SDL_GL_GetAttribute +#undef SDL_GL_GetAttribute +#endif + +#ifdef SDL_GL_CreateContext +#undef SDL_GL_CreateContext +#endif + +#ifdef SDL_GL_MakeCurrent +#undef SDL_GL_MakeCurrent +#endif + +#ifdef SDL_GL_GetCurrentWindow +#undef SDL_GL_GetCurrentWindow +#endif + +#ifdef SDL_GL_GetCurrentContext +#undef SDL_GL_GetCurrentContext +#endif + +#ifdef SDL_GL_GetDrawableSize +#undef SDL_GL_GetDrawableSize +#endif + +#ifdef SDL_GL_SetSwapInterval +#undef SDL_GL_SetSwapInterval +#endif + +#ifdef SDL_GL_GetSwapInterval +#undef SDL_GL_GetSwapInterval +#endif + +#ifdef SDL_GL_SwapWindow +#undef SDL_GL_SwapWindow +#endif + +#ifdef SDL_GL_DeleteContext +#undef SDL_GL_DeleteContext +#endif + +#ifdef SDL_vsscanf +#undef SDL_vsscanf +#endif + +#ifdef SDL_GameControllerAddMappingsFromRW +#undef SDL_GameControllerAddMappingsFromRW +#endif + +#ifdef SDL_GL_ResetAttributes +#undef SDL_GL_ResetAttributes +#endif + +#ifdef SDL_HasAVX +#undef SDL_HasAVX +#endif + +#ifdef SDL_GetDefaultAssertionHandler +#undef SDL_GetDefaultAssertionHandler +#endif + +#ifdef SDL_GetAssertionHandler +#undef SDL_GetAssertionHandler +#endif + +#ifdef SDL_DXGIGetOutputInfo +#undef SDL_DXGIGetOutputInfo +#endif + +#ifdef SDL_RenderIsClipEnabled +#undef SDL_RenderIsClipEnabled +#endif + +#ifdef SDL_WinRTRunApp +#undef SDL_WinRTRunApp +#endif + +#ifdef SDL_WarpMouseGlobal +#undef SDL_WarpMouseGlobal +#endif + +#ifdef SDL_WinRTGetFSPathUNICODE +#undef SDL_WinRTGetFSPathUNICODE +#endif + +#ifdef SDL_WinRTGetFSPathUTF8 +#undef SDL_WinRTGetFSPathUTF8 +#endif + +#ifdef SDL_sqrtf +#undef SDL_sqrtf +#endif + +#ifdef SDL_tan +#undef SDL_tan +#endif + +#ifdef SDL_tanf +#undef SDL_tanf +#endif + +#ifdef SDL_CaptureMouse +#undef SDL_CaptureMouse +#endif + +#ifdef SDL_SetWindowHitTest +#undef SDL_SetWindowHitTest +#endif + +#ifdef SDL_GetGlobalMouseState +#undef SDL_GetGlobalMouseState +#endif + +#ifdef SDL_HasAVX2 +#undef SDL_HasAVX2 +#endif + +#ifdef SDL_QueueAudio +#undef SDL_QueueAudio +#endif + +#ifdef SDL_GetQueuedAudioSize +#undef SDL_GetQueuedAudioSize +#endif + +#ifdef SDL_ClearQueuedAudio +#undef SDL_ClearQueuedAudio +#endif + +#ifdef SDL_GetGrabbedWindow +#undef SDL_GetGrabbedWindow +#endif + +#ifdef SDL_SetWindowsMessageHook +#undef SDL_SetWindowsMessageHook +#endif + +#ifdef SDL_JoystickCurrentPowerLevel +#undef SDL_JoystickCurrentPowerLevel +#endif + +#ifdef SDL_GameControllerFromInstanceID +#undef SDL_GameControllerFromInstanceID +#endif + +#ifdef SDL_JoystickFromInstanceID +#undef SDL_JoystickFromInstanceID +#endif + +#ifdef SDL_GetDisplayUsableBounds +#undef SDL_GetDisplayUsableBounds +#endif + +#ifdef SDL_GetWindowBordersSize +#undef SDL_GetWindowBordersSize +#endif + +#ifdef SDL_SetWindowOpacity +#undef SDL_SetWindowOpacity +#endif + +#ifdef SDL_GetWindowOpacity +#undef SDL_GetWindowOpacity +#endif + +#ifdef SDL_SetWindowInputFocus +#undef SDL_SetWindowInputFocus +#endif + +#ifdef SDL_SetWindowModalFor +#undef SDL_SetWindowModalFor +#endif + +#ifdef SDL_RenderSetIntegerScale +#undef SDL_RenderSetIntegerScale +#endif + +#ifdef SDL_RenderGetIntegerScale +#undef SDL_RenderGetIntegerScale +#endif + +#ifdef SDL_DequeueAudio +#undef SDL_DequeueAudio +#endif + +#ifdef SDL_SetWindowResizable +#undef SDL_SetWindowResizable +#endif + +#ifdef SDL_CreateRGBSurfaceWithFormat +#undef SDL_CreateRGBSurfaceWithFormat +#endif + +#ifdef SDL_CreateRGBSurfaceWithFormatFrom +#undef SDL_CreateRGBSurfaceWithFormatFrom +#endif + +#ifdef SDL_GetHintBoolean +#undef SDL_GetHintBoolean +#endif + +#ifdef SDL_JoystickGetDeviceVendor +#undef SDL_JoystickGetDeviceVendor +#endif + +#ifdef SDL_JoystickGetDeviceProduct +#undef SDL_JoystickGetDeviceProduct +#endif + +#ifdef SDL_JoystickGetDeviceProductVersion +#undef SDL_JoystickGetDeviceProductVersion +#endif + +#ifdef SDL_JoystickGetVendor +#undef SDL_JoystickGetVendor +#endif + +#ifdef SDL_JoystickGetProduct +#undef SDL_JoystickGetProduct +#endif + +#ifdef SDL_JoystickGetProductVersion +#undef SDL_JoystickGetProductVersion +#endif + +#ifdef SDL_GameControllerGetVendor +#undef SDL_GameControllerGetVendor +#endif + +#ifdef SDL_GameControllerGetProduct +#undef SDL_GameControllerGetProduct +#endif + +#ifdef SDL_GameControllerGetProductVersion +#undef SDL_GameControllerGetProductVersion +#endif + +#ifdef SDL_HasNEON +#undef SDL_HasNEON +#endif + +#ifdef SDL_GameControllerNumMappings +#undef SDL_GameControllerNumMappings +#endif + +#ifdef SDL_GameControllerMappingForIndex +#undef SDL_GameControllerMappingForIndex +#endif + +#ifdef SDL_JoystickGetAxisInitialState +#undef SDL_JoystickGetAxisInitialState +#endif + +#ifdef SDL_JoystickGetDeviceType +#undef SDL_JoystickGetDeviceType +#endif + +#ifdef SDL_JoystickGetType +#undef SDL_JoystickGetType +#endif + +#ifdef SDL_MemoryBarrierReleaseFunction +#undef SDL_MemoryBarrierReleaseFunction +#endif + +#ifdef SDL_MemoryBarrierAcquireFunction +#undef SDL_MemoryBarrierAcquireFunction +#endif + +#ifdef SDL_JoystickGetDeviceInstanceID +#undef SDL_JoystickGetDeviceInstanceID +#endif + +#ifdef SDL_utf8strlen +#undef SDL_utf8strlen +#endif + +#ifdef SDL_LoadFile_RW +#undef SDL_LoadFile_RW +#endif + +#ifdef SDL_wcscmp +#undef SDL_wcscmp +#endif + +#ifdef SDL_ComposeCustomBlendMode +#undef SDL_ComposeCustomBlendMode +#endif + +#ifdef SDL_DuplicateSurface +#undef SDL_DuplicateSurface +#endif + +#ifdef SDL_Vulkan_LoadLibrary +#undef SDL_Vulkan_LoadLibrary +#endif + +#ifdef SDL_Vulkan_GetVkGetInstanceProcAddr +#undef SDL_Vulkan_GetVkGetInstanceProcAddr +#endif + +#ifdef SDL_Vulkan_UnloadLibrary +#undef SDL_Vulkan_UnloadLibrary +#endif + +#ifdef SDL_Vulkan_GetInstanceExtensions +#undef SDL_Vulkan_GetInstanceExtensions +#endif + +#ifdef SDL_Vulkan_CreateSurface +#undef SDL_Vulkan_CreateSurface +#endif + +#ifdef SDL_Vulkan_GetDrawableSize +#undef SDL_Vulkan_GetDrawableSize +#endif + +#ifdef SDL_LockJoysticks +#undef SDL_LockJoysticks +#endif + +#ifdef SDL_UnlockJoysticks +#undef SDL_UnlockJoysticks +#endif + +#ifdef SDL_GetMemoryFunctions +#undef SDL_GetMemoryFunctions +#endif + +#ifdef SDL_SetMemoryFunctions +#undef SDL_SetMemoryFunctions +#endif + +#ifdef SDL_GetNumAllocations +#undef SDL_GetNumAllocations +#endif + +#ifdef SDL_NewAudioStream +#undef SDL_NewAudioStream +#endif + +#ifdef SDL_AudioStreamPut +#undef SDL_AudioStreamPut +#endif + +#ifdef SDL_AudioStreamGet +#undef SDL_AudioStreamGet +#endif + +#ifdef SDL_AudioStreamClear +#undef SDL_AudioStreamClear +#endif + +#ifdef SDL_AudioStreamAvailable +#undef SDL_AudioStreamAvailable +#endif + +#ifdef SDL_FreeAudioStream +#undef SDL_FreeAudioStream +#endif + +#ifdef SDL_AudioStreamFlush +#undef SDL_AudioStreamFlush +#endif + +#ifdef SDL_acosf +#undef SDL_acosf +#endif + +#ifdef SDL_asinf +#undef SDL_asinf +#endif + +#ifdef SDL_atanf +#undef SDL_atanf +#endif + +#ifdef SDL_atan2f +#undef SDL_atan2f +#endif + +#ifdef SDL_ceilf +#undef SDL_ceilf +#endif + +#ifdef SDL_copysignf +#undef SDL_copysignf +#endif + +#ifdef SDL_fabsf +#undef SDL_fabsf +#endif + +#ifdef SDL_floorf +#undef SDL_floorf +#endif + +#ifdef SDL_logf +#undef SDL_logf +#endif + +#ifdef SDL_powf +#undef SDL_powf +#endif + +#ifdef SDL_scalbnf +#undef SDL_scalbnf +#endif + +#ifdef SDL_fmod +#undef SDL_fmod +#endif + +#ifdef SDL_fmodf +#undef SDL_fmodf +#endif + +#ifdef SDL_SetYUVConversionMode +#undef SDL_SetYUVConversionMode +#endif + +#ifdef SDL_GetYUVConversionMode +#undef SDL_GetYUVConversionMode +#endif + +#ifdef SDL_GetYUVConversionModeForResolution +#undef SDL_GetYUVConversionModeForResolution +#endif + +#ifdef SDL_RenderGetMetalLayer +#undef SDL_RenderGetMetalLayer +#endif + +#ifdef SDL_RenderGetMetalCommandEncoder +#undef SDL_RenderGetMetalCommandEncoder +#endif + +#ifdef SDL_IsAndroidTV +#undef SDL_IsAndroidTV +#endif + +#ifdef SDL_WinRTGetDeviceFamily +#undef SDL_WinRTGetDeviceFamily +#endif + +#ifdef SDL_log10 +#undef SDL_log10 +#endif + +#ifdef SDL_log10f +#undef SDL_log10f +#endif + +#ifdef SDL_GameControllerMappingForDeviceIndex +#undef SDL_GameControllerMappingForDeviceIndex +#endif + +#ifdef SDL_LinuxSetThreadPriority +#undef SDL_LinuxSetThreadPriority +#endif + +#ifdef SDL_HasAVX512F +#undef SDL_HasAVX512F +#endif + +#ifdef SDL_IsChromebook +#undef SDL_IsChromebook +#endif + +#ifdef SDL_IsDeXMode +#undef SDL_IsDeXMode +#endif + +#ifdef SDL_AndroidBackButton +#undef SDL_AndroidBackButton +#endif + +#ifdef SDL_exp +#undef SDL_exp +#endif + +#ifdef SDL_expf +#undef SDL_expf +#endif + +#ifdef SDL_wcsdup +#undef SDL_wcsdup +#endif + +#ifdef SDL_GameControllerRumble +#undef SDL_GameControllerRumble +#endif + +#ifdef SDL_JoystickRumble +#undef SDL_JoystickRumble +#endif + +#ifdef SDL_NumSensors +#undef SDL_NumSensors +#endif + +#ifdef SDL_SensorGetDeviceName +#undef SDL_SensorGetDeviceName +#endif + +#ifdef SDL_SensorGetDeviceType +#undef SDL_SensorGetDeviceType +#endif + +#ifdef SDL_SensorGetDeviceNonPortableType +#undef SDL_SensorGetDeviceNonPortableType +#endif + +#ifdef SDL_SensorGetDeviceInstanceID +#undef SDL_SensorGetDeviceInstanceID +#endif + +#ifdef SDL_SensorOpen +#undef SDL_SensorOpen +#endif + +#ifdef SDL_SensorFromInstanceID +#undef SDL_SensorFromInstanceID +#endif + +#ifdef SDL_SensorGetName +#undef SDL_SensorGetName +#endif + +#ifdef SDL_SensorGetType +#undef SDL_SensorGetType +#endif + +#ifdef SDL_SensorGetNonPortableType +#undef SDL_SensorGetNonPortableType +#endif + +#ifdef SDL_SensorGetInstanceID +#undef SDL_SensorGetInstanceID +#endif + +#ifdef SDL_SensorGetData +#undef SDL_SensorGetData +#endif + +#ifdef SDL_SensorClose +#undef SDL_SensorClose +#endif + +#ifdef SDL_SensorUpdate +#undef SDL_SensorUpdate +#endif + +#ifdef SDL_IsTablet +#undef SDL_IsTablet +#endif + +#ifdef SDL_GetDisplayOrientation +#undef SDL_GetDisplayOrientation +#endif + +#ifdef SDL_HasColorKey +#undef SDL_HasColorKey +#endif + +#ifdef SDL_CreateThreadWithStackSize +#undef SDL_CreateThreadWithStackSize +#endif + +#ifdef SDL_JoystickGetDevicePlayerIndex +#undef SDL_JoystickGetDevicePlayerIndex +#endif + +#ifdef SDL_JoystickGetPlayerIndex +#undef SDL_JoystickGetPlayerIndex +#endif + +#ifdef SDL_GameControllerGetPlayerIndex +#undef SDL_GameControllerGetPlayerIndex +#endif + +#ifdef SDL_RenderFlush +#undef SDL_RenderFlush +#endif + +#ifdef SDL_RenderDrawPointF +#undef SDL_RenderDrawPointF +#endif + +#ifdef SDL_RenderDrawPointsF +#undef SDL_RenderDrawPointsF +#endif + +#ifdef SDL_RenderDrawLineF +#undef SDL_RenderDrawLineF +#endif + +#ifdef SDL_RenderDrawLinesF +#undef SDL_RenderDrawLinesF +#endif + +#ifdef SDL_RenderDrawRectF +#undef SDL_RenderDrawRectF +#endif + +#ifdef SDL_RenderDrawRectsF +#undef SDL_RenderDrawRectsF +#endif + +#ifdef SDL_RenderFillRectF +#undef SDL_RenderFillRectF +#endif + +#ifdef SDL_RenderFillRectsF +#undef SDL_RenderFillRectsF +#endif + +#ifdef SDL_RenderCopyF +#undef SDL_RenderCopyF +#endif + +#ifdef SDL_RenderCopyExF +#undef SDL_RenderCopyExF +#endif + +#ifdef SDL_GetTouchDeviceType +#undef SDL_GetTouchDeviceType +#endif + +#ifdef SDL_UIKitRunApp +#undef SDL_UIKitRunApp +#endif + +#ifdef SDL_SIMDGetAlignment +#undef SDL_SIMDGetAlignment +#endif + +#ifdef SDL_SIMDAlloc +#undef SDL_SIMDAlloc +#endif + +#ifdef SDL_SIMDFree +#undef SDL_SIMDFree +#endif + +#ifdef SDL_RWsize +#undef SDL_RWsize +#endif + +#ifdef SDL_RWseek +#undef SDL_RWseek +#endif + +#ifdef SDL_RWtell +#undef SDL_RWtell +#endif + +#ifdef SDL_RWread +#undef SDL_RWread +#endif + +#ifdef SDL_RWwrite +#undef SDL_RWwrite +#endif + +#ifdef SDL_RWclose +#undef SDL_RWclose +#endif + +#ifdef SDL_LoadFile +#undef SDL_LoadFile +#endif + +#ifdef SDL_Metal_CreateView +#undef SDL_Metal_CreateView +#endif + +#ifdef SDL_Metal_DestroyView +#undef SDL_Metal_DestroyView +#endif + +#ifdef SDL_LockTextureToSurface +#undef SDL_LockTextureToSurface +#endif + +#ifdef SDL_HasARMSIMD +#undef SDL_HasARMSIMD +#endif + +#ifdef SDL_strtokr +#undef SDL_strtokr +#endif + +#ifdef SDL_wcsstr +#undef SDL_wcsstr +#endif + +#ifdef SDL_wcsncmp +#undef SDL_wcsncmp +#endif + +#ifdef SDL_GameControllerTypeForIndex +#undef SDL_GameControllerTypeForIndex +#endif + +#ifdef SDL_GameControllerGetType +#undef SDL_GameControllerGetType +#endif + +#ifdef SDL_GameControllerFromPlayerIndex +#undef SDL_GameControllerFromPlayerIndex +#endif + +#ifdef SDL_GameControllerSetPlayerIndex +#undef SDL_GameControllerSetPlayerIndex +#endif + +#ifdef SDL_JoystickFromPlayerIndex +#undef SDL_JoystickFromPlayerIndex +#endif + +#ifdef SDL_JoystickSetPlayerIndex +#undef SDL_JoystickSetPlayerIndex +#endif + +#ifdef SDL_SetTextureScaleMode +#undef SDL_SetTextureScaleMode +#endif + +#ifdef SDL_GetTextureScaleMode +#undef SDL_GetTextureScaleMode +#endif + +#ifdef SDL_OnApplicationWillTerminate +#undef SDL_OnApplicationWillTerminate +#endif + +#ifdef SDL_OnApplicationDidReceiveMemoryWarning +#undef SDL_OnApplicationDidReceiveMemoryWarning +#endif + +#ifdef SDL_OnApplicationWillResignActive +#undef SDL_OnApplicationWillResignActive +#endif + +#ifdef SDL_OnApplicationDidEnterBackground +#undef SDL_OnApplicationDidEnterBackground +#endif + +#ifdef SDL_OnApplicationWillEnterForeground +#undef SDL_OnApplicationWillEnterForeground +#endif + +#ifdef SDL_OnApplicationDidBecomeActive +#undef SDL_OnApplicationDidBecomeActive +#endif + +#ifdef SDL_OnApplicationDidChangeStatusBarOrientation +#undef SDL_OnApplicationDidChangeStatusBarOrientation +#endif + +#ifdef SDL_GetAndroidSDKVersion +#undef SDL_GetAndroidSDKVersion +#endif + +#ifdef SDL_isupper +#undef SDL_isupper +#endif + +#ifdef SDL_islower +#undef SDL_islower +#endif + +#ifdef SDL_JoystickAttachVirtual +#undef SDL_JoystickAttachVirtual +#endif + +#ifdef SDL_JoystickDetachVirtual +#undef SDL_JoystickDetachVirtual +#endif + +#ifdef SDL_JoystickIsVirtual +#undef SDL_JoystickIsVirtual +#endif + +#ifdef SDL_JoystickSetVirtualAxis +#undef SDL_JoystickSetVirtualAxis +#endif + +#ifdef SDL_JoystickSetVirtualButton +#undef SDL_JoystickSetVirtualButton +#endif + +#ifdef SDL_JoystickSetVirtualHat +#undef SDL_JoystickSetVirtualHat +#endif + +#ifdef SDL_GetErrorMsg +#undef SDL_GetErrorMsg +#endif + +#ifdef SDL_LockSensors +#undef SDL_LockSensors +#endif + +#ifdef SDL_UnlockSensors +#undef SDL_UnlockSensors +#endif + +#ifdef SDL_Metal_GetLayer +#undef SDL_Metal_GetLayer +#endif + +#ifdef SDL_Metal_GetDrawableSize +#undef SDL_Metal_GetDrawableSize +#endif + +#ifdef SDL_trunc +#undef SDL_trunc +#endif + +#ifdef SDL_truncf +#undef SDL_truncf +#endif + +#ifdef SDL_GetPreferredLocales +#undef SDL_GetPreferredLocales +#endif + +#ifdef SDL_SIMDRealloc +#undef SDL_SIMDRealloc +#endif + +#ifdef SDL_AndroidRequestPermission +#undef SDL_AndroidRequestPermission +#endif + +#ifdef SDL_OpenURL +#undef SDL_OpenURL +#endif + +#ifdef SDL_HasSurfaceRLE +#undef SDL_HasSurfaceRLE +#endif + +#ifdef SDL_GameControllerHasLED +#undef SDL_GameControllerHasLED +#endif + +#ifdef SDL_GameControllerSetLED +#undef SDL_GameControllerSetLED +#endif + +#ifdef SDL_JoystickHasLED +#undef SDL_JoystickHasLED +#endif + +#ifdef SDL_JoystickSetLED +#undef SDL_JoystickSetLED +#endif + +#ifdef SDL_GameControllerRumbleTriggers +#undef SDL_GameControllerRumbleTriggers +#endif + +#ifdef SDL_JoystickRumbleTriggers +#undef SDL_JoystickRumbleTriggers +#endif + +#ifdef SDL_GameControllerHasAxis +#undef SDL_GameControllerHasAxis +#endif + +#ifdef SDL_GameControllerHasButton +#undef SDL_GameControllerHasButton +#endif + +#ifdef SDL_GameControllerGetNumTouchpads +#undef SDL_GameControllerGetNumTouchpads +#endif + +#ifdef SDL_GameControllerGetNumTouchpadFingers +#undef SDL_GameControllerGetNumTouchpadFingers +#endif + +#ifdef SDL_GameControllerGetTouchpadFinger +#undef SDL_GameControllerGetTouchpadFinger +#endif + +#ifdef SDL_crc32 +#undef SDL_crc32 +#endif + +#ifdef SDL_GameControllerGetSerial +#undef SDL_GameControllerGetSerial +#endif + +#ifdef SDL_JoystickGetSerial +#undef SDL_JoystickGetSerial +#endif + +#ifdef SDL_GameControllerHasSensor +#undef SDL_GameControllerHasSensor +#endif + +#ifdef SDL_GameControllerSetSensorEnabled +#undef SDL_GameControllerSetSensorEnabled +#endif + +#ifdef SDL_GameControllerIsSensorEnabled +#undef SDL_GameControllerIsSensorEnabled +#endif + +#ifdef SDL_GameControllerGetSensorData +#undef SDL_GameControllerGetSensorData +#endif + +#ifdef SDL_wcscasecmp +#undef SDL_wcscasecmp +#endif + +#ifdef SDL_wcsncasecmp +#undef SDL_wcsncasecmp +#endif + +#ifdef SDL_round +#undef SDL_round +#endif + +#ifdef SDL_roundf +#undef SDL_roundf +#endif + +#ifdef SDL_lround +#undef SDL_lround +#endif + +#ifdef SDL_lroundf +#undef SDL_lroundf +#endif + +#ifdef SDL_SoftStretchLinear +#undef SDL_SoftStretchLinear +#endif + +#ifdef SDL_RenderGetD3D11Device +#undef SDL_RenderGetD3D11Device +#endif + +#ifdef SDL_UpdateNVTexture +#undef SDL_UpdateNVTexture +#endif + +#ifdef SDL_SetWindowKeyboardGrab +#undef SDL_SetWindowKeyboardGrab +#endif + +#ifdef SDL_SetWindowMouseGrab +#undef SDL_SetWindowMouseGrab +#endif + +#ifdef SDL_GetWindowKeyboardGrab +#undef SDL_GetWindowKeyboardGrab +#endif + +#ifdef SDL_GetWindowMouseGrab +#undef SDL_GetWindowMouseGrab +#endif + +#ifdef SDL_isalpha +#undef SDL_isalpha +#endif + +#ifdef SDL_isalnum +#undef SDL_isalnum +#endif + +#ifdef SDL_isblank +#undef SDL_isblank +#endif + +#ifdef SDL_iscntrl +#undef SDL_iscntrl +#endif + +#ifdef SDL_isxdigit +#undef SDL_isxdigit +#endif + +#ifdef SDL_ispunct +#undef SDL_ispunct +#endif + +#ifdef SDL_isprint +#undef SDL_isprint +#endif + +#ifdef SDL_isgraph +#undef SDL_isgraph +#endif + +#ifdef SDL_AndroidShowToast +#undef SDL_AndroidShowToast +#endif + +#ifdef SDL_GetAudioDeviceSpec +#undef SDL_GetAudioDeviceSpec +#endif + +#ifdef SDL_TLSCleanup +#undef SDL_TLSCleanup +#endif + +#ifdef SDL_SetWindowAlwaysOnTop +#undef SDL_SetWindowAlwaysOnTop +#endif + +#ifdef SDL_FlashWindow +#undef SDL_FlashWindow +#endif + +#ifdef SDL_GameControllerSendEffect +#undef SDL_GameControllerSendEffect +#endif + +#ifdef SDL_JoystickSendEffect +#undef SDL_JoystickSendEffect +#endif + +#ifdef SDL_GameControllerGetSensorDataRate +#undef SDL_GameControllerGetSensorDataRate +#endif + +#ifdef SDL_SetTextureUserData +#undef SDL_SetTextureUserData +#endif + +#ifdef SDL_GetTextureUserData +#undef SDL_GetTextureUserData +#endif + +#ifdef SDL_RenderGeometry +#undef SDL_RenderGeometry +#endif + +#ifdef SDL_RenderGeometryRaw +#undef SDL_RenderGeometryRaw +#endif + +#ifdef SDL_RenderSetVSync +#undef SDL_RenderSetVSync +#endif + +#ifdef SDL_asprintf +#undef SDL_asprintf +#endif + +#ifdef SDL_vasprintf +#undef SDL_vasprintf +#endif + +#ifdef SDL_GetWindowICCProfile +#undef SDL_GetWindowICCProfile +#endif + +#ifdef SDL_GetTicks64 +#undef SDL_GetTicks64 +#endif + +#ifdef SDL_LinuxSetThreadPriorityAndPolicy +#undef SDL_LinuxSetThreadPriorityAndPolicy +#endif + +#ifdef SDL_GameControllerGetAppleSFSymbolsNameForButton +#undef SDL_GameControllerGetAppleSFSymbolsNameForButton +#endif + +#ifdef SDL_GameControllerGetAppleSFSymbolsNameForAxis +#undef SDL_GameControllerGetAppleSFSymbolsNameForAxis +#endif + +#ifdef SDL_hid_init +#undef SDL_hid_init +#endif + +#ifdef SDL_hid_exit +#undef SDL_hid_exit +#endif + +#ifdef SDL_hid_device_change_count +#undef SDL_hid_device_change_count +#endif + +#ifdef SDL_hid_enumerate +#undef SDL_hid_enumerate +#endif + +#ifdef SDL_hid_free_enumeration +#undef SDL_hid_free_enumeration +#endif + +#ifdef SDL_hid_open +#undef SDL_hid_open +#endif + +#ifdef SDL_hid_open_path +#undef SDL_hid_open_path +#endif + +#ifdef SDL_hid_write +#undef SDL_hid_write +#endif + +#ifdef SDL_hid_read_timeout +#undef SDL_hid_read_timeout +#endif + +#ifdef SDL_hid_read +#undef SDL_hid_read +#endif + +#ifdef SDL_hid_set_nonblocking +#undef SDL_hid_set_nonblocking +#endif + +#ifdef SDL_hid_send_feature_report +#undef SDL_hid_send_feature_report +#endif + +#ifdef SDL_hid_get_feature_report +#undef SDL_hid_get_feature_report +#endif + +#ifdef SDL_hid_close +#undef SDL_hid_close +#endif + +#ifdef SDL_hid_get_manufacturer_string +#undef SDL_hid_get_manufacturer_string +#endif + +#ifdef SDL_hid_get_product_string +#undef SDL_hid_get_product_string +#endif + +#ifdef SDL_hid_get_serial_number_string +#undef SDL_hid_get_serial_number_string +#endif + +#ifdef SDL_hid_get_indexed_string +#undef SDL_hid_get_indexed_string +#endif + +#ifdef SDL_SetWindowMouseRect +#undef SDL_SetWindowMouseRect +#endif + +#ifdef SDL_GetWindowMouseRect +#undef SDL_GetWindowMouseRect +#endif + +#ifdef SDL_RenderWindowToLogical +#undef SDL_RenderWindowToLogical +#endif + +#ifdef SDL_RenderLogicalToWindow +#undef SDL_RenderLogicalToWindow +#endif + +#ifdef SDL_JoystickHasRumble +#undef SDL_JoystickHasRumble +#endif + +#ifdef SDL_JoystickHasRumbleTriggers +#undef SDL_JoystickHasRumbleTriggers +#endif + +#ifdef SDL_GameControllerHasRumble +#undef SDL_GameControllerHasRumble +#endif + +#ifdef SDL_GameControllerHasRumbleTriggers +#undef SDL_GameControllerHasRumbleTriggers +#endif + +#ifdef SDL_hid_ble_scan +#undef SDL_hid_ble_scan +#endif + +#ifdef SDL_PremultiplyAlpha +#undef SDL_PremultiplyAlpha +#endif + +#ifdef SDL_AndroidSendMessage +#undef SDL_AndroidSendMessage +#endif + +#ifdef SDL_GetTouchName +#undef SDL_GetTouchName +#endif + +#ifdef SDL_ClearComposition +#undef SDL_ClearComposition +#endif + +#ifdef SDL_IsTextInputShown +#undef SDL_IsTextInputShown +#endif + +#ifdef SDL_HasIntersectionF +#undef SDL_HasIntersectionF +#endif + +#ifdef SDL_IntersectFRect +#undef SDL_IntersectFRect +#endif + +#ifdef SDL_UnionFRect +#undef SDL_UnionFRect +#endif + +#ifdef SDL_EncloseFPoints +#undef SDL_EncloseFPoints +#endif + +#ifdef SDL_IntersectFRectAndLine +#undef SDL_IntersectFRectAndLine +#endif + +#ifdef SDL_RenderGetWindow +#undef SDL_RenderGetWindow +#endif + +#ifdef SDL_bsearch +#undef SDL_bsearch +#endif + +#ifdef SDL_GameControllerPathForIndex +#undef SDL_GameControllerPathForIndex +#endif + +#ifdef SDL_GameControllerPath +#undef SDL_GameControllerPath +#endif + +#ifdef SDL_JoystickPathForIndex +#undef SDL_JoystickPathForIndex +#endif + +#ifdef SDL_JoystickPath +#undef SDL_JoystickPath +#endif + +#ifdef SDL_JoystickAttachVirtualEx +#undef SDL_JoystickAttachVirtualEx +#endif + +#ifdef SDL_GameControllerGetFirmwareVersion +#undef SDL_GameControllerGetFirmwareVersion +#endif + +#ifdef SDL_JoystickGetFirmwareVersion +#undef SDL_JoystickGetFirmwareVersion +#endif + +#ifdef SDL_GUIDToString +#undef SDL_GUIDToString +#endif + +#ifdef SDL_GUIDFromString +#undef SDL_GUIDFromString +#endif + +#ifdef SDL_HasLSX +#undef SDL_HasLSX +#endif + +#ifdef SDL_HasLASX +#undef SDL_HasLASX +#endif + +#ifdef SDL_RenderGetD3D12Device +#undef SDL_RenderGetD3D12Device +#endif + +#ifdef SDL_utf8strnlen +#undef SDL_utf8strnlen +#endif + +#ifdef SDL_GDKGetTaskQueue +#undef SDL_GDKGetTaskQueue +#endif + +#ifdef SDL_GDKRunApp +#undef SDL_GDKRunApp +#endif + +#ifdef SDL_GetOriginalMemoryFunctions +#undef SDL_GetOriginalMemoryFunctions +#endif + +#ifdef SDL_ResetKeyboard +#undef SDL_ResetKeyboard +#endif + +#ifdef SDL_GetDefaultAudioInfo +#undef SDL_GetDefaultAudioInfo +#endif + +#ifdef SDL_GetPointDisplayIndex +#undef SDL_GetPointDisplayIndex +#endif + +#ifdef SDL_GetRectDisplayIndex +#undef SDL_GetRectDisplayIndex +#endif + +#ifdef SDL_ResetHint +#undef SDL_ResetHint +#endif + +#ifdef SDL_crc16 +#undef SDL_crc16 +#endif + +#ifdef SDL_GetWindowSizeInPixels +#undef SDL_GetWindowSizeInPixels +#endif + +#ifdef SDL_GetJoystickGUIDInfo +#undef SDL_GetJoystickGUIDInfo +#endif + +#ifdef SDL_SetPrimarySelectionText +#undef SDL_SetPrimarySelectionText +#endif + +#ifdef SDL_GetPrimarySelectionText +#undef SDL_GetPrimarySelectionText +#endif + +#ifdef SDL_HasPrimarySelectionText +#undef SDL_HasPrimarySelectionText +#endif + +#ifdef SDL_GameControllerGetSensorDataWithTimestamp +#undef SDL_GameControllerGetSensorDataWithTimestamp +#endif + +#ifdef SDL_SensorGetDataWithTimestamp +#undef SDL_SensorGetDataWithTimestamp +#endif + +#ifdef SDL_ResetHints +#undef SDL_ResetHints +#endif + +#ifdef SDL_strcasestr +#undef SDL_strcasestr +#endif + +#ifdef SDL_GDKSuspendComplete +#undef SDL_GDKSuspendComplete +#endif + +#ifdef SDL_HasWindowSurface +#undef SDL_HasWindowSurface +#endif + +#ifdef SDL_DestroyWindowSurface +#undef SDL_DestroyWindowSurface +#endif + +#ifdef SDL_GDKGetDefaultUser +#undef SDL_GDKGetDefaultUser +#endif + +#ifdef SDL_GameControllerGetSteamHandle +#undef SDL_GameControllerGetSteamHandle +#endif + +/* undefine these macros, too: redefine as SDL2_xxx, if needed. + */ +#ifdef SDL_BlitSurface +#undef SDL_BlitSurface +#endif + +#ifdef SDL_mutexP +#undef SDL_mutexP +#endif + +#ifdef SDL_mutexV +#undef SDL_mutexV +#endif + +#ifdef SDL_enabled_assert +#undef SDL_enabled_assert +#endif + +#ifdef SDL_OutOfMemory +#undef SDL_OutOfMemory +#endif + +#ifdef SDL_Unsupported +#undef SDL_Unsupported +#endif + +#ifdef SDL_InvalidParamError +#undef SDL_InvalidParamError +#endif + +#ifdef AtomicIncRef +#undef AtomicIncRef +#endif + +#ifdef SDL_AtomicDecRef +#undef SDL_AtomicDecRef +#endif + +#ifdef SDL_copyp +#undef SDL_copyp +#endif + +#ifdef SDL_zero +#undef SDL_zero +#endif + +#ifdef SDL_zeroa +#undef SDL_zeroa +#endif + +#ifdef SDL_zerop +#undef SDL_zerop +#endif + +#ifdef SDL_stack_alloc +#undef SDL_stack_alloc +#endif + +#ifdef SDL_stack_free +#undef SDL_stack_free +#endif + +#ifdef SDL_iconv_utf8_locale +#undef SDL_iconv_utf8_locale +#endif + +#ifdef SDL_iconv_utf8_ucs2 +#undef SDL_iconv_utf8_ucs2 +#endif + +#ifdef SDL_iconv_utf8_ucs4 +#undef SDL_iconv_utf8_ucs4 +#endif + +#ifdef SDL_iconv_wchar_utf8 +#undef SDL_iconv_wchar_utf8 +#endif + +#ifdef SDL_LoadWAV +#undef SDL_LoadWAV +#endif + +#ifdef SDL_LoadBMP +#undef SDL_LoadBMP +#endif + +#ifdef SDL_SaveBMP +#undef SDL_SaveBMP +#endif + +#ifdef SDL_GameControllerAddMappingsFromFile +#undef SDL_GameControllerAddMappingsFromFile +#endif + +#ifdef SDL_iOSSetAnimationCallback +#undef SDL_iOSSetAnimationCallback +#endif + +#ifdef SDL_iOSSetEventPump +#undef SDL_iOSSetEventPump +#endif + +#endif + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h index 1f964e625..f0ac1e91d 100644 --- a/src/SDL20_syms.h +++ b/src/SDL20_syms.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2021 Sam Lantinga + Copyright (C) 1997-2026 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -46,7 +46,7 @@ SDL20_SYM_VARARGS(void,Log,(const char *fmt, ...)) SDL20_SYM(int,Init,(Uint32 a),(a),return) SDL20_SYM(int,InitSubSystem,(Uint32 a),(a),return) SDL20_SYM(Uint32,WasInit,(Uint32 a),(a),return) -SDL20_SYM(char*,GetError,(void),(),return) +SDL20_SYM(const char*,GetError,(void),(),return) SDL20_SYM_VARARGS(int,SetError,(const char *fmt, ...)) SDL20_SYM(const char *,GetHint,(const char *a),(a),return) @@ -70,16 +70,22 @@ SDL20_SYM(void,PumpEvents,(void),(),) SDL20_SYM(void,SetEventFilter,(SDL_EventFilter a, void *b),(a,b),) SDL20_SYM(void,AddEventWatch,(SDL_EventFilter a, void *b),(a,b),) SDL20_SYM(void,DelEventWatch,(SDL_EventFilter a, void *b),(a,b),) +SDL20_SYM(Uint8,EventState,(Uint32 a, int b),(a,b),return) +SDL20_SYM(SDL_bool,GetWindowWMInfo,(SDL_Window *a, SDL_SysWMinfo *b),(a,b),) + +SDL20_SYM(int,GetNumVideoDisplays,(void),(),return) SDL20_SYM(int,GetNumDisplayModes,(int a),(a),return) SDL20_SYM(int,GetDisplayMode,(int a, int b, SDL_DisplayMode *c),(a,b,c),return) SDL20_SYM(int,GetDesktopDisplayMode,(int a, SDL_DisplayMode *b),(a,b),return) SDL20_SYM(int,GetCurrentDisplayMode,(int a, SDL_DisplayMode *b),(a,b),return) - -SDL20_SYM(void,EnableScreenSaver,(void),(),) -SDL20_SYM(void,DisableScreenSaver,(void),(),) +SDL20_SYM(int,GetWindowDisplayMode,(SDL_Window *a, SDL_DisplayMode *b),(a,b),return) +SDL20_SYM(SDL_DisplayMode *,GetClosestDisplayMode,(int a, const SDL_DisplayMode *b, SDL_DisplayMode *c),(a,b,c),return) +SDL20_SYM(int,GetWindowDisplayIndex,(SDL_Window *a),(a),return) +SDL20_SYM(int,SetWindowDisplayMode,(SDL_Window *a, const SDL_DisplayMode *b),(a,b),return) SDL20_SYM(SDL_Window *,CreateWindow,(const char *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return) +SDL20_SYM(SDL_Window *,CreateWindowFrom,(const void *a),(a),return) SDL20_SYM(void,DestroyWindow,(SDL_Window *a),(a),) SDL20_SYM(void,SetWindowIcon,(SDL_Window *a,SDL_Surface *b),(a,b),) SDL20_SYM(int,UpdateWindowSurface,(SDL_Window *a),(a),return) @@ -108,9 +114,8 @@ SDL20_SYM(SDL_Surface *,ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b SDL20_SYM(int,LockSurface,(SDL_Surface *a),(a),return) SDL20_SYM(void,UnlockSurface,(SDL_Surface *a),(a),) SDL20_SYM(int,UpperBlit,(SDL_Surface *a,const SDL_Rect *b,SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return) -SDL20_SYM(int,LowerBlit,(SDL_Surface *a,const SDL_Rect *b,SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return) +SDL20_SYM(int,LowerBlit,(SDL_Surface *a,SDL_Rect *b,SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return) SDL20_SYM(int,SoftStretch,(SDL_Surface *a,const SDL_Rect *b,SDL_Surface *c,const SDL_Rect *d),(a,b,c,d),return) -SDL20_SYM(SDL_bool,HasColorKey,(SDL_Surface *a),(a),return) SDL20_SYM(int,SetColorKey,(SDL_Surface *a, int b, Uint32 c),(a,b,c),return) SDL20_SYM(int,GetColorKey,(SDL_Surface *a, Uint32 *b),(a,b),return) SDL20_SYM(void,FreeSurface,(SDL_Surface *a),(a),) @@ -131,6 +136,7 @@ SDL20_SYM(int,GL_GetAttribute,(SDL_GLattr a, int *b),(a,b),return) SDL20_SYM(int,GL_SetSwapInterval,(int a),(a),return) SDL20_SYM(int,GL_GetSwapInterval,(void),(),return) SDL20_SYM(SDL_GLContext,GL_CreateContext,(SDL_Window *a),(a),return) +SDL20_SYM(SDL_GLContext,GL_GetCurrentContext,(void),(),return) SDL20_SYM(int,GL_MakeCurrent,(SDL_Window *a, SDL_GLContext b),(a,b),return) SDL20_SYM(void,GL_SwapWindow,(SDL_Window *a),(a),) SDL20_SYM(void,GL_DeleteContext,(SDL_GLContext a),(a),) @@ -188,18 +194,30 @@ SDL20_SYM_PASSTHROUGH(int,CondBroadcast,(SDL_cond *a),(a),return) SDL20_SYM_PASSTHROUGH(int,CondWait,(SDL_cond *a, SDL_mutex *b),(a,b),return) SDL20_SYM_PASSTHROUGH(int,CondWaitTimeout,(SDL_cond *a, SDL_mutex *b, Uint32 c),(a,b,c),return) +SDL20_SYM(int,AtomicGet,(SDL_atomic_t *a),(a),return) +SDL20_SYM(int,AtomicSet,(SDL_atomic_t *a, int b),(a,b),return) +SDL20_SYM(int,AtomicAdd,(SDL_atomic_t *a, int b),(a,b),return) + SDL20_SYM(SDL_AudioSpec *,LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return) SDL20_SYM(int,OpenAudio,(SDL_AudioSpec *a, SDL_AudioSpec *b),(a,b),return) SDL20_SYM(void,CloseAudio,(void),(),) -SDL20_SYM_PASSTHROUGH(SDL_AudioStatus,GetAudioStatus,(void),(),return) -SDL20_SYM_PASSTHROUGH(void,PauseAudio,(int a),(a),) +SDL20_SYM(SDL_AudioStatus,GetAudioStatus,(void),(),return) +SDL20_SYM(void,PauseAudio,(int a),(a),) SDL20_SYM_PASSTHROUGH(void,FreeWAV,(Uint8 *a),(a),) -SDL20_SYM_PASSTHROUGH(int,BuildAudioCVT,(SDL_AudioCVT *a, Uint16 b, Uint8 c, int d, Uint16 e, Uint8 f, int g),(a,b,c,d,e,f,g),return) -SDL20_SYM_PASSTHROUGH(int,ConvertAudio,(SDL_AudioCVT *a),(a),return) -SDL20_SYM_PASSTHROUGH(void,MixAudio,(Uint8 *a, const Uint8 *b, Uint32 c, int d),(a,b,c,d),) +SDL20_SYM(int,BuildAudioCVT,(SDL_AudioCVT *a, Uint16 b, Uint8 c, int d, Uint16 e, Uint8 f, int g),(a,b,c,d,e,f,g),return) +SDL20_SYM(int,ConvertAudio,(SDL_AudioCVT *a),(a),return) +SDL20_SYM(void,MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),) SDL20_SYM_PASSTHROUGH(void,LockAudio,(void),(),) SDL20_SYM_PASSTHROUGH(void,UnlockAudio,(void),(),) +SDL20_SYM(SDL_AudioStream *,NewAudioStream,(const SDL_AudioFormat a, const Uint8 b, const int c, const SDL_AudioFormat d, const Uint8 e, const int f),(a,b,c,d,e,f),return) +SDL20_SYM(int,AudioStreamPut,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return) +SDL20_SYM(int,AudioStreamGet,(SDL_AudioStream *a, void *b, int c),(a,b,c),return) +SDL20_SYM(int,AudioStreamAvailable,(SDL_AudioStream *a),(a),return) +SDL20_SYM(int,AudioStreamFlush,(SDL_AudioStream *a),(a),return) +SDL20_SYM(void,AudioStreamClear,(SDL_AudioStream *a),(a),) +SDL20_SYM(void,FreeAudioStream,(SDL_AudioStream *a),(a),) + SDL20_SYM_PASSTHROUGH(void*,LoadObject,(const char *a),(a),return) SDL20_SYM_PASSTHROUGH(void*,LoadFunction,(void *a, const char *b),(a,b),return) SDL20_SYM_PASSTHROUGH(void,UnloadObject,(void *a),(a),) @@ -214,40 +232,53 @@ SDL20_SYM_PASSTHROUGH(SDL_bool,HasAltiVec,(void),(),return) SDL20_SYM(SDL_TimerID,AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return) SDL20_SYM(SDL_bool,RemoveTimer,(SDL_TimerID a),(a),return) SDL20_SYM_PASSTHROUGH(Uint32,GetTicks,(void),(),return) -SDL20_SYM_PASSTHROUGH(void,Delay,(Uint32 a),(a),) - -SDL20_SYM_PASSTHROUGH(int,NumJoysticks,(void),(),return) +SDL20_SYM(void,Delay,(Uint32 a),(a),) + +SDL20_SYM(SDL_bool,IsGameController,(int a),(a),return) +SDL20_SYM(const char *,GameControllerNameForIndex,(int a),(a),return) +SDL20_SYM(SDL_GameController *,GameControllerOpen,(int a),(a),return) +SDL20_SYM(void,GameControllerClose,(SDL_GameController *a),(a),) +SDL20_SYM(int,GameControllerEventState,(int a),(a),return) +SDL20_SYM(void,GameControllerUpdate,(void),(),) +SDL20_SYM(Sint16,GameControllerGetAxis,(SDL_GameController *a, int b),(a,b),return) /* SDL_GameControllerAxis b */ +SDL20_SYM(Uint8,GameControllerGetButton,(SDL_GameController *a, int b),(a,b),return) /* SDL_GameControllerButton b */ + +SDL20_SYM(int,NumJoysticks,(void),(),return) SDL20_SYM(const char *,JoystickNameForIndex,(int a),(a),return) +SDL20_SYM(SDL_JoystickID,JoystickGetDeviceInstanceID,(int a),(a),return) SDL20_SYM(SDL_Joystick *,JoystickOpen,(int a),(a),return) -SDL20_SYM_PASSTHROUGH(int,JoystickNumAxes,(SDL_Joystick *a),(a),return) -SDL20_SYM_PASSTHROUGH(int,JoystickNumBalls,(SDL_Joystick *a),(a),return) -SDL20_SYM_PASSTHROUGH(int,JoystickNumHats,(SDL_Joystick *a),(a),return) -SDL20_SYM_PASSTHROUGH(int,JoystickNumButtons,(SDL_Joystick *a),(a),return) -SDL20_SYM_PASSTHROUGH(void,JoystickUpdate,(void),(),) -SDL20_SYM_PASSTHROUGH(int,JoystickEventState,(int a),(a),return) -SDL20_SYM_PASSTHROUGH(Sint16,JoystickGetAxis,(SDL_Joystick *a, int b),(a,b),return) -SDL20_SYM_PASSTHROUGH(Uint8,JoystickGetHat,(SDL_Joystick *a, int b),(a,b),return) -SDL20_SYM_PASSTHROUGH(int,JoystickGetBall,(SDL_Joystick *a, int b, int *c, int *d),(a,b,c,d),return) -SDL20_SYM_PASSTHROUGH(Uint8,JoystickGetButton,(SDL_Joystick *a, int b),(a,b),return) +SDL20_SYM(int,JoystickNumAxes,(SDL_Joystick *a),(a),return) +SDL20_SYM(int,JoystickNumBalls,(SDL_Joystick *a),(a),return) +SDL20_SYM(int,JoystickNumHats,(SDL_Joystick *a),(a),return) +SDL20_SYM(int,JoystickNumButtons,(SDL_Joystick *a),(a),return) +SDL20_SYM(void,JoystickUpdate,(void),(),) +SDL20_SYM(int,JoystickEventState,(int a),(a),return) +SDL20_SYM(Sint16,JoystickGetAxis,(SDL_Joystick *a, int b),(a,b),return) +SDL20_SYM(Uint8,JoystickGetHat,(SDL_Joystick *a, int b),(a,b),return) +SDL20_SYM(int,JoystickGetBall,(SDL_Joystick *a, int b, int *c, int *d),(a,b,c,d),return) +SDL20_SYM(Uint8,JoystickGetButton,(SDL_Joystick *a, int b),(a,b),return) SDL20_SYM(void,JoystickClose,(SDL_Joystick *a),(a),return) SDL20_SYM(void,LockJoysticks,(void),(),) SDL20_SYM(void,UnlockJoysticks,(void),(),) SDL20_SYM(SDL_RWops *,RWFromFile,(const char *a, const char *b),(a,b),return) -SDL20_SYM(SDL_RWops *,RWFromFP,(void *a, int b),(a,b),return) /* FILE* */ +SDL20_SYM(SDL_RWops *,RWFromFP,(void *a, SDL_bool b),(a,b),return) /* FILE* */ SDL20_SYM(SDL_RWops *,RWFromMem,(void *a, int b),(a,b),return) SDL20_SYM(SDL_RWops *,RWFromConstMem,(const void *a, int b),(a,b),return) SDL20_SYM(SDL_RWops *,AllocRW,(void),(),return) SDL20_SYM(void,FreeRW,(SDL_RWops *a),(a),) +SDL20_SYM(void *,LoadFile_RW,(SDL_RWops *a, size_t *b, int c),(a,b,c),return) SDL20_SYM_PASSTHROUGH(void *,malloc,(size_t a),(a),return) SDL20_SYM_PASSTHROUGH(void *,calloc,(size_t a, size_t b),(a,b),return) SDL20_SYM_PASSTHROUGH(void *,realloc,(void *a, size_t b),(a,b),return) SDL20_SYM_PASSTHROUGH(void,free,(void *a),(a),) -SDL20_SYM_PASSTHROUGH(char *,getenv,(const char *a),(a),return) -SDL20_SYM_PASSTHROUGH(void,qsort,(void *a, size_t b, size_t c, int (*d)(const void *, const void *)),(a,b,c,d),) +SDL20_SYM_PASSTHROUGH(void,qsort,(void *a, size_t b, size_t c, int (SDLCALL *d)(const void *, const void *)),(a,b,c,d),) SDL20_SYM_PASSTHROUGH(void *,memset,(void *a, int b, size_t c),(a,b,c),return) SDL20_SYM_PASSTHROUGH(void *,memcpy,(void *a, const void *b, size_t c),(a,b,c),return) +SDL20_SYM(void *,memmove,(void *a, const void *b, size_t c),(a,b,c),return) +SDL20_SYM(double,atof,(const char *a),(a),return) + SDL20_SYM_PASSTHROUGH(int,memcmp,(const void *a, const void *b, size_t c),(a,b,c),return) SDL20_SYM_PASSTHROUGH(size_t,strlen,(const char *a),(a),return) SDL20_SYM_PASSTHROUGH(size_t,strlcpy,(char *a, const char *b, size_t c),(a,b,c),return) @@ -281,23 +312,20 @@ SDL20_SYM_PASSTHROUGH(int,iconv_close,(SDL_iconv_t a),(a),return) SDL20_SYM_PASSTHROUGH(size_t,iconv,(SDL_iconv_t a, const char **b, size_t *c, char **d, size_t *e),(a,b,c,d,e),return) SDL20_SYM_PASSTHROUGH(char *,iconv_string,(const char *a, const char *b, const char *c, size_t d),(a,b,c,d),return) SDL20_SYM(int,setenv,(const char *a, const char *b, int c),(a,b,c),return) -SDL20_SYM(int,atoi,(const char *a),(a),return) -#ifdef __WATCOMC__ /* Watcom builds are broken with SDL math functions. */ -#ifndef SDL12_MATH -#include -#define SDL20_fabsf fabs -#define SDL20_floorf floor -#define SDL12_MATH -#endif -#else -SDL20_SYM(float,fabsf,(float a),(a),return) -SDL20_SYM(float,floorf,(float a),(a),return) -#endif +SDL20_SYM(long,lroundf,(float a),(a),return) + +SDL20_SYM(double,fabs,(double a),(a),return) +SDL20_SYM(double,ceil,(double a),(a),return) +SDL20_SYM(double,floor,(double a),(a),return) +SDL20_SYM(int,GetRenderDriverInfo,(int a, SDL_RendererInfo *b),(a,b),return) SDL20_SYM(SDL_Renderer *,CreateRenderer,(SDL_Window *a, int b, Uint32 c),(a,b,c),return) SDL20_SYM(int,GetRendererInfo,(SDL_Renderer *a, SDL_RendererInfo *b),(a,b),return) +SDL20_SYM(void,RenderGetScale,(SDL_Renderer *a, float *b, float *c),(a,b,c),return) +SDL20_SYM(void,RenderGetViewport,(SDL_Renderer *a, SDL_Rect *b),(a,b),return) SDL20_SYM(SDL_Texture *,CreateTexture,(SDL_Renderer *a, Uint32 b, int c, int d, int e),(a,b,c,d,e),return) SDL20_SYM(int,LockTexture,(SDL_Texture *a, const SDL_Rect *b, void **c, int *d),(a,b,c,d),return) +SDL20_SYM(int,LockTextureToSurface,(SDL_Texture *a, const SDL_Rect *b, SDL_Surface **c),(a,b,c),return) SDL20_SYM(void,UnlockTexture,(SDL_Texture *a),(a),) SDL20_SYM(int,UpdateTexture,(SDL_Texture *a, const SDL_Rect *b, const void *c, int d),(a,b,c,d),return) SDL20_SYM(int,UpdateYUVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f, const Uint8 *g, int h),(a,b,c,d,e,f,g,h),return) @@ -309,6 +337,13 @@ SDL20_SYM(void,DestroyTexture,(SDL_Texture *a),(a),) SDL20_SYM(void,DestroyRenderer,(SDL_Renderer *a),(a),) SDL20_SYM(void,RenderPresent,(SDL_Renderer *a),(a),) +SDL20_SYM(SDL_bool,SetHintWithPriority,(const char *a, const char *b, SDL_HintPriority c),(a,b,c),return) + +#ifdef _WIN32 +SDL20_SYM_PASSTHROUGH(int,RegisterApp,(const char *a, Uint32 b, void *c),(a,b,c),return) +SDL20_SYM_PASSTHROUGH(void,UnregisterApp,(void),(),) +#endif + /* These are optional OpenGL entry points for sdl12-compat's internal use. */ OPENGL_SYM(Core,const GLubyte *,glGetString,(GLenum a),(a),return) OPENGL_SYM(Core,GLenum,glGetError,(),(),return) @@ -329,6 +364,30 @@ OPENGL_SYM(Core,void,glCopyTexImage2D,(GLenum a, GLint b, GLenum c, GLint d, GLi OPENGL_SYM(Core,void,glCopyTexSubImage2D,(GLenum a, GLint b, GLint c, GLint d, GLint e, GLint f, GLsizei g, GLsizei h),(a,b,c,d,e,f,g,h),) OPENGL_SYM(Core,void,glCopyTexSubImage3D,(GLenum a, GLint b, GLint c, GLint d, GLint e, GLint f, GLsizei g, GLsizei h, GLint i),(a,b,c,d,e,f,g,h,i),) +OPENGL_SYM(Core,void,glDeleteTextures,(GLsizei a, const GLuint *b),(a,b),) +OPENGL_SYM(Core,void,glGenTextures,(GLsizei a, GLuint *b),(a,b),) +OPENGL_SYM(Core,void,glPopAttrib,(),(),) +OPENGL_SYM(Core,void,glPopClientAttrib,(),(),) +OPENGL_SYM(Core,void,glPopMatrix,(),(),) +OPENGL_SYM(Core,void,glBegin,(GLenum a),(a),) +OPENGL_SYM(Core,void,glPushAttrib,(GLbitfield a),(a),) +OPENGL_SYM(Core,void,glPushClientAttrib,(GLbitfield a),(a),) +OPENGL_SYM(Core,void,glBindTexture,(GLenum a, GLuint b),(a,b),) +OPENGL_SYM(Core,void,glEnd,(),(),) +OPENGL_SYM(Core,void,glTexEnvf,(GLenum a, GLenum b, GLfloat c),(a,b,c),) +OPENGL_SYM(Core,void,glTexParameteri,(GLenum a, GLenum b, GLint c),(a,b,c),) +OPENGL_SYM(Core,void,glPixelStorei,(GLenum a, GLint b),(a,b),) +OPENGL_SYM(Core,void,glBlendFunc,(GLenum a, GLenum b),(a,b),) +OPENGL_SYM(Core,void,glColor4f,(GLfloat a, GLfloat b, GLfloat c, GLfloat d),(a,b,c,d),) +OPENGL_SYM(Core,void,glMatrixMode,(GLenum a),(a),) +OPENGL_SYM(Core,void,glLoadIdentity,(),(),) +OPENGL_SYM(Core,void,glPushMatrix,(),(),) +OPENGL_SYM(Core,void,glOrtho,(GLdouble a, GLdouble b, GLdouble c, GLdouble d, GLdouble e, GLdouble f),(a,b,c,d,e,f),) +OPENGL_SYM(Core,void,glTexImage2D,(GLenum a, GLint b, GLint c, GLsizei d, GLsizei e, GLint f, GLenum g, GLenum h, const GLvoid *i),(a,b,c,d,e,f,g,h,i),) +OPENGL_SYM(Core,void,glTexSubImage2D,(GLenum a, GLint b, GLint c, GLint d, GLsizei e, GLsizei f, GLenum g, GLenum h, const GLvoid *i),(a,b,c,d,e,f,g,h,i),) +OPENGL_SYM(Core,void,glVertex2i,(GLint a, GLint b),(a,b),) +OPENGL_SYM(Core,void,glTexCoord2f,(GLfloat a, GLfloat b),(a,b),) + OPENGL_EXT(GL_ARB_framebuffer_object) OPENGL_SYM(GL_ARB_framebuffer_object,void,glBindRenderbuffer,(GLenum a, GLuint b),(a,b),) OPENGL_SYM(GL_ARB_framebuffer_object,void,glDeleteRenderbuffers,(GLsizei a, const GLuint *b),(a,b),) @@ -344,6 +403,8 @@ OPENGL_SYM(GL_ARB_framebuffer_object,GLenum,glCheckFramebufferStatus,(GLenum a), OPENGL_SYM(GL_ARB_framebuffer_object,void,glFramebufferRenderbuffer,(GLenum a, GLenum b, GLenum c, GLuint d),(a,b,c,d),) OPENGL_SYM(GL_ARB_framebuffer_object,void,glBlitFramebuffer,(GLint a, GLint b, GLint c, GLint d, GLint e, GLint f, GLint g, GLint h, GLbitfield i, GLenum j),(a,b,c,d,e,f,g,h,i,j),) +OPENGL_EXT(GL_ARB_texture_non_power_of_two) + #undef SDL20_SYM #undef SDL20_SYM_PASSTHROUGH #undef SDL20_SYM_VARARGS @@ -351,4 +412,3 @@ OPENGL_SYM(GL_ARB_framebuffer_object,void,glBlitFramebuffer,(GLint a, GLint b, G #undef OPENGL_EXT /* vi: set ts=4 sw=4 expandtab: */ - diff --git a/src/SDLmain/dummy/SDL_dummy_main.c b/src/SDLmain/dummy/SDL_dummy_main.c new file mode 100644 index 000000000..da47d06a8 --- /dev/null +++ b/src/SDLmain/dummy/SDL_dummy_main.c @@ -0,0 +1,13 @@ + +/* Include the SDL main definition header */ +#include "SDL_main.h" + +#ifdef main +#undef main +int main(int argc, char *argv[]) +{ + return(SDL_main(argc, argv)); +} +#else +/* Nothing to do on this platform */ +#endif diff --git a/src/SDLmain/macosx/SDLMain.h b/src/SDLmain/macosx/SDLMain.h new file mode 100644 index 000000000..f6ea978b4 --- /dev/null +++ b/src/SDLmain/macosx/SDLMain.h @@ -0,0 +1,29 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#ifndef _SDLMain_h_ +#define _SDLMain_h_ + +#import + +/* Note that the following defines have not been changed in a long time and + it's likely going to need fixes if you try it. */ + +/* Use this flag to determine whether we use SDLMain.nib or not */ +#define SDL_USE_NIB_FILE 0 + +@interface SDLMain : NSObject +- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender; +- (void) setupWorkingDirectory:(BOOL)shouldChdir; +- (BOOL) application:(NSApplication *)theApplication openFile:(NSString *)filename; +- (void) applicationDidFinishLaunching: (NSNotification *) note; +#if SDL_USE_NIB_FILE +- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName; +#endif +@end + +#endif /* _SDLMain_h_ */ diff --git a/src/SDLmain/macosx/SDLMain.m b/src/SDLmain/macosx/SDLMain.m new file mode 100644 index 000000000..0b444e77b --- /dev/null +++ b/src/SDLmain/macosx/SDLMain.m @@ -0,0 +1,377 @@ +/* SDLMain.m - main entry point for our Cocoa-ized SDL app + Initial Version: Darrell Walisser + Non-NIB-Code & other changes: Max Horn + + Feel free to customize this file to suit your needs +*/ + +#include "SDL.h" +#include "SDLMain.h" +#include /* for MAXPATHLEN */ +#include + +/* For some reaon, Apple removed setAppleMenu from the headers in 10.4, + but the method still is there and works. To avoid warnings, we declare + it ourselves here. */ +@interface NSApplication(SDL_Missing_Methods) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +/* NSEventModifierFlagOption replaced NSAlternateKeyMask in 10.12, but it's the same value. */ +#define EventModifierFlagOption (1 << 19) +/* Same deal with the NSCommandKeyMask... */ +#define EventModifierFlagCommand (1 << 20) + + +static int gArgc; +static char **gArgv; +static BOOL gFinderLaunch; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + const NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +#if SDL_USE_NIB_FILE +/* A helper category for NSString */ +@interface NSString (ReplaceSubString) +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; +@end +#endif + +/* The main class of the application, the application's delegate */ +@implementation SDLMain + +- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender +{ + /* Post a SDL_QUIT event */ + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); + return NSTerminateCancel; +} + +/* Set the working directory to the .app's parent directory */ +- (void) setupWorkingDirectory:(BOOL)shouldChdir +{ + if (shouldChdir) + { + char parentdir[MAXPATHLEN]; + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); + if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { + chdir(parentdir); /* chdir to the binary app's parent */ + } + CFRelease(url); + CFRelease(url2); + } +} + +#if SDL_USE_NIB_FILE + +/* Fix menu to contain the real app name instead of "SDL App" */ +- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName +{ + NSRange aRange; + NSEnumerator *enumerator; + NSMenuItem *menuItem; + + aRange = [[aMenu title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; + + enumerator = [[aMenu itemArray] objectEnumerator]; + while ((menuItem = [enumerator nextObject])) + { + aRange = [[menuItem title] rangeOfString:@"SDL App"]; + if (aRange.length != 0) + [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; + if ([menuItem hasSubmenu]) + [self fixMenu:[menuItem submenu] withAppName:appName]; + } +} + +#else + +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(EventModifierFlagOption|EventModifierFlagCommand)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; +} + +/* Replacement for NSApplicationMain */ +static void CustomApplicationMain (int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDLMain *sdlMain; + + /* Ensure the application object is initialised and converted to a GUI app if necessary. */ + ProcessSerialNumber psn = { 0, kCurrentProcess}; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + /* Ensure the application object is initialised */ + [NSApplication sharedApplication]; + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + /* Create SDLMain and make it the app delegate */ + sdlMain = [[SDLMain alloc] init]; + [NSApp setDelegate:sdlMain]; + + /* Start the main event loop */ + [NSApp run]; + + [sdlMain release]; + [pool release]; +} + +#endif + + +/* + * Catch document open requests...this lets us notice files when the app + * was launched by double-clicking a document, or when a document was + * dragged/dropped on the app's icon. You need to have a + * CFBundleDocumentsType section in your Info.plist to get this message, + * apparently. + * + * Files are added to gArgv, so to the app, they'll look like command line + * arguments. Previously, apps launched from the finder had nothing but + * an argv[0]. + * + * This message may be received multiple times to open several docs on launch. + * + * This message is ignored once the app's mainline has been called. + */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + const char *temparg; + size_t arglen; + char *arg; + char **newargv; + + if (!gFinderLaunch) /* MacOS is passing command line args. */ + return FALSE; + + if (gCalledAppMainline) /* app has started, ignore this document. */ + return FALSE; + + temparg = [filename UTF8String]; + arglen = SDL_strlen(temparg) + 1; + arg = (char *) SDL_malloc(arglen); + if (arg == NULL) + return FALSE; + + newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); + if (newargv == NULL) + { + SDL_free(arg); + return FALSE; + } + gArgv = newargv; + + SDL_strlcpy(arg, temparg, arglen); + gArgv[gArgc++] = arg; + gArgv[gArgc] = NULL; + return TRUE; +} + + +/* Called when the internal event loop has just started running */ +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + int status; + + /* Set the working directory to the .app's parent directory */ + [self setupWorkingDirectory:gFinderLaunch]; + +#if SDL_USE_NIB_FILE + /* Set the main menu to contain the real app name instead of "SDL App" */ + [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; +#endif + + /* Hand off to main application code */ + gCalledAppMainline = TRUE; + status = SDL_main (gArgc, gArgv); + + /* We're done, thank you for playing */ + exit(status); +} +@end + + +@implementation NSString (ReplaceSubString) + +- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString +{ + unsigned int bufferSize; + unsigned int selfLen = [self length]; + unsigned int aStringLen = [aString length]; + unichar *buffer; + NSRange localRange; + NSString *result; + + bufferSize = selfLen + aStringLen - aRange.length; + buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); + + /* Get first part into buffer */ + localRange.location = 0; + localRange.length = aRange.location; + [self getCharacters:buffer range:localRange]; + + /* Get middle part into buffer */ + localRange.location = 0; + localRange.length = aStringLen; + [aString getCharacters:(buffer+aRange.location) range:localRange]; + + /* Get last part into buffer */ + localRange.location = aRange.location + aRange.length; + localRange.length = selfLen - localRange.location; + [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; + + /* Build output string */ + result = [NSString stringWithCharacters:buffer length:bufferSize]; + + NSDeallocateMemoryPages(buffer, bufferSize); + + return result; +} + +@end + + + +#ifdef main +# undef main +#endif + + +static int IsRootCwd() +{ + char buf[MAXPATHLEN]; + char *cwd = getcwd(buf, sizeof (buf)); + return (cwd && (strcmp(cwd, "/") == 0)); +} + +static int IsFinderLaunch(const int argc, char **argv) +{ + /* -psn_XXX is passed if we are launched from Finder, SOMETIMES */ + if ( (argc >= 2) && (strncmp(argv[1], "-psn", 4) == 0) ) { + return 1; + } else if ((argc == 1) && IsRootCwd()) { + /* we might still be launched from the Finder; on 10.9+, you might not + get the -psn command line anymore. If there's no + command line, and if our current working directory is "/", it + might as well be a Finder launch. */ + return 1; + } + return 0; /* not a Finder launch. */ +} + +/* Main entry point to executable - should *not* be SDL_main! */ +int main (int argc, char **argv) +{ + /* Copy the arguments into a global variable */ + if (IsFinderLaunch(argc, argv)) { + gArgv = (char **) SDL_malloc(sizeof (char *) * 2); + gArgv[0] = argv[0]; + gArgv[1] = NULL; + gArgc = 1; + gFinderLaunch = YES; + } else { + int i; + gArgc = argc; + gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); + for (i = 0; i <= argc; i++) + gArgv[i] = argv[i]; + gFinderLaunch = NO; + } + +#if SDL_USE_NIB_FILE + NSApplicationMain (argc, argv); +#else + CustomApplicationMain (argc, argv); +#endif + return 0; +} + diff --git a/src/SDLmain/win32/SDL_win32_main.c b/src/SDLmain/win32/SDL_win32_main.c new file mode 100644 index 000000000..52727f0f3 --- /dev/null +++ b/src/SDLmain/win32/SDL_win32_main.c @@ -0,0 +1,341 @@ +/* + SDL_main.c, placed in the public domain by Sam Lantinga 4/13/98 + + The WinMain function -- calls your program's main() function +*/ + +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +#define DIR_SEPERATOR "/" + +/* Include the SDL main definition header */ +#include "SDL.h" +#include "SDL_main.h" + +#ifdef main +# undef main +#endif /* main */ + +/* The standard output files */ +#define STDOUT_FILE "stdout.txt" +#define STDERR_FILE "stderr.txt" + +#undef SDL_isspace +#define SDL_isspace(a) ((a) == ' ' || (a) == '\t') + +/* Set a variable to tell if the stdio redirect has been enabled. */ +static int stdioRedirectEnabled = 0; + +static char stdoutPath[MAX_PATH]; +static char stderrPath[MAX_PATH]; + +static void UnEscapeQuotes( char *arg ) +{ + char *last = NULL; + + while( *arg ) { + if( *arg == '"' && *last == '\\' ) { + char *c_curr = arg; + char *c_last = last; + + while( *c_curr ) { + *c_last = *c_curr; + c_last = c_curr; + c_curr++; + } + *c_last = '\0'; + } + last = arg; + arg++; + } +} + +/* Parse a command line buffer into arguments */ +static int ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + char *lastp = NULL; + int argc, last_argc; + + argc = last_argc = 0; + for ( bufp = cmdline; *bufp; ) { + /* Skip leading whitespace */ + while ( SDL_isspace(*bufp) ) { + ++bufp; + } + /* Skip over argument */ + if ( *bufp == '"' ) { + ++bufp; + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && ( *bufp != '"' || (lastp && *lastp == '\\') ) ) { + lastp = bufp; + ++bufp; + } + } else { + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && ! SDL_isspace(*bufp) ) { + ++bufp; + } + } + if ( *bufp ) { + if ( argv ) { + *bufp = '\0'; + } + ++bufp; + } + + /* Strip out \ from \" sequences */ + if( argv && last_argc != argc ) { + UnEscapeQuotes( argv[last_argc] ); + } + last_argc = argc; + } + if ( argv ) { + argv[argc] = NULL; + } + return(argc); +} + +/* Show an error message */ +static void ShowError(const char *title, const char *message) +{ +/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */ +#ifdef USE_MESSAGEBOX + MessageBox(NULL, message, title, MB_ICONEXCLAMATION|MB_OK); +#else + fprintf(stderr, "%s: %s\n", title, message); +#endif +} + +/* Pop up an out of memory message, returns to Windows */ +static BOOL OutOfMemory(void) +{ + ShowError("Fatal Error", "Out of memory - aborting"); + return FALSE; +} + +/* SDL_Quit() shouldn't be used with atexit() directly because + calling conventions may differ... */ +static void cleanup(void) +{ + SDL_Quit(); +} + +/* Remove the output files if there was no output written */ +static void cleanup_output(void) { + FILE *file; + int empty; + + /* Flush the output in case anything is queued */ + fclose(stdout); + fclose(stderr); + + /* Without redirection we're done */ + if (!stdioRedirectEnabled) { + return; + } + + /* See if the files have any output in them */ + if ( stdoutPath[0] ) { + file = fopen(stdoutPath, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(stdoutPath); + } + } + } + if ( stderrPath[0] ) { + file = fopen(stderrPath, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(stderrPath); + } + } + } +} + +/* Redirect the output (stdout and stderr) to a file */ +static void redirect_output(void) +{ + DWORD pathlen; + char path[MAX_PATH]; + FILE *newfp; + + pathlen = GetModuleFileName(NULL, path, SDL_arraysize(path)); + while ( pathlen > 0 && path[pathlen] != '\\' ) { + --pathlen; + } + path[pathlen] = '\0'; + + SDL_strlcpy( stdoutPath, path, SDL_arraysize(stdoutPath) ); + SDL_strlcat( stdoutPath, DIR_SEPERATOR STDOUT_FILE, SDL_arraysize(stdoutPath) ); + + /* Redirect standard input and standard output */ + newfp = freopen(stdoutPath, "w", stdout); + + if ( newfp == NULL ) { /* This happens on NT */ +#if !defined(stdout) + stdout = fopen(stdoutPath, "w"); +#else + newfp = fopen(stdoutPath, "w"); + if ( newfp ) { + *stdout = *newfp; + } +#endif + } + + SDL_strlcpy( stderrPath, path, SDL_arraysize(stderrPath) ); + SDL_strlcat( stderrPath, DIR_SEPERATOR STDERR_FILE, SDL_arraysize(stderrPath) ); + + newfp = freopen(stderrPath, "w", stderr); + if ( newfp == NULL ) { /* This happens on NT */ +#if !defined(stderr) + stderr = fopen(stderrPath, "w"); +#else + newfp = fopen(stderrPath, "w"); + if ( newfp ) { + *stderr = *newfp; + } +#endif + } + + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */ + setbuf(stderr, NULL); /* No buffering */ + stdioRedirectEnabled = 1; +} + +#if defined(_MSC_VER) +/* The VC++ compiler needs main defined */ +#define console_main main +#endif + +/* This is where execution begins [console apps] */ +int console_main(int argc, char *argv[]) +{ + size_t n; + char *bufp, *appname; + int status; + + /* Get the class name from argv[0] */ + appname = argv[0]; + if ( (bufp=SDL_strrchr(argv[0], '\\')) != NULL ) { + appname = bufp+1; + } else + if ( (bufp=SDL_strrchr(argv[0], '/')) != NULL ) { + appname = bufp+1; + } + + if ( (bufp=SDL_strrchr(appname, '.')) == NULL ) + n = SDL_strlen(appname); + else + n = (bufp-appname); + + bufp = SDL_stack_alloc(char, n+1); + if ( bufp == NULL ) { + return OutOfMemory(); + } + SDL_strlcpy(bufp, appname, n+1); + appname = bufp; + + /* Load SDL dynamic link library */ + if ( SDL_Init(SDL_INIT_NOPARACHUTE) < 0 ) { + ShowError("WinMain() error", SDL_GetError()); + return(FALSE); + } + atexit(cleanup_output); + atexit(cleanup); + + /* Sam: + We still need to pass in the application handle so that + DirectInput will initialize properly when SDL_RegisterApp() + is called later in the video initialization. + */ + SDL_SetModuleHandle(GetModuleHandle(NULL)); + + /* Run the application main() code */ + status = SDL_main(argc, argv); + + /* Exit cleanly, calling atexit() functions */ + exit(status); + + /* Hush little compiler, don't you cry... */ + return 0; +} + +/* This is where execution begins [windowed apps] */ +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) +{ + HMODULE handle; + char **argv; + int argc; + char *cmdline; + char *env_str; + char *bufp; + size_t nLen; + + /* Start up DDHELP.EXE before opening any files, so DDHELP doesn't + keep them open. This is a hack.. hopefully it will be fixed + someday. DDHELP.EXE starts up the first time DDRAW.DLL is loaded. + */ + handle = LoadLibrary(TEXT("DDRAW.DLL")); + if ( handle != NULL ) { + FreeLibrary(handle); + } + + /* Check for stdio redirect settings and do the redirection */ + if ((env_str = SDL_getenv("SDL_STDIO_REDIRECT")) != NULL) { + if (SDL_atoi(env_str)) { + redirect_output(); + } + } +#ifndef NO_STDIO_REDIRECT + else { + redirect_output(); + } +#endif + + /* Grab the command line */ + bufp = GetCommandLine(); + nLen = SDL_strlen(bufp)+1; + cmdline = SDL_stack_alloc(char, nLen); + if ( cmdline == NULL ) { + return OutOfMemory(); + } + SDL_strlcpy(cmdline, bufp, nLen); + + /* Parse it into argv and argc */ + argc = ParseCommandLine(cmdline, NULL); + argv = SDL_stack_alloc(char*, argc+1); + if ( argv == NULL ) { + return OutOfMemory(); + } + ParseCommandLine(cmdline, argv); + + /* Run the main program (after a little SDL initialization) */ + console_main(argc, argv); + + /* Hush little compiler, don't you cry... */ + return 0; +} diff --git a/src/default_cursor.h b/src/default_cursor.h new file mode 100644 index 000000000..82f2a46d2 --- /dev/null +++ b/src/default_cursor.h @@ -0,0 +1,114 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2026 Sam Lantinga + + 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. +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Default cursor - it happens to be the Mac cursor, but could be anything */ + +#define DEFAULT_CWIDTH 16 +#define DEFAULT_CHEIGHT 16 +#define DEFAULT_CHOTX 0 +#define DEFAULT_CHOTY 0 + +/* Added a real MacOS cursor, at the request of Luc-Olivier de Charrière */ +#define USE_MACOS_CURSOR + +#ifdef USE_MACOS_CURSOR + +static unsigned char default_cdata[] = { + 0x00, 0x00, + 0x40, 0x00, + 0x60, 0x00, + 0x70, 0x00, + 0x78, 0x00, + 0x7C, 0x00, + 0x7E, 0x00, + 0x7F, 0x00, + 0x7F, 0x80, + 0x7C, 0x00, + 0x6C, 0x00, + 0x46, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x03, 0x00, + 0x00, 0x00 +}; + +static unsigned char default_cmask[] = { + 0xC0, 0x00, + 0xE0, 0x00, + 0xF0, 0x00, + 0xF8, 0x00, + 0xFC, 0x00, + 0xFE, 0x00, + 0xFF, 0x00, + 0xFF, 0x80, + 0xFF, 0xC0, + 0xFF, 0xE0, + 0xFE, 0x00, + 0xEF, 0x00, + 0xCF, 0x00, + 0x87, 0x80, + 0x07, 0x80, + 0x03, 0x00 +}; + +#else + +static unsigned char default_cdata[] = { + 0x00, 0x00, + 0x40, 0x00, + 0x60, 0x00, + 0x70, 0x00, + 0x78, 0x00, + 0x7C, 0x00, + 0x7E, 0x00, + 0x7F, 0x00, + 0x7F, 0x80, + 0x7C, 0x00, + 0x6C, 0x00, + 0x46, 0x00, + 0x06, 0x00, + 0x03, 0x00, + 0x03, 0x00, + 0x00, 0x00 +}; + +static unsigned char default_cmask[] = { + 0x40, 0x00, + 0xE0, 0x00, + 0xF0, 0x00, + 0xF8, 0x00, + 0xFC, 0x00, + 0xFE, 0x00, + 0xFF, 0x00, + 0xFF, 0x80, + 0xFF, 0xC0, + 0xFF, 0x80, + 0xFE, 0x00, + 0xEF, 0x00, + 0x4F, 0x00, + 0x07, 0x80, + 0x07, 0x80, + 0x03, 0x00 +}; + +#endif /* USE_MACOS_CURSOR */ +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/dr_mp3.h b/src/dr_mp3.h new file mode 100644 index 000000000..9243b4c2a --- /dev/null +++ b/src/dr_mp3.h @@ -0,0 +1,5453 @@ +/* +MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. +dr_mp3 - v0.7.4 - TBD + +David Reid - mackron@gmail.com + +GitHub: https://github.com/mackron/dr_libs + +Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for differences between minimp3 and dr_mp3. +*/ + +/* +Introduction +============= +dr_mp3 is a single file library. To use it, do something like the following in one .c file. + + ```c + #define DR_MP3_IMPLEMENTATION + #include "dr_mp3.h" + ``` + +You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following: + + ```c + drmp3 mp3; + if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { + // Failed to open file + } + + ... + + drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames); + ``` + +The drmp3 object is transparent so you can get access to the channel count and sample rate like so: + + ``` + drmp3_uint32 channels = mp3.channels; + drmp3_uint32 sampleRate = mp3.sampleRate; + ``` + +The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek callbacks with +`drmp3_init_memory()` and `drmp3_init()` respectively. + +You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request any number of PCM frames in each +call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount. + +You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, `drmp3_open_memory_and_read_pcm_frames_f32()` and +`drmp3_open_file_and_read_pcm_frames_f32()`. + + +Build Options +============= +#define these options before including this file. + +#define DR_MP3_NO_STDIO + Disable drmp3_init_file(), etc. + +#define DR_MP3_NO_SIMD + Disable SIMD optimizations. +*/ + +#ifndef dr_mp3_h +#define dr_mp3_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DR_MP3_NO_S16 +#ifndef DR_MP3_FLOAT_OUTPUT +#define DR_MP3_FLOAT_OUTPUT +#endif +#endif + +#define DRMP3_STRINGIFY(x) #x +#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) + +#define DRMP3_VERSION_MAJOR 0 +#define DRMP3_VERSION_MINOR 7 +#define DRMP3_VERSION_REVISION 4 +#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) + +#include /* For size_t. */ + +/* Sized Types */ +typedef signed char drmp3_int8; +typedef unsigned char drmp3_uint8; +typedef signed short drmp3_int16; +typedef unsigned short drmp3_uint16; +typedef signed int drmp3_int32; +typedef unsigned int drmp3_uint32; +#if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 drmp3_int64; + typedef unsigned __int64 drmp3_uint64; +#else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long drmp3_int64; + typedef unsigned long long drmp3_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif +#endif +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__) + typedef drmp3_uint64 drmp3_uintptr; +#else + typedef drmp3_uint32 drmp3_uintptr; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 + +/* Weird shifting syntax is for VC6 compatibility. */ +#define DRMP3_UINT64_MAX (((drmp3_uint64)0xFFFFFFFF << 32) | (drmp3_uint64)0xFFFFFFFF) +/* End Sized Types */ + +/* Decorations */ +#if !defined(DRMP3_API) + #if defined(DRMP3_DLL) + #if defined(_WIN32) + #define DRMP3_DLL_IMPORT __declspec(dllimport) + #define DRMP3_DLL_EXPORT __declspec(dllexport) + #define DRMP3_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) + #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define DRMP3_DLL_IMPORT + #define DRMP3_DLL_EXPORT + #define DRMP3_DLL_PRIVATE static + #endif + #endif + + #if defined(DR_MP3_IMPLEMENTATION) + #define DRMP3_API DRMP3_DLL_EXPORT + #else + #define DRMP3_API DRMP3_DLL_IMPORT + #endif + #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE + #else + #define DRMP3_API extern + #define DRMP3_PRIVATE static + #endif +#endif +/* End Decorations */ + +/* Result Codes */ +typedef drmp3_int32 drmp3_result; +#define DRMP3_SUCCESS 0 +#define DRMP3_ERROR -1 /* A generic error. */ +#define DRMP3_INVALID_ARGS -2 +#define DRMP3_INVALID_OPERATION -3 +#define DRMP3_OUT_OF_MEMORY -4 +#define DRMP3_OUT_OF_RANGE -5 +#define DRMP3_ACCESS_DENIED -6 +#define DRMP3_DOES_NOT_EXIST -7 +#define DRMP3_ALREADY_EXISTS -8 +#define DRMP3_TOO_MANY_OPEN_FILES -9 +#define DRMP3_INVALID_FILE -10 +#define DRMP3_TOO_BIG -11 +#define DRMP3_PATH_TOO_LONG -12 +#define DRMP3_NAME_TOO_LONG -13 +#define DRMP3_NOT_DIRECTORY -14 +#define DRMP3_IS_DIRECTORY -15 +#define DRMP3_DIRECTORY_NOT_EMPTY -16 +#define DRMP3_END_OF_FILE -17 +#define DRMP3_NO_SPACE -18 +#define DRMP3_BUSY -19 +#define DRMP3_IO_ERROR -20 +#define DRMP3_INTERRUPT -21 +#define DRMP3_UNAVAILABLE -22 +#define DRMP3_ALREADY_IN_USE -23 +#define DRMP3_BAD_ADDRESS -24 +#define DRMP3_BAD_SEEK -25 +#define DRMP3_BAD_PIPE -26 +#define DRMP3_DEADLOCK -27 +#define DRMP3_TOO_MANY_LINKS -28 +#define DRMP3_NOT_IMPLEMENTED -29 +#define DRMP3_NO_MESSAGE -30 +#define DRMP3_BAD_MESSAGE -31 +#define DRMP3_NO_DATA_AVAILABLE -32 +#define DRMP3_INVALID_DATA -33 +#define DRMP3_TIMEOUT -34 +#define DRMP3_NO_NETWORK -35 +#define DRMP3_NOT_UNIQUE -36 +#define DRMP3_NOT_SOCKET -37 +#define DRMP3_NO_ADDRESS -38 +#define DRMP3_BAD_PROTOCOL -39 +#define DRMP3_PROTOCOL_UNAVAILABLE -40 +#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 +#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 +#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 +#define DRMP3_SOCKET_NOT_SUPPORTED -44 +#define DRMP3_CONNECTION_RESET -45 +#define DRMP3_ALREADY_CONNECTED -46 +#define DRMP3_NOT_CONNECTED -47 +#define DRMP3_CONNECTION_REFUSED -48 +#define DRMP3_NO_HOST -49 +#define DRMP3_IN_PROGRESS -50 +#define DRMP3_CANCELLED -51 +#define DRMP3_MEMORY_ALREADY_MAPPED -52 +#define DRMP3_AT_END -53 +/* End Result Codes */ + +#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) + +/* Inline */ +#ifdef _MSC_VER + #define DRMP3_INLINE __forceinline +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define DRMP3_GNUC_INLINE_HINT __inline__ + #else + #define DRMP3_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) + #else + #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT + #endif +#elif defined(__WATCOMC__) + #define DRMP3_INLINE __inline +#else + #define DRMP3_INLINE +#endif +/* End Inline */ + + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); +DRMP3_API const char* drmp3_version_string(void); + + +/* Allocation Callbacks */ +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} drmp3_allocation_callbacks; +/* End Allocation Callbacks */ + + +/* +Low Level Push API +================== +*/ +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +typedef struct +{ + int frame_bytes, channels, sample_rate, layer, bitrate_kbps; +} drmp3dec_frame_info; + +typedef struct +{ + const drmp3_uint8 *buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + drmp3_uint8 header[4], reserv_buf[511]; + drmp3dec_scratch scratch; +} drmp3dec; + +/* Initializes a low level decoder. */ +DRMP3_API void drmp3dec_init(drmp3dec *dec); + +/* Reads a frame from a low level decoder. */ +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); + +#ifndef DR_MP3_NO_S16 +/* Helper for converting between f32 and s16. */ +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); +#endif + + +/* +Main API (Pull API) +=================== +*/ +typedef enum +{ + DRMP3_SEEK_SET, + DRMP3_SEEK_CUR, + DRMP3_SEEK_END +} drmp3_seek_origin; + +typedef struct +{ + drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */ + drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */ + drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */ + drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */ +} drmp3_seek_point; + +typedef enum +{ + DRMP3_METADATA_TYPE_ID3V1, + DRMP3_METADATA_TYPE_ID3V2, + DRMP3_METADATA_TYPE_APE, + DRMP3_METADATA_TYPE_XING, + DRMP3_METADATA_TYPE_VBRI +} drmp3_metadata_type; + +typedef struct +{ + drmp3_metadata_type type; + const void* pRawData; /* A pointer to the raw data. */ + size_t rawDataSize; +} drmp3_metadata; + + +/* +Callback for when data is read. Return value is the number of bytes actually read. + +pUserData [in] The user data that was passed to drmp3_init(), and family. +pBufferOut [out] The output buffer. +bytesToRead [in] The number of bytes to read. + +Returns the number of bytes actually read. + +A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +either the entire bytesToRead is filled or you have reached the end of the stream. +*/ +typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +/* +Callback for when data needs to be seeked. + +pUserData [in] The user data that was passed to drmp3_init(), and family. +offset [in] The number of bytes to move, relative to the origin. Can be negative. +origin [in] The origin of the seek. + +Returns whether or not the seek was successful. +*/ +typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + +/* +Callback for retrieving the current cursor position. + +pUserData [in] The user data that was passed to drmp3_init(), and family. +pCursor [out] The cursor position in bytes from the start of the stream. + +Returns whether or not the cursor position was successfully retrieved. +*/ +typedef drmp3_bool32 (* drmp3_tell_proc)(void* pUserData, drmp3_int64* pCursor); + + +/* +Callback for when metadata is read. + +Only the raw data is provided. The client is responsible for parsing the contents of the data themsevles. +*/ +typedef void (* drmp3_meta_proc)(void* pUserData, const drmp3_metadata* pMetadata); + + +typedef struct +{ + drmp3_uint32 channels; + drmp3_uint32 sampleRate; +} drmp3_config; + +typedef struct +{ + drmp3dec decoder; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; + drmp3_meta_proc onMeta; + void* pUserData; + void* pUserDataMeta; + drmp3_allocation_callbacks allocationCallbacks; + drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */ + drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */ + drmp3_uint32 pcmFramesConsumedInMP3Frame; + drmp3_uint32 pcmFramesRemainingInMP3Frame; + drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */ + drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally. */ + drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */ + drmp3_uint64 streamLength; /* The length of the stream in bytes. dr_mp3 will not read beyond this. If a ID3v1 or APE tag is present, this will be set to the first byte of the tag. */ + drmp3_uint64 streamStartOffset; /* The offset of the start of the MP3 data. This is used for skipping ID3v2 and VBR tags. */ + drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */ + drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */ + drmp3_uint32 delayInPCMFrames; + drmp3_uint32 paddingInPCMFrames; + drmp3_uint64 totalPCMFrameCount; /* Set to DRMP3_UINT64_MAX if the length is unknown. Includes delay and padding. */ + drmp3_bool32 isVBR; + drmp3_bool32 isCBR; + size_t dataSize; + size_t dataCapacity; + size_t dataConsumed; + drmp3_uint8* pData; + drmp3_bool32 atEnd; + struct + { + const drmp3_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ +} drmp3; + +/* +Initializes an MP3 decoder. + +onRead [in] The function to call when data needs to be read from the client. +onSeek [in] The function to call when the read position of the client data needs to move. +onTell [in] The function to call when the read position of the client data needs to be retrieved. +pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. + +Returns true if successful; false otherwise. + +Close the loader with drmp3_uninit(). + +See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() +*/ +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); + +/* +Initializes an MP3 decoder from a block of memory. + +This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +the lifetime of the drmp3 object. + +The buffer should contain the contents of the entire MP3 file. +*/ +DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); + +#ifndef DR_MP3_NO_STDIO +/* +Initializes an MP3 decoder from a file. + +This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3 +objects because the operating system may restrict the number of file handles an application can have open at +any given time. +*/ +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks); + +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif + +/* +Uninitializes an MP3 decoder. +*/ +DRMP3_API void drmp3_uninit(drmp3* pMP3); + +/* +Reads PCM frames as interleaved 32-bit IEEE floating point PCM. + +Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. +*/ +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); + +#ifndef DR_MP3_NO_S16 +/* +Reads PCM frames as interleaved signed 16-bit integer PCM. + +Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. +*/ +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); +#endif + +/* +Seeks to a specific frame. + +Note that this is _not_ an MP3 frame, but rather a PCM frame. +*/ +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); + +/* +Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. +*/ +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); + +/* +Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. +*/ +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); + +/* +Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet +radio. Runs in linear time. Returns 0 on error. + +This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient. +*/ +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); + +/* +Calculates the seekpoints based on PCM frames. This is slow. + +pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count. +On output it contains the actual count. The reason for this design is that the client may request too many +seekpoints, in which case dr_mp3 will return a corrected count. + +Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates. +*/ +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); + +/* +Binds a seek table to the decoder. + +This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this +remains valid while it is bound to the decoder. + +Use drmp3_calculate_seek_points() to calculate the seek points. +*/ +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); + + +#ifndef DR_MP3_NO_FULL_READ +/* +Opens an decodes an entire MP3 stream as a single operation. + +On output pConfig will receive the channel count and sample rate of the stream. + +Free the returned pointer with drmp3_free(). +*/ +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif + +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif + +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +#endif +#endif +#endif + +/* +Allocates a block of memory on the heap. +*/ +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); + +/* +Frees any memory that was allocated by a public drmp3 API. +*/ +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* dr_mp3_h */ + + +/************************************************************************************************************************************************************ + ************************************************************************************************************************************************************ + + IMPLEMENTATION + + ************************************************************************************************************************************************************ + ************************************************************************************************************************************************************/ +#if defined(DR_MP3_IMPLEMENTATION) +#ifndef dr_mp3_c +#define dr_mp3_c + +#include +#include +#include /* For INT_MAX */ + +DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) +{ + if (pMajor) { + *pMajor = DRMP3_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = DRMP3_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = DRMP3_VERSION_REVISION; + } +} + +DRMP3_API const char* drmp3_version_string(void) +{ + return DRMP3_VERSION_STRING; +} + +/* Disable SIMD when compiling with TCC for now. */ +#if defined(__TINYC__) +#define DR_MP3_NO_SIMD +#endif + +#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) + +#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES +#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#endif + +#define DRMP3_SHORT_BLOCK_TYPE 2 +#define DRMP3_STOP_BLOCK_TYPE 3 +#define DRMP3_MODE_MONO 3 +#define DRMP3_MODE_JOINT_STEREO 1 +#define DRMP3_HDR_SIZE 4 +#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define DRMP3_BITS_DEQUANTIZER_OUT -1 +#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) + +#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(DR_MP3_NO_SIMD) + +#if !defined(DR_MP3_ONLY_SIMD) && ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#define DR_MP3_ONLY_SIMD +#endif +#if !defined(DR_MP3_ONLY_SIMD) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) +#define DR_MP3_ONLY_SIMD +#endif + +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#if defined(_MSC_VER) +#include +#endif +#include +#define DRMP3_HAVE_SSE 1 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE _mm_storeu_ps +#define DRMP3_VLD _mm_loadu_ps +#define DRMP3_VSET _mm_set1_ps +#define DRMP3_VADD _mm_add_ps +#define DRMP3_VSUB _mm_sub_ps +#define DRMP3_VMUL _mm_mul_ps +#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; +#if (defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)) && !defined(__clang__) +#define drmp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int drmp3_have_simd(void) +{ +#ifdef DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif + if (g_have_simd) + goto end; + drmp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + drmp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + return g_have_simd - 1; + } + +end: + return g_have_simd - 1; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) +#include +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE vst1q_f32 +#define DRMP3_VLD vld1q_f32 +#define DRMP3_VSET vmovq_n_f32 +#define DRMP3_VADD vaddq_f32 +#define DRMP3_VSUB vsubq_f32 +#define DRMP3_VMUL vmulq_f32 +#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd(void) +{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ + return 1; +} +#else +#define DRMP3_HAVE_SSE 0 +#define DRMP3_HAVE_SIMD 0 +#ifdef DR_MP3_ONLY_SIMD +#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif + +#else + +#define DRMP3_HAVE_SIMD 0 + +#endif + +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__) +#define DRMP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) +{ + drmp3_int32 x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#else +#define DRMP3_HAVE_ARMV6 0 +#endif + + +/* Standard library stuff. */ +#ifndef DRMP3_ASSERT +#include +#define DRMP3_ASSERT(expression) assert(expression) +#endif +#ifndef DRMP3_COPY_MEMORY +#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRMP3_MOVE_MEMORY +#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif +#ifndef DRMP3_ZERO_MEMORY +#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef DRMP3_MALLOC +#define DRMP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRMP3_REALLOC +#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRMP3_FREE +#define DRMP3_FREE(p) free((p)) +#endif + + + +typedef struct +{ + float scf[3*64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} drmp3_L12_scale_info; + +typedef struct +{ + drmp3_uint8 tab_offset, code_tab_width, band_count; +} drmp3_L12_subband_alloc; + +static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +{ + drmp3_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int drmp3_hdr_valid(const drmp3_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (DRMP3_HDR_GET_LAYER(h) != 0) && + (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +{ + return drmp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +{ + static const drmp3_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; +} + +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); +} + +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +{ + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); +} + +static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +{ + int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int drmp3_hdr_padding(const drmp3_uint8 *h) +{ + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +{ + const drmp3_L12_subband_alloc *alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (DRMP3_HDR_IS_LAYER_1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = drmp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +{ + static const drmp3_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + drmp3_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + } + + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif + +static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + static const drmp3_uint8 g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const drmp3_uint8 g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const drmp3_uint8 g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (DRMP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (drmp3_bs_get_bits(bs, 1)) + { + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = drmp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables) & 31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + DRMP3_COPY_MEMORY(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + DRMP3_ZERO_MEMORY(scf, cnt); + DRMP3_ZERO_MEMORY(ist_pos, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float drmp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = DRMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +/* +I've had reports of GCC 14 throwing an incorrect -Wstringop-overflow warning here. This is an attempt +to silence this warning. +*/ +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif +static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +{ + static const drmp3_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + } else + { + static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + } + } else if (gr->preflag) + { + static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); + } + } + + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif + +static const float g_drmp3_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + +static float drmp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_drmp3_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const drmp3_int16 tabs[] = { 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, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const drmp3_uint8 *sfb = gr_info->sfbtab; + const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const drmp3_int16 *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) + { + break; + } +#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void drmp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) + { + for (; i < n - 3; i += 4) + { + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + } +#ifdef __GNUC__ + /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. + * For more info see: https://github.com/lieff/minimp3/issues/88 + */ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + { + drmp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void drmp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + } +#endif +#ifndef DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} + +static void drmp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void drmp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); + DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (DRMP3_HDR_TEST_I_STEREO(h->header)) + { + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + { + drmp3_L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); + } +} + +static void drmp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; k < n; k += 4) + { + drmp3_f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if DRMP3_HAVE_SSE +#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); + } else + { +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} + +#ifndef DR_MP3_FLOAT_OUTPUT +typedef drmp3_int16 drmp3d_sample_t; + +static drmp3_int16 drmp3d_scale_pcm(float sample) +{ + drmp3_int16 s; +#if DRMP3_HAVE_ARMV6 + drmp3_int32 s32 = (drmp3_int32)(sample + .5f); + s32 -= (s32 < 0); + s = (drmp3_int16)drmp3_clip_int16_arm(s32); +#else + if (sample >= 32766.5f) return (drmp3_int16) 32767; + if (sample <= -32767.5f) return (drmp3_int16)-32768; + s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ +#endif + return s; +} +#else +typedef float drmp3d_sample_t; + +static float drmp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif + +static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = drmp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = drmp3d_scale_pcm(a); +} + +static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + drmp3d_sample_t *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4*15); + drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } +#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } +#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } + drmp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + + { +#ifndef DR_MP3_FLOAT_OUTPUT +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif +#else + #if DRMP3_HAVE_SSE + static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif + a = DRMP3_VMUL(a, g_scale); + b = DRMP3_VMUL(b, g_scale); +#if DRMP3_HAVE_SSE + _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); + _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); + _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); +#else + vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); + vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); + vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); + vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); + vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); + vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); + vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); + vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); +#endif +#endif /* DR_MP3_FLOAT_OUTPUT */ + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ +#else + for (i = 14; i >= 0; i--) + { +#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + + dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + } +#endif +} + +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + drmp3d_DCT_II(grbuf + 576*i, nbands); + } + + DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!drmp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + { + if (drmp3_hdr_valid(mp3)) + { + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + { + if (drmp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} + +DRMP3_API void drmp3dec_init(drmp3dec *dec) +{ + dec->header[0] = 0; +} + +DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const drmp3_uint8 *hdr; + drmp3_bs bs_frame[1]; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + { + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->sample_rate = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) + { + drmp3_bs_get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = drmp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + success = drmp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin); + if (success && pcm != NULL) + { + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) + { + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); + } + } + drmp3_L3_save_reservoir(dec, &dec->scratch); + } else + { +#ifdef DR_MP3_ONLY_MP3 + return 0; +#else + drmp3_L12_scale_info sci[1]; + + if (pcm == NULL) { + return drmp3_hdr_frame_samples(hdr); + } + + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += drmp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + drmp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); + pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); + } + if (bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + } +#endif + } + + return success*drmp3_hdr_frame_samples(dec->header); +} + +#ifndef DR_MP3_NO_S16 +DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) +{ + size_t i = 0; +#if DRMP3_HAVE_SIMD + size_t aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + drmp3_f4 scale = DRMP3_VSET(32768.0f); + drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); + drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); +#if DRMP3_HAVE_SSE + drmp3_f4 s16max = DRMP3_VSET( 32767.0f); + drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); + out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); +#endif + } +#endif + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5f) + out[i] = (drmp3_int16) 32767; + else if (sample <= -32767.5f) + out[i] = (drmp3_int16)-32768; + else + { + short s = (drmp3_int16)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; + } + } +} +#endif + + +/************************************************************************************************************************************************************ + + Main Public API + + ************************************************************************************************************************************************************/ +/* SIZE_MAX */ +#if defined(SIZE_MAX) + #define DRMP3_SIZE_MAX SIZE_MAX +#else + #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) + #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define DRMP3_SIZE_MAX 0xFFFFFFFF + #endif +#endif +/* End SIZE_MAX */ + +/* Options. */ +#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES +#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 +#endif + +#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 + +/* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */ +#ifndef DRMP3_DATA_CHUNK_SIZE +#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) +#endif + + +#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) + +#ifndef DRMP3_PI_D +#define DRMP3_PI_D 3.14159265358979323846264 +#endif + +#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 + +static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; + /*return x + (y - x)*a;*/ +} + + +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + drmp3_uint32 t = a; + a = b; + b = t % a; + } + } + + return a; +} + + +static void* drmp3__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_MALLOC(sz); +} + +static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return DRMP3_REALLOC(p, sz); +} + +static void drmp3__free_default(void* p, void* pUserData) +{ + (void)pUserData; + DRMP3_FREE(p); +} + + +static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + + /* Try using realloc(). */ + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + + return NULL; +} + +static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + + /* Try emulating realloc() in terms of malloc()/free(). */ + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + + if (p != NULL) { + DRMP3_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + + return p2; + } + + return NULL; +} + +static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} + + +static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + /* Copy. */ + return *pAllocationCallbacks; + } else { + /* Defaults. */ + drmp3_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = drmp3__malloc_default; + allocationCallbacks.onRealloc = drmp3__realloc_default; + allocationCallbacks.onFree = drmp3__free_default; + return allocationCallbacks; + } +} + + + +static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRead; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + /* + Don't try reading 0 bytes from the callback. This can happen when the stream is clamped against + ID3v1 or APE tags at the end of the stream. + */ + if (bytesToRead == 0) { + return 0; + } + + bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + + return bytesRead; +} + +static size_t drmp3__on_read_clamped(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->streamLength == DRMP3_UINT64_MAX) { + return drmp3__on_read(pMP3, pBufferOut, bytesToRead); + } else { + drmp3_uint64 bytesRemaining; + + bytesRemaining = (pMP3->streamLength - pMP3->streamCursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = (size_t)bytesRemaining; + } + + return drmp3__on_read(pMP3, pBufferOut, bytesToRead); + } +} + +static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) +{ + DRMP3_ASSERT(offset >= 0); + DRMP3_ASSERT(origin == DRMP3_SEEK_SET || origin == DRMP3_SEEK_CUR); + + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { + return DRMP3_FALSE; + } + + if (origin == DRMP3_SEEK_SET) { + pMP3->streamCursor = (drmp3_uint64)offset; + } else{ + pMP3->streamCursor += offset; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) +{ + if (offset <= 0x7FFFFFFF) { + return drmp3__on_seek(pMP3, (int)offset, origin); + } + + /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */ + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; + } + + offset -= 0x7FFFFFFF; + while (offset > 0) { + if (offset <= 0x7FFFFFFF) { + if (!drmp3__on_seek(pMP3, (int)offset, DRMP3_SEEK_CUR)) { + return DRMP3_FALSE; + } + offset = 0; + } else { + if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_CUR)) { + return DRMP3_FALSE; + } + offset -= 0x7FFFFFFF; + } + } + + return DRMP3_TRUE; +} + +static void drmp3__on_meta(drmp3* pMP3, drmp3_metadata_type type, const void* pRawData, size_t rawDataSize) +{ + if (pMP3->onMeta) { + drmp3_metadata metadata; + + DRMP3_ZERO_OBJECT(&metadata); + metadata.type = type; + metadata.pRawData = pRawData; + metadata.rawDataSize = rawDataSize; + + pMP3->onMeta(pMP3->pUserDataMeta, &metadata); + } +} + + +static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) +{ + drmp3_uint32 pcmFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + if (pMP3->atEnd) { + return 0; + } + + for (;;) { + drmp3dec_frame_info info; + + /* minimp3 recommends doing data submission in chunks of at least 16K. If we don't have at least 16K bytes available, get more. */ + if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { + size_t bytesRead; + + /* First we need to move the data down. */ + if (pMP3->pData != NULL) { + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } + + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + if (pMP3->dataSize == 0) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* No data. */ + } + } + + pMP3->dataSize += bytesRead; + } + + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* File too big. */ + } + + DRMP3_ASSERT(pMP3->pData != NULL); + DRMP3_ASSERT(pMP3->dataCapacity > 0); + + /* Do a runtime check here to try silencing a false-positive from clang-analyzer. */ + if (pMP3->pData == NULL) { + return 0; + } + + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */ + + /* Consume the data. */ + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + + /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */ + if (pcmFramesRead > 0) { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.sample_rate; + + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes; + } + + break; + } else if (info.frame_bytes == 0) { + /* Need more data. minimp3 recommends doing data submission in 16K chunks. */ + size_t bytesRead; + + /* First we need to move the data down. */ + DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + + if (pMP3->dataCapacity == pMP3->dataSize) { + /* No room. Expand. */ + drmp3_uint8* pNewData; + size_t newDataCap; + + newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; + + pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; /* Out of memory. */ + } + + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + + /* Fill in a chunk. */ + bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = DRMP3_TRUE; + return 0; /* Error reading more data. */ + } + + pMP3->dataSize += bytesRead; + } + }; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) +{ + drmp3_uint32 pcmFramesRead = 0; + drmp3dec_frame_info info; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.pData != NULL); + + if (pMP3->atEnd) { + return 0; + } + + for (;;) { + pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + if (pcmFramesRead > 0) { + pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.sample_rate; + + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos; + } + + break; + } else if (info.frame_bytes > 0) { + /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; + } else { + /* Nothing at all was read. Abort. */ + break; + } + } + + /* Consume the data. */ + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; + + return pcmFramesRead; +} + +static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData) +{ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { + return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); + } else { + return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); + } +} + +static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, NULL, NULL); +} + +#if 0 +static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) +{ + drmp3_uint32 pcmFrameCount; + + DRMP3_ASSERT(pMP3 != NULL); + + pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + if (pcmFrameCount == 0) { + return 0; + } + + /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */ + pMP3->currentPCMFrame += pcmFrameCount; + pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; + pMP3->pcmFramesRemainingInMP3Frame = 0; + + return pcmFrameCount; +} +#endif + +static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3dec_frame_info firstFrameInfo; + const drmp3_uint8* pFirstFrameData; + drmp3_uint32 firstFramePCMFrameCount; + drmp3_uint32 detectedMP3FrameCount = 0xFFFFFFFF; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(onRead != NULL); + + /* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */ + drmp3dec_init(&pMP3->decoder); + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->onMeta = onMeta; + pMP3->pUserData = pUserData; + pMP3->pUserDataMeta = pUserDataMeta; + pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + + if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { + return DRMP3_FALSE; /* Invalid allocation callbacks. */ + } + + pMP3->streamCursor = 0; + pMP3->streamLength = DRMP3_UINT64_MAX; + pMP3->streamStartOffset = 0; + pMP3->delayInPCMFrames = 0; + pMP3->paddingInPCMFrames = 0; + pMP3->totalPCMFrameCount = DRMP3_UINT64_MAX; + + /* We'll first check for any ID3v1 or APE tags. */ + #if 1 + if (onSeek != NULL && onTell != NULL) { + if (onSeek(pUserData, 0, DRMP3_SEEK_END)) { + drmp3_int64 streamLen; + int streamEndOffset = 0; + + /* First get the length of the stream. We need this so we can ensure the stream is big enough to store the tags. */ + if (onTell(pUserData, &streamLen)) { + /* ID3v1 */ + if (streamLen > 128) { + char id3[3]; + if (onSeek(pUserData, streamEndOffset - 128, DRMP3_SEEK_END)) { + if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') { + /* We have an ID3v1 tag. */ + streamEndOffset -= 128; + streamLen -= 128; + + /* Fire a metadata callback for the TAG data. */ + if (onMeta != NULL) { + drmp3_uint8 tag[128]; + tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G'; + + if (onRead(pUserData, tag + 3, 125) == 125) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V1, tag, 128); + } + } + } else { + /* No ID3v1 tag. */ + } + } else { + /* Failed to seek to the ID3v1 tag. */ + } + } else { + /* Stream too short. No ID3v1 tag. */ + } + + /* APE */ + if (streamLen > 32) { + char ape[32]; /* The footer. */ + if (onSeek(pUserData, streamEndOffset - 32, DRMP3_SEEK_END)) { + if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') { + /* We have an APE tag. */ + drmp3_uint32 tagSize = + ((drmp3_uint32)ape[24] << 0) | + ((drmp3_uint32)ape[25] << 8) | + ((drmp3_uint32)ape[26] << 16) | + ((drmp3_uint32)ape[27] << 24); + + if (32 + tagSize < streamLen) { + streamEndOffset -= 32 + tagSize; + streamLen -= 32 + tagSize; + + /* Fire a metadata callback for the APE data. Must include both the main content and footer. */ + if (onMeta != NULL) { + /* We first need to seek to the start of the APE tag. */ + if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) { + size_t apeTagSize = (size_t)tagSize + 32; + drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks); + if (pTagData != NULL) { + if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize); + } + + drmp3_free(pTagData, pAllocationCallbacks); + } + } + } + } else { + /* The tag size is larger than the stream. Invalid APE tag. */ + } + } + } + } else { + /* Stream too short. No APE tag. */ + } + + /* Seek back to the start. */ + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + + pMP3->streamLength = (drmp3_uint64)streamLen; + + if (pMP3->memory.pData != NULL) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; + } + } else { + /* Failed to get the length of the stream. ID3v1 and APE tags cannot be skipped. */ + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + } + } else { + /* Failed to seek to the end. Cannot skip ID3v1 or APE tags. */ + } + } else { + /* No onSeek or onTell callback. Cannot skip ID3v1 or APE tags. */ + } + #endif + + + /* ID3v2 tags */ + #if 1 + { + char header[10]; + if (onRead(pUserData, header, 10) == 10) { + if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') { + drmp3_uint32 tagSize = + (((drmp3_uint32)header[6] & 0x7F) << 21) | + (((drmp3_uint32)header[7] & 0x7F) << 14) | + (((drmp3_uint32)header[8] & 0x7F) << 7) | + (((drmp3_uint32)header[9] & 0x7F) << 0); + + /* Account for the footer. */ + if (header[5] & 0x10) { + tagSize += 10; + } + + /* Read the tag content and fire a metadata callback. */ + if (onMeta != NULL) { + size_t tagSizeWithHeader = 10 + tagSize; + drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(tagSizeWithHeader, pAllocationCallbacks); + if (pTagData != NULL) { + DRMP3_COPY_MEMORY(pTagData, header, 10); + + if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader); + } + + drmp3_free(pTagData, pAllocationCallbacks); + } + } else { + /* Don't have a metadata callback, so just skip the tag. */ + if (onSeek != NULL) { + if (!onSeek(pUserData, tagSize, DRMP3_SEEK_CUR)) { + return DRMP3_FALSE; /* Failed to seek past the ID3v2 tag. */ + } + } else { + /* Don't have a seek callback. Read and discard. */ + char discard[1024]; + + while (tagSize > 0) { + size_t bytesToRead = tagSize; + if (bytesToRead > sizeof(discard)) { + bytesToRead = sizeof(discard); + } + + if (onRead(pUserData, discard, bytesToRead) != bytesToRead) { + return DRMP3_FALSE; /* Failed to read data. */ + } + + tagSize -= (drmp3_uint32)bytesToRead; + } + } + } + + pMP3->streamStartOffset += 10 + tagSize; /* +10 for the header. */ + pMP3->streamCursor = pMP3->streamStartOffset; + } else { + /* Not an ID3v2 tag. Seek back to the start. */ + if (onSeek != NULL) { + if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek back to the start. */ + } + } else { + /* Don't have a seek callback to move backwards. We'll just fall through and let the decoding process re-sync. The ideal solution here would be to read into the cache. */ + + /* + TODO: Copy the header into the cache. Will need to allocate space. See drmp3_decode_next_frame_ex__callbacks. There is not need + to handle the memory case because that will always have a seek implementation and will never hit this code path. + */ + } + } + } else { + /* Failed to read the header. We can return false here. If we couldn't read 10 bytes there's no way we'll have a valid MP3 stream. */ + return DRMP3_FALSE; + } + } + #endif + + /* + Decode the first frame to confirm that it is indeed a valid MP3 stream. Note that it's possible the first frame + is actually a Xing/LAME/VBRI header. If this is the case we need to skip over it. + */ + firstFramePCMFrameCount = drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData); + if (firstFramePCMFrameCount > 0) { + DRMP3_ASSERT(pFirstFrameData != NULL); + + /* + It might be a header. If so, we need to clear out the cached PCM frames in order to trigger a reload of fresh + data when decoding starts. We can assume all validation has already been performed to check if this is a valid + MP3 frame and that there is more than 0 bytes making up the frame. + + We're going to be basing this parsing code off the minimp3_ex implementation. + */ + #if 1 + DRMP3_ASSERT(firstFrameInfo.frame_bytes > 0); + { + drmp3_bs bs; + drmp3_L3_gr_info grInfo[4]; + + drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE); + + if (DRMP3_HDR_IS_CRC(pFirstFrameData)) { + drmp3_bs_get_bits(&bs, 16); /* CRC. */ + } + + if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { + drmp3_bool32 isXing = DRMP3_FALSE; + drmp3_bool32 isInfo = DRMP3_FALSE; + const drmp3_uint8* pTagData; + const drmp3_uint8* pTagDataBeg; + const void* pDataBufferEnd = NULL; + size_t frameBytes; + + pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8); + pTagData = pTagDataBeg; + + /* + We need to determine how many bytes are actually available in pTagData. Unfortunately this is different depending on + whether or not it's being decoded from memory or callbacks. + */ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { + pDataBufferEnd = pMP3->memory.pData + pMP3->memory.dataSize; + } else { + pDataBufferEnd = pMP3->pData + pMP3->dataCapacity; + } + + frameBytes = DRMP3_MIN((size_t)firstFrameInfo.frame_bytes, (size_t)((drmp3_uint8*)pDataBufferEnd - pTagDataBeg)); + + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 8) { + goto done_xing_info; /* Frame too small for a Xing/Info tag. */ + } + + /* Check for both "Xing" and "Info" identifiers. */ + isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g'); + isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o'); + + if (isXing || isInfo) { + drmp3_uint32 bytes = 0; + drmp3_uint32 flags = pTagData[7]; + + pTagData += 8; /* Skip past the ID and flags. */ + + if (flags & 0x01) { /* FRAMES flag. */ + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) { + goto done_xing_info; /* Invalid Xing/Info tag. */ + } + + detectedMP3FrameCount = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3]; + pTagData += 4; + } + + if (flags & 0x02) { /* BYTES flag. */ + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) { + goto done_xing_info; /* Invalid Xing/Info tag. */ + } + + bytes = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3]; + (void)bytes; /* <-- Just to silence a warning about `bytes` being assigned but unused. Want to leave this here in case I want to make use of it later. */ + pTagData += 4; + } + + if (flags & 0x04) { /* TOC flag. */ + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 100) { + goto done_xing_info; /* Invalid Xing/Info tag. */ + } + + /* TODO: Extract and bind seek points. */ + pTagData += 100; + } + + if (flags & 0x08) { /* SCALE flag. */ + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 4) { + goto done_xing_info; /* Invalid Xing/Info tag. */ + } + + pTagData += 4; + } + + /* At this point we're done with the Xing/Info header. Now we can look at the LAME data. */ + if (pTagData[0]) { + int delayInPCMFrames; + int paddingInPCMFrames; + + if (frameBytes - (size_t)(pTagData - pFirstFrameData) < 36) { + goto done_xing_info; /* Invalid Xing/Info tag. */ + } + + pTagData += 21; + + delayInPCMFrames = (( (drmp3_uint32)pTagData[0] << 4) | ((drmp3_uint32)pTagData[1] >> 4)) + (528 + 1); + paddingInPCMFrames = ((((drmp3_uint32)pTagData[1] & 0xF) << 8) | ((drmp3_uint32)pTagData[2] )) - (528 + 1); + if (paddingInPCMFrames < 0) { + paddingInPCMFrames = 0; /* Padding cannot be negative. Probably a malformed file. Ignore. */ + } + + pMP3->delayInPCMFrames = (drmp3_uint32)delayInPCMFrames; + pMP3->paddingInPCMFrames = (drmp3_uint32)paddingInPCMFrames; + } + + /* + My understanding is that if the "Xing" header is present we can consider this to be a VBR stream and if the "Info" header is + present it's a CBR stream. If this is not the case let me know! I'm just tracking this for the time being in case I want to + look at doing some CBR optimizations later on, such as faster seeking. + */ + if (isXing) { + pMP3->isVBR = DRMP3_TRUE; + } else if (isInfo) { + pMP3->isCBR = DRMP3_TRUE; + } + + /* Post the raw data of the tag to the metadata callback. */ + if (onMeta != NULL) { + drmp3_metadata_type metadataType = isXing ? DRMP3_METADATA_TYPE_XING : DRMP3_METADATA_TYPE_VBRI; + size_t tagDataSize; + + tagDataSize = (size_t)firstFrameInfo.frame_bytes; + tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData); + + drmp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize); + } + + /* Since this was identified as a tag, we don't want to treat it as audio. We need to clear out the PCM cache. */ + pMP3->pcmFramesRemainingInMP3Frame = 0; + + /* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */ + pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes); + pMP3->streamCursor = pMP3->streamStartOffset; + + /* + The internal decoder needs to be reset to clear out any state. If we don't reset this state, it's possible for + there to be inconsistencies in the number of samples read when reading to the end of the stream depending on + whether or not the caller seeks to the start of the stream. + */ + drmp3dec_init(&pMP3->decoder); + } + + done_xing_info:; + } else { + /* Failed to read the side info. */ + } + } + #endif + } else { + /* Not a valid MP3 stream. */ + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make sure it's freed before aborting. */ + return DRMP3_FALSE; + } + + if (detectedMP3FrameCount != 0xFFFFFFFF) { + pMP3->totalPCMFrameCount = (drmp3_uint64)detectedMP3FrameCount * firstFramePCMFrameCount; + } + + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL || onRead == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks); +} + + +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drmp3* pMP3 = (drmp3*)pUserData; + size_t bytesRemaining; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_int64 newCursor; + + DRMP3_ASSERT(pMP3 != NULL); + + if (origin == DRMP3_SEEK_SET) { + newCursor = 0; + } else if (origin == DRMP3_SEEK_CUR) { + newCursor = (drmp3_int64)pMP3->memory.currentReadPos; + } else if (origin == DRMP3_SEEK_END) { + newCursor = (drmp3_int64)pMP3->memory.dataSize; + } else { + DRMP3_ASSERT(!"Invalid seek origin"); + return DRMP3_FALSE; + } + + newCursor += byteOffset; + + if (newCursor < 0) { + return DRMP3_FALSE; /* Trying to seek prior to the start of the buffer. */ + } + if ((size_t)newCursor > pMP3->memory.dataSize) { + return DRMP3_FALSE; /* Trying to seek beyond the end of the buffer. */ + } + + pMP3->memory.currentReadPos = (size_t)newCursor; + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3__on_tell_memory(void* pUserData, drmp3_int64* pCursor) +{ + drmp3* pMP3 = (drmp3*)pUserData; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pCursor != NULL); + + *pCursor = (drmp3_int64)pMP3->memory.currentReadPos; + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + + if (pData == NULL || dataSize == 0) { + return DRMP3_FALSE; + } + + pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + + result = drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, drmp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks); + if (result == DRMP3_FALSE) { + return DRMP3_FALSE; + } + + /* Adjust the length of the memory stream to account for ID3v1 and APE tags. */ + if (pMP3->streamLength <= (drmp3_uint64)DRMP3_SIZE_MAX) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; /* Safe cast. */ + } + + if (pMP3->streamStartOffset > (drmp3_uint64)DRMP3_SIZE_MAX) { + return DRMP3_FALSE; /* Tags too big. */ + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks); +} + + +#ifndef DR_MP3_NO_STDIO +#include +#include /* For wcslen(), wcsrtombs() */ + +/* Errno */ +/* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out if it's ever used elsewhere. */ +#include +static drmp3_result drmp3_result_from_errno(int e) +{ + switch (e) + { + case 0: return DRMP3_SUCCESS; + #ifdef EPERM + case EPERM: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ENOENT + case ENOENT: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ESRCH + case ESRCH: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EINTR + case EINTR: return DRMP3_INTERRUPT; + #endif + #ifdef EIO + case EIO: return DRMP3_IO_ERROR; + #endif + #ifdef ENXIO + case ENXIO: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef E2BIG + case E2BIG: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENOEXEC + case ENOEXEC: return DRMP3_INVALID_FILE; + #endif + #ifdef EBADF + case EBADF: return DRMP3_INVALID_FILE; + #endif + #ifdef ECHILD + case ECHILD: return DRMP3_ERROR; + #endif + #ifdef EAGAIN + case EAGAIN: return DRMP3_UNAVAILABLE; + #endif + #ifdef ENOMEM + case ENOMEM: return DRMP3_OUT_OF_MEMORY; + #endif + #ifdef EACCES + case EACCES: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EFAULT + case EFAULT: return DRMP3_BAD_ADDRESS; + #endif + #ifdef ENOTBLK + case ENOTBLK: return DRMP3_ERROR; + #endif + #ifdef EBUSY + case EBUSY: return DRMP3_BUSY; + #endif + #ifdef EEXIST + case EEXIST: return DRMP3_ALREADY_EXISTS; + #endif + #ifdef EXDEV + case EXDEV: return DRMP3_ERROR; + #endif + #ifdef ENODEV + case ENODEV: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef ENOTDIR + case ENOTDIR: return DRMP3_NOT_DIRECTORY; + #endif + #ifdef EISDIR + case EISDIR: return DRMP3_IS_DIRECTORY; + #endif + #ifdef EINVAL + case EINVAL: return DRMP3_INVALID_ARGS; + #endif + #ifdef ENFILE + case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef EMFILE + case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; + #endif + #ifdef ENOTTY + case ENOTTY: return DRMP3_INVALID_OPERATION; + #endif + #ifdef ETXTBSY + case ETXTBSY: return DRMP3_BUSY; + #endif + #ifdef EFBIG + case EFBIG: return DRMP3_TOO_BIG; + #endif + #ifdef ENOSPC + case ENOSPC: return DRMP3_NO_SPACE; + #endif + #ifdef ESPIPE + case ESPIPE: return DRMP3_BAD_SEEK; + #endif + #ifdef EROFS + case EROFS: return DRMP3_ACCESS_DENIED; + #endif + #ifdef EMLINK + case EMLINK: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef EPIPE + case EPIPE: return DRMP3_BAD_PIPE; + #endif + #ifdef EDOM + case EDOM: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef ERANGE + case ERANGE: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EDEADLK + case EDEADLK: return DRMP3_DEADLOCK; + #endif + #ifdef ENAMETOOLONG + case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; + #endif + #ifdef ENOLCK + case ENOLCK: return DRMP3_ERROR; + #endif + #ifdef ENOSYS + case ENOSYS: return DRMP3_NOT_IMPLEMENTED; + #endif + #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ + case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; + #endif + #ifdef ELOOP + case ELOOP: return DRMP3_TOO_MANY_LINKS; + #endif + #ifdef ENOMSG + case ENOMSG: return DRMP3_NO_MESSAGE; + #endif + #ifdef EIDRM + case EIDRM: return DRMP3_ERROR; + #endif + #ifdef ECHRNG + case ECHRNG: return DRMP3_ERROR; + #endif + #ifdef EL2NSYNC + case EL2NSYNC: return DRMP3_ERROR; + #endif + #ifdef EL3HLT + case EL3HLT: return DRMP3_ERROR; + #endif + #ifdef EL3RST + case EL3RST: return DRMP3_ERROR; + #endif + #ifdef ELNRNG + case ELNRNG: return DRMP3_OUT_OF_RANGE; + #endif + #ifdef EUNATCH + case EUNATCH: return DRMP3_ERROR; + #endif + #ifdef ENOCSI + case ENOCSI: return DRMP3_ERROR; + #endif + #ifdef EL2HLT + case EL2HLT: return DRMP3_ERROR; + #endif + #ifdef EBADE + case EBADE: return DRMP3_ERROR; + #endif + #ifdef EBADR + case EBADR: return DRMP3_ERROR; + #endif + #ifdef EXFULL + case EXFULL: return DRMP3_ERROR; + #endif + #ifdef ENOANO + case ENOANO: return DRMP3_ERROR; + #endif + #ifdef EBADRQC + case EBADRQC: return DRMP3_ERROR; + #endif + #ifdef EBADSLT + case EBADSLT: return DRMP3_ERROR; + #endif + #ifdef EBFONT + case EBFONT: return DRMP3_INVALID_FILE; + #endif + #ifdef ENOSTR + case ENOSTR: return DRMP3_ERROR; + #endif + #ifdef ENODATA + case ENODATA: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ETIME + case ETIME: return DRMP3_TIMEOUT; + #endif + #ifdef ENOSR + case ENOSR: return DRMP3_NO_DATA_AVAILABLE; + #endif + #ifdef ENONET + case ENONET: return DRMP3_NO_NETWORK; + #endif + #ifdef ENOPKG + case ENOPKG: return DRMP3_ERROR; + #endif + #ifdef EREMOTE + case EREMOTE: return DRMP3_ERROR; + #endif + #ifdef ENOLINK + case ENOLINK: return DRMP3_ERROR; + #endif + #ifdef EADV + case EADV: return DRMP3_ERROR; + #endif + #ifdef ESRMNT + case ESRMNT: return DRMP3_ERROR; + #endif + #ifdef ECOMM + case ECOMM: return DRMP3_ERROR; + #endif + #ifdef EPROTO + case EPROTO: return DRMP3_ERROR; + #endif + #ifdef EMULTIHOP + case EMULTIHOP: return DRMP3_ERROR; + #endif + #ifdef EDOTDOT + case EDOTDOT: return DRMP3_ERROR; + #endif + #ifdef EBADMSG + case EBADMSG: return DRMP3_BAD_MESSAGE; + #endif + #ifdef EOVERFLOW + case EOVERFLOW: return DRMP3_TOO_BIG; + #endif + #ifdef ENOTUNIQ + case ENOTUNIQ: return DRMP3_NOT_UNIQUE; + #endif + #ifdef EBADFD + case EBADFD: return DRMP3_ERROR; + #endif + #ifdef EREMCHG + case EREMCHG: return DRMP3_ERROR; + #endif + #ifdef ELIBACC + case ELIBACC: return DRMP3_ACCESS_DENIED; + #endif + #ifdef ELIBBAD + case ELIBBAD: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBSCN + case ELIBSCN: return DRMP3_INVALID_FILE; + #endif + #ifdef ELIBMAX + case ELIBMAX: return DRMP3_ERROR; + #endif + #ifdef ELIBEXEC + case ELIBEXEC: return DRMP3_ERROR; + #endif + #ifdef EILSEQ + case EILSEQ: return DRMP3_INVALID_DATA; + #endif + #ifdef ERESTART + case ERESTART: return DRMP3_ERROR; + #endif + #ifdef ESTRPIPE + case ESTRPIPE: return DRMP3_ERROR; + #endif + #ifdef EUSERS + case EUSERS: return DRMP3_ERROR; + #endif + #ifdef ENOTSOCK + case ENOTSOCK: return DRMP3_NOT_SOCKET; + #endif + #ifdef EDESTADDRREQ + case EDESTADDRREQ: return DRMP3_NO_ADDRESS; + #endif + #ifdef EMSGSIZE + case EMSGSIZE: return DRMP3_TOO_BIG; + #endif + #ifdef EPROTOTYPE + case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; + #endif + #ifdef ENOPROTOOPT + case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; + #endif + #ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; + #endif + #ifdef EPFNOSUPPORT + case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; + #endif + #ifdef EADDRINUSE + case EADDRINUSE: return DRMP3_ALREADY_IN_USE; + #endif + #ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: return DRMP3_ERROR; + #endif + #ifdef ENETDOWN + case ENETDOWN: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETUNREACH + case ENETUNREACH: return DRMP3_NO_NETWORK; + #endif + #ifdef ENETRESET + case ENETRESET: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNABORTED + case ECONNABORTED: return DRMP3_NO_NETWORK; + #endif + #ifdef ECONNRESET + case ECONNRESET: return DRMP3_CONNECTION_RESET; + #endif + #ifdef ENOBUFS + case ENOBUFS: return DRMP3_NO_SPACE; + #endif + #ifdef EISCONN + case EISCONN: return DRMP3_ALREADY_CONNECTED; + #endif + #ifdef ENOTCONN + case ENOTCONN: return DRMP3_NOT_CONNECTED; + #endif + #ifdef ESHUTDOWN + case ESHUTDOWN: return DRMP3_ERROR; + #endif + #ifdef ETOOMANYREFS + case ETOOMANYREFS: return DRMP3_ERROR; + #endif + #ifdef ETIMEDOUT + case ETIMEDOUT: return DRMP3_TIMEOUT; + #endif + #ifdef ECONNREFUSED + case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; + #endif + #ifdef EHOSTDOWN + case EHOSTDOWN: return DRMP3_NO_HOST; + #endif + #ifdef EHOSTUNREACH + case EHOSTUNREACH: return DRMP3_NO_HOST; + #endif + #ifdef EALREADY + case EALREADY: return DRMP3_IN_PROGRESS; + #endif + #ifdef EINPROGRESS + case EINPROGRESS: return DRMP3_IN_PROGRESS; + #endif + #ifdef ESTALE + case ESTALE: return DRMP3_INVALID_FILE; + #endif + #ifdef EUCLEAN + case EUCLEAN: return DRMP3_ERROR; + #endif + #ifdef ENOTNAM + case ENOTNAM: return DRMP3_ERROR; + #endif + #ifdef ENAVAIL + case ENAVAIL: return DRMP3_ERROR; + #endif + #ifdef EISNAM + case EISNAM: return DRMP3_ERROR; + #endif + #ifdef EREMOTEIO + case EREMOTEIO: return DRMP3_IO_ERROR; + #endif + #ifdef EDQUOT + case EDQUOT: return DRMP3_NO_SPACE; + #endif + #ifdef ENOMEDIUM + case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; + #endif + #ifdef EMEDIUMTYPE + case EMEDIUMTYPE: return DRMP3_ERROR; + #endif + #ifdef ECANCELED + case ECANCELED: return DRMP3_CANCELLED; + #endif + #ifdef ENOKEY + case ENOKEY: return DRMP3_ERROR; + #endif + #ifdef EKEYEXPIRED + case EKEYEXPIRED: return DRMP3_ERROR; + #endif + #ifdef EKEYREVOKED + case EKEYREVOKED: return DRMP3_ERROR; + #endif + #ifdef EKEYREJECTED + case EKEYREJECTED: return DRMP3_ERROR; + #endif + #ifdef EOWNERDEAD + case EOWNERDEAD: return DRMP3_ERROR; + #endif + #ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: return DRMP3_ERROR; + #endif + #ifdef ERFKILL + case ERFKILL: return DRMP3_ERROR; + #endif + #ifdef EHWPOISON + case EHWPOISON: return DRMP3_ERROR; + #endif + default: return DRMP3_ERROR; + } +} +/* End Errno */ + +/* fopen */ +static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + drmp3_result result = drmp3_result_from_errno(errno); + if (result == DRMP3_SUCCESS) { + result = DRMP3_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return DRMP3_SUCCESS; +} + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define DRMP3_HAS_WFOPEN + #endif +#endif + +static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return DRMP3_INVALID_ARGS; + } + +#if defined(DRMP3_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return drmp3_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return drmp3_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because + fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note + that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler + error I'll look into improving compatibility. + */ + + /* + Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just + need to abort with an error. If you encounter a compiler lacking such support, add it to this list + and submit a bug report and it'll be added to the library upstream. + */ + #if defined(__DJGPP__) + { + /* Nothing to do here. This will fall through to the error check below. */ + } + #else + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + DRMP3_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return drmp3_result_from_errno(errno); + } + + pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return DRMP3_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + DRMP3_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); + } + #endif + + if (*ppFile == NULL) { + return DRMP3_ERROR; + } +#endif + + return DRMP3_SUCCESS; +} +/* End fopen */ + + +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +{ + int whence = SEEK_SET; + if (origin == DRMP3_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == DRMP3_SEEK_END) { + whence = SEEK_END; + } + + return fseek((FILE*)pUserData, offset, whence) == 0; +} + +static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + drmp3_int64 result; + + /* These were all validated at a higher level. */ + DRMP3_ASSERT(pFileStdio != NULL); + DRMP3_ASSERT(pCursor != NULL); + +#if defined(_WIN32) && !defined(NXDK) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + + *pCursor = result; + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + + if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { + return DRMP3_FALSE; + } + + result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3_bool32 result; + FILE* pFile; + + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + DRMP3_ZERO_OBJECT(pMP3); + + if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { + return DRMP3_FALSE; + } + + result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); + if (result != DRMP3_TRUE) { + fclose(pFile); + return result; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} + +DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + return drmp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} +#endif + +DRMP3_API void drmp3_uninit(drmp3* pMP3) +{ + if (pMP3 == NULL) { + return; + } + +#ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) { + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) { + fclose(pFile); + pMP3->pUserData = NULL; /* Make sure the file handle is cleared to NULL to we don't attempt to close it a second time. */ + } + } +#endif + + drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); +} + +#ifndef DR_MP3_NO_S16 +#if defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + drmp3_uint64 i4; + drmp3_uint64 sampleCount4; + + /* Unrolled. */ + i = 0; + sampleCount4 = sampleCount >> 2; + for (i4 = 0; i4 < sampleCount4; i4 += 1) { + float x0 = src[i+0]; + float x1 = src[i+1]; + float x2 = src[i+2]; + float x3 = src[i+3]; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst[i+0] = (drmp3_int16)x0; + dst[i+1] = (drmp3_int16)x1; + dst[i+2] = (drmp3_int16)x2; + dst[i+3] = (drmp3_int16)x3; + + i += 4; + } + + /* Leftover. */ + for (; i < sampleCount; i += 1) { + float x = src[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst[i] = (drmp3_int16)x; + } +} +#endif + +#if !defined(DR_MP3_FLOAT_OUTPUT) +static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) +{ + drmp3_uint64 i; + for (i = 0; i < sampleCount; i += 1) { + float x = (float)src[i]; + x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ + dst[i] = x; + } +} +#endif +#endif + +static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) +{ + drmp3_uint64 totalFramesRead = 0; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onRead != NULL); + + while (framesToRead > 0) { + drmp3_uint32 framesToConsume; + + /* Skip frames if necessary. */ + if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) { + drmp3_uint32 framesToSkip = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame); + + pMP3->currentPCMFrame += framesToSkip; + pMP3->pcmFramesConsumedInMP3Frame += framesToSkip; + pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip; + } + + framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + + /* Clamp the number of frames to read to the padding. */ + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) { + if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + drmp3_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame; + if (framesToConsume > framesRemainigToPadding) { + framesToConsume = (drmp3_uint32)framesRemainigToPadding; + } + } else { + /* We're into the padding. Abort. */ + break; + } + } + + if (pBufferOut != NULL) { + #if defined(DR_MP3_FLOAT_OUTPUT) + { + /* f32 */ + float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + } + #else + { + /* s16 */ + drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); + drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); + } + #endif + } + + pMP3->currentPCMFrame += framesToConsume; + pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; + pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; + totalFramesRead += framesToConsume; + framesToRead -= framesToConsume; + + if (framesToRead == 0) { + break; + } + + /* If the cursor is already at the padding we need to abort. */ + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + break; + } + + DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + + /* At this point we have exhausted our in-memory buffer so we need to re-fill. */ + if (drmp3_decode_next_frame(pMP3) == 0) { + break; + } + } + + return totalFramesRead; +} + + +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } + +#if defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + /* Slow path. Convert from s16 to f32. */ + { + drmp3_int16 pTempS16[1152*2]; /* MP3 frames have a maximum per-channel sample count of 1152. Times 2 to account for stereo. */ + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + if (framesJustRead == 0) { + break; + } + + drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } +#endif +} + +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } + +#if !defined(DR_MP3_FLOAT_OUTPUT) + /* Fast path. No conversion required. */ + return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + /* Slow path. Convert from f32 to s16. */ + { + float pTempF32[1152*2]; /* MP3 frames have a maximum per-channel sample count of 1152. Times 2 to account for stereo. */ + drmp3_uint64 totalPCMFramesRead = 0; + + while (totalPCMFramesRead < framesToRead) { + drmp3_uint64 framesJustRead; + drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + + framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) { + break; + } + + drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + + return totalPCMFramesRead; + } +#endif +} +#endif + +static void drmp3_reset(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->currentPCMFrame = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; + drmp3dec_init(&pMP3->decoder); +} + +static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +{ + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->onSeek != NULL); + + /* Seek to the start of the stream to begin with. */ + if (!drmp3__on_seek_64(pMP3, pMP3->streamStartOffset, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + return DRMP3_TRUE; +} + + +static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) +{ + drmp3_uint64 framesRead; + + /* + Just using a dumb read-and-discard for now. What would be nice is to parse only the header of the MP3 frame, and then skip over leading + frames without spending the time doing a full decode. I cannot see an easy way to do this in minimp3, however, so it may involve some + kind of manual processing. + */ +#if defined(DR_MP3_FLOAT_OUTPUT) + framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); +#else + framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); +#endif + if (framesRead != frameOffset) { + return DRMP3_FALSE; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + DRMP3_ASSERT(pMP3 != NULL); + + if (frameIndex == pMP3->currentPCMFrame) { + return DRMP3_TRUE; + } + + /* + If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of + the stream and read from the beginning. + */ + if (frameIndex < pMP3->currentPCMFrame) { + /* Moving backward. Move to the start of the stream and then move forward. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + } + + DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); +} + +static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) +{ + drmp3_uint32 iSeekPoint; + + DRMP3_ASSERT(pSeekPointIndex != NULL); + + *pSeekPointIndex = 0; + + if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { + return DRMP3_FALSE; + } + + /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */ + for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { + if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { + break; /* Found it. */ + } + + *pSeekPointIndex = iSeekPoint; + } + + return DRMP3_TRUE; +} + +static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + drmp3_seek_point seekPoint; + drmp3_uint32 priorSeekPointIndex; + drmp3_uint16 iMP3Frame; + drmp3_uint64 leftoverFrames; + + DRMP3_ASSERT(pMP3 != NULL); + DRMP3_ASSERT(pMP3->pSeekPoints != NULL); + DRMP3_ASSERT(pMP3->seekPointCount > 0); + + /* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */ + if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; + } else { + seekPoint.seekPosInBytes = 0; + seekPoint.pcmFrameIndex = 0; + seekPoint.mp3FramesToDiscard = 0; + seekPoint.pcmFramesToDiscard = 0; + } + + /* First thing to do is seek to the first byte of the relevant MP3 frame. */ + if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, DRMP3_SEEK_SET)) { + return DRMP3_FALSE; /* Failed to seek. */ + } + + /* Clear any cached data. */ + drmp3_reset(pMP3); + + /* Whole MP3 frames need to be discarded first. */ + for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { + drmp3_uint32 pcmFramesRead; + drmp3d_sample_t* pPCMFrames; + + /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */ + pPCMFrames = NULL; + if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { + pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; + } + + /* We first need to decode the next frame. */ + pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL); + if (pcmFramesRead == 0) { + return DRMP3_FALSE; + } + } + + /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */ + pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; + + /* + Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then + read-and-discard at least 2 whole MP3 frames. + */ + leftoverFrames = frameIndex - pMP3->currentPCMFrame; + return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); +} + +DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) { + return DRMP3_FALSE; + } + + if (frameIndex == 0) { + return drmp3_seek_to_start_of_stream(pMP3); + } + + /* Use the seek table if we have one. */ + if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { + return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + } else { + return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + } +} + +DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) +{ + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalPCMFrameCount; + drmp3_uint64 totalMP3FrameCount; + + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + /* + The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based + on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function. + */ + + /* The stream must support seeking for this to work. */ + if (pMP3->onSeek == NULL) { + return DRMP3_FALSE; + } + + /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */ + currentPCMFrame = pMP3->currentPCMFrame; + + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + totalPCMFrameCount = 0; + totalMP3FrameCount = 0; + + for (;;) { + drmp3_uint32 pcmFramesInCurrentMP3Frame; + + pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + if (pcmFramesInCurrentMP3Frame == 0) { + break; + } + + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; + totalMP3FrameCount += 1; + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; + } + + if (pMP3FrameCount != NULL) { + *pMP3FrameCount = totalMP3FrameCount; + } + if (pPCMFrameCount != NULL) { + *pPCMFrameCount = totalPCMFrameCount; + } + + return DRMP3_TRUE; +} + +DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalPCMFrameCount; + + if (pMP3 == NULL) { + return 0; + } + + if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX) { + totalPCMFrameCount = pMP3->totalPCMFrameCount; + + if (totalPCMFrameCount >= pMP3->delayInPCMFrames) { + totalPCMFrameCount -= pMP3->delayInPCMFrames; + } else { + /* The delay is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */ + } + + if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) { + totalPCMFrameCount -= pMP3->paddingInPCMFrames; + } else { + /* The padding is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */ + } + + return totalPCMFrameCount; + } else { + /* Unknown frame count. Need to calculate it. */ + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + + return totalPCMFrameCount; + } +} + +DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +{ + drmp3_uint64 totalMP3FrameCount; + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + return 0; + } + + return totalMP3FrameCount; +} + +static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +{ + float srcRatio; + float pcmFrameCountOutF; + drmp3_uint32 pcmFrameCountOut; + + srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; + DRMP3_ASSERT(srcRatio > 0); + + pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); + pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; + *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; + *pRunningPCMFrameCount += pcmFrameCountOut; +} + +typedef struct +{ + drmp3_uint64 bytePos; + drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */ +} drmp3__seeking_mp3_frame_info; + +DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) +{ + drmp3_uint32 seekPointCount; + drmp3_uint64 currentPCMFrame; + drmp3_uint64 totalMP3FrameCount; + drmp3_uint64 totalPCMFrameCount; + + if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { + return DRMP3_FALSE; /* Invalid args. */ + } + + seekPointCount = *pSeekPointCount; + if (seekPointCount == 0) { + return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */ + } + + /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */ + currentPCMFrame = pMP3->currentPCMFrame; + + /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ + if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return DRMP3_FALSE; + } + + /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */ + if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { + seekPointCount = 1; + pSeekPoints[0].seekPosInBytes = 0; + pSeekPoints[0].pcmFrameIndex = 0; + pSeekPoints[0].mp3FramesToDiscard = 0; + pSeekPoints[0].pcmFramesToDiscard = 0; + } else { + drmp3_uint64 pcmFramesBetweenSeekPoints; + drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; + drmp3_uint64 runningPCMFrameCount = 0; + float runningPCMFrameCountFractionalPart = 0; + drmp3_uint64 nextTargetPCMFrame; + drmp3_uint32 iMP3Frame; + drmp3_uint32 iSeekPoint; + + if (seekPointCount > totalMP3FrameCount-1) { + seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; + } + + pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); + + /* + Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each + MP3 frame. + */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + + /* + We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this + array. The value in the first item in this array is the byte position that will be reported in the next seek point. + */ + + /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */ + for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */ + DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; + + /* We need to get information about this frame so we can know how many samples it contained. */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + return DRMP3_FALSE; /* This should never happen. */ + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + + /* + At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and + calculate them. + */ + nextTargetPCMFrame = 0; + for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { + nextTargetPCMFrame += pcmFramesBetweenSeekPoints; + + for (;;) { + if (nextTargetPCMFrame < runningPCMFrameCount) { + /* The next seek point is in the current MP3 frame. */ + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } else { + size_t i; + drmp3_uint32 pcmFramesInCurrentMP3FrameIn; + + /* + The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached + MP3 frame info. + */ + for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { + mp3FrameInfo[i] = mp3FrameInfo[i+1]; + } + + /* Cache previous MP3 frame info. */ + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + + /* + Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it + should only ever do it for the last seek point. + */ + pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } + + drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + } + } + + /* Finally, we need to seek back to where we were. */ + if (!drmp3_seek_to_start_of_stream(pMP3)) { + return DRMP3_FALSE; + } + if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return DRMP3_FALSE; + } + } + + *pSeekPointCount = seekPointCount; + return DRMP3_TRUE; +} + +DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) +{ + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + if (seekPointCount == 0 || pSeekPoints == NULL) { + /* Unbinding. */ + pMP3->seekPointCount = 0; + pMP3->pSeekPoints = NULL; + } else { + /* Binding. */ + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + } + + return DRMP3_TRUE; +} + +#ifndef DR_MP3_NO_FULL_READ +static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + float* pFrames = NULL; + float temp[1152*2]; /* MP3 frames have a maximum per-channel sample count of 1152. Times 2 to account for stereo. */ + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) { + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 newFramesCap; + float* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + break; + } + + pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ + if (framesJustRead != framesToReadRightNow) { + break; + } + } + + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} + +#ifndef DR_MP3_NO_S16 +static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + drmp3_int16* pFrames = NULL; + drmp3_int16 temp[1152*2]; /* MP3 frames have a maximum per-channel sample count of 1152. Times 2 to account for stereo. */ + + DRMP3_ASSERT(pMP3 != NULL); + + for (;;) { + drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + /* Reallocate the output buffer if there's not enough room. */ + if (framesCapacity < totalFramesRead + framesJustRead) { + drmp3_uint64 newFramesBufferSize; + drmp3_uint64 oldFramesBufferSize; + drmp3_uint64 newFramesCap; + drmp3_int16* pNewFrames; + + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); + if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + break; + } + + pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; + break; + } + + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + + DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); + totalFramesRead += framesJustRead; + + /* If the number of frames we asked for is less that what we actually read it means we've reached the end. */ + if (framesJustRead != framesToReadRightNow) { + break; + } + } + + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + + return pFrames; +} +#endif + +DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif + +DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif + +#ifndef DR_MP3_NO_STDIO +DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +#ifndef DR_MP3_NO_S16 +DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + + return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif +#endif +#endif + +DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); + } else { + return drmp3__malloc_default(sz, NULL); + } +} + +DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + drmp3__free_from_callbacks(p, pAllocationCallbacks); + } else { + drmp3__free_default(p, NULL); + } +} + +#endif /* dr_mp3_c */ +#endif /*DR_MP3_IMPLEMENTATION*/ + +/* +DIFFERENCES BETWEEN minimp3 AND dr_mp3 +====================================== +- First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the + code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes. +- dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data + to the decoder, the decoder _pulls_ data from your callbacks. +- In addition to callbacks, a decoder can be initialized from a block of memory and a file. +- The dr_mp3 pull API reads PCM frames rather than whole MP3 frames. +- dr_mp3 adds convenience APIs for opening and decoding entire files in one go. +- dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects + as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when + using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. +*/ + +/* +REVISION HISTORY +================ +v0.7.4 - TBD + - Fix an overflow error with "Xing" and "Info" tag parsing. + - Add some validation checks for "Xing" and "Info" tag parsing. + - Reduce size of some stack allocations. + - Improvements to SIMD detection. + +v0.7.3 - 2026-01-17 + - Fix an error in drmp3_open_and_read_pcm_frames_s16() and family when memory allocation fails. + - Fix some compilation warnings. + +v0.7.2 - 2025-12-02 + - Reduce stack space to improve robustness on embedded systems. + - Fix a compilation error with MSVC Clang toolset relating to cpuid. + - Fix an error with APE tag parsing. + +v0.7.1 - 2025-09-10 + - Silence a warning with GCC. + - Fix an error with the NXDK build. + - Fix a decoding inconsistency when seeking. Prior to this change, reading to the end of the stream immediately after initializing will result in a different number of samples read than if the stream is seeked to the start and read to the end. + +v0.7.0 - 2025-07-23 + - The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`. + - API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries. + - drmp3_seek_origin_start -> DRMP3_SEEK_SET + - drmp3_seek_origin_current -> DRMP3_SEEK_CUR + - DRMP3_SEEK_END (new) + - API CHANGE: Add DRMP3_SEEK_END as a seek origin for the seek callback. This is required for detection of ID3v1 and APE tags. + - API CHANGE: Add onTell callback to `drmp3_init()`. This is needed in order to track the location of ID3v1 and APE tags. + - API CHANGE: Add onMeta callback to `drmp3_init()`. This is used for reporting tag data back to the caller. Currently this only reports the raw tag data which means applications need to parse the data themselves. + - API CHANGE: Rename `drmp3dec_frame_info.hz` to `drmp3dec_frame_info.sample_rate`. + - Add detection of ID3v2, ID3v1, APE and Xing/VBRI tags. This should fix errors with some files where the decoder was reading tags as audio data. + - Delay and padding samples from LAME tags are now handled. + - Fix compilation for AIX OS. + +v0.6.40 - 2024-12-17 + - Improve detection of ARM64EC + +v0.6.39 - 2024-02-27 + - Fix a Wdouble-promotion warning. + +v0.6.38 - 2023-11-02 + - Fix build for ARMv6-M. + +v0.6.37 - 2023-07-07 + - Silence a static analysis warning. + +v0.6.36 - 2023-06-17 + - Fix an incorrect date in revision history. No functional change. + +v0.6.35 - 2023-05-22 + - Minor code restructure. No functional change. + +v0.6.34 - 2022-09-17 + - Fix compilation with DJGPP. + - Fix compilation when compiling with x86 with no SSE2. + - Remove an unnecessary variable from the drmp3 structure. + +v0.6.33 - 2022-04-10 + - Fix compilation error with the MSVC ARM64 build. + - Fix compilation error on older versions of GCC. + - Remove some unused functions. + +v0.6.32 - 2021-12-11 + - Fix a warning with Clang. + +v0.6.31 - 2021-08-22 + - Fix a bug when loading from memory. + +v0.6.30 - 2021-08-16 + - Silence some warnings. + - Replace memory operations with DRMP3_* macros. + +v0.6.29 - 2021-08-08 + - Bring up to date with minimp3. + +v0.6.28 - 2021-07-31 + - Fix platform detection for ARM64. + - Fix a compilation error with C89. + +v0.6.27 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + +v0.6.26 - 2021-01-31 + - Bring up to date with minimp3. + +v0.6.25 - 2020-12-26 + - Remove DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE which are leftovers from some removed APIs. + +v0.6.24 - 2020-12-07 + - Fix a typo in version date for 0.6.23. + +v0.6.23 - 2020-12-03 + - Fix an error where a file can be closed twice when initialization of the decoder fails. + +v0.6.22 - 2020-12-02 + - Fix an error where it's possible for a file handle to be left open when initialization of the decoder fails. + +v0.6.21 - 2020-11-28 + - Bring up to date with minimp3. + +v0.6.20 - 2020-11-21 + - Fix compilation with OpenWatcom. + +v0.6.19 - 2020-11-13 + - Minor code clean up. + +v0.6.18 - 2020-11-01 + - Improve compiler support for older versions of GCC. + +v0.6.17 - 2020-09-28 + - Bring up to date with minimp3. + +v0.6.16 - 2020-08-02 + - Simplify sized types. + +v0.6.15 - 2020-07-25 + - Fix a compilation warning. + +v0.6.14 - 2020-07-23 + - Fix undefined behaviour with memmove(). + +v0.6.13 - 2020-07-06 + - Fix a bug when converting from s16 to f32 in drmp3_read_pcm_frames_f32(). + +v0.6.12 - 2020-06-23 + - Add include guard for the implementation section. + +v0.6.11 - 2020-05-26 + - Fix use of uninitialized variable error. + +v0.6.10 - 2020-05-16 + - Add compile-time and run-time version querying. + - DRMP3_VERSION_MINOR + - DRMP3_VERSION_MAJOR + - DRMP3_VERSION_REVISION + - DRMP3_VERSION_STRING + - drmp3_version() + - drmp3_version_string() + +v0.6.9 - 2020-04-30 + - Change the `pcm` parameter of drmp3dec_decode_frame() to a `const drmp3_uint8*` for consistency with internal APIs. + +v0.6.8 - 2020-04-26 + - Optimizations to decoding when initializing from memory. + +v0.6.7 - 2020-04-25 + - Fix a compilation error with DR_MP3_NO_STDIO + - Optimization to decoding by reducing some data movement. + +v0.6.6 - 2020-04-23 + - Fix a minor bug with the running PCM frame counter. + +v0.6.5 - 2020-04-19 + - Fix compilation error on ARM builds. + +v0.6.4 - 2020-04-19 + - Bring up to date with changes to minimp3. + +v0.6.3 - 2020-04-13 + - Fix some pedantic warnings. + +v0.6.2 - 2020-04-10 + - Fix a crash in drmp3_open_*_and_read_pcm_frames_*() if the output config object is NULL. + +v0.6.1 - 2020-04-05 + - Fix warnings. + +v0.6.0 - 2020-04-04 + - API CHANGE: Remove the pConfig parameter from the following APIs: + - drmp3_init() + - drmp3_init_memory() + - drmp3_init_file() + - Add drmp3_init_file_w() for opening a file from a wchar_t encoded path. + +v0.5.6 - 2020-02-12 + - Bring up to date with minimp3. + +v0.5.5 - 2020-01-29 + - Fix a memory allocation bug in high level s16 decoding APIs. + +v0.5.4 - 2019-12-02 + - Fix a possible null pointer dereference when using custom memory allocators for realloc(). + +v0.5.3 - 2019-11-14 + - Fix typos in documentation. + +v0.5.2 - 2019-11-02 + - Bring up to date with minimp3. + +v0.5.1 - 2019-10-08 + - Fix a warning with GCC. + +v0.5.0 - 2019-10-07 + - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation + routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs: + - drmp3_init() + - drmp3_init_file() + - drmp3_init_memory() + - drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_pcm_frames_s16() + - API CHANGE: Renamed the following APIs: + - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32() + - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16() + - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32() + - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16() + - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32() + - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16() + +v0.4.7 - 2019-07-28 + - Fix a compiler error. + +v0.4.6 - 2019-06-14 + - Fix a compiler error. + +v0.4.5 - 2019-06-06 + - Bring up to date with minimp3. + +v0.4.4 - 2019-05-06 + - Fixes to the VC6 build. + +v0.4.3 - 2019-05-05 + - Use the channel count and/or sample rate of the first MP3 frame instead of DRMP3_DEFAULT_CHANNELS and + DRMP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to + DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE. + - Add s16 reading APIs + - drmp3_read_pcm_frames_s16 + - drmp3_open_memory_and_read_pcm_frames_s16 + - drmp3_open_and_read_pcm_frames_s16 + - drmp3_open_file_and_read_pcm_frames_s16 + - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section. + - Add support for C89. + - Change license to choice of public domain or MIT-0. + +v0.4.2 - 2019-02-21 + - Fix a warning. + +v0.4.1 - 2018-12-30 + - Fix a warning. + +v0.4.0 - 2018-12-16 + - API CHANGE: Rename some APIs: + - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32 + - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame + - drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32 + - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32 + - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32 + - Add drmp3_get_pcm_frame_count(). + - Add drmp3_get_mp3_frame_count(). + - Improve seeking performance. + +v0.3.2 - 2018-09-11 + - Fix a couple of memory leaks. + - Bring up to date with minimp3. + +v0.3.1 - 2018-08-25 + - Fix C++ build. + +v0.3.0 - 2018-08-25 + - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has + been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or + not the DR_MP3_FLOAT_OUTPUT option is set. + +v0.2.11 - 2018-08-08 + - Fix a bug where the last part of a file is not read. + +v0.2.10 - 2018-08-07 + - Improve 64-bit detection. + +v0.2.9 - 2018-08-05 + - Fix C++ build on older versions of GCC. + - Bring up to date with minimp3. + +v0.2.8 - 2018-08-02 + - Fix compilation errors with older versions of GCC. + +v0.2.7 - 2018-07-13 + - Bring up to date with minimp3. + +v0.2.6 - 2018-07-12 + - Bring up to date with minimp3. + +v0.2.5 - 2018-06-22 + - Bring up to date with minimp3. + +v0.2.4 - 2018-05-12 + - Bring up to date with minimp3. + +v0.2.3 - 2018-04-29 + - Fix TCC build. + +v0.2.2 - 2018-04-28 + - Fix bug when opening a decoder from memory. + +v0.2.1 - 2018-04-27 + - Efficiency improvements when the decoder reaches the end of the stream. + +v0.2 - 2018-04-21 + - Bring up to date with minimp3. + - Start using major.minor.revision versioning. + +v0.1d - 2018-03-30 + - Bring up to date with minimp3. + +v0.1c - 2018-03-11 + - Fix C++ build error. + +v0.1b - 2018-03-07 + - Bring up to date with minimp3. + +v0.1a - 2018-02-28 + - Fix compilation error on GCC/Clang. + - Fix some warnings. + +v0.1 - 2018-02-xx + - Initial versioned release. +*/ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +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 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. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +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. + +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. +*/ + +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ diff --git a/src/version.rc b/src/version.rc index 6292ef1fa..ec0c033a2 100644 --- a/src/version.rc +++ b/src/version.rc @@ -3,8 +3,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,50,0 - PRODUCTVERSION 1,2,50,0 + FILEVERSION 1,2,76,0 + PRODUCTVERSION 1,2,76,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L @@ -17,12 +17,12 @@ BEGIN BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL\0" - VALUE "FileVersion", "1, 2, 50, 0\0" + VALUE "FileVersion", "1, 2, 76, 0\0" VALUE "InternalName", "SDL\0" - VALUE "LegalCopyright", "Copyright © 2021 Sam Lantinga\0" + VALUE "LegalCopyright", "Copyright (C) 2026 Sam Lantinga\0" VALUE "OriginalFilename", "SDL.dll\0" VALUE "ProductName", "Simple DirectMedia Layer 1.2 wrapper\0" - VALUE "ProductVersion", "1, 2, 50, 0\0" + VALUE "ProductVersion", "1, 2, 76, 0\0" END END BLOCK "VarFileInfo" diff --git a/src/x86_msvc.h b/src/x86_msvc.h new file mode 100644 index 000000000..d241d0293 --- /dev/null +++ b/src/x86_msvc.h @@ -0,0 +1,656 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2026 Sam Lantinga + + 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. +*/ + +/* taken from SDL_stdlib.c of SDL2 */ + +/* *INDENT-OFF* */ + +/* Float to long */ +void +__declspec(naked) +_ftol() +{ + __asm { + push ebp + mov ebp,esp + sub esp,20h + and esp,0FFFFFFF0h + fld st(0) + fst dword ptr [esp+18h] + fistp qword ptr [esp+10h] + fild qword ptr [esp+10h] + mov edx,dword ptr [esp+18h] + mov eax,dword ptr [esp+10h] + test eax,eax + je integer_QnaN_or_zero +arg_is_not_integer_QnaN: + fsubp st(1),st + test edx,edx + jns positive + fstp dword ptr [esp] + mov ecx,dword ptr [esp] + xor ecx,80000000h + add ecx,7FFFFFFFh + adc eax,0 + mov edx,dword ptr [esp+14h] + adc edx,0 + jmp localexit +positive: + fstp dword ptr [esp] + mov ecx,dword ptr [esp] + add ecx,7FFFFFFFh + sbb eax,0 + mov edx,dword ptr [esp+14h] + sbb edx,0 + jmp localexit +integer_QnaN_or_zero: + mov edx,dword ptr [esp+14h] + test edx,7FFFFFFFh + jne arg_is_not_integer_QnaN + fstp dword ptr [esp+18h] + fstp dword ptr [esp+18h] +localexit: + leave + ret + } +} + +void +_ftol2() +{ + _ftol(); +} + +void +_ftol2_sse() +{ + _ftol(); +} + +/* 64-bit math operators for 32-bit systems */ +void +__declspec(naked) +_allmul() +{ + __asm { + mov eax, dword ptr[esp+8] + mov ecx, dword ptr[esp+10h] + or ecx, eax + mov ecx, dword ptr[esp+0Ch] + jne hard + mov eax, dword ptr[esp+4] + mul ecx + ret 10h +hard: + push ebx + mul ecx + mov ebx, eax + mov eax, dword ptr[esp+8] + mul dword ptr[esp+14h] + add ebx, eax + mov eax, dword ptr[esp+8] + mul ecx + add edx, ebx + pop ebx + ret 10h + } +} + +void +__declspec(naked) +_alldiv() +{ + __asm { + push edi + push esi + push ebx + xor edi,edi + mov eax,dword ptr [esp+14h] + or eax,eax + jge L1 + inc edi + mov edx,dword ptr [esp+10h] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+14h],eax + mov dword ptr [esp+10h],edx +L1: + mov eax,dword ptr [esp+1Ch] + or eax,eax + jge L2 + inc edi + mov edx,dword ptr [esp+18h] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+1Ch],eax + mov dword ptr [esp+18h],edx +L2: + or eax,eax + jne L3 + mov ecx,dword ptr [esp+18h] + mov eax,dword ptr [esp+14h] + xor edx,edx + div ecx + mov ebx,eax + mov eax,dword ptr [esp+10h] + div ecx + mov edx,ebx + jmp L4 +L3: + mov ebx,eax + mov ecx,dword ptr [esp+18h] + mov edx,dword ptr [esp+14h] + mov eax,dword ptr [esp+10h] +L5: + shr ebx,1 + rcr ecx,1 + shr edx,1 + rcr eax,1 + or ebx,ebx + jne L5 + div ecx + mov esi,eax + mul dword ptr [esp+1Ch] + mov ecx,eax + mov eax,dword ptr [esp+18h] + mul esi + add edx,ecx + jb L6 + cmp edx,dword ptr [esp+14h] + ja L6 + jb L7 + cmp eax,dword ptr [esp+10h] + jbe L7 +L6: + dec esi +L7: + xor edx,edx + mov eax,esi +L4: + dec edi + jne L8 + neg edx + neg eax + sbb edx,0 +L8: + pop ebx + pop esi + pop edi + ret 10h + } +} + +void +__declspec(naked) +_aulldiv() +{ + __asm { + push ebx + push esi + mov eax,dword ptr [esp+18h] + or eax,eax + jne L1 + mov ecx,dword ptr [esp+14h] + mov eax,dword ptr [esp+10h] + xor edx,edx + div ecx + mov ebx,eax + mov eax,dword ptr [esp+0Ch] + div ecx + mov edx,ebx + jmp L2 +L1: + mov ecx,eax + mov ebx,dword ptr [esp+14h] + mov edx,dword ptr [esp+10h] + mov eax,dword ptr [esp+0Ch] +L3: + shr ecx,1 + rcr ebx,1 + shr edx,1 + rcr eax,1 + or ecx,ecx + jne L3 + div ebx + mov esi,eax + mul dword ptr [esp+18h] + mov ecx,eax + mov eax,dword ptr [esp+14h] + mul esi + add edx,ecx + jb L4 + cmp edx,dword ptr [esp+10h] + ja L4 + jb L5 + cmp eax,dword ptr [esp+0Ch] + jbe L5 +L4: + dec esi +L5: + xor edx,edx + mov eax,esi +L2: + pop esi + pop ebx + ret 10h + } +} + +void +__declspec(naked) +_allrem() +{ + __asm { + push ebx + push edi + xor edi,edi + mov eax,dword ptr [esp+10h] + or eax,eax + jge L1 + inc edi + mov edx,dword ptr [esp+0Ch] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+10h],eax + mov dword ptr [esp+0Ch],edx +L1: + mov eax,dword ptr [esp+18h] + or eax,eax + jge L2 + mov edx,dword ptr [esp+14h] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+18h],eax + mov dword ptr [esp+14h],edx +L2: + or eax,eax + jne L3 + mov ecx,dword ptr [esp+14h] + mov eax,dword ptr [esp+10h] + xor edx,edx + div ecx + mov eax,dword ptr [esp+0Ch] + div ecx + mov eax,edx + xor edx,edx + dec edi + jns L4 + jmp L8 +L3: + mov ebx,eax + mov ecx,dword ptr [esp+14h] + mov edx,dword ptr [esp+10h] + mov eax,dword ptr [esp+0Ch] +L5: + shr ebx,1 + rcr ecx,1 + shr edx,1 + rcr eax,1 + or ebx,ebx + jne L5 + div ecx + mov ecx,eax + mul dword ptr [esp+18h] + xchg eax,ecx + mul dword ptr [esp+14h] + add edx,ecx + jb L6 + cmp edx,dword ptr [esp+10h] + ja L6 + jb L7 + cmp eax,dword ptr [esp+0Ch] + jbe L7 +L6: + sub eax,dword ptr [esp+14h] + sbb edx,dword ptr [esp+18h] +L7: + sub eax,dword ptr [esp+0Ch] + sbb edx,dword ptr [esp+10h] + dec edi + jns L8 +L4: + neg edx + neg eax + sbb edx,0 +L8: + pop edi + pop ebx + ret 10h + } +} + +void +__declspec(naked) +_aullrem() +{ + __asm { + push ebx + mov eax,dword ptr [esp+14h] + or eax,eax + jne L1 + mov ecx,dword ptr [esp+10h] + mov eax,dword ptr [esp+0Ch] + xor edx,edx + div ecx + mov eax,dword ptr [esp+8] + div ecx + mov eax,edx + xor edx,edx + jmp L2 +L1: + mov ecx,eax + mov ebx,dword ptr [esp+10h] + mov edx,dword ptr [esp+0Ch] + mov eax,dword ptr [esp+8] +L3: + shr ecx,1 + rcr ebx,1 + shr edx,1 + rcr eax,1 + or ecx,ecx + jne L3 + div ebx + mov ecx,eax + mul dword ptr [esp+14h] + xchg eax,ecx + mul dword ptr [esp+10h] + add edx,ecx + jb L4 + cmp edx,dword ptr [esp+0Ch] + ja L4 + jb L5 + cmp eax,dword ptr [esp+8] + jbe L5 +L4: + sub eax,dword ptr [esp+10h] + sbb edx,dword ptr [esp+14h] +L5: + sub eax,dword ptr [esp+8] + sbb edx,dword ptr [esp+0Ch] + neg edx + neg eax + sbb edx,0 +L2: + pop ebx + ret 10h + } +} + +void +__declspec(naked) +_alldvrm() +{ + __asm { + push edi + push esi + push ebp + xor edi,edi + xor ebp,ebp + mov eax,dword ptr [esp+14h] + or eax,eax + jge L1 + inc edi + inc ebp + mov edx,dword ptr [esp+10h] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+14h],eax + mov dword ptr [esp+10h],edx +L1: + mov eax,dword ptr [esp+1Ch] + or eax,eax + jge L2 + inc edi + mov edx,dword ptr [esp+18h] + neg eax + neg edx + sbb eax,0 + mov dword ptr [esp+1Ch],eax + mov dword ptr [esp+18h],edx +L2: + or eax,eax + jne L3 + mov ecx,dword ptr [esp+18h] + mov eax,dword ptr [esp+14h] + xor edx,edx + div ecx + mov ebx,eax + mov eax,dword ptr [esp+10h] + div ecx + mov esi,eax + mov eax,ebx + mul dword ptr [esp+18h] + mov ecx,eax + mov eax,esi + mul dword ptr [esp+18h] + add edx,ecx + jmp L4 +L3: + mov ebx,eax + mov ecx,dword ptr [esp+18h] + mov edx,dword ptr [esp+14h] + mov eax,dword ptr [esp+10h] +L5: + shr ebx,1 + rcr ecx,1 + shr edx,1 + rcr eax,1 + or ebx,ebx + jne L5 + div ecx + mov esi,eax + mul dword ptr [esp+1Ch] + mov ecx,eax + mov eax,dword ptr [esp+18h] + mul esi + add edx,ecx + jb L6 + cmp edx,dword ptr [esp+14h] + ja L6 + jb L7 + cmp eax,dword ptr [esp+10h] + jbe L7 +L6: + dec esi + sub eax,dword ptr [esp+18h] + sbb edx,dword ptr [esp+1Ch] +L7: + xor ebx,ebx +L4: + sub eax,dword ptr [esp+10h] + sbb edx,dword ptr [esp+14h] + dec ebp + jns L9 + neg edx + neg eax + sbb edx,0 +L9: + mov ecx,edx + mov edx,ebx + mov ebx,ecx + mov ecx,eax + mov eax,esi + dec edi + jne L8 + neg edx + neg eax + sbb edx,0 +L8: + pop ebp + pop esi + pop edi + ret 10h + } +} + +void +__declspec(naked) +_aulldvrm() +{ + __asm { + push esi + mov eax,dword ptr [esp+14h] + or eax,eax + jne L1 + mov ecx,dword ptr [esp+10h] + mov eax,dword ptr [esp+0Ch] + xor edx,edx + div ecx + mov ebx,eax + mov eax,dword ptr [esp+8] + div ecx + mov esi,eax + mov eax,ebx + mul dword ptr [esp+10h] + mov ecx,eax + mov eax,esi + mul dword ptr [esp+10h] + add edx,ecx + jmp L2 +L1: + mov ecx,eax + mov ebx,dword ptr [esp+10h] + mov edx,dword ptr [esp+0Ch] + mov eax,dword ptr [esp+8] +L3: + shr ecx,1 + rcr ebx,1 + shr edx,1 + rcr eax,1 + or ecx,ecx + jne L3 + div ebx + mov esi,eax + mul dword ptr [esp+14h] + mov ecx,eax + mov eax,dword ptr [esp+10h] + mul esi + add edx,ecx + jb L4 + cmp edx,dword ptr [esp+0Ch] + ja L4 + jb L5 + cmp eax,dword ptr [esp+8] + jbe L5 +L4: + dec esi + sub eax,dword ptr [esp+10h] + sbb edx,dword ptr [esp+14h] +L5: + xor ebx,ebx +L2: + sub eax,dword ptr [esp+8] + sbb edx,dword ptr [esp+0Ch] + neg edx + neg eax + sbb edx,0 + mov ecx,edx + mov edx,ebx + mov ebx,ecx + mov ecx,eax + mov eax,esi + pop esi + ret 10h + } +} + +void +__declspec(naked) +_allshl() +{ + __asm { + cmp cl,40h + jae RETZERO + cmp cl,20h + jae MORE32 + shld edx,eax,cl + shl eax,cl + ret +MORE32: + mov edx,eax + xor eax,eax + and cl,1Fh + shl edx,cl + ret +RETZERO: + xor eax,eax + xor edx,edx + ret + } +} + +void +__declspec(naked) +_allshr() +{ + __asm { + cmp cl,3Fh + jae RETSIGN + cmp cl,20h + jae MORE32 + shrd eax,edx,cl + sar edx,cl + ret +MORE32: + mov eax,edx + sar edx,1Fh + and cl,1Fh + sar eax,cl + ret +RETSIGN: + sar edx,1Fh + mov eax,edx + ret + } +} + +void +__declspec(naked) +_aullshr() +{ + __asm { + cmp cl,40h + jae RETZERO + cmp cl,20h + jae MORE32 + shrd eax,edx,cl + shr edx,cl + ret +MORE32: + mov eax,edx + xor edx,edx + and cl,1Fh + shr eax,cl + ret +RETZERO: + xor eax,eax + xor edx,edx + ret + } +} +/* *INDENT-ON* */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000..b23b88ef0 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,116 @@ +cmake_minimum_required(VERSION 3.0.0...4.0) +project(sdl12_compat_tests C) + +if(NOT TARGET SDL::SDL) + find_package(SDL REQUIRED) +endif() +if(NOT TARGET SDL::SDLmain) + add_library(SDL::SDLmain STATIC IMPORTED) + if(SDLMAIN_LIBRARY) + message("SDLMAIN_LIBRARY=${SDLMAIN_LIBRARY}") + set_property(TARGET SDL::SDLmain PROPERTY IMPORTED_LOCATION "${SDLMAIN_LIBRARY}") + endif() + if(MINGW OR CYGWIN) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set_property(TARGET SDL::SDLmain APPEND INTERFACE_LINK_LIBRARIES "$<$,EXECUTABLE>:-Wl,--undefined=_WinMain@16>") + else() + set_property(TARGET SDL::SDLmain APPEND INTERFACE_LINK_LIBRARIES "$<$,EXECUTABLE>:-Wl,--undefined=WinMain>") + endif() + endif() +endif() + +option(SDL12COMPAT_INSTALL_TESTS "Install manual tests into libexecdir" OFF) + +set(SDL12COMPAT_TEST_EXECUTABLES) +set(SDL12COMPAT_TEST_RESOURCE_FILES) + +if(NOT (WIN32 OR APPLE OR CYGWIN OR HAIKU OR BEOS)) + find_library(MATH_LIBRARY m) +endif() + +find_package(OpenGL COMPONENTS OpenGL) +if(OPENGL_FOUND) + set(HAVE_OPENGL_DEFINE "HAVE_OPENGL") + if(WIN32) + set(OPENGL_gl_LIBRARY "opengl32") + set(OPENGL_opengl_LIBRARY "opengl32") + elseif(APPLE) + set(OPENGL_gl_LIBRARY "-Wl,-framework,OpenGL") + set(OPENGL_opengl_LIBRARY "-Wl,-framework,OpenGL") + endif() +endif() + +macro(test_program _NAME _SRCS) + add_executable(${_NAME} ${_SRCS}) + list(APPEND SDL12COMPAT_TEST_EXECUTABLES ${_NAME}) + target_include_directories(${_NAME} PRIVATE "include/SDL") + target_link_libraries(${_NAME} PRIVATE SDL::SDLmain SDL::SDL) + # Turn off MSVC's aggressive C runtime warnings for the old test programs. + if(MSVC) + set_target_properties(${_NAME} PROPERTIES COMPILE_DEFINITIONS "${HAVE_OPENGL_DEFINE};_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE") + elseif(APPLE) + set_target_properties(${_NAME} PROPERTIES COMPILE_DEFINITIONS "${HAVE_OPENGL_DEFINE};GL_SILENCE_DEPRECATION=1") + target_link_libraries(${_NAME} PRIVATE "-Wl,-framework,Cocoa") + else() + set_target_properties(${_NAME} PROPERTIES COMPILE_DEFINITIONS "${HAVE_OPENGL_DEFINE}") + endif() + if(MATH_LIBRARY) + target_link_libraries(${_NAME} PRIVATE ${MATH_LIBRARY}) + endif() +endmacro() + +test_program(checkkeys "checkkeys.c") +test_program(graywin "graywin.c") +test_program(loopwave "loopwave.c") +test_program(testalpha "testalpha.c") +test_program(testbitmap "testbitmap.c") +test_program(testblitspeed "testblitspeed.c") +test_program(testcdrom "testcdrom.c") +test_program(testcursor "testcursor.c") +test_program(testerror "testerror.c") +test_program(testfile "testfile.c") +test_program(testgamma "testgamma.c") +test_program(testthread "testthread.c") +test_program(testiconv "testiconv.c") +test_program(testjoystick "testjoystick.c") +test_program(testkeys "testkeys.c") +test_program(testloadso "testloadso.c") +test_program(testlock "testlock.c") +test_program(testoverlay "testoverlay.c") +test_program(testoverlay2 "testoverlay2.c") +test_program(testpalette "testpalette.c") +test_program(testplatform "testplatform.c") +test_program(testsem "testsem.c") +test_program(testsprite "testsprite.c") +test_program(testtimer "testtimer.c") +test_program(testver "testver.c") +test_program(testvidinfo "testvidinfo.c") +test_program(testwin "testwin.c") +test_program(testwm "testwm.c") +test_program(threadwin "threadwin.c") +test_program(torturethread "torturethread.c") +test_program(testdyngl "testdyngl.c") +test_program(testgl "testgl.c") +if(OPENGL_FOUND) + if(CMAKE_VERSION VERSION_LESS 3.10 OR NOT OPENGL_opengl_LIBRARY) + target_link_libraries(testgl PRIVATE ${OPENGL_gl_LIBRARY}) + else() + target_link_libraries(testgl PRIVATE ${OPENGL_opengl_LIBRARY}) + endif() +endif() + +foreach(fname "icon.bmp" "moose.dat" "picture.xbm" "sail.bmp" "sample.bmp" "sample.wav" "utf8.txt") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/${fname}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + list(APPEND SDL12COMPAT_TEST_RESOURCE_FILES "${fname}") +endforeach(fname) + +if(SDL12COMPAT_INSTALL_TESTS) + install( + TARGETS ${SDL12COMPAT_TEST_EXECUTABLES} + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL12_compat + ) + install( + FILES ${SDL12COMPAT_TEST_RESOURCE_FILES} + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/SDL12_compat + ) +endif() diff --git a/test/Makefile.os2 b/test/Makefile.os2 new file mode 100644 index 000000000..5f45c2580 --- /dev/null +++ b/test/Makefile.os2 @@ -0,0 +1,34 @@ +TARGETS = checkkeys.exe graywin.exe loopwave.exe testalpha.exe testbitmap.exe & + testblitspeed.exe testcdrom.exe testcursor.exe testdyngl.exe & + testerror.exe testfile.exe testgamma.exe testgl.exe testthread.exe & + testiconv.exe testjoystick.exe testkeys.exe testlock.exe & + testoverlay2.exe testoverlay.exe testpalette.exe testplatform.exe & + testsem.exe testsprite.exe testtimer.exe testver.exe testvidinfo.exe & + testwin.exe testwm.exe threadwin.exe torturethread.exe testloadso.exe + +OBJS = $(TARGETS:.exe=.obj) + +all: $(TARGETS) + +BINPATH = . +INCPATH = -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h" -I"../include/SDL" +CFLAGS_DEF = $(INCPATH) -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oteanbmier +CFLAGS_EXE = $(CFLAGS_DEF) +CFLAGS = $(CFLAGS_EXE) -ei -5s + +LIBPATH = ../src +LIBS = SDL12.lib + +.obj.exe: + wlink SYS os2v2 libpath $(LIBPATH) lib {$(LIBS)} op q file {$<} name $* + +.c.obj: + wcc386 $(CFLAGS) -fo=$^@ $< + +clean: .SYMBOLIC + @echo * Clean tests in $(BINPATH) + @if exist *.obj rm *.obj + @if exist *.err rm *.err + +distclean: clean .SYMBOLIC + @if exist *.exe rm *.exe diff --git a/test/Makefile.w32 b/test/Makefile.w32 new file mode 100644 index 000000000..793031c27 --- /dev/null +++ b/test/Makefile.w32 @@ -0,0 +1,35 @@ +TARGETS = checkkeys.exe graywin.exe loopwave.exe testalpha.exe testbitmap.exe & + testblitspeed.exe testcdrom.exe testcursor.exe testdyngl.exe & + testerror.exe testfile.exe testgamma.exe testgl.exe testthread.exe & + testiconv.exe testjoystick.exe testkeys.exe testlock.exe & + testoverlay2.exe testoverlay.exe testpalette.exe testplatform.exe & + testsem.exe testsprite.exe testtimer.exe testver.exe testvidinfo.exe & + testwin.exe testwm.exe threadwin.exe torturethread.exe testloadso.exe + +OBJS = $(TARGETS:.exe=.obj) + +all: $(TARGETS) + +BINPATH = . +INCPATH = -I"$(%WATCOM)/h/nt" -I"$(%WATCOM)/h" -I"../include/SDL" +CFLAGS_DEF = $(INCPATH) -bt=nt -d0 -q -bm -5s -fp5 -fpi87 -sg -oteanbmier +CFLAGS_EXE = $(CFLAGS_DEF) +CFLAGS = $(CFLAGS_EXE) -ei -5s + +LIBPATH = ../src +LIBS = SDL.lib +LIBF = SDLmain.lib + +.obj.exe: + wlink SYS nt libpath $(LIBPATH) libf {$(LIBF)} lib {$(LIBS)} op q file {$<} name $* + +.c.obj: + wcc386 $(CFLAGS) -fo=$^@ $< + +clean: .SYMBOLIC + @echo * Clean tests in $(BINPATH) + @if exist *.obj rm *.obj + @if exist *.err rm *.err + +distclean: clean .SYMBOLIC + @if exist *.exe rm *.exe diff --git a/test/README b/test/README index 9cf5659c7..e158b4e42 100644 --- a/test/README +++ b/test/README @@ -14,7 +14,6 @@ These are test programs for the SDL library: testfile Tests RWops layer testgamma Tests video device gamma ramp testgl A very simple example of using OpenGL with SDL - testhread Hacked up test of multi-threading testiconv Tests international string conversion testjoystick List joysticks and watch joystick events testkeys List the available keyboard keys @@ -26,6 +25,7 @@ These are test programs for the SDL library: testplatform Tests types, endianness and cpu capabilities testsem Tests SDL's semaphore implementation testsprite Example of fast sprite movement on the screen + testthread Hacked up test of multi-threading testtimer Test the timer facilities testver Check the version and dynamic loading and endianness testvidinfo Show the pixel format of the display and perfom the benchmark diff --git a/test/checkkeys.c b/test/checkkeys.c index 8dbb24f94..38ab43cdb 100644 --- a/test/checkkeys.c +++ b/test/checkkeys.c @@ -54,10 +54,10 @@ static void PrintKey(SDL_keysym *sym, int pressed) { /* Print the keycode, name and state */ if ( sym->sym ) { - printf("Key %s: %d-%s ", pressed ? "pressed" : "released", - sym->sym, SDL_GetKeyName(sym->sym)); + printf("Key %s: %d-%s (scancode = %d [0x%x])", pressed ? "pressed" : "released", + sym->sym, SDL_GetKeyName(sym->sym), sym->scancode, sym->scancode); } else { - printf("Unknown Key (scancode = %d) %s ", sym->scancode, + printf("Unknown Key (scancode = %d [0x%x]) %s ", sym->scancode, sym->scancode, pressed ? "pressed" : "released"); } @@ -68,13 +68,22 @@ static void PrintKey(SDL_keysym *sym, int pressed) printf(" (^%c)", sym->unicode+'@'); } else { #ifdef UNICODE - printf(" (%c)", sym->unicode); -#else + printf(" '%c' (0x%.4X)", sym->unicode, (int)sym->unicode); +#elif defined(_WIN32) || defined(__OS2__) /* This is a Latin-1 program, so only show 8-bits */ if ( !(sym->unicode & 0xFF00) ) - printf(" (%c)", sym->unicode); + printf(" '%c' (0x%.4X)", sym->unicode, (int)sym->unicode); else - printf(" (0x%X)", sym->unicode); + printf(" (0x%.4X)", (int)sym->unicode); +#else /* other platforms than Windows hopefully use UTF-8 for 8bit chars */ + const char* utf32type = (SDL_BYTEORDER == SDL_LIL_ENDIAN) ? "UTF-32LE" : "UTF-32BE"; + char* utf8str; + Uint32 utf32str[2]; + utf32str[0] = sym->unicode; + utf32str[1] = 0; + utf8str = SDL_iconv_string("UTF-8", utf32type, (const char*)utf32str, 2*4); + printf(" '%s' (0x%.4X)", utf8str, (int)sym->unicode); + SDL_free(utf8str); #endif } } diff --git a/test/graywin.c b/test/graywin.c index 456311496..c1f8a5dd3 100644 --- a/test/graywin.c +++ b/test/graywin.c @@ -24,7 +24,7 @@ void DrawBox(SDL_Surface *screen, int X, int Y, int width, int height) /* Seed the random number generator */ if ( seeded == 0 ) { - srand(time(NULL)); + srand((unsigned int) time(NULL)); seeded = 1; } diff --git a/test/loopwave.c b/test/loopwave.c index e1df747a7..414b9eda0 100644 --- a/test/loopwave.c +++ b/test/loopwave.c @@ -63,20 +63,17 @@ void poked(int sig) int main(int argc, char *argv[]) { char name[32]; + const char *file; /* Load the SDL library */ if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) { fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); return(1); } - if ( argv[1] == NULL ) { - argv[1] = "sample.wav"; - } + file = (argc < 2) ? "sample.wav" : argv[1]; /* Load the wave file into memory */ - if ( SDL_LoadWAV(argv[1], - &wave.spec, &wave.sound, &wave.soundlen) == NULL ) { - fprintf(stderr, "Couldn't load %s: %s\n", - argv[1], SDL_GetError()); + if ( SDL_LoadWAV(file, &wave.spec, &wave.sound, &wave.soundlen) == NULL ) { + fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError()); quit(1); } diff --git a/test/testalpha.c b/test/testalpha.c index 7e04fbe08..2ba899d03 100644 --- a/test/testalpha.c +++ b/test/testalpha.c @@ -178,7 +178,7 @@ static SDL_Rect position; static int x_vel, y_vel; static int alpha_vel; -int LoadSprite(SDL_Surface *screen, char *file) +int LoadSprite(SDL_Surface *screen, const char *file) { SDL_Surface *converted; diff --git a/test/testcdrom.c b/test/testcdrom.c index 782a66cc3..f1c4b2a34 100644 --- a/test/testcdrom.c +++ b/test/testcdrom.c @@ -17,8 +17,8 @@ static void quit(int rc) static void PrintStatus(int driveindex, SDL_CD *cdrom) { + const char *status_str = "unknown"; CDstatus status; - char *status_str; status = SDL_CDStatus(cdrom); switch (status) { @@ -51,7 +51,7 @@ static void ListTracks(SDL_CD *cdrom) { int i; int m, s, f; - char* trtype; + const char *trtype; SDL_CDStatus(cdrom); printf("Drive tracks: %d\n", cdrom->numtracks); diff --git a/test/testdyngl.c b/test/testdyngl.c index a20bd1a48..538b47fcb 100644 --- a/test/testdyngl.c +++ b/test/testdyngl.c @@ -138,9 +138,9 @@ int main(int argc,char *argv[]) for(i=0;iflags&SDL_HWSURFACE) ? "video" : "system"); if ( screen->flags & SDL_DOUBLEBUF ) { printf("Double-buffering enabled\n"); - flip = 1; } /* Set the window manager title bar */ SDL_WM_SetCaption("SDL test overlay", "testoverlay"); /* Load picture */ - bmpfile=(argv[1]?argv[1]:"sample.bmp"); + bmpfile = argv[1] ? argv[1]:"sample.bmp"; pic = SDL_LoadBMP(bmpfile); if ( pic == NULL ) { fprintf(stderr, "Couldn't load %s: %s\n", bmpfile, diff --git a/test/testoverlay2.c b/test/testoverlay2.c index 5f491f22d..b93989a11 100644 --- a/test/testoverlay2.c +++ b/test/testoverlay2.c @@ -60,11 +60,11 @@ void RGBtoYUV(Uint8 *rgb, int *yuv, int monochrome, int luminance) if (monochrome) { #if 1 /* these are the two formulas that I found on the FourCC site... */ - yuv[0] = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]; + yuv[0] = (int) (0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]); yuv[1] = 128; yuv[2] = 128; #else - yuv[0] = (0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16; + yuv[0] = (int) ((0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16); yuv[1] = 128; yuv[2] = 128; #endif @@ -72,13 +72,13 @@ void RGBtoYUV(Uint8 *rgb, int *yuv, int monochrome, int luminance) else { #if 1 /* these are the two formulas that I found on the FourCC site... */ - yuv[0] = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]; - yuv[1] = (rgb[2]-yuv[0])*0.565 + 128; - yuv[2] = (rgb[0]-yuv[0])*0.713 + 128; + yuv[0] = (int) (0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]); + yuv[1] = (int) ((rgb[2]-yuv[0])*0.565 + 128); + yuv[2] = (int) ((rgb[0]-yuv[0])*0.713 + 128); #else - yuv[0] = (0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16; - yuv[1] = 128 - (0.148 * rgb[0]) - (0.291 * rgb[1]) + (0.439 * rgb[2]); - yuv[2] = 128 + (0.439 * rgb[0]) - (0.368 * rgb[1]) - (0.071 * rgb[2]); + yuv[0] = (int) ((0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16); + yuv[1] = (int) (128 - (0.148 * rgb[0]) - (0.291 * rgb[1]) + (0.439 * rgb[2])); + yuv[2] = (int) (128 + (0.439 * rgb[0]) - (0.368 * rgb[1]) - (0.071 * rgb[2])); #endif } @@ -290,7 +290,7 @@ int main(int argc, char **argv) int resized=0; int i; int fps=12; - int fpsdelay; + Uint32 fpsdelay; int overlay_format=SDL_YUY2_OVERLAY; int scale=5; @@ -503,7 +503,7 @@ int main(int argc, char **argv) /* set the start frame */ i=0; - fpsdelay=1000/fps; + fpsdelay=(Uint32)(1000/fps); /* Ignore key up events, they don't even get filtered */ SDL_EventState(SDL_KEYUP, SDL_IGNORE); diff --git a/test/testpalette.c b/test/testpalette.c index 2ad49164a..efa07327c 100644 --- a/test/testpalette.c +++ b/test/testpalette.c @@ -61,7 +61,7 @@ static void quit(int rc) exit(rc); } -static void sdlerr(char *when) +static void sdlerr(const char *when) { fprintf(stderr, "SDL error: %s: %s\n", when, SDL_GetError()); quit(1); diff --git a/test/testplatform.c b/test/testplatform.c index a7b0916a4..aed0f5fd0 100644 --- a/test/testplatform.c +++ b/test/testplatform.c @@ -109,7 +109,7 @@ int TestEndian(SDL_bool verbose) } #ifdef SDL_HAS_64BIT_TYPE if ( verbose ) { -#ifdef _MSC_VER +#ifdef _WIN32 printf("Value 64 = 0x%I64X, swapped = 0x%I64X\n", value64, SDL_Swap64(value64)); #else printf("Value 64 = 0x%llX, swapped = 0x%llX\n", (unsigned long long) value64, (unsigned long long) SDL_Swap64(value64)); diff --git a/test/testsprite.c b/test/testsprite.c index 6d9e0a859..f3705e256 100644 --- a/test/testsprite.c +++ b/test/testsprite.c @@ -20,6 +20,7 @@ SDL_Rect *velocities; int sprites_visible; int debug_flip; Uint16 sprite_w, sprite_h; +int refresh_rate = SDL_REFRESH_DEFAULT; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) @@ -28,7 +29,7 @@ static void quit(int rc) exit(rc); } -int LoadSprite(char *file) +int LoadSprite(const char *file) { SDL_Surface *temp; @@ -97,7 +98,7 @@ void MoveSprites(SDL_Surface *screen, Uint32 background) Uint32 color = SDL_MapRGB (screen->format, 255, 0, 0); SDL_Rect r; - r.x = (sin((float)t * 2 * 3.1459) + 1.0) / 2.0 * (screen->w-20); + r.x = (Sint16) ((sin((float)t * 2 * 3.1459) + 1.0) / 2.0 * (screen->w-20)); r.y = 0; r.w = 20; r.h = screen->h; @@ -137,7 +138,7 @@ Uint32 FastestFlags(Uint32 flags, int width, int height, int bpp) /* Direct hardware blitting without double-buffering causes really bad flickering. */ - if ( info->video_mem*1024 > (height*width*bpp/8) ) { + if ( info->video_mem*1024 > ((Uint32)(height*width*bpp/8)) ) { flags |= SDL_DOUBLEBUF; } else { flags &= ~SDL_HWSURFACE; @@ -182,6 +183,10 @@ int main(int argc, char *argv[]) height = atoi(argv[argc]); --argc; } else + if ( strcmp(argv[argc-1], "-refresh") == 0 ) { + refresh_rate = atoi(argv[argc]); + --argc; + } else if ( strcmp(argv[argc-1], "-bpp") == 0 ) { video_bpp = atoi(argv[argc]); videoflags &= ~SDL_ANYFORMAT; @@ -202,17 +207,21 @@ int main(int argc, char *argv[]) if ( strcmp(argv[argc], "-fullscreen") == 0 ) { videoflags ^= SDL_FULLSCREEN; } else + if ( strcmp(argv[argc], "-noframe") == 0 ) { + videoflags ^= SDL_NOFRAME; + } else if ( isdigit(argv[argc][0]) ) { numsprites = atoi(argv[argc]); } else { fprintf(stderr, - "Usage: %s [-bpp N] [-hw] [-flip] [-fast] [-fullscreen] [numsprites]\n", + "Usage: %s [-bpp N] [-refresh N] [-hw] [-flip] [-fast] [-fullscreen] [numsprites]\n", argv[0]); quit(1); } } /* Set video mode */ + SDL_SetRefreshRate(refresh_rate); screen = SDL_SetVideoMode(width, height, video_bpp, videoflags); if ( ! screen ) { fprintf(stderr, "Couldn't set %dx%d video mode: %s\n", @@ -239,7 +248,7 @@ int main(int argc, char *argv[]) sprite_rects += numsprites; sprite_w = sprite->w; sprite_h = sprite->h; - srand(time(NULL)); + srand((unsigned int) time(NULL)); for ( i=0; iw - sprite_w); positions[i].y = rand()%(screen->h - sprite_h); diff --git a/test/testhread.c b/test/testthread.c similarity index 100% rename from test/testhread.c rename to test/testthread.c diff --git a/test/testtimer.c b/test/testtimer.c index 95608c120..591133995 100644 --- a/test/testtimer.c +++ b/test/testtimer.c @@ -82,6 +82,13 @@ int main(int argc, char *argv[]) SDL_RemoveTimer(t2); SDL_RemoveTimer(t3); + printf("Removing bogus timer..."); + if (SDL_RemoveTimer(t1)) { + printf("UHOH, SHOULD HAVE FAILED\n"); + } else { + printf("OK!\n"); + } + SDL_Quit(); return(0); } diff --git a/test/testwin.c b/test/testwin.c index 261ea9942..2fcbd1084 100644 --- a/test/testwin.c +++ b/test/testwin.c @@ -18,7 +18,7 @@ static void quit(int rc) exit(rc); } -void DrawPict(SDL_Surface *screen, char *bmpfile, +void DrawPict(SDL_Surface *screen, const char *bmpfile, int speedy, int flip, int nofade) { SDL_Surface *picture; diff --git a/test/testwm.c b/test/testwm.c index 9c3c0506b..233ed51fe 100644 --- a/test/testwm.c +++ b/test/testwm.c @@ -5,8 +5,14 @@ #include #include +#define TEST_SYSWM 0 + #include "SDL.h" +#ifdef TEST_SYSWM +#include "SDL_syswm.h" +#endif + /* Is the cursor visible? */ static int visible = 1; @@ -60,7 +66,7 @@ int SetVideoMode(int w, int h) return(0); } -SDL_Surface *LoadIconSurface(char *file, Uint8 **maskp) +SDL_Surface *LoadIconSurface(const char *file, Uint8 **maskp) { SDL_Surface *icon; Uint8 *pixels; @@ -318,6 +324,18 @@ int SDLCALL FilterEvents(const SDL_Event *event) printf("Quit demanded\n"); return(1); + case SDL_SYSWMEVENT: + #ifdef TEST_SYSWM + #ifdef _WIN32 + printf("Windows syswm event: hwnd=%X msg=%X wparam=%X lparam=%X\n", (unsigned int) (size_t) event->syswm.msg->hwnd, (unsigned int) (size_t) event->syswm.msg->msg, (unsigned int) (size_t) event->syswm.msg->wParam, (unsigned int) (size_t) event->syswm.msg->lParam); + #elif defined(SDL_VIDEO_DRIVER_X11) + printf("X11 syswm event: %d\n", event->syswm.msg->event.xevent.type); + #else + printf("Generic syswm event: data=%d\n", event->syswm.msg->data); + #endif + #endif + return(1); + /* This will never happen because events queued directly to the event queue are not filtered. */ @@ -330,6 +348,8 @@ int SDLCALL FilterEvents(const SDL_Event *event) } } +static char testtitle[] = "Testing 1.. 2.. 3..."; + int main(int argc, char *argv[]) { SDL_Event event; @@ -396,7 +416,7 @@ int main(int argc, char *argv[]) /* Set the title bar */ if ( argv[1] == NULL ) - title = "Testing 1.. 2.. 3..."; + title = testtitle; else title = argv[1]; SDL_WM_SetCaption(title, "testwm"); @@ -413,6 +433,44 @@ int main(int argc, char *argv[]) quit(1); } +#ifdef TEST_SYSWM + { + SDL_SysWMinfo syswm_info; + SDL_VERSION(&syswm_info.version); + if (SDL_GetWMInfo(&syswm_info) != 1) { + printf("Failed to get syswm info: %s\n", SDL_GetError()); + } else { + #ifdef _WIN32 + printf("Windows syswm info: hwnd=%X hglrc=%X\n", + (unsigned int) (size_t) syswm_info.window, + (unsigned int) (size_t) syswm_info.hglrc); + #elif defined(SDL_VIDEO_DRIVER_X11) + printf("X11 syswm info: display=%p window=%X lock_func=%p unlock_func=%p fswindow=%X wmwindow=%X gfxdisplay=%p\n", + syswm_info.info.x11.display, + (unsigned int) (size_t) syswm_info.info.x11.window, + syswm_info.info.x11.lock_func, + syswm_info.info.x11.unlock_func, + (unsigned int) (size_t) syswm_info.info.x11.fswindow, + (unsigned int) (size_t) syswm_info.info.x11.wmwindow, + syswm_info.info.x11.gfxdisplay); + #else + printf("Generic syswm info: data=%X\n", syswm_info.data); + #endif + } + } + + { + typedef SDL_Window* (SDLCALL *fnSDL12COMPAT_GetWindow)(void); + fnSDL12COMPAT_GetWindow pfnSDL12COMPAT_GetWindow = (fnSDL12COMPAT_GetWindow) SDL_GL_GetProcAddress("SDL12COMPAT_GetWindow"); + printf("SDL12COMPAT_GetWindow address is %p%s\n", pfnSDL12COMPAT_GetWindow, pfnSDL12COMPAT_GetWindow ? "" : " (probably using classic SDL 1.2)"); + if (pfnSDL12COMPAT_GetWindow != NULL) { + printf("SDL 2.0 window: %p\n", pfnSDL12COMPAT_GetWindow()); + } + } + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); +#endif + /* Set an event filter that discards everything but QUIT */ SDL_SetEventFilter(FilterEvents); diff --git a/test/threadwin.c b/test/threadwin.c index c704b30d1..761fdbc8f 100644 --- a/test/threadwin.c +++ b/test/threadwin.c @@ -21,7 +21,7 @@ static void quit(int rc) exit(rc); } -SDL_Surface *LoadIconSurface(char *file, Uint8 **maskp) +SDL_Surface *LoadIconSurface(const char *file, Uint8 **maskp) { SDL_Surface *icon; Uint8 *pixels;