Merge remote-tracking branch 'yquake2/master'

This commit is contained in:
Denis Pauk 2025-02-10 00:34:31 +02:00
commit 3f74954db6
16 changed files with 1060 additions and 379 deletions

View file

@ -1,63 +0,0 @@
name: Testbuild for Linux
run-name: testbuild_linux
on:
push:
branches:
- 'master'
pull_request:
types:
- edited
- opened
- synchronize
concurrency:
# Cancel concurrent workflows for the same PR or commit hash.
group: ${{github.workflow}}-${{github.event_name == 'pull_request' && github.head_ref || github.sha}}
cancel-in-progress: true
jobs:
build_ubuntu_x86_64:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- env: ubuntu
steps:
- name: Install build dependencies
run: |
sudo apt update
sudo apt install libgl1-mesa-dev libsdl2-dev libopenal-dev libcurl4-openssl-dev \
libavformat-dev libswscale-dev libvulkan-dev build-essential
- name: Check out repository code
uses: actions/checkout@v4
- name: Build
run: |
sed -i 's|WITH_AVCODEC:=yes|WITH_AVCODEC:=no|g' Makefile
# Public runners come with 2 CPUs.
make -j2
make -j2 ref_gles1
- name: Create testbuild package
run: |
# Create release directory tree
mkdir -p publish/quake2-linux-${{github.sha}}/misc/docs
# Copy release assets
cp -r release/* publish/quake2-linux-${{github.sha}}/
# Copy misc assets
cp -r stuff/yq2.cfg publish/quake2-linux-${{github.sha}}/misc/yq2.cfg
cp -r stuff/mapfixes publish/quake2-linux-${{github.sha}}/misc
cp LICENSE publish/quake2-linux-${{github.sha}}/misc/docs/LICENSE.txt
cp README.md publish/quake2-linux-${{github.sha}}/misc/docs/README.txt
cp doc/010_index.md publish/quake2-linux-${{github.sha}}/misc/docs/010_index.txt
cp doc/020_installation.md publish/quake2-linux-${{github.sha}}/misc/docs/020_installation.txt
cp doc/030_configuration.md publish/quake2-linux-${{github.sha}}/misc/docs/030_configuration.txt
cp doc/040_cvarlist.md publish/quake2-linux-${{github.sha}}/misc/docs/040_cvarlist.txt
cp doc/050_commands.md publish/quake2-linux-${{github.sha}}/misc/docs/050_commands.txt
cp doc/060_multiplayer.md publish/quake2-linux-${{github.sha}}/misc/docs/060_multiplayer.txt
cp doc/070_packaging.md publish/quake2-linux-${{github.sha}}/misc/docs/070_packaging.txt
cp doc/080_contributing.md publish/quake2-linux-${{github.sha}}/misc/docs/080_contributing.txt
cp doc/090_filelists.md publish/quake2-linux-${{github.sha}}/misc/docs/090_filelists.md
- name: Upload testbuild package
uses: actions/upload-artifact@v4
with:
name: quake2-linux-${{github.sha}}
path: publish/
if-no-files-found: error

63
.github/workflows/linux_aarch64.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Testbuild for Linux (aarch64)
run-name: testbuild_linux_aarch64
on:
push:
branches:
- 'master'
pull_request:
types:
- edited
- opened
- synchronize
concurrency:
# Cancel concurrent workflows for the same PR or commit hash.
group: ${{github.workflow}}-${{github.event_name == 'pull_request' && github.head_ref || github.sha}}
cancel-in-progress: true
jobs:
build_ubuntu_aarch64:
runs-on: ubuntu-22.04-arm
strategy:
fail-fast: false
matrix:
include:
- env: ubuntu
steps:
- name: Install build dependencies
run: |
sudo apt update
sudo apt install libgl1-mesa-dev libsdl2-dev libopenal-dev libcurl4-openssl-dev \
libavformat-dev libswscale-dev libvulkan-dev build-essential
- name: Check out repository code
uses: actions/checkout@v4
- name: Build
run: |
sed -i 's|WITH_AVCODEC:=yes|WITH_AVCODEC:=no|g' Makefile
# Public runners come with 4 CPUs.
make -j4
make -j4 ref_gles1
- name: Create testbuild package
run: |
# Create release directory tree
mkdir -p publish/quake2-linux_aarch64-${{github.sha}}/misc/docs
# Copy release assets
cp -r release/* publish/quake2-linux_aarch64-${{github.sha}}/
# Copy misc assets
cp -r stuff/yq2.cfg publish/quake2-linux_aarch64-${{github.sha}}/misc/yq2.cfg
cp -r stuff/mapfixes publish/quake2-linux_aarch64-${{github.sha}}/misc
cp LICENSE publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/LICENSE.txt
cp README.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/README.txt
cp doc/010_index.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/010_index.txt
cp doc/020_installation.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/020_installation.txt
cp doc/030_configuration.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/030_configuration.txt
cp doc/040_cvarlist.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/040_cvarlist.txt
cp doc/050_commands.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/050_commands.txt
cp doc/060_multiplayer.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/060_multiplayer.txt
cp doc/070_packaging.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/070_packaging.txt
cp doc/080_contributing.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/080_contributing.txt
cp doc/090_filelists.md publish/quake2-linux_aarch64-${{github.sha}}/misc/docs/090_filelists.md
- name: Upload testbuild package
uses: actions/upload-artifact@v4
with:
name: quake2-linux_aarch64-${{github.sha}}
path: publish/
if-no-files-found: error

63
.github/workflows/linux_x86_64.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Testbuild for Linux (x86_64)
run-name: testbuild_linux_x86_64
on:
push:
branches:
- 'master'
pull_request:
types:
- edited
- opened
- synchronize
concurrency:
# Cancel concurrent workflows for the same PR or commit hash.
group: ${{github.workflow}}-${{github.event_name == 'pull_request' && github.head_ref || github.sha}}
cancel-in-progress: true
jobs:
build_ubuntu_x86_64:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- env: ubuntu
steps:
- name: Install build dependencies
run: |
sudo apt update
sudo apt install libgl1-mesa-dev libsdl2-dev libopenal-dev libcurl4-openssl-dev \
libavformat-dev libswscale-dev libvulkan-dev build-essential
- name: Check out repository code
uses: actions/checkout@v4
- name: Build
run: |
sed -i 's|WITH_AVCODEC:=yes|WITH_AVCODEC:=no|g' Makefile
# Public runners come with 4 CPUs.
make -j4
make -j4 ref_gles1
- name: Create testbuild package
run: |
# Create release directory tree
mkdir -p publish/quake2-linux_x86_64-${{github.sha}}/misc/docs
# Copy release assets
cp -r release/* publish/quake2-linux_x86_64-${{github.sha}}/
# Copy misc assets
cp -r stuff/yq2.cfg publish/quake2-linux_x86_64-${{github.sha}}/misc/yq2.cfg
cp -r stuff/mapfixes publish/quake2-linux_x86_64-${{github.sha}}/misc
cp LICENSE publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/LICENSE.txt
cp README.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/README.txt
cp doc/010_index.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/010_index.txt
cp doc/020_installation.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/020_installation.txt
cp doc/030_configuration.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/030_configuration.txt
cp doc/040_cvarlist.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/040_cvarlist.txt
cp doc/050_commands.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/050_commands.txt
cp doc/060_multiplayer.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/060_multiplayer.txt
cp doc/070_packaging.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/070_packaging.txt
cp doc/080_contributing.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/080_contributing.txt
cp doc/090_filelists.md publish/quake2-linux_x86_64-${{github.sha}}/misc/docs/090_filelists.md
- name: Upload testbuild package
uses: actions/upload-artifact@v4
with:
name: quake2-linux_x86_64-${{github.sha}}
path: publish/
if-no-files-found: error

View file

@ -46,8 +46,8 @@ jobs:
shell: msys2 {0}
run: |
sed -i 's|WITH_AVCODEC:=yes|WITH_AVCODEC:=no|g' Makefile
# Public runners come with 2 CPUs.
make -j2
# Public runners come with 4 CPUs.
make -j4
- name: Create testbuild package
shell: msys2 {0}
run: |
@ -70,8 +70,8 @@ jobs:
cp doc/080_contributing.md publish/quake2-win32-${{github.sha}}/misc/docs/080_contributing.txt
cp doc/090_filelists.md publish/quake2-win32-${{github.sha}}/misc/docs/090_filelists.md
# SDL2
wget -c https://github.com/libsdl-org/SDL/releases/download/release-2.30.11/SDL2-2.30.11-win32-x86.zip
unzip -o SDL2-2.30.11-win32-x86.zip
wget -c https://github.com/libsdl-org/SDL/releases/download/release-2.32.0/SDL2-2.32.0-win32-x86.zip
unzip -o SDL2-2.32.0-win32-x86.zip
cp SDL2.dll publish/quake2-win32-${{github.sha}}/
# openal-soft
wget -c https://github.com/kcat/openal-soft/releases/download/1.23.1/openal-soft-1.23.1-bin.zip

View file

@ -1,4 +1,4 @@
name: Testbuild for Win64
name: Testbuild for Win64 (SDL3)
run-name: testbuild_win64
on:
push:
@ -8,8 +8,9 @@ on:
- "*"
pull_request:
types:
- opened
- edited
- opened
- synchronize
concurrency:
# Cancel concurrent workflows for the same PR or commit hash.
group: ${{github.workflow}}-${{github.event_name == 'pull_request' && github.head_ref || github.sha}}
@ -37,17 +38,19 @@ jobs:
mingw-w64-${{matrix.env}}-gcc
mingw-w64-${{matrix.env}}-make
mingw-w64-${{matrix.env}}-openal
mingw-w64-${{matrix.env}}-SDL2
mingw-w64-${{matrix.env}}-sdl3
mingw-w64-${{matrix.env}}-vulkan-headers
mingw-w64-${{matrix.env}}-wget
mingw-w64-${{matrix.env}}-pkgconf
- name: Check out repository code
uses: actions/checkout@v4
- name: Build
shell: msys2 {0}
run: |
# Public runners come with 2 CPUs.
make -j2
make -j2 ref_gles1
sed -i 's|WITH_SDL3:=no|WITH_SDL3:=yes|g' Makefile
# Public runners come with 4 CPUs.
make -j4
make -j4 ref_gles1
- name: Create testbuild package
shell: msys2 {0}
run: |
@ -69,10 +72,10 @@ jobs:
cp doc/070_packaging.md publish/quake2-win64-${{github.sha}}/misc/docs/070_packaging.txt
cp doc/080_contributing.md publish/quake2-win64-${{github.sha}}/misc/docs/080_contributing.txt
cp doc/090_filelists.md publish/quake2-win64-${{github.sha}}/misc/docs/090_filelists.md
# SDL2
wget -c https://github.com/libsdl-org/SDL/releases/download/release-2.30.11/SDL2-2.30.11-win32-x64.zip
unzip -o SDL2-2.30.11-win32-x64.zip
cp SDL2.dll publish/quake2-win64-${{github.sha}}/
# SDL3
wget -c https://github.com/libsdl-org/SDL/releases/download/release-3.2.4/SDL3-3.2.4-win32-x64.zip
unzip -o SDL3-3.2.4-win32-x64.zip
cp SDL3.dll publish/quake2-win64-${{github.sha}}/
# static ffmpeg
wget -c https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2024-08-31-12-50/ffmpeg-n7.0.2-6-g7e69129d2f-win64-lgpl-shared-7.0.zip
unzip -o ffmpeg-n7.0.2-6-g7e69129d2f-win64-lgpl-shared-7.0.zip
@ -84,9 +87,9 @@ jobs:
unzip -o openal-soft-1.23.1-bin.zip
cp openal-soft-1.23.1-bin/bin/Win64/soft_oal.dll publish/quake2-win64-${{github.sha}}/openal32.dll
# curl (releases use a custom build curl.dll)
wget -c https://curl.se/windows/dl-8.9.1_1/curl-8.9.1_1-win64-mingw.zip
unzip -o curl-8.9.1_1-win64-mingw.zip
cp curl-8.9.1_1-win64-mingw/bin/libcurl-x64.dll publish/quake2-win64-${{github.sha}}/curl.dll
wget -c -O curl-mingw-latest.zip "https://curl.se/windows/latest.cgi?p=win64-mingw.zip"
unzip -o curl-mingw-latest.zip
cp curl-*-win64-mingw/bin/libcurl-x64.dll publish/quake2-win64-${{github.sha}}/curl.dll
- name: Upload testbuild package
uses: actions/upload-artifact@v4
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}

View file

@ -617,18 +617,33 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable`
* **sw_colorlight**: enable experimental color lighting.
## Game Controller
## Gamepad
* **in_initjoy**: Toggles initialization of game controller. Default is
`1`, which enables gamepad usage; `0` disables its detection at
startup. Can only be set from command line.
* **in_sdlbackbutton**: Defines which button is used in the gamepad as
* **joy_escbutton**: Defines which button is used in the gamepad as
the `Esc` key, to pull the main menu and 'cancel' / 'go back' on its
options. Valid values are `0` = Back / Select / Minus, `1` = Start /
Menu / Plus (default), or `2` = Guide / Home / PS. Requires a game
options. Valid values are `0` = Start / Menu / Plus (default), `1` =
Back / Select / Minus, or `2` = Guide / Home / PS. Requires a game
restart, or gamepad replug, when changed.
* **joy_labels**: Defines style of button labels in binding menus. Note
that binding through console only uses the SDL nomenclature (`0`).
Default is `-1`, which requires at least SDL 2.0.12 to work.
- `-1`: *Autodetect*, sets to `0` if gamepad type isn't detected
- `0`: *SDL*, face buttons appear as cardinal points
- `1`: *Xbox*, with One / Series X / S labels
- `2`: *Playstation*, 4 & 5 format
- `3`: *Switch*, traditional Nintendo button format
* **joy_confirm**: Style of *confirm* and *cancel* buttons in menus. As
with the previous one, SDL 2.0.12 is required for `-1` to work.
- `-1`: *Autodetect*, sets to `1` if Nintendo, `0` otherwise
- `0`: SOUTH to confirm, EAST to cancel (standard style)
- `1`: EAST to confirm, SOUTH to cancel (Japanese style)
* **joy_layout**: Allows to select the stick layout of the gamepad.
- `0`: *Default*, left stick moves, right aims
- `1`: *Southpaw*, same as previous one with inverted sticks

View file

@ -139,62 +139,6 @@ keyname_t keynames[] = {
{"MWHEELUP", K_MWHEELUP},
{"MWHEELDOWN", K_MWHEELDOWN},
{"BTN_A", K_BTN_A},
{"BTN_B", K_BTN_B},
{"BTN_X", K_BTN_X},
{"BTN_Y", K_BTN_Y},
{"STICK_LEFT", K_STICK_LEFT},
{"STICK_RIGHT", K_STICK_RIGHT},
{"SHOULDR_LEFT", K_SHOULDER_LEFT},
{"SHOULDR_RIGHT", K_SHOULDER_RIGHT},
{"TRIG_LEFT", K_TRIG_LEFT},
{"TRIG_RIGHT", K_TRIG_RIGHT},
{"DP_UP", K_DPAD_UP},
{"DP_DOWN", K_DPAD_DOWN},
{"DP_LEFT", K_DPAD_LEFT},
{"DP_RIGHT", K_DPAD_RIGHT},
{"PADDLE_1", K_PADDLE_1},
{"PADDLE_2", K_PADDLE_2},
{"PADDLE_3", K_PADDLE_3},
{"PADDLE_4", K_PADDLE_4},
{"BTN_MISC1", K_BTN_MISC1},
{"TOUCHPAD", K_TOUCHPAD},
{"BTN_BACK", K_BTN_BACK},
{"BTN_GUIDE", K_BTN_GUIDE},
{"BTN_START", K_BTN_START},
// virtual keys you get by pressing the corresponding normal joy key
// and the altselector key
{"BTN_A_ALT", K_BTN_A_ALT},
{"BTN_B_ALT", K_BTN_B_ALT},
{"BTN_X_ALT", K_BTN_X_ALT},
{"BTN_Y_ALT", K_BTN_Y_ALT},
{"STICK_LEFT_ALT", K_STICK_LEFT_ALT},
{"STICK_RIGHT_ALT", K_STICK_RIGHT_ALT},
{"SHOULDR_LEFT_ALT", K_SHOULDER_LEFT_ALT},
{"SHOULDR_RIGHT_ALT", K_SHOULDER_RIGHT_ALT},
{"TRIG_LEFT_ALT", K_TRIG_LEFT_ALT},
{"TRIG_RIGHT_ALT", K_TRIG_RIGHT_ALT},
{"DP_UP_ALT", K_DPAD_UP_ALT},
{"DP_DOWN_ALT", K_DPAD_DOWN_ALT},
{"DP_LEFT_ALT", K_DPAD_LEFT_ALT},
{"DP_RIGHT_ALT", K_DPAD_RIGHT_ALT},
{"PADDLE_1_ALT", K_PADDLE_1_ALT},
{"PADDLE_2_ALT", K_PADDLE_2_ALT},
{"PADDLE_3_ALT", K_PADDLE_3_ALT},
{"PADDLE_4_ALT", K_PADDLE_4_ALT},
{"BTN_MISC1_ALT", K_BTN_MISC1_ALT},
{"TOUCHPAD_ALT", K_TOUCHPAD_ALT},
{"BTN_BACK_ALT", K_BTN_BACK_ALT},
{"BTN_GUIDE_ALT", K_BTN_GUIDE_ALT},
{"BTN_START_ALT", K_BTN_START_ALT},
{"JOY_BACK", K_JOY_BACK},
{"SUPER", K_SUPER},
{"COMPOSE", K_COMPOSE},
{"MODE", K_MODE},
@ -267,6 +211,147 @@ keyname_t keynames[] = {
{NULL, 0}
};
static char *gamepadbtns[] =
{
// It is imperative that this list of buttons follow EXACTLY the order they
// appear in QKEYS enum in keyboard.h, which in turn is the same order as
// they appear in SDL_GamepadButton / SDL_GameControllerButton enum.
"BTN_SOUTH",
"BTN_EAST",
"BTN_WEST",
"BTN_NORTH",
"BTN_BACK",
"BTN_GUIDE",
"BTN_START",
"STICK_LEFT",
"STICK_RIGHT",
"SHOULDR_LEFT",
"SHOULDR_RIGHT",
"DP_UP",
"DP_DOWN",
"DP_LEFT",
"DP_RIGHT",
"BTN_MISC1",
"PADDL_RIGHT1",
"PADDL_LEFT1",
"PADDL_RIGHT2",
"PADDL_LEFT2",
"TOUCHPAD",
"BTN_MISC2",
"BTN_MISC3",
"BTN_MISC4",
"BTN_MISC5",
"BTN_MISC6",
"TRIG_LEFT",
"TRIG_RIGHT",
// Same with _ALT buttons ( button + 'alt modifier' pressed )
"BTN_SOUTH_ALT",
"BTN_EAST_ALT",
"BTN_WEST_ALT",
"BTN_NORTH_ALT",
"BTN_BACK_ALT",
"BTN_GUIDE_ALT",
"BTN_START_ALT",
"STICK_LEFT_ALT",
"STICK_RIGHT_ALT",
"SHOULDR_LEFT_ALT",
"SHOULDR_RIGHT_ALT",
"DP_UP_ALT",
"DP_DOWN_ALT",
"DP_LEFT_ALT",
"DP_RIGHT_ALT",
"BTN_MISC1_ALT",
"PADDL_RIGHT1_ALT",
"PADDL_LEFT1_ALT",
"PADDL_RIGHT2_ALT",
"PADDL_LEFT2_ALT",
"TOUCHPAD_ALT",
"BTN_MISC2_ALT",
"BTN_MISC3_ALT",
"BTN_MISC4_ALT",
"BTN_MISC5_ALT",
"BTN_MISC6_ALT",
"TRIG_LEFT_ALT",
"TRIG_RIGHT_ALT"
};
#define NUM_GAMEPAD_BTNS (sizeof gamepadbtns / sizeof gamepadbtns[0])
static char *gpbtns_face[] =
{
// Xbox
"A",
"B",
"X",
"Y",
"VIEW",
"XBOX",
"MENU",
"LS",
"RS",
"LB",
"RB",
// Playstation
"CROSS",
"CIRCLE",
"SQUARE",
"TRIANGLE",
"CREATE",
"PS",
"OPTIONS",
"L3",
"R3",
"L1",
"R1",
// Nintendo Switch
"B",
"A",
"Y",
"X",
"-",
"HOME",
"+",
"L stick",
"R stick",
"L btn",
"R btn",
};
static char *gpbtns_paddles[] =
{
// Xbox
"SHARE",
"P1",
"P3",
"P2",
"P4",
// Playstation
"MIC",
"RB",
"LB",
"Right Fn",
"Left Fn",
// Switch
"CAPTURE",
"Right SR",
"Left SL",
"Right SL",
"Left SR" // JoyCon btn positions suck
};
static char *gpbtns_triggers[] =
{
// Xbox
"LT",
"RT",
// Playstation
"L2",
"R2",
// Switch
"ZL",
"ZR"
};
/* ------------------------------------------------------------------ */
static void
@ -738,6 +823,7 @@ static int
Key_StringToKeynum(char *str)
{
keyname_t *kn;
int i;
if (!str || !str[0])
{
@ -757,6 +843,14 @@ Key_StringToKeynum(char *str)
}
}
for (i = 0; i < NUM_GAMEPAD_BTNS; i++)
{
if (!Q_stricmp(str, gamepadbtns[i]))
{
return K_JOY_FIRST_BTN + i;
}
}
return -1;
}
@ -784,6 +878,11 @@ Key_KeynumToString(int keynum)
return tinystr;
}
if (keynum >= K_JOY_FIRST_BTN) // gamepad button
{
return gamepadbtns[keynum - K_JOY_FIRST_BTN];
}
for (kn = keynames; kn->name; kn++)
{
if (keynum == kn->keynum)
@ -795,6 +894,49 @@ Key_KeynumToString(int keynum)
return "<UNKNOWN KEYNUM>";
}
/*
* Same as Key_KeynumToString(), but for joystick/gamepad buttons.
*/
char *
Key_KeynumToString_Joy(int key)
{
extern gamepad_labels_t joy_current_lbls;
const int lbl_style = (int)joy_current_lbls - 1;
if (key < K_JOY_FIRST_BTN)
{
return Key_KeynumToString(key);
}
// Don't print the _ALT buttons (buttons with the alt modifier pressed)
if (key >= K_JOY_FIRST_BTN_ALT)
{
key -= K_JOY_FIRST_BTN_ALT - K_JOY_FIRST_BTN;
}
if (lbl_style < 0) // was SDL
{
goto exit_sdl;
}
// Alter this logic if new gamepad buttons are added in SDL
if (key < K_DPAD_UP) // face & shoulder buttons
{
return gpbtns_face[lbl_style * (K_DPAD_UP - K_BTN_SOUTH) + key - K_BTN_SOUTH];
}
else if (key >= K_TRIG_LEFT) // triggers
{
return gpbtns_triggers[lbl_style * (K_JOY_FIRST_BTN_ALT - K_TRIG_LEFT) + key - K_TRIG_LEFT];
}
else if (key > K_DPAD_RIGHT && key < K_TOUCHPAD) // paddles & misc1
{
return gpbtns_paddles[lbl_style * (K_TOUCHPAD - K_BTN_MISC1) + key - K_BTN_MISC1];
}
exit_sdl:
return gamepadbtns[key - K_JOY_FIRST_BTN];
}
void
Key_SetBinding(int keynum, char *binding)
{
@ -884,7 +1026,7 @@ Key_Bind_f(void)
}
/* don't allow binding escape or the special console keys */
if(b == K_ESCAPE || b == '^' || b == '`' || b == '~' || b == K_JOY_BACK)
if(b == K_ESCAPE || b == '^' || b == '`' || b == '~')
{
if(doneWithDefaultCfg)
{
@ -1202,12 +1344,12 @@ Key_Event(int key, qboolean down, qboolean special)
unsigned int time = Sys_Milliseconds();
// evil hack for the joystick key altselector, which turns K_BTN_x into K_BTN_x_ALT
if(joy_altselector_pressed && key >= K_JOY_FIRST_REGULAR && key <= K_JOY_LAST_REGULAR)
if(joy_altselector_pressed && key >= K_JOY_FIRST_BTN && key < K_JOY_FIRST_BTN_ALT)
{
// make sure key is not the altselector itself (which we won't turn into *_ALT)
if(keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0)
{
int altkey = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR);
int altkey = key + (K_JOY_FIRST_BTN_ALT - K_JOY_FIRST_BTN);
// allow fallback to binding with non-alt key
if(keybindings[altkey] != NULL || keybindings[key] == NULL)
key = altkey;
@ -1274,7 +1416,7 @@ Key_Event(int key, qboolean down, qboolean special)
}
/* Key is unbound */
if ((key >= K_MOUSE1 && key != K_JOY_BACK) && !keybindings[key] && (cls.key_dest != key_console) &&
if ((key >= K_MOUSE1) && !keybindings[key] && (cls.key_dest != key_console) &&
(cls.state == ca_active))
{
Com_Printf("%s (%d) is unbound, hit F4 to set.\n", Key_KeynumToString(key), key);
@ -1295,47 +1437,43 @@ Key_Event(int key, qboolean down, qboolean special)
- moves one menu level up
- closes the menu
- closes the help computer
- closes the chat window
Fully same logic for K_JOY_BACK */
if (!cls.disable_screen)
- closes the chat window */
if (key == K_ESCAPE && !cls.disable_screen)
{
if (key == K_ESCAPE || key == K_JOY_BACK)
if (!down)
{
if (!down)
{
return;
}
/* Close the help computer */
if (cl.frame.playerstate.stats[STAT_LAYOUTS] &&
(cls.key_dest == key_game))
{
Cbuf_AddText("cmd putaway\n");
return;
}
switch (cls.key_dest)
{
/* Close chat window */
case key_message:
Key_Message(key);
break;
/* Close menu or one layer up */
case key_menu:
M_Keydown(key);
break;
/* Pause game and / or leave console,
break into the menu. */
case key_game:
case key_console:
M_Menu_Main_f();
break;
}
return;
}
/* Close the help computer */
if (cl.frame.playerstate.stats[STAT_LAYOUTS] &&
(cls.key_dest == key_game))
{
Cbuf_AddText("cmd putaway\n");
return;
}
switch (cls.key_dest)
{
/* Close chat window */
case key_message:
Key_Message(key);
break;
/* Close menu or one layer up */
case key_menu:
M_Keydown(key);
break;
/* Pause game and / or leave console,
break into the menu. */
case key_game:
case key_console:
M_Menu_Main_f();
break;
}
return;
}
/* This is one of the most ugly constructs I've

View file

@ -491,8 +491,18 @@ void CL_BaseMove (usercmd_t *cmd);
void IN_CenterView (void);
typedef enum
{
LBL_SDL = 0,
LBL_XBOX,
LBL_PLAYSTATION,
LBL_SWITCH,
LBL_MAX_COUNT
} gamepad_labels_t;
float CL_KeyState (kbutton_t *key);
char *Key_KeynumToString (int keynum);
char *Key_KeynumToString_Joy (int key);
void CL_WriteDemoMessage (void);
void CL_Stop_f (void);

View file

@ -208,11 +208,11 @@ enum QKEYS {
// From here on, only gamepad controls must be allowed.
// Otherwise, separate bindings (keyboard / controller) menu options will not work.
K_BTN_A,
K_JOY_FIRST_REGULAR = K_BTN_A,
K_BTN_B,
K_BTN_X,
K_BTN_Y,
K_BTN_SOUTH,
K_JOY_FIRST_BTN = K_BTN_SOUTH,
K_BTN_EAST,
K_BTN_WEST,
K_BTN_NORTH,
K_BTN_BACK,
K_BTN_GUIDE,
K_BTN_START,
@ -225,26 +225,28 @@ enum QKEYS {
K_DPAD_LEFT,
K_DPAD_RIGHT,
K_BTN_MISC1,
K_PADDLE_1,
K_PADDLE_2,
K_PADDLE_3,
K_PADDLE_4,
K_TOUCHPAD, // SDL_CONTROLLER_BUTTON_MAX - 1
K_PADDLE_RIGHT_1,
K_PADDLE_LEFT_1,
K_PADDLE_RIGHT_2,
K_PADDLE_LEFT_2,
K_TOUCHPAD, // SDL_CONTROLLER_BUTTON_MAX - 1, SDL2 limit
K_BTN_MISC2,
K_BTN_MISC3,
K_BTN_MISC4,
K_BTN_MISC5,
K_BTN_MISC6, // SDL_GAMEPAD_BUTTON_COUNT - 1, current SDL3 count
K_TRIG_LEFT, // buttons for triggers (axes)
K_TRIG_RIGHT,
// add other joystick/controller keys before this one
// and adjust it accordingly, also remember to add corresponding _ALT key below!
K_JOY_LAST_REGULAR = K_TRIG_RIGHT,
// Add other gamepad keys before this one, adjust from SDL 2/3 definitions, and
// add the corresponding _ALT key below! Respect the order, must be the same as above.
// Also, verify if cl_keyboard.c needs a refactor on its arrays.
/* Can't be mapped to any action (=> not regular) */
K_JOY_BACK,
K_BTN_A_ALT,
K_JOY_FIRST_REGULAR_ALT = K_BTN_A_ALT,
K_BTN_B_ALT,
K_BTN_X_ALT,
K_BTN_Y_ALT,
K_BTN_SOUTH_ALT,
K_JOY_FIRST_BTN_ALT = K_BTN_SOUTH_ALT,
K_BTN_EAST_ALT,
K_BTN_WEST_ALT,
K_BTN_NORTH_ALT,
K_BTN_BACK_ALT,
K_BTN_GUIDE_ALT,
K_BTN_START_ALT,
@ -257,11 +259,16 @@ enum QKEYS {
K_DPAD_LEFT_ALT,
K_DPAD_RIGHT_ALT,
K_BTN_MISC1_ALT,
K_PADDLE_1_ALT,
K_PADDLE_2_ALT,
K_PADDLE_3_ALT,
K_PADDLE_4_ALT,
K_PADDLE_RIGHT_1_ALT,
K_PADDLE_LEFT_1_ALT,
K_PADDLE_RIGHT_2_ALT,
K_PADDLE_LEFT_2_ALT,
K_TOUCHPAD_ALT,
K_BTN_MISC2_ALT,
K_BTN_MISC3_ALT,
K_BTN_MISC4_ALT,
K_BTN_MISC5_ALT,
K_BTN_MISC6_ALT,
K_TRIG_LEFT_ALT,
K_TRIG_RIGHT_ALT,

View file

@ -78,7 +78,7 @@ typedef enum
// IN_Update() called at the beginning of a frame to the
// actual movement functions called at a later time.
static float mouse_x, mouse_y;
static unsigned char sdl_back_button = SDL_CONTROLLER_BUTTON_START;
static unsigned char joy_escbutton = SDL_CONTROLLER_BUTTON_START;
static int joystick_left_x, joystick_left_y, joystick_right_x, joystick_right_y;
static float gyro_yaw, gyro_pitch;
static qboolean mlooking;
@ -91,6 +91,12 @@ int sys_frame_time;
// is pressed
qboolean joy_altselector_pressed = false;
// Gamepad labels' style (Xbox, Playstation, etc.) in use, normally set after detection
gamepad_labels_t joy_current_lbls = LBL_SDL;
// Using japanese style for confirm & cancel buttons on gamepad
qboolean japanese_confirm = false;
// Console Variables
cvar_t *freelook;
cvar_t *lookstrafe;
@ -134,6 +140,12 @@ static int last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE;
static int last_haptic_effect_pos = 0;
static haptic_effects_cache_t last_haptic_effect[HAPTIC_EFFECT_LIST_SIZE];
// Gamepad labels' style (Xbox, Playstation, etc.) requested by user
static cvar_t *joy_labels;
// Gamepad style for confirm and cancel buttons (traditional or japanese)
static cvar_t *joy_confirm;
// Joystick sensitivity
static cvar_t *joy_yawsensitivity;
static cvar_t *joy_pitchsensitivity;
@ -506,6 +518,110 @@ IN_TranslateScancodeToQ2Key(SDL_Scancode sc)
static void IN_Controller_Init(qboolean notify_user);
static void IN_Controller_Shutdown(qboolean notify_user);
/*
* Sets the gamepad buttons' style of labels (SDL, Xbox, PS, Switch).
* They are only visible in the gamepad binding options.
* Traditional binding uses SDL style, no matter the gamepad.
*/
static void
IN_GamepadLabels_Changed(void)
{
const int requested = (int)joy_labels->value;
joy_labels->modified = false;
joy_current_lbls = LBL_SDL;
#if SDL_VERSION_ATLEAST(2, 0, 12)
if (requested < 0 && controller) // try to autodetect...
{
switch (SDL_GameControllerGetType(controller))
{
case SDL_CONTROLLER_TYPE_XBOX360:
case SDL_CONTROLLER_TYPE_XBOXONE:
joy_current_lbls = LBL_XBOX;
return;
case SDL_CONTROLLER_TYPE_PS3:
case SDL_CONTROLLER_TYPE_PS4:
#if SDL_VERSION_ATLEAST(2, 0, 14)
case SDL_CONTROLLER_TYPE_PS5:
#endif // SDL_VERSION_ATLEAST(2, 0, 14)
joy_current_lbls = LBL_PLAYSTATION;
return;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
#endif // SDL_VERSION_ATLEAST(2, 24, 0)
joy_current_lbls = LBL_SWITCH;
default:
return;
}
}
else
#endif // SDL_VERSION_ATLEAST(2, 0, 12)
if (requested >= LBL_SDL && requested < LBL_MAX_COUNT)
{
joy_current_lbls = (gamepad_labels_t)requested;
}
}
/*
* Sets which gamepad button works as "confirm", and which
* works as "cancel", in menus.
*/
static void
IN_GamepadConfirm_Changed(void)
{
const int requested = (int)joy_confirm->value;
japanese_confirm = false;
joy_confirm->modified = false;
#if SDL_VERSION_ATLEAST(2, 0, 12)
if (requested < 0 && controller) // try to autodetect...
{
switch (SDL_GameControllerGetType(controller))
{
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
#endif // SDL_VERSION_ATLEAST(2, 24, 0)
japanese_confirm = true;
default:
return;
}
}
else
#endif // SDL_VERSION_ATLEAST(2, 0, 12)
if (requested == 1)
{
japanese_confirm = true;
}
}
static void
IN_GyroMode_Changed(void)
{
if (gyro_mode->value < 2)
{
gyro_active = false;
}
else
{
gyro_active = true;
}
gyro_mode->modified = false;
}
static void
IN_VirtualKeyEvent(int keynum, qboolean *state_store, qboolean new_state)
{
if (new_state != *state_store)
{
*state_store = new_state;
Key_Event(keynum, *state_store, true);
}
}
qboolean IN_NumpadIsOn()
{
SDL_Keymod mod = SDL_GetModState();
@ -534,6 +650,7 @@ IN_Update(void)
static qboolean left_trigger = false;
static qboolean right_trigger = false;
static qboolean left_stick[4] = {false, false, false, false}; // left, right, up, down virtual keys
static int consoleKeyCode = 0;
@ -734,8 +851,8 @@ IN_Update(void)
qboolean down = (event.type == SDL_CONTROLLERBUTTONDOWN);
unsigned char btn = event.cbutton.button;
// Handle Back Button, to override its original key
Key_Event( (btn == sdl_back_button)? K_JOY_BACK : K_BTN_A + btn,
// Handle Esc button first, to override its original key
Key_Event( (btn == joy_escbutton)? K_ESCAPE : K_JOY_FIRST_BTN + btn,
down, true );
break;
}
@ -747,26 +864,12 @@ IN_Update(void)
switch (event.caxis.axis)
{
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
{
qboolean new_left_trigger = axis_value > 8192;
if (new_left_trigger != left_trigger)
{
left_trigger = new_left_trigger;
Key_Event(K_TRIG_LEFT, left_trigger, true);
}
IN_VirtualKeyEvent(K_TRIG_LEFT, &left_trigger, axis_value > 8192);
break;
}
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
{
qboolean new_right_trigger = axis_value > 8192;
if (new_right_trigger != right_trigger)
{
right_trigger = new_right_trigger;
Key_Event(K_TRIG_RIGHT, right_trigger, true);
}
IN_VirtualKeyEvent(K_TRIG_RIGHT, &right_trigger, axis_value > 8192);
break;
}
}
if (!cl_paused->value && cls.key_dest == key_game)
@ -786,6 +889,24 @@ IN_Update(void)
joystick_right_y = axis_value;
break;
}
break;
}
// Virtual keys to navigate menus with left stick
if (cls.key_dest == key_menu)
{
switch (event.caxis.axis)
{
case SDL_CONTROLLER_AXIS_LEFTX:
IN_VirtualKeyEvent(K_LEFTARROW, &left_stick[0], axis_value < -16896);
IN_VirtualKeyEvent(K_RIGHTARROW, &left_stick[1], axis_value > 16896);
break;
case SDL_CONTROLLER_AXIS_LEFTY:
IN_VirtualKeyEvent(K_UPARROW, &left_stick[2], axis_value < -16896);
IN_VirtualKeyEvent(K_DOWNARROW, &left_stick[3], axis_value > 16896);
break;
}
}
break;
}
@ -834,8 +955,7 @@ IN_Update(void)
#endif // !NO_SDL_GYRO
if (gyro_active && gyro_mode->value &&
!cl_paused->value && cls.key_dest == key_game)
if (gyro_active && !cl_paused->value && cls.key_dest == key_game)
{
#ifndef NO_SDL_GYRO
if (!gyro_turning_axis->value)
@ -986,6 +1106,19 @@ IN_Update(void)
countdown_reason = REASON_NONE;
}
}
if (joy_labels->modified)
{
IN_GamepadLabels_Changed();
}
if (joy_confirm->modified)
{
IN_GamepadConfirm_Changed();
}
if (gyro_mode->modified)
{
IN_GyroMode_Changed();
}
}
/*
@ -1960,19 +2093,19 @@ IN_Controller_Init(qboolean notify_user)
SDL_Joystick *joystick = NULL;
SDL_bool is_controller = SDL_FALSE;
cvar = Cvar_Get("in_sdlbackbutton", "1", CVAR_ARCHIVE);
cvar = Cvar_Get("joy_escbutton", "0", CVAR_ARCHIVE);
if (cvar)
{
switch ((int)cvar->value)
{
case 0:
sdl_back_button = SDL_CONTROLLER_BUTTON_BACK;
case 1:
joy_escbutton = SDL_CONTROLLER_BUTTON_BACK;
break;
case 2:
sdl_back_button = SDL_CONTROLLER_BUTTON_GUIDE;
joy_escbutton = SDL_CONTROLLER_BUTTON_GUIDE;
break;
default:
sdl_back_button = SDL_CONTROLLER_BUTTON_START;
joy_escbutton = SDL_CONTROLLER_BUTTON_START;
}
}
@ -1996,6 +2129,9 @@ IN_Controller_Init(qboolean notify_user)
#ifdef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE
SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1" );
#endif
#ifdef SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS // use button positions instead of labels, like SDL3
SDL_SetHint( SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0" );
#endif
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1)
{
@ -2181,6 +2317,10 @@ IN_Controller_Init(qboolean notify_user)
#endif
}
}
IN_GamepadLabels_Changed();
IN_GamepadConfirm_Changed();
IN_GyroMode_Changed();
}
/*
@ -2216,6 +2356,8 @@ IN_Init(void)
joy_forwardsensitivity = Cvar_Get("joy_forwardsensitivity", "1.0", CVAR_ARCHIVE);
joy_sidesensitivity = Cvar_Get("joy_sidesensitivity", "1.0", CVAR_ARCHIVE);
joy_labels = Cvar_Get("joy_labels", "-1", CVAR_ARCHIVE);
joy_confirm = Cvar_Get("joy_confirm", "-1", CVAR_ARCHIVE);
joy_layout = Cvar_Get("joy_layout", "0", CVAR_ARCHIVE);
joy_left_expo = Cvar_Get("joy_left_expo", "2.0", CVAR_ARCHIVE);
joy_left_snapaxis = Cvar_Get("joy_left_snapaxis", "0.15", CVAR_ARCHIVE);
@ -2230,16 +2372,11 @@ IN_Init(void)
gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
gyro_calibration_z = Cvar_Get("gyro_calibration_z", "0.0", CVAR_ARCHIVE);
gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "1.0", CVAR_ARCHIVE);
gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "1.0", CVAR_ARCHIVE);
gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "2.5", CVAR_ARCHIVE);
gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "2.5", CVAR_ARCHIVE);
gyro_tightening = Cvar_Get("gyro_tightening", "3.5", CVAR_ARCHIVE);
gyro_turning_axis = Cvar_Get("gyro_turning_axis", "0", CVAR_ARCHIVE);
gyro_mode = Cvar_Get("gyro_mode", "2", CVAR_ARCHIVE);
if ((int)gyro_mode->value == 2)
{
gyro_active = true;
}
windowed_pauseonfocuslost = Cvar_Get("vid_pauseonfocuslost", "0", CVAR_USERINFO | CVAR_ARCHIVE);
windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE);

View file

@ -81,7 +81,7 @@ typedef enum
// IN_Update() called at the beginning of a frame to the
// actual movement functions called at a later time.
static float mouse_x, mouse_y;
static unsigned char sdl_back_button = SDL_GAMEPAD_BUTTON_START;
static unsigned char joy_escbutton = SDL_GAMEPAD_BUTTON_START;
static int joystick_left_x, joystick_left_y, joystick_right_x, joystick_right_y;
static float gyro_yaw, gyro_pitch;
static qboolean mlooking;
@ -94,6 +94,12 @@ int sys_frame_time;
// is pressed
qboolean joy_altselector_pressed = false;
// Gamepad labels' style (Xbox, Playstation, etc.) in use, normally set after detection
gamepad_labels_t joy_current_lbls = LBL_SDL;
// Using japanese style for confirm & cancel buttons on gamepad
qboolean japanese_confirm = false;
// Console Variables
cvar_t *freelook;
cvar_t *lookstrafe;
@ -137,6 +143,12 @@ static int last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE;
static int last_haptic_effect_pos = 0;
static haptic_effects_cache_t last_haptic_effect[HAPTIC_EFFECT_LIST_SIZE];
// Gamepad labels' style (Xbox, Playstation, etc.) requested by user
static cvar_t *joy_labels;
// Gamepad style for confirm and cancel buttons (traditional or japanese)
static cvar_t *joy_confirm;
// Joystick sensitivity
static cvar_t *joy_yawsensitivity;
static cvar_t *joy_pitchsensitivity;
@ -504,6 +516,98 @@ IN_TranslateScancodeToQ2Key(SDL_Scancode sc)
static void IN_Controller_Init(qboolean notify_user);
static void IN_Controller_Shutdown(qboolean notify_user);
/*
* Sets the gamepad buttons' style of labels (SDL, Xbox, PS, Switch).
* They are only visible in the gamepad binding options.
* Traditional binding uses SDL style, no matter the gamepad.
*/
static void
IN_GamepadLabels_Changed(void)
{
const int requested = (int)joy_labels->value;
joy_labels->modified = false;
joy_current_lbls = LBL_SDL;
if (requested < 0 && controller) // try to autodetect...
{
switch (SDL_GetGamepadType(controller))
{
case SDL_GAMEPAD_TYPE_XBOX360:
case SDL_GAMEPAD_TYPE_XBOXONE:
joy_current_lbls = LBL_XBOX;
return;
case SDL_GAMEPAD_TYPE_PS3:
case SDL_GAMEPAD_TYPE_PS4:
case SDL_GAMEPAD_TYPE_PS5:
joy_current_lbls = LBL_PLAYSTATION;
return;
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
joy_current_lbls = LBL_SWITCH;
default:
return;
}
}
else if (requested >= LBL_SDL && requested < LBL_MAX_COUNT)
{
joy_current_lbls = (gamepad_labels_t)requested;
}
}
/*
* Sets which gamepad button works as "confirm", and which
* works as "cancel", in menus.
*/
static void
IN_GamepadConfirm_Changed(void)
{
const int requested = (int)joy_confirm->value;
japanese_confirm = false;
joy_confirm->modified = false;
if (requested < 0 && controller) // try to autodetect...
{
switch (SDL_GetGamepadType(controller))
{
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
japanese_confirm = true;
default:
return;
}
}
else if (requested == 1)
{
japanese_confirm = true;
}
}
static void
IN_GyroMode_Changed(void)
{
if (gyro_mode->value < 2)
{
gyro_active = false;
}
else
{
gyro_active = true;
}
gyro_mode->modified = false;
}
static void
IN_VirtualKeyEvent(int keynum, qboolean *state_store, qboolean new_state)
{
if (new_state != *state_store)
{
*state_store = new_state;
Key_Event(keynum, *state_store, true);
}
}
qboolean IN_NumpadIsOn()
{
SDL_Keymod mod = SDL_GetModState();
@ -532,6 +636,7 @@ IN_Update(void)
static qboolean left_trigger = false;
static qboolean right_trigger = false;
static qboolean left_stick[4] = {false, false, false, false}; // left, right, up, down virtual keys
static int consoleKeyCode = 0;
@ -733,8 +838,8 @@ IN_Update(void)
qboolean down = (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
unsigned char btn = event.gbutton.button;
// Handle Back Button, to override its original key
Key_Event( (btn == sdl_back_button)? K_JOY_BACK : K_BTN_A + btn,
// Handle Esc button first, to override its original key
Key_Event( (btn == joy_escbutton)? K_ESCAPE : K_JOY_FIRST_BTN + btn,
down, true );
break;
}
@ -746,26 +851,12 @@ IN_Update(void)
switch (event.gaxis.axis)
{
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER :
{
qboolean new_left_trigger = axis_value > 8192;
if (new_left_trigger != left_trigger)
{
left_trigger = new_left_trigger;
Key_Event(K_TRIG_LEFT, left_trigger, true);
}
IN_VirtualKeyEvent(K_TRIG_LEFT, &left_trigger, axis_value > 8192);
break;
}
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER :
{
qboolean new_right_trigger = axis_value > 8192;
if (new_right_trigger != right_trigger)
{
right_trigger = new_right_trigger;
Key_Event(K_TRIG_RIGHT, right_trigger, true);
}
IN_VirtualKeyEvent(K_TRIG_RIGHT, &right_trigger, axis_value > 8192);
break;
}
}
if (!cl_paused->value && cls.key_dest == key_game)
@ -785,6 +876,24 @@ IN_Update(void)
joystick_right_y = axis_value;
break;
}
break;
}
// Virtual keys to navigate menus with left stick
if (cls.key_dest == key_menu)
{
switch (event.gaxis.axis)
{
case SDL_GAMEPAD_AXIS_LEFTX :
IN_VirtualKeyEvent(K_LEFTARROW, &left_stick[0], axis_value < -16896);
IN_VirtualKeyEvent(K_RIGHTARROW, &left_stick[1], axis_value > 16896);
break;
case SDL_GAMEPAD_AXIS_LEFTY :
IN_VirtualKeyEvent(K_UPARROW, &left_stick[2], axis_value < -16896);
IN_VirtualKeyEvent(K_DOWNARROW, &left_stick[3], axis_value > 16896);
break;
}
}
break;
}
@ -833,8 +942,7 @@ IN_Update(void)
#endif // !NO_SDL_GYRO
if (gyro_active && gyro_mode->value &&
!cl_paused->value && cls.key_dest == key_game)
if (gyro_active && !cl_paused->value && cls.key_dest == key_game)
{
#ifndef NO_SDL_GYRO
if (!gyro_turning_axis->value)
@ -983,6 +1091,20 @@ IN_Update(void)
countdown_reason = REASON_NONE;
}
}
// Gamepad labels' type and "confirm & cancel style" change handling
if (joy_labels->modified)
{
IN_GamepadLabels_Changed();
}
if (joy_confirm->modified)
{
IN_GamepadConfirm_Changed();
}
if (gyro_mode->modified)
{
IN_GyroMode_Changed();
}
}
/*
@ -2068,19 +2190,19 @@ IN_Controller_Init(qboolean notify_user)
SDL_Joystick *joystick = NULL;
bool is_controller = false;
cvar = Cvar_Get("in_sdlbackbutton", "1", CVAR_ARCHIVE);
cvar = Cvar_Get("joy_escbutton", "0", CVAR_ARCHIVE);
if (cvar)
{
switch ((int)cvar->value)
{
case 0:
sdl_back_button = SDL_GAMEPAD_BUTTON_BACK;
case 1:
joy_escbutton = SDL_GAMEPAD_BUTTON_BACK;
break;
case 2:
sdl_back_button = SDL_GAMEPAD_BUTTON_GUIDE;
joy_escbutton = SDL_GAMEPAD_BUTTON_GUIDE;
break;
default:
sdl_back_button = SDL_GAMEPAD_BUTTON_START;
joy_escbutton = SDL_GAMEPAD_BUTTON_START;
}
}
@ -2131,6 +2253,8 @@ IN_Controller_Init(qboolean notify_user)
show_haptic = true;
}
SDL_free((void *)joysticks);
return;
}
@ -2280,6 +2404,9 @@ IN_Controller_Init(qboolean notify_user)
}
SDL_free((void *)joysticks);
IN_GamepadLabels_Changed();
IN_GamepadConfirm_Changed();
IN_GyroMode_Changed();
}
/*
@ -2315,6 +2442,8 @@ IN_Init(void)
joy_forwardsensitivity = Cvar_Get("joy_forwardsensitivity", "1.0", CVAR_ARCHIVE);
joy_sidesensitivity = Cvar_Get("joy_sidesensitivity", "1.0", CVAR_ARCHIVE);
joy_labels = Cvar_Get("joy_labels", "-1", CVAR_ARCHIVE);
joy_confirm = Cvar_Get("joy_confirm", "-1", CVAR_ARCHIVE);
joy_layout = Cvar_Get("joy_layout", "0", CVAR_ARCHIVE);
joy_left_expo = Cvar_Get("joy_left_expo", "2.0", CVAR_ARCHIVE);
joy_left_snapaxis = Cvar_Get("joy_left_snapaxis", "0.15", CVAR_ARCHIVE);
@ -2329,16 +2458,11 @@ IN_Init(void)
gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE);
gyro_calibration_z = Cvar_Get("gyro_calibration_z", "0.0", CVAR_ARCHIVE);
gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "1.0", CVAR_ARCHIVE);
gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "1.0", CVAR_ARCHIVE);
gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "2.5", CVAR_ARCHIVE);
gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "2.5", CVAR_ARCHIVE);
gyro_tightening = Cvar_Get("gyro_tightening", "3.5", CVAR_ARCHIVE);
gyro_turning_axis = Cvar_Get("gyro_turning_axis", "0", CVAR_ARCHIVE);
gyro_mode = Cvar_Get("gyro_mode", "2", CVAR_ARCHIVE);
if ((int)gyro_mode->value == 2)
{
gyro_active = true;
}
windowed_pauseonfocuslost = Cvar_Get("vid_pauseonfocuslost", "0", CVAR_USERINFO | CVAR_ARCHIVE);
windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE);

View file

@ -257,6 +257,8 @@ M_PushMenu(menuframework_s* menu)
cls.key_dest = key_menu;
}
extern qboolean japanese_confirm;
int
Key_GetMenuKey(int key)
{
@ -297,24 +299,27 @@ Key_GetMenuKey(int key)
case K_KP_ENTER:
case K_ENTER:
case K_BTN_A:
return K_ENTER;
case K_ESCAPE:
case K_JOY_BACK:
case K_BTN_B:
return K_ESCAPE;
case K_BACKSPACE:
case K_DEL:
case K_KP_DEL:
if (IN_NumpadIsOn() == true) { break; }
case K_BTN_Y:
case K_BACKSPACE:
case K_DEL:
case K_BTN_NORTH:
return K_BACKSPACE;
case K_KP_INS:
if (IN_NumpadIsOn() == true) { break; }
case K_INS:
return K_INS;
case K_BTN_SOUTH:
if (japanese_confirm) return K_ESCAPE;
else return K_ENTER;
case K_BTN_EAST:
if (japanese_confirm) return K_ENTER;
else return K_ESCAPE;
}
return key;
@ -954,14 +959,14 @@ M_UnbindCommand(char *command, int scope)
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
end = K_JOY_FIRST_BTN;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
end = K_JOY_LAST_REGULAR + 1;
begin = K_JOY_FIRST_BTN;
end = K_JOY_FIRST_BTN_ALT;
break;
case KEYS_CONTROLLER_ALT:
begin = K_JOY_FIRST_REGULAR_ALT;
begin = K_JOY_FIRST_BTN_ALT;
}
for (j = begin; j < end; j++)
@ -990,14 +995,14 @@ M_FindKeysForCommand(char *command, int *twokeys, int scope)
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
end = K_JOY_FIRST_BTN;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
end = K_JOY_LAST_REGULAR + 1;
begin = K_JOY_FIRST_BTN;
end = K_JOY_FIRST_BTN_ALT;
break;
case KEYS_CONTROLLER_ALT:
begin = K_JOY_FIRST_REGULAR_ALT;
begin = K_JOY_FIRST_BTN_ALT;
}
twokeys[0] = twokeys[1] = -1;
@ -1138,7 +1143,7 @@ Keys_MenuKey(int key)
if (menukeyitem_bind)
{
// Any key/button except from the game controller and escape keys
if ((key != K_ESCAPE) && (key != '`') && (key < K_JOY_FIRST_REGULAR))
if ((key != K_ESCAPE) && (key != '`') && (key < K_JOY_FIRST_BTN))
{
char cmd[1024];
@ -1291,7 +1296,7 @@ MultiplayerKeys_MenuKey(int key)
if (menukeyitem_bind)
{
// Any key/button but the escape ones
if ((key != K_ESCAPE) && (key != '`') && (key != K_JOY_BACK))
if ((key != K_ESCAPE) && (key != '`'))
{
char cmd[1024];
@ -1333,6 +1338,30 @@ M_Menu_Multiplayer_Keys_f(void)
* GAME CONTROLLER ( GAMEPAD / JOYSTICK ) BUTTONS MENU
*/
static void
GamepadMenu_StatusPrompt(menuframework_s *m)
{
static char m_gamepadbind_statusbar[64];
int btn_confirm, btn_cancel;
if (japanese_confirm)
{
btn_confirm = K_BTN_EAST;
btn_cancel = K_BTN_SOUTH;
}
else
{
btn_confirm = K_BTN_SOUTH;
btn_cancel = K_BTN_EAST;
}
snprintf(m_gamepadbind_statusbar, 64, "%s assigns, %s clears, %s exits",
Key_KeynumToString_Joy(btn_confirm), Key_KeynumToString_Joy(K_BTN_NORTH),
Key_KeynumToString_Joy(btn_cancel));
Menu_SetStatusBar(m, m_gamepadbind_statusbar);
}
char *controller_bindnames[][2] =
{
{"+attack", "attack"},
@ -1388,7 +1417,7 @@ DrawControllerButtonBindingFunc(void *self)
int x;
const char *name;
name = Key_KeynumToString(keys[0]);
name = Key_KeynumToString_Joy(keys[0]);
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, name);
@ -1401,7 +1430,7 @@ DrawControllerButtonBindingFunc(void *self)
a->generic.y + a->generic.parent->y, "or");
Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale),
a->generic.y + a->generic.parent->y,
Key_KeynumToString(keys[1]));
Key_KeynumToString_Joy(keys[1]));
}
}
}
@ -1446,7 +1475,7 @@ ControllerButtons_MenuInit(void)
Menu_AddItem(&s_controller_buttons_menu, (void *)&s_controller_buttons_actions[i]);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
GamepadMenu_StatusPrompt(&s_controller_buttons_menu);
Menu_Center(&s_controller_buttons_menu);
}
@ -1465,7 +1494,7 @@ ControllerButtons_MenuKey(int key)
if (menukeyitem_bind)
{
// Only controller buttons allowed
if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK)
if (key >= K_JOY_FIRST_BTN)
{
char cmd[1024];
@ -1474,7 +1503,7 @@ ControllerButtons_MenuKey(int key)
Cbuf_InsertText(cmd);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
GamepadMenu_StatusPrompt(&s_controller_buttons_menu);
menukeyitem_bind = false;
return menu_out_sound;
}
@ -1562,7 +1591,7 @@ DrawControllerAltButtonBindingFunc(void *self)
size_t x;
const char *name;
name = Key_KeynumToString(keys[0]);
name = Key_KeynumToString_Joy(keys[0]);
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, name);
@ -1575,7 +1604,7 @@ DrawControllerAltButtonBindingFunc(void *self)
a->generic.y + a->generic.parent->y, "or");
Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale),
a->generic.y + a->generic.parent->y,
Key_KeynumToString(keys[1]));
Key_KeynumToString_Joy(keys[1]));
}
}
}
@ -1620,7 +1649,7 @@ ControllerAltButtons_MenuInit(void)
Menu_AddItem(&s_controller_alt_buttons_menu, (void *)&s_controller_alt_buttons_actions[i]);
}
Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
GamepadMenu_StatusPrompt(&s_controller_alt_buttons_menu);
Menu_Center(&s_controller_alt_buttons_menu);
}
@ -1639,17 +1668,17 @@ ControllerAltButtons_MenuKey(int key)
if (menukeyitem_bind)
{
// Only controller buttons allowed, different from the alt buttons modifier
if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK && (keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0))
if (key >= K_JOY_FIRST_BTN && (keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0))
{
char cmd[1024];
key = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR); // change input to its ALT mode
key = key + (K_JOY_FIRST_BTN_ALT - K_JOY_FIRST_BTN); // change input to its ALT mode
Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n",
Key_KeynumToString(key), controller_alt_bindnames[item->generic.localdata[0]][0]);
Cbuf_InsertText(cmd);
}
Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
GamepadMenu_StatusPrompt(&s_controller_alt_buttons_menu);
menukeyitem_bind = false;
return menu_out_sound;
}

View file

@ -100,7 +100,7 @@ RECENT REVISION HISTORY:
Bug & warning fixes
Marc LeBlanc David Woo Guillaume George Martins Mozeiko
Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
Phil Jordan Dave Moore Roy Eltham
Phil Jordan Henner Zeller Dave Moore Roy Eltham
Hayaki Saito Nathan Reed Won Chun
Luke Graham Johan Duparc Nick Verigakis the Horde3D community
Thomas Ruf Ronny Chevalier github:rlyeh
@ -111,7 +111,7 @@ RECENT REVISION HISTORY:
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
Josh Tobin Nia Bickford Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko github:mosra
@ -373,6 +373,15 @@ RECENT REVISION HISTORY:
#define STBI_VERSION 1
#if defined(__has_attribute)
# if __has_attribute(fallthrough)
# define STBI_FALLTHROUGH __attribute__((fallthrough));
# endif
#endif
#if !defined(STBI_FALLTHROUGH)
# define STBI_FALLTHROUGH
#endif
enum
{
STBI_default = 0, // only used for desired_channels
@ -1209,7 +1218,7 @@ static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int chan
int img_len = w * h * channels;
stbi__uint16 *enlarged;
enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
enlarged = (stbi__uint16 *) stbi__malloc(((size_t)img_len)*2);
if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
for (i = 0; i < img_len; ++i)
@ -1269,7 +1278,12 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x,
STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
if (ri.bits_per_channel != 8) {
result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
stbi_uc *converted = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
if (converted == NULL) {
STBI_FREE(result);
return NULL;
}
result = converted;
ri.bits_per_channel = 8;
}
@ -1295,7 +1309,12 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x,
STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
if (ri.bits_per_channel != 16) {
result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
stbi__uint16 * converted = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
if (converted == NULL) {
STBI_FREE(result);
return NULL;
}
result = converted;
ri.bits_per_channel = 16;
}
@ -1381,7 +1400,12 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req
result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR) != 0) {
// fseek() failed; we can no longer maintain the file cursor position
// guarantee of this function, so return null.
STBI_FREE(result);
return stbi__errpuc("bad file", "fseek() failed; seek position unreliable");
}
}
return result;
}
@ -1394,7 +1418,12 @@ STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp,
result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR) != 0) {
// fseek() failed; we can no longer maintain the file cursor position
// guarantee of this function, so return null.
STBI_FREE(result);
return (stbi__uint16 *) stbi__errpuc("bad file", "fseek() failed; seek position unreliable");
}
}
return result;
}
@ -1448,8 +1477,9 @@ STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int *
stbi__start_mem(&s,buffer,len);
result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
if (stbi__vertically_flip_on_load) {
stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
if (stbi__vertically_flip_on_load && result) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip_slices( result, *x, *y, *z, channels );
}
return result;
@ -1542,12 +1572,13 @@ STBIDEF int stbi_is_hdr (char const *filename)
STBIDEF int stbi_is_hdr_from_file(FILE *f)
{
#ifndef STBI_NO_HDR
long pos = ftell(f);
int res;
long pos = ftell(f);
if (pos < 0) return stbi__err("bad file", "ftell() failed");
stbi__context s;
stbi__start_file(&s,f);
res = stbi__hdr_test(&s);
fseek(f, pos, SEEK_SET);
if (fseek(f, pos, SEEK_SET) != 0) return stbi__err("bad file", "fseek() failed");
return res;
#else
STBI_NOTUSED(f);
@ -1757,6 +1788,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
int i,j;
unsigned char *good;
if (data == NULL) return data;
if (req_comp == img_n) return data;
STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
@ -1797,7 +1829,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PNM)
// nothing
#else
static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
@ -1806,7 +1838,7 @@ static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
}
#endif
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PNM)
// nothing
#else
static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
@ -2223,8 +2255,8 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * dequant[0]);
if ((dc > SHRT_MAX) || (dequant[0] > SHRT_MAX) || !stbi__mul2shorts_valid((short) dc, (short) dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) ((size_t)dc * dequant[0]);
// decode AC components, see JPEG spec
k = 1;
@ -2280,7 +2312,7 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
if ((dc > SHRT_MAX) || !stbi__mul2shorts_valid((short) dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
@ -5556,7 +5588,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
if (info.hsz == 12) {
if (info.bpp < 24)
psize = (info.offset - info.extra_read - 24) / 3;
psize = (info.offset - info.extra_read - info.hsz) / 3;
} else {
if (info.bpp < 16)
psize = (info.offset - info.extra_read - info.hsz) >> 2;
@ -5743,7 +5775,7 @@ static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
switch(bits_per_pixel) {
case 8: return STBI_grey;
case 16: if(is_grey) return STBI_grey_alpha;
// fallthrough
STBI_FALLTHROUGH;
case 15: if(is_rgb16) *is_rgb16 = 1;
return STBI_rgb;
case 24: // fallthrough
@ -5934,7 +5966,10 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req
for (i=0; i < tga_height; ++i) {
int row = tga_inverted ? tga_height -i - 1 : i;
stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
stbi__getn(s, tga_row, tga_width * tga_comp);
if(!stbi__getn(s, tga_row, tga_width * tga_comp)) {
STBI_FREE(tga_data);
return stbi__errpuc("bad palette", "Corrupt TGA");
}
}
} else {
// do I need to load a palette?
@ -6528,7 +6563,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c
if (!stbi__pic_load_core(s,x,y,comp, result)) {
STBI_FREE(result);
result=0;
return 0;
}
*px = x;
*py = y;
@ -6991,9 +7026,20 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
stride = g.w * g.h * 4;
if (out) {
if (stride == 0) {
void *ret = stbi__load_gif_main_outofmem(&g, out, delays);
return ret;
}
if (!stbi__mul2sizes_valid(layers, stride)) {
void *ret = stbi__load_gif_main_outofmem(&g, out, delays);
return ret;
}
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
if (!tmp)
return stbi__load_gif_main_outofmem(&g, out, delays);
if (!tmp) {
void *ret = stbi__load_gif_main_outofmem(&g, out, delays);
if (delays && *delays) *delays = 0;
return ret;
}
else {
out = (stbi_uc*) tmp;
out_size = layers * stride;
@ -7007,9 +7053,16 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
delays_size = layers * sizeof(int);
}
} else {
if (!stbi__mul2sizes_valid(layers, stride)) {
void *ret = stbi__load_gif_main_outofmem(&g, out, delays);
return ret;
}
out = (stbi_uc*)stbi__malloc( layers * stride );
if (!out)
return stbi__load_gif_main_outofmem(&g, out, delays);
if (!out) {
void *ret = stbi__load_gif_main_outofmem(&g, out, delays);
if (delays && *delays) *delays = 0;
return ret;
}
out_size = layers * stride;
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
@ -7020,7 +7073,7 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
}
memcpy( out + ((layers - 1) * stride), u, stride );
if (layers >= 2) {
two_back = out - 2 * stride;
two_back = out + (layers - 2) * stride;
}
if (delays) {
@ -7145,10 +7198,10 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
if (req_comp == 4) output[3] = 1;
} else {
switch (req_comp) {
case 4: output[3] = 1; /* fallthrough */
case 4: output[3] = 1; STBI_FALLTHROUGH;
case 3: output[0] = output[1] = output[2] = 0;
break;
case 2: output[1] = 1; /* fallthrough */
case 2: output[1] = 1; STBI_FALLTHROUGH;
case 1: output[0] = 0;
break;
}
@ -7219,7 +7272,10 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
for (i=0; i < width; ++i) {
stbi_uc rgbe[4];
main_decode_loop:
stbi__getn(s, rgbe, 4);
if (!stbi__getn(s, rgbe, 4)) {
STBI_FREE(hdr_data);
return stbi__errpf("invalid decoded scanline length", "corrupt HDR");
}
stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
}
}
@ -7703,9 +7759,10 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
int r;
stbi__context s;
long pos = ftell(f);
if (pos < 0) return stbi__err("bad file", "ftell() failed");
stbi__start_file(&s, f);
r = stbi__info_main(&s,x,y,comp);
fseek(f,pos,SEEK_SET);
if (fseek(f, pos, SEEK_SET) != 0) return stbi__err("bad file", "fseek() failed");
return r;
}
@ -7724,9 +7781,10 @@ STBIDEF int stbi_is_16_bit_from_file(FILE *f)
int r;
stbi__context s;
long pos = ftell(f);
if (pos < 0) return stbi__err("bad file", "ftell() failed");
stbi__start_file(&s, f);
r = stbi__is_16_main(&s);
fseek(f,pos,SEEK_SET);
if (fseek(f, pos, SEEK_SET) != 0) return stbi__err("bad file", "fseek() failed");
return r;
}
#endif // !STBI_NO_STDIO

View file

@ -270,7 +270,7 @@ int RI_InitContext(void* win)
#ifdef YQ2_GL1_GLES
// Load GL pointers through GLAD and check context.
if( !gladLoadGLES1Loader(SDL_GL_GetProcAddress))
if( !gladLoadGLES1Loader( (void * (*)(const char *)) SDL_GL_GetProcAddress ) )
{
R_Printf(PRINT_ALL, "RI_InitContext(): ERROR: loading OpenGL ES function pointers failed!\n");
return false;

View file

@ -21,6 +21,7 @@
//
// Feature contributors:
// Dougall Johnson (sample-exact seeking)
// Vitaly Novichkov (sample-accurate tell)
//
// Bugfix/warning contributors:
// Terje Mathisen Niklas Frykholm Andy Hill
@ -34,6 +35,7 @@
// github:audinowho Dougall Johnson David Reid
// github:Clownacy Pedro J. Estebanez Remi Verschelde
// AnthoFoxo github:morlat Gabriel Ravier
// Seb de Graffenried Alice Rowan
//
// Partial history:
// 1.22 - 2021-07-11 - various small fixes
@ -648,6 +650,17 @@ typedef signed short int16;
typedef unsigned int uint32;
typedef signed int int32;
#ifdef __has_feature
#if __has_feature(undefined_behavior_sanitizer)
#define HAS_UBSAN
#endif
#endif
#ifdef HAS_UBSAN
#define STB_NO_SANITIZE(s) __attribute__((no_sanitize(s)))
#else
#define STB_NO_SANITIZE(s)
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
@ -752,11 +765,11 @@ typedef struct
typedef struct
{
uint16 coupling_steps;
MappingChannel *chan;
uint16 coupling_steps;
uint8 submaps;
uint8 submap_floor[15]; // varies
uint8 submap_residue[15]; // varies
uint8 submap_floor[16]; // varies
uint8 submap_residue[16]; // varies
} Mapping;
typedef struct
@ -898,6 +911,12 @@ struct stb_vorbis
// sample-access
int channel_buffer_start;
int channel_buffer_end;
// temporary buffers
void *temp_lengths;
void *temp_codewords;
void *temp_values;
void *temp_mults;
};
#if defined(STB_VORBIS_NO_PUSHDATA_API)
@ -949,9 +968,12 @@ static void *make_block_array(void *mem, int count, int size)
static void *setup_malloc(vorb *f, int sz)
{
if (INT_MAX - 7 < sz) return NULL;
if (sz < 0) return NULL;
sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs.
f->setup_memory_required += sz;
if (f->alloc.alloc_buffer) {
if (sz == 0) return NULL;
void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
if (f->setup_offset + sz > f->temp_offset) return NULL;
f->setup_offset += sz;
@ -977,8 +999,10 @@ static void *setup_temp_malloc(vorb *f, int sz)
return malloc(sz);
}
static void setup_temp_free(vorb *f, void *p, int sz)
static void setup_temp_free(vorb *f, void **_p, int sz)
{
void *p = *_p;
*_p = NULL;
if (f->alloc.alloc_buffer) {
f->temp_offset += (sz+7)&~7;
return;
@ -1238,6 +1262,10 @@ static int vorbis_validate(uint8 *data)
// called from setup only, once per code book
// (formula implied by specification)
//
// suppress an UBSan error caused by invalid input data.
// upstream: https://github.com/nothings/stb/issues/1168.
STB_NO_SANITIZE("float-cast-overflow")
static int lookup1_values(int entries, int dim)
{
int r = (int) floor(exp((float) log((float) entries) / dim));
@ -1583,8 +1611,8 @@ static int get32_packet(vorb *f)
{
uint32 x;
x = get8_packet(f);
x += get8_packet(f) << 8;
x += get8_packet(f) << 16;
x += (uint32) get8_packet(f) << 8;
x += (uint32) get8_packet(f) << 16;
x += (uint32) get8_packet(f) << 24;
return x;
}
@ -1696,7 +1724,7 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
assert(!c->sparse);
for (i=0; i < c->entries; ++i) {
if (c->codeword_lengths[i] == NO_CODE) continue;
if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) {
if (c->codewords[i] == (f->acc & ((1U << c->codeword_lengths[i])-1))) {
if (f->valid_bits >= c->codeword_lengths[i]) {
f->acc >>= c->codeword_lengths[i];
f->valid_bits -= c->codeword_lengths[i];
@ -1753,7 +1781,7 @@ static int codebook_decode_scalar(vorb *f, Codebook *c)
#define DECODE(var,f,c) \
DECODE_RAW(var,f,c) \
if (c->sparse) var = c->sorted_values[var];
if (c->sparse && var >= 0) var = c->sorted_values[var];
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
#define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c)
@ -1888,7 +1916,7 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **out
// buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
// and the length we'll be using (effective)
if (c_inter + p_inter*ch + effective > len * ch) {
effective = len*ch - (p_inter*ch - c_inter);
effective = len*ch - (p_inter*ch + c_inter);
}
#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
@ -2067,7 +2095,7 @@ static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y
ady -= abs(base) * adx;
if (x1 > n) x1 = n;
if (x < x1) {
LINE_OP(output[x], inverse_db_table[y&255]);
LINE_OP(output[x], inverse_db_table[(uint32)y&255]);
for (++x; x < x1; ++x) {
err += ady;
if (err >= adx) {
@ -2075,7 +2103,7 @@ static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y
y += sy;
} else
y += base;
LINE_OP(output[x], inverse_db_table[y&255]);
LINE_OP(output[x], inverse_db_table[(uint32)y&255]);
}
}
}
@ -3058,7 +3086,7 @@ void inverse_mdct_naive(float *buffer, int n)
static float *get_window(vorb *f, int len)
{
len <<= 1;
len = (unsigned int)len << 1;
if (len == f->blocksize_0) return f->window[0];
if (len == f->blocksize_1) return f->window[1];
return NULL;
@ -3650,7 +3678,7 @@ static int start_decoder(vorb *f)
if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup);
//file vendor
len = get32_packet(f);
f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1));
f->vendor = (INT_MAX == len) ? NULL : (char*)setup_malloc(f, sizeof(char) * (len+1));
if (f->vendor == NULL) return error(f, VORBIS_outofmem);
for(i=0; i < len; ++i) {
f->vendor[i] = get8_packet(f);
@ -3661,14 +3689,25 @@ static int start_decoder(vorb *f)
f->comment_list = NULL;
if (f->comment_list_length > 0)
{
if (INT_MAX / sizeof(char*) < f->comment_list_length) {
f->comment_list_length = 0;
return error(f, VORBIS_outofmem);
}
f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length));
if (f->comment_list == NULL) return error(f, VORBIS_outofmem);
if (f->comment_list == NULL)
{
f->comment_list_length = 0;
return error(f, VORBIS_outofmem);
}
memset(f->comment_list, 0, sizeof(char*) * f->comment_list_length);
}
for(i=0; i < f->comment_list_length; ++i) {
len = get32_packet(f);
f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1));
if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem);
f->comment_list[i] = (INT_MAX == len) ? NULL : (char*)setup_malloc(f, sizeof(char) * (len+1));
if (f->comment_list[i] == NULL) {
return error(f, VORBIS_outofmem);
}
for(j=0; j < len; ++j) {
f->comment_list[i][j] = get8_packet(f);
@ -3713,6 +3752,7 @@ static int start_decoder(vorb *f)
// codebooks
f->codebook_count = get_bits(f,8) + 1;
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count);
if (f->codebooks == NULL) return error(f, VORBIS_outofmem);
memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count);
@ -3735,10 +3775,12 @@ static int start_decoder(vorb *f)
c->sparse = ordered ? 0 : get_bits(f,1);
if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup);
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (c->sparse)
if (c->sparse) {
lengths = (uint8 *) setup_temp_malloc(f, c->entries);
else
f->temp_lengths = lengths;
} else
lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
if (!lengths) return error(f, VORBIS_outofmem);
@ -3749,8 +3791,9 @@ static int start_decoder(vorb *f)
while (current_entry < c->entries) {
int limit = c->entries - current_entry;
int n = get_bits(f, ilog(limit));
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (current_length >= 32) return error(f, VORBIS_invalid_setup);
if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); }
if (current_entry + n > (int) c->entries) return error(f, VORBIS_invalid_setup);
memset(lengths + current_entry, current_length, n);
current_entry += n;
++current_length;
@ -3758,11 +3801,14 @@ static int start_decoder(vorb *f)
} else {
for (j=0; j < c->entries; ++j) {
int present = c->sparse ? get_bits(f,1) : 1;
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (present) {
lengths[j] = get_bits(f, 5) + 1;
++total;
if (lengths[j] == 32)
return error(f, VORBIS_invalid_setup);
if (lengths[j] == 32) {
if (c->sparse) setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_invalid_setup);
}
} else {
lengths[j] = NO_CODE;
}
@ -3775,9 +3821,12 @@ static int start_decoder(vorb *f)
f->setup_temp_memory_required = c->entries;
c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
if (c->codeword_lengths == NULL) {
setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
memcpy(c->codeword_lengths, lengths, c->entries);
setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
setup_temp_free(f, &f->temp_lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
lengths = c->codeword_lengths;
c->sparse = 0;
}
@ -3805,11 +3854,22 @@ static int start_decoder(vorb *f)
unsigned int size;
if (c->sorted_entries) {
c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries);
if (!c->codeword_lengths) return error(f, VORBIS_outofmem);
if (!c->codeword_lengths) {
setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries);
if (!c->codewords) return error(f, VORBIS_outofmem);
f->temp_codewords = c->codewords;
if (!c->codewords) {
setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
if (!values) return error(f, VORBIS_outofmem);
f->temp_values = values;
if (!values) {
setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
}
size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
if (size > f->setup_temp_memory_required)
@ -3817,27 +3877,32 @@ static int start_decoder(vorb *f)
}
if (!compute_codewords(c, lengths, c->entries, values)) {
if (c->sparse) setup_temp_free(f, values, 0);
return error(f, VORBIS_invalid_setup);
}
if (c->sorted_entries) {
// allocate an extra slot for sentinels
c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
if (c->sorted_codewords == NULL) {
if (c->sparse) setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
// allocate an extra slot at the front so that c->sorted_values[-1] is defined
// so that we can catch that case without an extra if
c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1));
if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
if (c->sorted_values == NULL) {
if (c->sparse) setup_temp_free(f, &f->temp_lengths, c->entries);
return error(f, VORBIS_outofmem);
}
++c->sorted_values;
c->sorted_values[-1] = -1;
compute_sorted_huffman(c, lengths, values);
}
if (c->sparse) {
setup_temp_free(f, values, sizeof(*values)*c->sorted_entries);
setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries);
setup_temp_free(f, lengths, c->entries);
setup_temp_free(f, &f->temp_values, sizeof(*values)*c->sorted_entries);
setup_temp_free(f, &f->temp_codewords, sizeof(*c->codewords)*c->sorted_entries);
setup_temp_free(f, &f->temp_lengths, c->entries);
c->codewords = NULL;
}
@ -3857,14 +3922,22 @@ static int start_decoder(vorb *f)
if (values < 0) return error(f, VORBIS_invalid_setup);
c->lookup_values = (uint32) values;
} else {
/* If this overflows, the file's not valid.
* See https://github.com/nothings/stb/issues/1168. */
if (c->entries != 0 && c->dimensions > INT_MAX / c->entries) return error(f, VORBIS_invalid_setup);
c->lookup_values = c->entries * c->dimensions;
}
if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
// Before we allocate the lookup table, verify that it can be read
// from the file. Otherwise, c->lookup_values can be arbitrarily
// large, and we run out of memory.
if (c->lookup_values > (8 * f->stream_len) / c->value_bits) return error(f, VORBIS_invalid_setup);
mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
f->temp_mults = mults;
if (mults == NULL) return error(f, VORBIS_outofmem);
for (j=0; j < (int) c->lookup_values; ++j) {
int q = get_bits(f, c->value_bits);
if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); }
if (f->valid_bits < 0) return error(f, VORBIS_invalid_setup);
mults[j] = q;
}
@ -3878,7 +3951,7 @@ static int start_decoder(vorb *f)
c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions);
} else
c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions);
if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
if (c->multiplicands == NULL) return error(f, VORBIS_outofmem);
len = sparse ? c->sorted_entries : c->entries;
for (j=0; j < len; ++j) {
unsigned int z = sparse ? c->sorted_values[j] : j;
@ -3891,7 +3964,6 @@ static int start_decoder(vorb *f)
last = val;
if (k+1 < c->dimensions) {
if (div > UINT_MAX / (unsigned int) c->lookup_values) {
setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
return error(f, VORBIS_invalid_setup);
}
div *= c->lookup_values;
@ -3906,7 +3978,7 @@ static int start_decoder(vorb *f)
float last=0;
CHECK(f);
c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
if (c->multiplicands == NULL) return error(f, VORBIS_outofmem);
for (j=0; j < (int) c->lookup_values; ++j) {
float val = mults[j] * c->delta_value + c->minimum_value + last;
c->multiplicands[j] = val;
@ -3917,7 +3989,7 @@ static int start_decoder(vorb *f)
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
skip:;
#endif
setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
setup_temp_free(f, &f->temp_mults, sizeof(mults[0])*c->lookup_values);
CHECK(f);
}
@ -3934,6 +4006,7 @@ static int start_decoder(vorb *f)
// Floors
f->floor_count = get_bits(f, 6)+1;
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
for (i=0; i < f->floor_count; ++i) {
@ -3963,6 +4036,7 @@ static int start_decoder(vorb *f)
for (j=0; j <= max_class; ++j) {
g->class_dimensions[j] = get_bits(f, 3)+1;
g->class_subclasses[j] = get_bits(f, 2);
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (g->class_subclasses[j]) {
g->class_masterbooks[j] = get_bits(f, 8);
if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
@ -4010,6 +4084,7 @@ static int start_decoder(vorb *f)
// Residue
f->residue_count = get_bits(f, 6)+1;
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
@ -4024,6 +4099,7 @@ static int start_decoder(vorb *f)
r->part_size = get_bits(f,24)+1;
r->classifications = get_bits(f,6)+1;
r->classbook = get_bits(f,8);
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
for (j=0; j < r->classifications; ++j) {
uint8 high_bits=0;
@ -4032,12 +4108,14 @@ static int start_decoder(vorb *f)
high_bits = get_bits(f,5);
residue_cascade[j] = high_bits*8 + low_bits;
}
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
for (j=0; j < r->classifications; ++j) {
for (k=0; k < 8; ++k) {
if (residue_cascade[j] & (1 << k)) {
r->residue_books[j][k] = get_bits(f, 8);
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
} else {
r->residue_books[j][k] = -1;
@ -4062,6 +4140,7 @@ static int start_decoder(vorb *f)
}
f->mapping_count = get_bits(f,6)+1;
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
if (f->mapping == NULL) return error(f, VORBIS_outofmem);
memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
@ -4083,6 +4162,7 @@ static int start_decoder(vorb *f)
for (k=0; k < m->coupling_steps; ++k) {
m->chan[k].magnitude = get_bits(f, ilog(f->channels-1));
m->chan[k].angle = get_bits(f, ilog(f->channels-1));
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup);
if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup);
if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup);
@ -4119,6 +4199,7 @@ static int start_decoder(vorb *f)
m->windowtype = get_bits(f,16);
m->transformtype = get_bits(f,16);
m->mapping = get_bits(f,8);
if (f->valid_bits < 0) return error(f, VORBIS_unexpected_eof);
if (m->windowtype != 0) return error(f, VORBIS_invalid_setup);
if (m->transformtype != 0) return error(f, VORBIS_invalid_setup);
if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup);
@ -4161,7 +4242,8 @@ static int start_decoder(vorb *f)
int i,max_part_read=0;
for (i=0; i < f->residue_count; ++i) {
Residue *r = f->residue_config + i;
unsigned int actual_size = f->blocksize_1 / 2;
unsigned int rtype = f->residue_types[i];
unsigned int actual_size = rtype == 2 ? f->blocksize_1 : f->blocksize_1 / 2;
unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
int n_read = limit_r_end - limit_r_begin;
@ -4233,7 +4315,8 @@ static void vorbis_deinit(stb_vorbis *p)
Codebook *c = p->codebooks + i;
setup_free(p, c->codeword_lengths);
setup_free(p, c->multiplicands);
setup_free(p, c->codewords);
if (c->codewords != p->temp_codewords) // Might be the temporary buffer-allocated array.
setup_free(p, c->codewords);
setup_free(p, c->sorted_codewords);
// c->sorted_values[-1] is the first entry in the array
setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL);
@ -4263,6 +4346,12 @@ static void vorbis_deinit(stb_vorbis *p)
setup_free(p, p->window[i]);
setup_free(p, p->bit_reverse[i]);
}
if (!p->alloc.alloc_buffer) {
setup_temp_free(p, &p->temp_lengths, 0);
setup_temp_free(p, &p->temp_codewords, 0);
setup_temp_free(p, &p->temp_values, 0);
setup_temp_free(p, &p->temp_mults, 0);
}
#ifndef STB_VORBIS_NO_STDIO
if (p->close_on_free) fclose(p->f);
#endif
@ -4649,10 +4738,12 @@ static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
z->page_start = stb_vorbis_get_file_offset(f);
// parse the header
getn(f, header, 27);
if (!getn(f, header, 27))
return 0;
if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
return 0;
getn(f, lacing, header[26]);
if (!getn(f, lacing, header[26]))
return 0;
// determine the length of the payload
len = 0;
@ -5147,7 +5238,9 @@ static int8 channel_position[7][6] =
#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
typedef union {
float f;
int i;
// changed this to unsigned to suppress an UBSan error.
// upstream: https://github.com/nothings/stb/issues/1168.
unsigned int i;
} float_conv;
typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4];
#define FASTDEF(x) float_conv x
@ -5169,7 +5262,7 @@ static void copy_samples(short *dest, float *src, int len)
for (i=0; i < len; ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15);
if ((unsigned int) (v + 32768) > 65535)
if (((unsigned int)v + 32768) > 65535)
v = v < 0 ? -32768 : 32767;
dest[i] = v;
}
@ -5193,7 +5286,7 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in
for (i=0; i < n; ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
if ((unsigned int) (v + 32768) > 65535)
if (((unsigned int)v + 32768) > 65535)
v = v < 0 ? -32768 : 32767;
output[o+i] = v;
}
@ -5233,7 +5326,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d
for (i=0; i < (n<<1); ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
if ((unsigned int) (v + 32768) > 65535)
if (((unsigned int)v + 32768) > 65535)
v = v < 0 ? -32768 : 32767;
output[o2+i] = v;
}
@ -5283,7 +5376,7 @@ static void convert_channels_short_interleaved(int buf_c, short *buffer, int dat
FASTDEF(temp);
float f = data[i][d_offset+j];
int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15);
if ((unsigned int) (v + 32768) > 65535)
if (((unsigned int)v + 32768) > 65535)
v = v < 0 ? -32768 : 32767;
*buffer++ = v;
}

View file

@ -765,6 +765,8 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
else {
// Each component is stored separately. Allocate scratch space for full output scanline.
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
if (NULL == scratch)
return 0;
int i, len;
char buffer[128];
char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
@ -772,6 +774,8 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
#ifdef __STDC_LIB_EXT1__
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
len = snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#else
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
#endif