Merge remote-tracking branch 'upstream/master' into merge-from-upstream-2023-02-16

This commit is contained in:
Ivan Avdeev 2023-02-16 10:19:30 -08:00
commit 604bd702d4
258 changed files with 12065 additions and 6057 deletions

View File

@ -23,17 +23,20 @@ jobs:
# targetos: linux
# targetarch: aarch64
# - os: ubuntu-18.04
# targetos: android
# targetarch: 32
# - os: ubuntu-18.04
# targetos: android
# targetarch: 64
# - os: ubuntu-18.04
# targetos: android
# targetarch: 32
# - os: ubuntu-18.04
# targetos: android
# targetarch: 64
# - os: ubuntu-18.04
# targetos: motomagx
# targetarch: armv6
# - os: ubuntu-18.04
# targetos: motomagx
# targetarch: armv6
- os: ubuntu-20.04
targetos: nswitch
targetarch: arm64
- os: windows-latest
targetos: win32
targetarch: amd64
@ -41,7 +44,7 @@ jobs:
targetos: win32
targetarch: i386
env:
SDL_VERSION: 2.0.14
SDL_VERSION: 2.26.2
VULKAN_SDK_VERSION: 1.2.176.1
GH_CPU_ARCH: ${{ matrix.targetarch }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -49,25 +52,42 @@ jobs:
UPLOADTOOL_ISPRERELEASE: true
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
submodules: recursive
- name: Checkout xash-extras
uses: actions/checkout@v2
with:
repository: FWGS/xash-extras
path: xash-extras
- name: Install dependencies
run: bash scripts/gha/deps_${{ matrix.targetos }}.sh
- name: Build engine
run: bash scripts/gha/build_${{ matrix.targetos }}.sh
- name: Upload engine (prereleases)
run: bash scripts/continious_upload.sh artifacts/*
- name: Upload engine (artifacts)
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: artifact-${{ matrix.targetos }}-${{ matrix.targetarch }}
path: artifacts/*
flatpak:
name: "Flatpak"
runs-on: ubuntu-latest
strategy:
matrix:
include:
- app: su.xash.Engine.Compat.i386
container:
image: bilelmoussaoui/flatpak-github-actions:freedesktop-22.08
options: --privileged
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
UPLOADTOOL_ISPRERELEASE: true
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build flatpak (Compat.i386)
uses: FWGS/flatpak-github-actions/flatpak-builder@v5
with:
bundle: ${{ matrix.app }}.flatpak
manifest-path: scripts/flatpak/${{ matrix.app }}.yml
- name: Upload engine (prereleases)
run: bash scripts/continious_upload.sh ${{ matrix.app }}.flatpak

3
.gitignore vendored
View File

@ -216,7 +216,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
@ -338,3 +338,4 @@ core
*.code-workspace
.history/*
.cache/*
enc_temp_folder/

19
.gitmodules vendored
View File

@ -1,16 +1,21 @@
[submodule "mainui"]
path = mainui
url = https://github.com/zgdump/mainui_cpp
branch = vk_menu
path = 3rdparty/mainui
url = https://github.com/FWGS/mainui_cpp
[submodule "ref_gl/nanogl"]
path = ref_gl/nanogl
path = 3rdparty/nanogl
url = https://github.com/FWGS/nanogl
[submodule "ref_gl/gl-wes-v2"]
path = ref_gl/gl-wes-v2
path = 3rdparty/gl-wes-v2
url = https://github.com/FWGS/gl-wes-v2
[submodule "ref_gl/gl4es"]
path = ref_gl/gl4es
path = 3rdparty/gl4es/gl4es
url = https://github.com/ptitSeb/gl4es
[submodule "vgui_support"]
path = vgui_support
path = 3rdparty/vgui_support
url = https://github.com/FWGS/vgui_support
[submodule "opus"]
path = 3rdparty/opus/opus
url = https://github.com/xiph/opus
[submodule "3rdparty/xash-extras"]
path = 3rdparty/extras/xash-extras
url = https://github.com/FWGS/xash-extras

29
3rdparty/extras/wscript vendored Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
# encoding: utf-8
import os
def options(opt):
pass
def configure(conf):
if not conf.path.find_dir('xash-extras'):
conf.fatal('Can\'t find xash-extras submodule.')
return
conf.load('zip')
def build(bld):
srcdir = bld.path.find_dir('xash-extras')
if bld.env.DEST_OS in ['android']:
install_path = bld.env.PREFIX
else:
install_path = os.path.join(bld.env.SHAREDIR, bld.env.GAMEDIR)
bld(features='zip',
name = 'extras.pk3',
files = srcdir.ant_glob('**/*'),
relative_to = srcdir,
compresslevel = 0,
install_path = install_path)

1
3rdparty/extras/xash-extras vendored Submodule

@ -0,0 +1 @@
Subproject commit 9aba4527435b1beda97ca8d8a5f1937cd0088c57

1
3rdparty/gl-wes-v2 vendored Submodule

@ -0,0 +1 @@
Subproject commit 0be6803f816b5cc3a9f7b990f3d19449559eb0bd

1
3rdparty/gl4es/gl4es vendored Submodule

@ -0,0 +1 @@
Subproject commit 277be116c1fce0c0344ab41359aeadfa7f023b93

24
3rdparty/gl4es/wscript vendored Normal file
View File

@ -0,0 +1,24 @@
#! /usr/bin/env python
# encoding: utf-8
import os
def options(opt):
pass
def configure(conf):
if not conf.path.find_dir('gl4es') or not conf.path.find_dir('gl4es/src'):
conf.fatal('Can\'t find gl4es submodule. Run `git submodule update --init --recursive`.')
return
def build(bld):
gl4es_srcdir = bld.path.find_node('gl4es/src')
bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']),
target = 'gl4es',
features = 'c',
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],
defines = ['NOX11', 'NO_GBM', 'NO_INIT_CONSTRUCTOR', 'DEFAULT_ES=2', 'NOEGL', 'EXTERNAL_GETPROCADDRESS=GL4ES_GetProcAddress', 'NO_LOADER', 'STATICLIB'],
cflags = ['-w', '-fvisibility=hidden', '-std=gnu99'],
subsystem = bld.env.MSVC_SUBSYSTEM,
export_includes = '.')

1
3rdparty/mainui vendored Submodule

@ -0,0 +1 @@
Subproject commit 2f615a74802e665014cddaf766e4edc2bac24a55

1
3rdparty/nanogl vendored Submodule

@ -0,0 +1 @@
Subproject commit 5f2892a37e70e8baaccecfba84be424d2bd29aa7

1
3rdparty/opus/opus vendored Submodule

@ -0,0 +1 @@
Subproject commit 997fdf54e781ae1c04dee42018f35388a04fe483

40
3rdparty/opus/wscript vendored Normal file
View File

@ -0,0 +1,40 @@
#! /usr/bin/env python
# encoding: utf-8
import os
def options(opt):
pass
def configure(conf):
if not conf.path.find_dir('opus') or not conf.path.find_dir('opus/src'):
conf.fatal('Can\'t find opus submodule. Run `git submodule update --init --recursive`.')
return
# TODO: ARM/x86 intrinsics detection
# TODO: maybe call autotools/cmake/meson instead?
def build(bld):
sources = bld.path.ant_glob([
'opus/src/*.c',
'opus/celt/*.c',
'opus/silk/*.c',
'opus/silk/float/*.c'
], excl = [
'opus/src/repacketizer_demo.c',
'opus/src/opus_demo.c',
'opus/src/opus_compare.c',
'opus/celt/opus_custom_demo.c'
])
includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/']
defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.3.1"', 'CUSTOM_MODES']
bld.stlib(
source = sources,
target = 'opus',
features = 'c',
includes = includes,
defines = defines,
subsystem = bld.env.MSVC_SUBSYSTEM,
export_includes = ['opus/include/']
)

1
3rdparty/vgui_support vendored Submodule

@ -0,0 +1 @@
Subproject commit 63c134f188e7c0891927f5a4149f4444b43b0be8

View File

@ -0,0 +1,17 @@
# Bug-compatibility in Xash3D FWGS
Xash3D FWGS has special mode for games that rely on original engine bugs.
In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
At this time, we only have implemented GoldSrc bug-compatibility. It can be enabled with `-bugcomp` command line switch.
## GoldSrc bug-compatibility
### Emulated bugs
* `pfnPEntityOfEntIndex` in GoldSrc returns NULL for last player due to incorrect player index comparison
### Games and mods that require this
* Counter-Strike: Condition Zero - Deleted Scenes

View File

@ -0,0 +1,9 @@
# Cross-compiling for Windows with Wine
This can be useful to test engine in Wine without using virtual machines or dual-booting to Windows.
0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part)
1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain
2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot`
3. Run `./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
4. .. other typical steps to build from console ...

View File

@ -0,0 +1,150 @@
# There are few new commands availiable in xash3d fork:
## Commands:
### ent_create
Create entity with specified classname and key/values
`ent_create <classname> <key> <value> <key> <value> ...`
for example:
`ent_create monster_zombie targetname zomb1`
after creating entity, ent_last_xxx cvars are set to new entity and ent_last_cb called, look at ent_getvars description
### ent_fire
Make some actions on entity
`ent_fire <pattern> <command> <args>`
Availiavle commands:
* Set fields (Only set entity field, does not call any functions):
* health
* gravity
* movetype
* solid
* rendermode
* rendercolor (vector)
* renderfx
* renderamt
* hullmin (vector)
* hullmax (vector)
* Actions
* rename: set entity targetname
* settarget: set entity target (only targetnames)
* setmodel: set entity model (does not update)
* set: set key/value by server library
* See game FGD to get list.
* command takes two arguments
* touch: touch entity by current player.
* use: use entity by current player.
* movehere: place entity in player fov.
* drop2floor: place entity to nearest floor surface
* moveup: move entity to 25 units up
* moveup (value): move by y axis relatively to specified value
* Flags (Set/clear specified flag bit, arg is bit number):
* setflag
* clearflag
* setspawnflag
* clearspawnflag
### ent_info
Print information about entity by identificator
`ent_info <identificator>`
### ent_getvars
Set client cvars containing entity information (useful for [[Scripting]]) and call ent_last_cb
`ent_getvars <identificator>`
These cvars are set:
```
ent_last_name
ent_last_num
ent_last_inst
ent_last_origin
ent_last_class
```
### ent_list
Print short information about antities, filtered by pattern
`ent_list <pattern>`
## Syntax description
### \<identificator\>
* !cross: entity under aim
* Instance code: !\<number\>_\<seria\l>
* set by ent_getvars command
* Entity index
* targetname pattern
### \<pattern\>
Pattern is like identificator, but may filter many entities by classname
### (vector)
used by ent_fire command. vector means three float values, entered without quotes
### key/value
All entities parameters may be set by specifiing key and value strings.
Originally, this mechanizm is used in map/bsp format, but it can be used in enttools too.
Keys and values are passed to server library and processed by entity keyvalue function, setting edict and entity owns parameters.
If value contains spaces, it must be put in quotes:
`ent_fire !cross set origin "0 0 0"`
## Using with scripting
ent_create and ent_getvars commands are setting cvars on client
It can be used with ent_last_cb alias that is executed after setting cvars.
Simple example:
```
ent_create weapon_c4
alias ent_last_cb "ent_fire \$ent_last_inst use"
```
Use weapon_c4 after creating it.
Note that you cannot use many dfferent callbacks at the same time.
You can set entity name by by pattern and create special script, contatning all callbacks.
Example:
example.cfg
```
alias ent_last_cb exec entity_cb.cfg
ent create \<class\> targetname my_ent1_$name
ent_create \<class\> targetname my_ent2_$name
```
entity_cb.cfg
```
if $ent_last_name == my_ent1_$name
:(ent1 actions)
if $ent_last_name == my_ent2_$name
:(ent2 actions)
```
Note that scripting cannot be blocking. You cannot wait for server answer and continue. But you can use small scripts, connected with ent_last_cb command. The best usage is user interaction. You can add touch buttons to screen or call user command menu actions by callbacks.
## Server side
To enable entity tools on server, set sv_enttools_enable to 1
To change maximum number of entities, touched by ent_fire, change sv_enttools_maxfire to required number.
To enable actions on players, set sv_enttools_players to 1.
To enable entity tools for player by nickname, set sv_enttools_godplayer to nickname. Useful to temporary enable from rcon.
To prevent crash on some actions, set host_mapdesign_fatal to 0

View File

@ -0,0 +1,8 @@
# Expanded structures that used by engine and mods
To make porting and developing mods on 64-bit platforms less painful, we decided to expand size of several structures.
This information important in case you are using codebase like XashXT, Paranoia 2: Savior and want to compile your mod for platform with 64-bit pointer size: you should replace old definitions with new ones, otherwise your mod will not work with Xash3D FWGS (typically, it's just crashing when starting map).
| Structure name | Locates in file | Original size on 64-bit | Current size on 64-bit |
|----------------|-----------------|-------------------------|------------------------|
|`mfaceinfo_t` | `common/com_model.h` | 176 bytes | 304 bytes |
|`decal_s` | `common/com_model.h` | 72 bytes | 88 bytes |
|`mextrasurf_t` | `common/com_model.h` | 376 bytes | 504 bytes |

View File

@ -31,11 +31,14 @@ Issue #0. Inconsistency between ABI and Q_buildarch.\
Resolution: Change Q_buildarch return value to use Debian-styled architectures list: https://www.debian.org/ports/, which includes a special naming for big/little-endian and hard/soft-float ARM.
Issue #1: Build-system integration.\
Resolution: implemented as [LibraryNaming.cmake](https://github.com/FWGS/hlsdk-xash3d/blob/master/cmake/LibraryNaming.cmake) and [library_naming.py](https://github.com/FWGS/hlsdk-xash3d/blob/master/scripts/waifulib/library_naming.py) extensions, see
Resolution: implemented as [LibraryNaming.cmake](https://github.com/FWGS/hlsdk-portable/blob/master/cmake/LibraryNaming.cmake) and [library_naming.py](https://github.com/FWGS/hlsdk-portable/blob/master/scripts/waifulib/library_naming.py) extensions, see
Issue #2(related to #0): Which ARM flavours we actually need to handle?\
Resolution: Little-endian only, as there is no known big-endian ARM platforms in the wild.
Architecture is coded this way:
* ```armvxy```, where `x` is ARM instruction set level and `y` is hard-float ABI presence: `hf` where hard float ABI used, otherwise `l`.
Issue #3: Some mods (like The Specialists, Tyrian, ...) already apply suffixes _i386, _i686 to the gamedll path:\
Resolution: On x86 on **Win/Lin/Mac**, don't change anything. Otherwise, strip the _i?86 part and follow the usual scheme.
See discussion: https://github.com/FWGS/xash3d-fwgs/issues/39

View File

@ -127,6 +127,9 @@ Official github repository - https://github.com/unknownworlds/NS
## Overturn
Available in mod archive on ModDB - https://www.moddb.com/mods/overturn
## Oz Deathmatch
Mirrored on github - https://github.com/nekonomicon/OZDM
## Spirit of Half-Life
[Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life

View File

@ -58,19 +58,22 @@ Read more about Xash3D on ModDB: https://www.moddb.com/engines/xash3d-engine
* Mobility API: allows better game integration on mobile devices(vibration, touch controls)
* Different input methods: touch, gamepad and classic mouse & keyboard.
* TrueType font rendering, as a part of mainui_cpp.
* Multiple renderers support: OpenGL, GLESv1, GLESv2, Software
* Multiple renderers support: OpenGL, GLESv1, GLESv2, Software.
* Voice support.
* External filesystem module like in GoldSrc engine.
* External vgui support module.
* PNG image format support.
* A set of small improvements, without broken compatibility.
## Planned fork features
* Virtual Reality support and game API
* Voice support
* Vulkan renderer
* Virtual Reality support and game API.
* Vulkan renderer.
## Installation & Running
0) Get Xash3D FWGS binaries: you can use [testing](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous) build or you can compile engine from source code.
1) Copy engine binaries to some directory.
2) Copy `valve` directory from [Half-Life](https://store.steampowered.com/app/70/HalfLife/) to directory with engine binaries.
If your CPU is NOT x86 compatible or you're running 64-bit version of the engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d).
If your CPU is NOT x86 compatible or you're running 64-bit version of the engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-portable).
This repository contains our fork of HLSDK and restored source code for some of the mods. Not all of them, of course.
You still needed to copy `valve` directory as all game resources located there.
3) Run the main executable (`xash3d.exe` or AppImage).
@ -89,10 +92,10 @@ NOTE: NEVER USE GitHub's ZIP ARCHIVES. GitHub doesn't include external dependenc
### Prerequisites
If your CPU is x86 compatible, we are building 32-bit code by default. This was dont for keeping compatibility with Steam releases of Half-Life and based on it's engine games.
If your CPU is x86 compatible, we are building 32-bit code by default. This was done to maintain compatibility with Steam releases of Half-Life and based on it's engine games.
Even if Xash3D FWGS does support targetting 64-bit, you can't load games without recompiling them from source code!
If your CPU is NOT x86 compatible or you decided build 64-bit version of engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d).
If your CPU is NOT x86 compatible or you decided build 64-bit version of engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-portable).
This repository contains our fork of HLSDK and restored source code for some of the mods. Not all of them, of course.
#### Windows (Visual Studio)

View File

@ -29,12 +29,6 @@ GNU General Public License for more details.
#define SOUND_OPENSLES 2
#define SOUND_ALSA 3
// crash handler (XASH_CRASHHANDLER)
#define CRASHHANDLER_NULL 0
#define CRASHHANDLER_UCONTEXT 1
#define CRASHHANDLER_DBGHELP 2
#define CRASHHANDLER_WIN32 3
// input (XASH_INPUT)
#define INPUT_NULL 0
#define INPUT_SDL 1
@ -53,6 +47,7 @@ GNU General Public License for more details.
#define MSGBOX_SDL 1
#define MSGBOX_ANDROID 2
#define MSGBOX_WIN32 3
#define MSGBOX_NSWITCH 4
// library loading (XASH_LIB)

View File

@ -61,23 +61,27 @@ BRUSH MODELS
#define LS_UNUSED 0xFE
#define LS_NONE 0xFF
#define MAX_MAP_CLIPNODES_HLBSP 32767
#define MAX_MAP_CLIPNODES_BSP2 524288
// these limis not using by modelloader but only for displaying 'mapstats' correctly
#ifdef SUPPORT_BSP2_FORMAT
#define MAX_MAP_MODELS 2048 // embedded models
#define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough
#define MAX_MAP_PLANES 131072 // can be increased without problems
#define MAX_MAP_NODES 262144 // can be increased without problems
#define MAX_MAP_CLIPNODES 524288 // can be increased without problems
#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_BSP2 // can be increased without problems
#define MAX_MAP_LEAFS 131072 // CRITICAL STUFF to run ad_sepulcher!!!
#define MAX_MAP_VERTS 524288 // can be increased without problems
#define MAX_MAP_FACES 262144 // can be increased without problems
#define MAX_MAP_MARKSURFACES 524288 // can be increased without problems
#else
#define MAX_MAP_MODELS 768 // embedded models
// increased to match PrimeXT compilers
#define MAX_MAP_MODELS 1024 // embedded models
#define MAX_MAP_ENTSTRING 0x100000 // 1 Mb should be enough
#define MAX_MAP_PLANES 65536 // can be increased without problems
#define MAX_MAP_NODES 32767 // because negative shorts are leafs
#define MAX_MAP_CLIPNODES 32767 // because negative shorts are contents
#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_HLBSP // because negative shorts are contents
#define MAX_MAP_LEAFS 32767 // signed short limit
#define MAX_MAP_VERTS 65535 // unsigned short limit
#define MAX_MAP_FACES 65535 // unsigned short limit

View File

@ -49,6 +49,7 @@ typedef enum
IL_DDS_HARDWARE = BIT(4), // DXT compression is support
IL_LOAD_DECAL = BIT(5), // special mode for load gradient decals
IL_OVERVIEW = BIT(6), // overview required some unque operations
IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals
} ilFlags_t;
// goes into rgbdata_t->encode

View File

@ -105,7 +105,7 @@ typedef struct
vec3_t mins, maxs; // terrain bounds (fill by user)
int reserved[32]; // just for future expansions or mod-makers
intptr_t reserved[32]; // just for future expansions or mod-makers
} mfaceinfo_t;
typedef struct
@ -173,8 +173,8 @@ struct decal_s
short entityIndex; // Entity this is attached to
// Xash3D specific
vec3_t position; // location of the decal center in world space.
glpoly_t *polys; // precomputed decal vertices
int reserved[4]; // just for future expansions or mod-makers
glpoly_t *polys; // precomputed decal vertices
intptr_t reserved[4]; // just for future expansions or mod-makers
};
typedef struct mleaf_s
@ -228,7 +228,7 @@ typedef struct mextrasurf_s
unsigned short numverts; // world->vertexes[]
int firstvertex; // fisrt look up in tr.tbn_vectors[], then acess to world->vertexes[]
int reserved[32]; // just for future expansions or mod-makers
intptr_t reserved[32]; // just for future expansions or mod-makers
} mextrasurf_t;
struct msurface_s

View File

@ -47,7 +47,9 @@ SETUP BACKENDS DEFINITIONS
#endif // XASH_TIMER
#ifndef XASH_MESSAGEBOX
#define XASH_MESSAGEBOX MSGBOX_SDL
#if !XASH_NSWITCH // SDL2 messageboxes not available
#define XASH_MESSAGEBOX MSGBOX_SDL
#endif
#endif // XASH_MESSAGEBOX
#endif
#elif XASH_ANDROID
@ -105,22 +107,13 @@ SETUP BACKENDS DEFINITIONS
#ifndef XASH_MESSAGEBOX
#if XASH_WIN32
#define XASH_MESSAGEBOX MSGBOX_WIN32
#elif XASH_NSWITCH
#define XASH_MESSAGEBOX MSGBOX_NSWITCH
#else // !XASH_WIN32
#define XASH_MESSAGEBOX MSGBOX_STDERR
#endif // !XASH_WIN32
#endif // XASH_MESSAGEBOX
//
// select crashhandler based on defines
//
#ifndef XASH_CRASHHANDLER
#if XASH_WIN32 && defined(DBGHELP)
#define XASH_CRASHHANDLER CRASHHANDLER_DBGHELP
#elif XASH_LINUX || XASH_BSD
#define XASH_CRASHHANDLER CRASHHANDLER_UCONTEXT
#endif // !(XASH_LINUX || XASH_BSD || XASH_WIN32)
#endif
//
// no timer - no xash
//
@ -157,10 +150,6 @@ SETUP BACKENDS DEFINITIONS
#define XASH_INPUT INPUT_NULL
#endif // XASH_INPUT
#ifndef XASH_CRASHHANDLER
#define XASH_CRASHHANDLER CRASHHANDLER_NULL
#endif // XASH_CRASHHANDLER
/*
=========================================================================
@ -169,25 +158,41 @@ Default build-depended cvar and constant values
=========================================================================
*/
#if XASH_MOBILE_PLATFORM
#define DEFAULT_TOUCH_ENABLE "1"
#define DEFAULT_M_IGNORE "1"
#else // !XASH_MOBILE_PLATFORM
// Platform overrides
#if XASH_NSWITCH
#define DEFAULT_TOUCH_ENABLE "0"
#define DEFAULT_M_IGNORE "0"
#endif // !XASH_MOBILE_PLATFORM
#define DEFAULT_M_IGNORE "1"
#define DEFAULT_MODE_WIDTH 1280
#define DEFAULT_MODE_HEIGHT 720
#define DEFAULT_ALLOWCONSOLE 1
#elif XASH_MOBILE_PLATFORM
#define DEFAULT_TOUCH_ENABLE "1"
#define DEFAULT_M_IGNORE "1"
#endif // !XASH_MOBILE_PLATFORM && !XASH_NSWITCH
#if XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN
#define XASH_INTERNAL_GAMELIBS
// this means that libraries are provided with engine, but not in game data
// You need add library loading code to library.c when adding new platform
// this means that libraries are provided with engine, but not in game data
// You need add library loading code to library.c when adding new platform
#define XASH_INTERNAL_GAMELIBS
#endif // XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN
// allow override for developer/debug builds
// Defaults
#ifndef DEFAULT_TOUCH_ENABLE
#define DEFAULT_TOUCH_ENABLE "0"
#endif // DEFAULT_TOUCH_ENABLE
#ifndef DEFAULT_M_IGNORE
#define DEFAULT_M_IGNORE "0"
#endif // DEFAULT_M_IGNORE
#ifndef DEFAULT_DEV
#define DEFAULT_DEV 0
#endif // DEFAULT_DEV
#ifndef DEFAULT_ALLOWCONSOLE
#define DEFAULT_ALLOWCONSOLE 0
#endif // DEFAULT_ALLOWCONSOLE
#ifndef DEFAULT_FULLSCREEN
#define DEFAULT_FULLSCREEN 1
#endif // DEFAULT_FULLSCREEN

View File

@ -16,22 +16,61 @@
#ifndef NETADR_H
#define NETADR_H
#include "build.h"
#include STDINT_H
typedef enum
{
NA_UNUSED,
NA_UNUSED = 0,
NA_LOOPBACK,
NA_BROADCAST,
NA_IP,
NA_IPX,
NA_BROADCAST_IPX
NA_BROADCAST_IPX,
NA_IP6,
NA_MULTICAST_IP6, // all nodes multicast
} netadrtype_t;
// Original structure:
// typedef struct netadr_s
// {
// netadrtype_t type;
// unsigned char ip[4];
// unsigned char ipx[10];
// unsigned short port;
// } netadr_t;
#pragma pack( push, 1 )
typedef struct netadr_s
{
netadrtype_t type;
unsigned char ip[4];
unsigned char ipx[10];
unsigned short port;
union
{
struct
{
uint32_t type;
union
{
uint8_t ip[4];
uint32_t ip4;
};
uint8_t ipx[10];
};
struct
{
#if XASH_LITTLE_ENDIAN
uint16_t type6;
uint8_t ip6[16];
#elif XASH_BIG_ENDIAN
uint8_t ip6_0[2];
uint16_t type6;
uint8_t ip6_2[14];
#endif
};
};
uint16_t port;
} netadr_t;
#pragma pack( pop )
STATIC_ASSERT( sizeof( netadr_t ) == 20, "invalid netadr_t size" );
#endif//NETADR_H

View File

@ -39,16 +39,16 @@ GNU General Public License for more details.
#if XASH_POSIX
#include <unistd.h>
#include <dlfcn.h>
#define PATH_SPLITTER "/"
#define HAVE_DUP
#define O_BINARY 0
#define O_TEXT 0
#if XASH_NSWITCH
#define SOLDER_LIBDL_COMPAT
#include <solder.h>
#else
#include <dlfcn.h>
#define HAVE_DUP
#define O_BINARY 0
#endif
#define O_TEXT 0
#define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH )
#elif XASH_DOS4GW
#define PATH_SPLITTER "\\"
#endif
typedef void* HANDLE;
@ -59,7 +59,6 @@ GNU General Public License for more details.
int x, y;
} POINT;
#else // WIN32
#define PATH_SPLITTER "\\"
#ifdef __MINGW32__
#define _inline static inline
#define FORCEINLINE inline __attribute__((always_inline))

View File

@ -162,7 +162,7 @@ struct ref_viewpass_s;
typedef struct render_api_s
{
// Get renderer info (doesn't changes engine state at all)
int (*RenderGetParm)( int parm, int arg ); // generic
intptr_t (*RenderGetParm)( int parm, int arg ); // generic
void (*GetDetailScaleForTexture)( int texture, float *xScale, float *yScale );
void (*GetExtraParmsForTexture)( int texture, byte *red, byte *green, byte *blue, byte *alpha );
lightstyle_t* (*GetLightStyle)( int number );

24
common/synctype.h Normal file
View File

@ -0,0 +1,24 @@
/*
synctype.h -- shared synctype_t definition
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2023 Alibek Omarov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef SYNCTYPE_H
#define SYNCTYPE_H
typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
#endif

View File

@ -4,12 +4,17 @@
#include "build.h"
#if XASH_IRIX
#include <port.h>
#endif
#if XASH_WIN32
#include <wchar.h> // off_t
#endif // _WIN32
#include <sys/types.h> // off_t
#include STDINT_H
#include <assert.h>
typedef unsigned char byte;
typedef int sound_t;
@ -22,7 +27,12 @@ typedef byte rgba_t[4]; // unsigned byte colorpack
typedef byte rgb_t[3]; // unsigned byte colorpack
typedef vec_t matrix3x4[3][4];
typedef vec_t matrix4x4[4][4];
#if XASH_64BIT
typedef uint32_t poolhandle_t;
#else
typedef void* poolhandle_t;
#endif
#undef true
#undef false
@ -103,6 +113,11 @@ typedef uint64_t longtime_t;
#define likely(x) (x)
#endif
#if defined( static_assert ) // C11 static_assert
#define STATIC_ASSERT static_assert
#else
#define STATIC_ASSERT( x, y ) extern int _static_assert_##__LINE__[( x ) ? 1 : -1]
#endif
#ifdef XASH_BIG_ENDIAN
#define LittleLong(x) (((int)(((x)&255)<<24)) + ((int)((((x)>>8)&255)<<16)) + ((int)(((x)>>16)&255)<<8) + (((x) >> 24)&255))

View File

@ -16,6 +16,10 @@
#ifndef ALIAS_H
#define ALIAS_H
#include "build.h"
#include STDINT_H
#include "synctype.h"
/*
==============================================================================
@ -39,16 +43,6 @@ Alias models are position independent, so the cache manager can move them.
#define ALIAS_TRACER2 0x0040 // orange split trail + rotate
#define ALIAS_TRACER3 0x0080 // purple trail
// must match definition in sprite.h
#ifndef SYNCTYPE_T
#define SYNCTYPE_T
typedef enum
{
ST_SYNC = 0,
ST_RAND
} synctype_t;
#endif
typedef enum
{
ALIAS_SINGLE = 0,
@ -63,36 +57,42 @@ typedef enum
typedef struct
{
int ident;
int version;
int32_t ident;
int32_t version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int numskins;
int skinwidth;
int skinheight;
int numverts;
int numtris;
int numframes;
synctype_t synctype;
int flags;
int32_t numskins;
int32_t skinwidth;
int32_t skinheight;
int32_t numverts;
int32_t numtris;
int32_t numframes;
uint32_t synctype; // was synctype_t
int32_t flags;
float size;
} daliashdr_t;
STATIC_ASSERT( sizeof( daliashdr_t ) == 84, "invalid daliashdr_t size" );
typedef struct
{
int onseam;
int s;
int t;
int32_t onseam;
int32_t s;
int32_t t;
} stvert_t;
STATIC_ASSERT( sizeof( stvert_t ) == 12, "invalid stvert_t size" );
typedef struct dtriangle_s
{
int facesfront;
int vertindex[3];
int32_t facesfront;
int32_t vertindex[3];
} dtriangle_t;
STATIC_ASSERT( sizeof( dtriangle_t ) == 16, "invalid dtriangle_t size" );
#define DT_FACES_FRONT 0x0010
#define ALIAS_ONSEAM 0x0020
@ -103,36 +103,50 @@ typedef struct
char name[16]; // frame name from grabbing
} daliasframe_t;
STATIC_ASSERT( sizeof( daliasframe_t ) == 24, "invalid daliasframe_t size" );
typedef struct
{
int numframes;
int32_t numframes;
trivertex_t bboxmin; // lightnormal isn't used
trivertex_t bboxmax; // lightnormal isn't used
} daliasgroup_t;
STATIC_ASSERT( sizeof( daliasgroup_t ) == 12, "invalid daliasgrou_t size" );
typedef struct
{
int numskins;
int32_t numskins;
} daliasskingroup_t;
STATIC_ASSERT( sizeof( daliasskingroup_t ) == 4, "invalid daliasskingroup_t size" );
typedef struct
{
float interval;
} daliasinterval_t;
STATIC_ASSERT( sizeof( daliasinterval_t ) == 4, "invalid daliasinterval_t size" );
typedef struct
{
float interval;
} daliasskininterval_t;
STATIC_ASSERT( sizeof( daliasskininterval_t ) == 4, "invalid daliasskininterval_t size" );
typedef struct
{
aliasframetype_t type;
uint32_t type; // was aliasframetype_t
} daliasframetype_t;
STATIC_ASSERT( sizeof( daliasframetype_t ) == 4, "invalid daliasframetype_t size" );
typedef struct
{
aliasskintype_t type;
uint32_t type; // was aliasskintype_t
} daliasskintype_t;
STATIC_ASSERT( sizeof( daliasskintype_t ) == 4, "invalid daliasskintype_t size" );
#endif//ALIAS_H

View File

@ -25,70 +25,6 @@ GNU General Public License for more details.
#define MSG_COUNT 32 // last 32 messages parsed
#define MSG_MASK (MSG_COUNT - 1)
const char *svc_strings[svc_lastmsg+1] =
{
"svc_bad",
"svc_nop",
"svc_disconnect",
"svc_event",
"svc_changing",
"svc_setview",
"svc_sound",
"svc_time",
"svc_print",
"svc_stufftext",
"svc_setangle",
"svc_serverdata",
"svc_lightstyle",
"svc_updateuserinfo",
"svc_deltatable",
"svc_clientdata",
"svc_resource",
"svc_pings",
"svc_particle",
"svc_restoresound",
"svc_spawnstatic",
"svc_event_reliable",
"svc_spawnbaseline",
"svc_temp_entity",
"svc_setpause",
"svc_signonnum",
"svc_centerprint",
"svc_unused27",
"svc_unused28",
"svc_unused29",
"svc_intermission",
"svc_finale",
"svc_cdtrack",
"svc_restore",
"svc_cutscene",
"svc_weaponanim",
"svc_bspdecal",
"svc_roomtype",
"svc_addangle",
"svc_usermessage",
"svc_packetentities",
"svc_deltapacketentities",
"svc_choke",
"svc_resourcelist",
"svc_deltamovevars",
"svc_resourcerequest",
"svc_customization",
"svc_crosshairangle",
"svc_soundfade",
"svc_filetxferfailed",
"svc_hltv",
"svc_director",
"svc_voiceinit",
"svc_voicedata",
"svc_deltapacketbones",
"svc_unused55",
"svc_resourcelocation",
"svc_querycvarvalue",
"svc_querycvarvalue2",
"svc_exec",
};
typedef struct
{
int command;

View File

@ -518,14 +518,26 @@ CL_ReadDemoCmdHeader
read the demo command
=================
*/
void CL_ReadDemoCmdHeader( byte *cmd, float *dt )
qboolean CL_ReadDemoCmdHeader( byte *cmd, float *dt )
{
// read the command
FS_Read( cls.demofile, cmd, sizeof( byte ));
Assert( *cmd >= 1 && *cmd <= dem_lastcmd );
// HACKHACK: skip NOPs
do
{
FS_Read( cls.demofile, cmd, sizeof( byte ));
} while( *cmd == dem_unknown );
if( *cmd > dem_lastcmd )
{
Con_Printf( S_ERROR "Demo cmd %d > %d, file offset = %d\n", *cmd, dem_lastcmd, (int)FS_Tell( cls.demofile ));
CL_DemoCompleted();
return false;
}
// read the timestamp
FS_Read( cls.demofile, dt, sizeof( float ));
return true;
}
/*
@ -913,7 +925,8 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
if( !cls.demofile ) break;
curpos = FS_Tell( cls.demofile );
CL_ReadDemoCmdHeader( &cmd, &demo.timestamp );
if( !CL_ReadDemoCmdHeader( &cmd, &demo.timestamp ))
return false;
fElapsedTime = CL_GetDemoPlaybackClock() - demo.starttime;
if( !cls.timedemo ) bSkipMessage = ((demo.timestamp - cl_serverframetime()) >= fElapsedTime) ? true : false;
@ -1408,7 +1421,7 @@ void CL_PlayDemo_f( void )
if( Cmd_Argc() < 2 )
{
Con_Printf( S_USAGE "playdemo <demoname>\n" );
Con_Printf( S_USAGE "%s <demoname>\n", Cmd_Argv( 0 ));
return;
}
@ -1535,12 +1548,6 @@ timedemo <demoname>
*/
void CL_TimeDemo_f( void )
{
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "timedemo <demoname>\n" );
return;
}
CL_PlayDemo_f ();
// cls.td_starttime will be grabbed at the second frame of the demo, so

View File

@ -1111,9 +1111,13 @@ R_ParticleExplosion2
void GAME_EXPORT R_ParticleExplosion2( const vec3_t org, int colorStart, int colorLength )
{
int i, j;
int colorMod = 0;
int colorMod = 0, packedColor;
particle_t *p;
if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))
packedColor = 255; // use old code for blob particles
else packedColor = 0;
for( i = 0; i < 512; i++ )
{
p = R_AllocParticle( NULL );
@ -1121,7 +1125,7 @@ void GAME_EXPORT R_ParticleExplosion2( const vec3_t org, int colorStart, int col
p->die = cl.time + 0.3f;
p->color = colorStart + ( colorMod % colorLength );
p->packedColor = 255; // use old code for blob particles
p->packedColor = packedColor;
colorMod++;
p->type = pt_blob;
@ -1143,15 +1147,19 @@ R_BlobExplosion
void GAME_EXPORT R_BlobExplosion( const vec3_t org )
{
particle_t *p;
int i, j;
int i, j, packedColor;
if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))
packedColor = 255; // use old code for blob particles
else packedColor = 0;
for( i = 0; i < 1024; i++ )
{
p = R_AllocParticle( NULL );
if( !p ) return;
p->die = cl.time + COM_RandomFloat( 2.0f, 2.4f );
p->packedColor = 255; // use old code for blob particles
p->die = cl.time + COM_RandomFloat( 1.0f, 1.4f );
p->packedColor = packedColor;
if( i & 1 )
{

338
engine/client/cl_font.c Normal file
View File

@ -0,0 +1,338 @@
/*
cl_font.c - bare bones engine font manager
Copyright (C) 2023 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "filesystem.h"
#include "client.h"
#include "qfont.h"
qboolean CL_FixedFont( cl_font_t *font )
{
return font && font->valid && font->type == FONT_FIXED;
}
static int CL_LoadFontTexture( const char *fontname, uint texFlags, int *width )
{
int font_width;
int tex;
if( !g_fsapi.FileExists( fontname, false ))
return 0;
tex = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags );
if( !tex )
return 0;
font_width = REF_GET_PARM( PARM_TEX_WIDTH, tex );
if( !font_width )
{
ref.dllFuncs.GL_FreeTexture( tex );
return 0;
}
*width = font_width;
return tex;
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
{
int font_width, i;
if( font->valid )
return true; // already loaded
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width );
if( !font->hFontTexture )
return false;
font->type = FONT_FIXED;
font->valid = true;
font->scale = scale;
font->nearest = FBitSet( texFlags, TF_NEAREST );
font->rendermode = rendermode;
font->charHeight = Q_rint( font_width / 16 * scale );
for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ )
{
font->fontRc[i].left = ( i * font_width / 16 ) % font_width;
font->fontRc[i].right = font->fontRc[i].left + font_width / 16;
font->fontRc[i].top = ( i / 16 ) * ( font_width / 16 );
font->fontRc[i].bottom = font->fontRc[i].top + font_width / 16;
font->charWidths[i] = Q_rint( font_width / 16 * scale );
}
return true;
}
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
{
fs_offset_t length;
qfont_t src;
byte *pfile;
int font_width, i;
if( font->valid )
return true;
pfile = g_fsapi.LoadFile( fontname, &length, false );
if( !pfile )
return false;
if( length < sizeof( src ))
{
Mem_Free( pfile );
return false;
}
memcpy( &src, pfile, sizeof( src ));
Mem_Free( pfile );
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width );
if( !font->hFontTexture )
return false;
font->type = FONT_VARIABLE;
font->valid = true;
font->scale = scale;
font->nearest = FBitSet( texFlags, TF_NEAREST );
font->rendermode = rendermode;
font->charHeight = Q_rint( src.rowheight * scale );
for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ )
{
const charinfo *ci = &src.fontinfo[i];
font->fontRc[i].left = (word)ci->startoffset % font_width;
font->fontRc[i].right = font->fontRc[i].left + ci->charwidth;
font->fontRc[i].top = (word)ci->startoffset / font_width;
font->fontRc[i].bottom = font->fontRc[i].top + src.rowheight;
font->charWidths[i] = Q_rint( src.fontinfo[i].charwidth * scale );
}
return true;
}
void CL_FreeFont( cl_font_t *font )
{
if( !font || !font->valid )
return;
ref.dllFuncs.GL_FreeTexture( font->hFontTexture );
memset( font, 0, sizeof( *font ));
}
static int CL_CalcTabStop( const cl_font_t *font, int x )
{
int space = font->charWidths[' '];
int tab = space * 6; // 6 spaces
int stop = tab - x % tab;
if( stop < space )
return tab * 2 - x % tab; // select next
return stop;
}
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags )
{
wrect_t *rc;
float w, h;
float s1, t1, s2, t2, half = 0.5f;
int texw, texh;
if( !font || !font->valid || y < -font->charHeight )
return 0;
// check if printable
if( number <= 32 )
{
if( number == ' ' )
return font->charWidths[' '];
else if( number == '\t' )
return CL_CalcTabStop( font, x );
return 0;
}
if( FBitSet( flags, FONT_DRAW_UTF8 ))
number = Con_UtfProcessChar( number & 255 );
else number &= 255;
if( !number || !font->charWidths[number])
return 0;
R_GetTextureParms( &texw, &texh, font->hFontTexture );
if( !texw || !texh )
return font->charWidths[number];
rc = &font->fontRc[number];
if( font->nearest || font->scale <= 1.0f )
half = 0;
s1 = ((float)rc->left + half ) / texw;
t1 = ((float)rc->top + half ) / texh;
s2 = ((float)rc->right - half ) / texw;
t2 = ((float)rc->bottom - half ) / texh;
w = ( rc->right - rc->left ) * font->scale;
h = ( rc->bottom - rc->top ) * font->scale;
if( FBitSet( flags, FONT_DRAW_HUD ))
SPR_AdjustSize( &x, &y, &w, &h );
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
// don't apply color to fixed fonts it's already colored
if( font->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, font->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] );
ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, font->hFontTexture );
return font->charWidths[number];
}
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags )
{
rgba_t current_color;
int draw_len = 0;
if( !font || !font->valid )
return 0;
if( FBitSet( flags, FONT_DRAW_UTF8 ))
Con_UtfProcessChar( 0 ); // clear utf state
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
Vector4Copy( color, current_color );
while( *s )
{
if( *s == '\n' )
{
s++;
if( !*s )
break;
// some client functions ignore newlines
if( !FBitSet( flags, FONT_DRAW_NOLF ))
{
draw_len = 0;
y += font->charHeight;
}
if( FBitSet( flags, FONT_DRAW_RESETCOLORONLF ))
Vector4Copy( color, current_color );
continue;
}
if( IsColorString( s ))
{
// don't copy alpha
if( !FBitSet( flags, FONT_DRAW_FORCECOL ))
VectorCopy( g_color_table[ColorIndex(*( s + 1 ))], current_color );
s += 2;
continue;
}
// skip setting rendermode, it was changed for this string already
draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, current_color, font, flags | FONT_DRAW_NORENDERMODE );
s++;
}
return draw_len;
}
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height )
{
if( !font || !font->valid ) return;
if( width )
{
if( number == '\t' )
*width = CL_CalcTabStop( font, 0 ); // at least return max tabstop
else *width = font->charWidths[number & 255];
}
if( height ) *height = font->charHeight;
}
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags )
{
int draw_len = 0;
if( !font || !font->valid )
return;
if( height )
*height = font->charHeight;
if( width )
*width = 0;
if( !COM_CheckString( s ))
return;
if( FBitSet( flags, FONT_DRAW_UTF8 ))
Con_UtfProcessChar( 0 ); // reset utf state
while( *s )
{
int number;
if( *s == '\n' )
{
// BUG: no check for end string here
// but high chances somebody's relying on this
s++;
draw_len = 0;
if( !FBitSet( flags, FONT_DRAW_NOLF ))
{
if( height )
*height += font->charHeight;
}
continue;
}
else if( *s == '\t' )
{
draw_len += CL_CalcTabStop( font, 0 ); // at least return max tabstop
s++;
continue;
}
if( IsColorString( s ))
{
s += 2;
continue;
}
if( FBitSet( flags, FONT_DRAW_UTF8 ))
number = Con_UtfProcessChar( (byte)*s );
else number = (byte)*s;
if( number )
{
draw_len += font->charWidths[number];
if( draw_len > *width )
*width = draw_len;
}
s++;
}
}

View File

@ -534,7 +534,7 @@ void CL_ComputePlayerOrigin( cl_entity_t *ent )
vec3_t origin;
vec3_t angles;
if( !ent->player || ent->index == ( cl.playernum + 1 ))
if( !ent->player )
return;
if( cl_nointerp->value > 0.f )
@ -1094,6 +1094,9 @@ void CL_LinkPlayers( frame_t *frame )
if ( i == cl.playernum )
{
// using interpolation only for local player angles
CL_ComputePlayerOrigin( ent );
if( cls.demoplayback == DEMO_QUAKE1 )
VectorLerp( ent->prevstate.origin, cl.lerpFrac, ent->curstate.origin, cl.simorg );
VectorCopy( cl.simorg, ent->origin );

View File

@ -147,21 +147,6 @@ qboolean CL_IsThirdPerson( void )
return false;
}
/*
====================
CL_GetPlayerInfo
get player info by render request
====================
*/
player_info_t *CL_GetPlayerInfo( int playerIndex )
{
if( playerIndex < 0 || playerIndex >= cl.maxclients )
return NULL;
return &cl.players[playerIndex];
}
/*
====================
CL_CreatePlaylist
@ -249,22 +234,6 @@ void CL_InitCDAudio( const char *filename )
Mem_Free( afile );
}
/*
====================
CL_PointContents
Return contents for point
====================
*/
int CL_PointContents( const vec3_t p )
{
int cont = PM_PointContents( clgame.pmove, p );
if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN )
cont = CONTENTS_WATER;
return cont;
}
/*
=============
CL_AdjustXPos
@ -337,9 +306,7 @@ print centerscreen message
*/
void CL_CenterPrint( const char *text, float y )
{
int length = 0;
int width = 0;
char *s;
cl_font_t *font = Con_GetCurFont();
if( !COM_CheckString( text ))
return;
@ -348,24 +315,13 @@ void CL_CenterPrint( const char *text, float y )
clgame.centerPrint.totalWidth = 0;
clgame.centerPrint.time = cl.mtime[0]; // allow pause for centerprint
Q_strncpy( clgame.centerPrint.message, text, sizeof( clgame.centerPrint.message ));
s = clgame.centerPrint.message;
// count the number of lines for centering
while( *s )
{
if( *s == '\n' )
{
clgame.centerPrint.lines++;
if( width > clgame.centerPrint.totalWidth )
clgame.centerPrint.totalWidth = width;
width = 0;
}
else width += clgame.scrInfo.charWidths[*s];
s++;
length++;
}
CL_DrawStringLen( font,
clgame.centerPrint.message,
&clgame.centerPrint.totalWidth,
&clgame.centerPrint.totalHeight,
FONT_DRAW_HUD | FONT_DRAW_UTF8 );
clgame.centerPrint.totalHeight = ( clgame.centerPrint.lines * clgame.scrInfo.iCharHeight );
clgame.centerPrint.y = CL_AdjustYPos( y, clgame.centerPrint.totalHeight );
}
@ -376,102 +332,43 @@ SPR_AdjustSize
draw hudsprite routine
====================
*/
static void SPR_AdjustSize( float *x, float *y, float *w, float *h )
void SPR_AdjustSize( float *x, float *y, float *w, float *h )
{
float xscale, yscale;
if( refState.width == clgame.scrInfo.iWidth && refState.height == clgame.scrInfo.iHeight )
return;
// scale for screen sizes
xscale = refState.width / (float)clgame.scrInfo.iWidth;
yscale = refState.height / (float)clgame.scrInfo.iHeight;
if( x ) *x *= xscale;
if( y ) *y *= yscale;
if( w ) *w *= xscale;
if( h ) *h *= yscale;
*x *= xscale;
*y *= yscale;
*w *= xscale;
*h *= yscale;
}
/*
====================
SPR_AdjustSize
draw hudsprite routine
====================
*/
static void SPR_AdjustSizei( int *x, int *y, int *w, int *h )
void SPR_AdjustTexCoords( float width, float height, float *s1, float *t1, float *s2, float *t2 )
{
float xscale, yscale;
// scale for screen sizes
xscale = refState.width / (float)clgame.scrInfo.iWidth;
yscale = refState.height / (float)clgame.scrInfo.iHeight;
if( x ) *x *= xscale;
if( y ) *y *= yscale;
if( w ) *w *= xscale;
if( h ) *h *= yscale;
}
/*
====================
PictAdjustSize
draw hudsprite routine
====================
*/
void PicAdjustSize( float *x, float *y, float *w, float *h )
{
if( !clgame.ds.adjust_size ) return;
SPR_AdjustSize( x, y, w, h );
}
static qboolean SPR_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 )
{
float dudx, dvdy;
// clip sub rect to sprite
if(( width == 0 ) || ( height == 0 ))
return false;
if( *x + *width <= clgame.ds.scissor_x )
return false;
if( *x >= clgame.ds.scissor_x + clgame.ds.scissor_width )
return false;
if( *y + *height <= clgame.ds.scissor_y )
return false;
if( *y >= clgame.ds.scissor_y + clgame.ds.scissor_height )
return false;
dudx = (*u1 - *u0) / *width;
dvdy = (*v1 - *v0) / *height;
if( *x < clgame.ds.scissor_x )
if( refState.width != clgame.scrInfo.iWidth )
{
*u0 += (clgame.ds.scissor_x - *x) * dudx;
*width -= clgame.ds.scissor_x - *x;
*x = clgame.ds.scissor_x;
// align to texel if scaling
*s1 += 0.5f;
*s2 -= 0.5f;
}
if( *x + *width > clgame.ds.scissor_x + clgame.ds.scissor_width )
if( refState.height != clgame.scrInfo.iHeight )
{
*u1 -= (*x + *width - (clgame.ds.scissor_x + clgame.ds.scissor_width)) * dudx;
*width = clgame.ds.scissor_x + clgame.ds.scissor_width - *x;
// align to texel if scaling
*t1 += 0.5f;
*t2 -= 0.5f;
}
if( *y < clgame.ds.scissor_y )
{
*v0 += (clgame.ds.scissor_y - *y) * dvdy;
*height -= clgame.ds.scissor_y - *y;
*y = clgame.ds.scissor_y;
}
if( *y + *height > clgame.ds.scissor_y + clgame.ds.scissor_height )
{
*v1 -= (*y + *height - (clgame.ds.scissor_y + clgame.ds.scissor_height)) * dvdy;
*height = clgame.ds.scissor_y + clgame.ds.scissor_height - *y;
}
return true;
*s1 /= width;
*t1 /= height;
*s2 /= width;
*t2 /= height;
}
/*
@ -499,9 +396,7 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei
if( prc )
{
wrect_t rc;
rc = *prc;
wrect_t rc = *prc;
// Sigh! some stupid modmakers set wrong rectangles in hud.txt
if( rc.left <= 0 || rc.left >= width ) rc.left = 0;
@ -509,11 +404,13 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei
if( rc.right <= 0 || rc.right > width ) rc.right = width;
if( rc.bottom <= 0 || rc.bottom > height ) rc.bottom = height;
s1 = rc.left;
t1 = rc.top;
s2 = rc.right;
t2 = rc.bottom;
// calc user-defined rectangle
s1 = (float)rc.left / width;
t1 = (float)rc.top / height;
s2 = (float)rc.right / width;
t2 = (float)rc.bottom / height;
SPR_AdjustTexCoords( width, height, &s1, &t1, &s2, &t2 );
width = rc.right - rc.left;
height = rc.bottom - rc.top;
}
@ -524,7 +421,7 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei
}
// pass scissor test if supposed
if( clgame.ds.scissor_test && !SPR_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
if( !CL_Scissor( &clgame.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
return;
// scale for screen sizes
@ -543,6 +440,7 @@ called each frame
*/
void CL_DrawCenterPrint( void )
{
cl_font_t *font = Con_GetCurFont();
char *pText;
int i, j, x, y;
int width, lineLength;
@ -562,8 +460,10 @@ void CL_DrawCenterPrint( void )
y = clgame.centerPrint.y; // start y
colorDefault = g_color_table[7];
pText = clgame.centerPrint.message;
Con_DrawCharacterLen( 0, NULL, &charHeight );
CL_DrawCharacterLen( font, 0, NULL, &charHeight );
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
for( i = 0; i < clgame.centerPrint.lines; i++ )
{
lineLength = 0;
@ -573,7 +473,7 @@ void CL_DrawCenterPrint( void )
{
byte c = *pText;
line[lineLength] = c;
Con_DrawCharacterLen( c, &charWidth, NULL );
CL_DrawCharacterLen( font, c, &charWidth, NULL );
width += charWidth;
lineLength++;
pText++;
@ -590,12 +490,47 @@ void CL_DrawCenterPrint( void )
for( j = 0; j < lineLength; j++ )
{
if( x >= 0 && y >= 0 && x <= refState.width )
x += Con_DrawCharacter( x, y, line[j], colorDefault );
x += CL_DrawCharacter( x, y, line[j], colorDefault, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD | FONT_DRAW_NORENDERMODE );
}
y += charHeight;
}
}
static int V_FadeAlpha( screenfade_t *sf )
{
int alpha;
if( cl.time > sf->fadeReset && cl.time > sf->fadeEnd )
{
if( !FBitSet( sf->fadeFlags, FFADE_STAYOUT ))
return 0;
}
if( FBitSet( sf->fadeFlags, FFADE_STAYOUT ))
{
alpha = sf->fadealpha;
if( FBitSet( sf->fadeFlags, FFADE_OUT ) && sf->fadeTotalEnd > cl.time )
{
alpha += sf->fadeSpeed * ( sf->fadeTotalEnd - cl.time );
}
else
{
sf->fadeEnd = cl.time + 0.1;
}
}
else
{
alpha = sf->fadeSpeed * ( sf->fadeEnd - cl.time );
if( FBitSet( sf->fadeFlags, FFADE_OUT ))
{
alpha += sf->fadealpha;
}
}
alpha = bound( 0, alpha, sf->fadealpha );
return alpha;
}
/*
=============
CL_DrawScreenFade
@ -607,41 +542,29 @@ can be modulated
void CL_DrawScreenFade( void )
{
screenfade_t *sf = &clgame.fade;
int iFadeAlpha, testFlags;
int alpha;
// keep pushing reset time out indefinitely
if( sf->fadeFlags & FFADE_STAYOUT )
sf->fadeReset = cl.time + 0.1f;
alpha = V_FadeAlpha( sf );
if( sf->fadeReset == 0.0f && sf->fadeEnd == 0.0f )
return; // inactive
// all done?
if(( cl.time > sf->fadeReset ) && ( cl.time > sf->fadeEnd ))
{
memset( &clgame.fade, 0, sizeof( clgame.fade ));
if( !alpha )
return;
}
testFlags = (sf->fadeFlags & ~FFADE_MODULATE);
// fading...
if( testFlags == FFADE_STAYOUT )
if( FBitSet( sf->fadeFlags, FFADE_MODULATE ))
{
iFadeAlpha = sf->fadealpha;
ref.dllFuncs.GL_SetRenderMode( kRenderScreenFadeModulate );
ref.dllFuncs.Color4ub(
(uint16_t)( sf->fader * alpha + ( 255 - alpha ) * 255 ) >> 8,
(uint16_t)( sf->fadeg * alpha + ( 255 - alpha ) * 255 ) >> 8,
(uint16_t)( sf->fadeb * alpha + ( 255 - alpha ) * 255 ) >> 8,
255 );
}
else
{
iFadeAlpha = sf->fadeSpeed * ( sf->fadeEnd - cl.time );
if( sf->fadeFlags & FFADE_OUT ) iFadeAlpha += sf->fadealpha;
iFadeAlpha = bound( 0, iFadeAlpha, sf->fadealpha );
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
ref.dllFuncs.Color4ub( sf->fader, sf->fadeg, sf->fadeb, alpha );
}
ref.dllFuncs.Color4ub( sf->fader, sf->fadeg, sf->fadeb, iFadeAlpha );
if( sf->fadeFlags & FFADE_MODULATE )
ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd );
else ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
ref.dllFuncs.R_DrawStretchPic( 0, 0, refState.width, refState.height, 0, 0, 1, 1,
R_GetBuiltinTexture( REF_WHITE_TEXTURE ));
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
@ -853,6 +776,92 @@ const char *CL_SoundFromIndex( int index )
return sfx->name;
}
/*
================
CL_EnableScissor
enable scissor test
================
*/
void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int height )
{
scissor->x = x;
scissor->y = y;
scissor->width = width;
scissor->height = height;
scissor->test = true;
}
/*
================
CL_DisableScissor
disable scissor test
================
*/
void CL_DisableScissor( scissor_state_t *scissor )
{
scissor->test = false;
}
/*
================
CL_Scissor
perform common scissor test
================
*/
qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 )
{
float dudx, dvdy;
if( !scissor->test )
return true;
// clip sub rect to sprite
if( *width == 0 || *height == 0 )
return false;
if( *x + *width <= scissor->x )
return false;
if( *x >= scissor->x + scissor->width )
return false;
if( *y + *height <= scissor->y )
return false;
if( *y >= scissor->y + scissor->height )
return false;
dudx = (*u1 - *u0) / *width;
dvdy = (*v1 - *v0) / *height;
if( *x < scissor->x )
{
*u0 += (scissor->x - *x) * dudx;
*width -= scissor->x - *x;
*x = scissor->x;
}
if( *x + *width > scissor->x + scissor->width )
{
*u1 -= (*x + *width - (scissor->x + scissor->width)) * dudx;
*width = scissor->x + scissor->width - *x;
}
if( *y < scissor->y )
{
*v0 += (scissor->y - *y) * dvdy;
*height -= scissor->y - *y;
*y = scissor->y;
}
if( *y + *height > scissor->y + scissor->height )
{
*v1 -= (*y + *height - (scissor->y + scissor->height)) * dvdy;
*height = scissor->y + scissor->height - *y;
}
return true;
}
/*
=========
SPR_EnableScissor
@ -867,11 +876,7 @@ static void GAME_EXPORT SPR_EnableScissor( int x, int y, int width, int height )
width = bound( 0, width, clgame.scrInfo.iWidth - x );
height = bound( 0, height, clgame.scrInfo.iHeight - y );
clgame.ds.scissor_x = x;
clgame.ds.scissor_width = width;
clgame.ds.scissor_y = y;
clgame.ds.scissor_height = height;
clgame.ds.scissor_test = true;
CL_EnableScissor( &clgame.ds.scissor, x, y, width, height );
}
/*
@ -882,11 +887,7 @@ SPR_DisableScissor
*/
static void GAME_EXPORT SPR_DisableScissor( void )
{
clgame.ds.scissor_x = 0;
clgame.ds.scissor_width = 0;
clgame.ds.scissor_y = 0;
clgame.ds.scissor_height = 0;
clgame.ds.scissor_test = false;
CL_DisableScissor( &clgame.ds.scissor );
}
/*
@ -956,28 +957,21 @@ CL_DrawLoading
draw loading progress bar
=============
*/
static void CL_DrawLoadingOrPaused( qboolean paused, float percent )
static void CL_DrawLoadingOrPaused( int tex )
{
int x, y, width, height, right;
float x, y, width, height;
int iWidth, iHeight;
R_GetTextureParms( &width, &height, paused ? cls.pauseIcon : cls.loadingBar );
x = ( clgame.scrInfo.iWidth - width ) >> 1;
y = ( clgame.scrInfo.iHeight - height) >> 1;
R_GetTextureParms( &iWidth, &iHeight, tex );
x = ( clgame.scrInfo.iWidth - iWidth ) / 2.0f;
y = ( clgame.scrInfo.iHeight - iHeight ) / 2.0f;
width = iWidth;
height = iHeight;
SPR_AdjustSizei( &x, &y, &width, &height );
if( !paused )
{
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.loadingBar );
}
else
{
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.pauseIcon );
}
SPR_AdjustSize( &x, &y, &width, &height );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, tex );
}
void CL_DrawHUD( int state )
@ -1003,15 +997,15 @@ void CL_DrawHUD( int state )
CL_DrawCrosshair ();
CL_DrawCenterPrint ();
clgame.dllFuncs.pfnRedraw( cl.time, cl.intermission );
CL_DrawLoadingOrPaused( true, 0.0f );
CL_DrawLoadingOrPaused( cls.pauseIcon );
break;
case CL_LOADING:
CL_DrawLoadingOrPaused( false, scr_loading->value );
CL_DrawLoadingOrPaused( cls.loadingBar );
break;
case CL_CHANGELEVEL:
if( cls.draw_changelevel )
{
CL_DrawLoadingOrPaused( false, 100.0f );
CL_DrawLoadingOrPaused( cls.loadingBar );
cls.draw_changelevel = false;
}
break;
@ -1177,6 +1171,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite,
// it's hud sprite, make difference names to prevent free shared textures
if( type == SPR_CLIENT || type == SPR_HUDSPRITE )
SetBits( m_pSprite->flags, MODEL_CLIENT );
m_pSprite->numtexinfo = texFlags; // store texFlags into numtexinfo
if( !FS_FileExists( szSpriteName, false ) )
@ -1624,6 +1619,14 @@ int GAME_EXPORT CL_GetScreenInfo( SCREENINFO *pscrinfo )
{
float scale_factor = hud_scale->value;
if( FBitSet( hud_fontscale->flags, FCVAR_CHANGED ))
{
CL_FreeFont( &cls.creditsFont );
SCR_LoadCreditsFont();
ClearBits( hud_fontscale->flags, FCVAR_CHANGED );
}
// setup screen info
clgame.scrInfo.iSize = sizeof( clgame.scrInfo );
clgame.scrInfo.iFlags = SCRINFO_SCREENFLASH;
@ -1742,7 +1745,8 @@ static int GAME_EXPORT pfnClientCmd( const char *szCmdString )
else
{
// will exec later
Q_strncat( host.deferred_cmd, va( "%s\n", szCmdString ), sizeof( host.deferred_cmd ));
Q_strncat( host.deferred_cmd, szCmdString, sizeof( host.deferred_cmd ));
Q_strncat( host.deferred_cmd, "\n", sizeof( host.deferred_cmd ));
}
return 1;
@ -1867,24 +1871,13 @@ returns drawed chachter width (in real screen pixels)
*/
static int GAME_EXPORT pfnDrawCharacter( int x, int y, int number, int r, int g, int b )
{
if( !cls.creditsFont.valid )
return 0;
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
if( hud_utf8->value )
number = Con_UtfProcessChar( number );
flags |= FONT_DRAW_UTF8;
number &= 255;
if( number < 32 ) return 0;
if( y < -clgame.scrInfo.iCharHeight )
return 0;
clgame.ds.adjust_size = true;
pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 );
pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] );
clgame.ds.adjust_size = false;
return clgame.scrInfo.charWidths[number];
return CL_DrawCharacter( x, y, number, color, &cls.creditsFont, flags );
}
/*
@ -1896,20 +1889,12 @@ drawing string like a console string
*/
int GAME_EXPORT pfnDrawConsoleString( int x, int y, char *string )
{
int drawLen;
cl_font_t *font = Con_GetFont( con_fontsize->value );
rgba_t color;
Vector4Copy( clgame.ds.textColor, color );
Vector4Set( clgame.ds.textColor, 255, 255, 255, 255 );
if( !COM_CheckString( string ))
return 0; // silent ignore
Con_SetFont( con_fontsize->value );
clgame.ds.adjust_size = true;
drawLen = Con_DrawString( x, y, string, clgame.ds.textColor );
MakeRGBA( clgame.ds.textColor, 255, 255, 255, 255 );
clgame.ds.adjust_size = false;
Con_RestoreFont();
return (x + drawLen); // exclude color prexfixes
return x + CL_DrawString( x, y, string, color, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD );
}
/*
@ -1937,9 +1922,10 @@ compute string length in screen pixels
*/
void GAME_EXPORT pfnDrawConsoleStringLen( const char *pText, int *length, int *height )
{
Con_SetFont( con_fontsize->value );
Con_DrawStringLen( pText, length, height );
Con_RestoreFont();
cl_font_t *font = Con_GetFont( con_fontsize->value );
if( height ) *height = font->charHeight;
CL_DrawStringLen( font, pText, length, NULL, FONT_DRAW_UTF8 | FONT_DRAW_HUD );
}
/*
@ -1951,7 +1937,14 @@ prints directly into console (can skip notify)
*/
static void GAME_EXPORT pfnConsolePrint( const char *string )
{
Con_Printf( "%s", string );
if( !COM_CheckString( string ))
return;
// WON GoldSrc behavior
if( string[0] != 1 )
Con_Printf( "%s", string );
else
Con_NPrintf( 0, "%s", string + 1 );
}
/*
@ -2199,45 +2192,14 @@ pfnPointContents
=============
*/
static int GAME_EXPORT pfnPointContents( const float *p, int *truecontents )
int GAME_EXPORT PM_CL_PointContents( const float *p, int *truecontents )
{
int cont, truecont;
truecont = cont = PM_PointContents( clgame.pmove, p );
if( truecontents ) *truecontents = truecont;
if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN )
cont = CONTENTS_WATER;
return cont;
return PM_PointContentsPmove( clgame.pmove, p, truecontents );
}
/*
=============
pfnTraceLine
=============
*/
static pmtrace_t *pfnTraceLine( float *start, float *end, int flags, int usehull, int ignore_pe )
pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe )
{
static pmtrace_t tr;
int old_usehull;
old_usehull = clgame.pmove->usehull;
clgame.pmove->usehull = usehull;
switch( flags )
{
case PM_TRACELINE_PHYSENTSONLY:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL );
break;
case PM_TRACELINE_ANYVISIBLE:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, ignore_pe, NULL );
break;
}
clgame.pmove->usehull = old_usehull;
return &tr;
return PM_TraceLine( clgame.pmove, start, end, flags, usehull, ignore_pe );
}
static void GAME_EXPORT pfnPlaySoundByNameAtLocation( char *szSound, float volume, float *origin )
@ -2484,7 +2446,6 @@ pfnSetTraceHull
void GAME_EXPORT CL_SetTraceHull( int hull )
{
clgame.pmove->usehull = bound( 0, hull, 3 );
}
/*
@ -2513,19 +2474,13 @@ void GAME_EXPORT CL_PlayerTraceExt( float *start, float *end, int traceFlags, in
/*
=============
pfnTraceTexture
CL_TraceTexture
=============
*/
static const char *pfnTraceTexture( int ground, float *vstart, float *vend )
const char * GAME_EXPORT PM_CL_TraceTexture( int ground, float *vstart, float *vend )
{
physent_t *pe;
if( ground < 0 || ground >= clgame.pmove->numphysent )
return NULL; // bad ground
pe = &clgame.pmove->physents[ground];
return PM_TraceTexture( pe, vstart, vend );
return PM_TraceTexture( clgame.pmove, ground, vstart, vend );
}
/*
@ -2536,13 +2491,7 @@ pfnTraceSurface
*/
struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend )
{
physent_t *pe;
if( ground < 0 || ground >= clgame.pmove->numphysent )
return NULL; // bad ground
pe = &clgame.pmove->physents[ground];
return PM_TraceSurface( pe, vstart, vend );
return PM_TraceSurfacePmove( clgame.pmove, ground, vstart, vend );
}
/*
@ -2633,7 +2582,10 @@ static const char *pfnGetLevelName( void )
{
static char mapname[64];
if( cls.state >= ca_connected )
// a1ba: don't return maps/.bsp if no map is loaded yet
// in GoldSrc this is handled by cl.levelname field but we don't have it
// so emulate this behavior here
if( cls.state >= ca_connected && COM_CheckStringEmpty( clgame.mapname ))
Q_snprintf( mapname, sizeof( mapname ), "maps/%s.bsp", clgame.mapname );
else mapname[0] = '\0'; // not in game
@ -2899,23 +2851,7 @@ pfnVGUI2DrawCharacter
*/
static int GAME_EXPORT pfnVGUI2DrawCharacter( int x, int y, int number, unsigned int font )
{
if( !cls.creditsFont.valid )
return 0;
number &= 255;
number = Con_UtfProcessChar( number );
if( number < 32 ) return 0;
if( y < -clgame.scrInfo.iCharHeight )
return 0;
clgame.ds.adjust_size = true;
gameui.ds.gl_texturenum = cls.creditsFont.hFontTexture;
pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] );
clgame.ds.adjust_size = false;
return clgame.scrInfo.charWidths[number];
return pfnDrawCharacter( x, y, number, 255, 255, 255 );
}
/*
@ -2926,9 +2862,6 @@ pfnVGUI2DrawCharacterAdditive
*/
static int GAME_EXPORT pfnVGUI2DrawCharacterAdditive( int x, int y, int ch, int r, int g, int b, unsigned int font )
{
if( !hud_utf8->value )
ch = Con_UtfProcessChar( ch );
return pfnDrawCharacter( x, y, ch, r, g, b );
}
@ -2940,16 +2873,13 @@ pfnDrawString
*/
static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b )
{
int iWidth = 0;
Con_UtfProcessChar(0);
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF;
// draw the string until we hit the null character or a newline character
for ( ; *str != 0 && *str != '\n'; str++ )
{
iWidth += pfnVGUI2DrawCharacterAdditive( x + iWidth, y, (unsigned char)*str, r, g, b, 0 );
}
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
return iWidth;
return CL_DrawString( x, y, str, color, &cls.creditsFont, flags );
}
/*
@ -2960,11 +2890,18 @@ pfnDrawStringReverse
*/
static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b )
{
// find the end of the string
char *szIt;
for( szIt = (char*)str; *szIt != 0; szIt++ )
x -= clgame.scrInfo.charWidths[ (unsigned char) *szIt ];
return pfnDrawString( x, y, str, r, g, b );
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF;
int width;
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
CL_DrawStringLen( &cls.creditsFont, str, &width, NULL, flags );
x -= width;
return CL_DrawString( x, y, str, color, &cls.creditsFont, flags );
}
/*
@ -3368,7 +3305,7 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double
return;
}
if( remote_address->type >= NA_IPX )
if( remote_address->type != NA_IPX && remote_address->type != NA_BROADCAST_IPX )
return; // IPX no longer support
// find a free request
@ -3409,9 +3346,12 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double
if( request == NETAPI_REQUEST_SERVERLIST )
{
char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\";
char fullquery[512];
size_t len;
// make sure what port is specified
len = CL_BuildMasterServerScanRequest( fullquery, sizeof( fullquery ), false );
// make sure that port is specified
if( !nr->resp.remote_address.port )
nr->resp.remote_address.port = MSG_BigShort( PORT_MASTER );
@ -3439,7 +3379,7 @@ void GAME_EXPORT NetAPI_CancelRequest( int context )
{
net_request_t *nr;
int i;
;
// find a specified request
for( i = 0; i < MAX_REQUESTS; i++ )
{
@ -3717,7 +3657,7 @@ static event_api_t gEventApi =
CL_WeaponAnim,
pfnPrecacheEvent,
CL_PlaybackEvent,
pfnTraceTexture,
PM_CL_TraceTexture,
pfnStopAllSounds,
pfnKillEvents,
CL_PlayerTraceExt, // Xash3D added
@ -3822,9 +3762,9 @@ static cl_enginefunc_t gEngfuncs =
pfnGetClientTime,
pfnCalcShake,
pfnApplyShake,
pfnPointContents,
PM_CL_PointContents,
CL_WaterEntity,
pfnTraceLine,
PM_CL_TraceLine,
CL_LoadModel,
CL_AddEntity,
CL_GetSpritePointer,
@ -3917,6 +3857,9 @@ void CL_UnloadProgs( void )
if( Q_stricmp( GI->gamefolder, "hlfx" ) || GI->version != 0.5f )
clgame.dllFuncs.pfnShutdown();
if( GI->internal_vgui_support )
VGui_Shutdown();
Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY );
Cvar_FullSet( "host_clientloaded", "0", FCVAR_READ_ONLY );
@ -3945,10 +3888,6 @@ qboolean CL_LoadProgs( const char *name )
clgame.mempool = Mem_AllocPool( "Client Edicts Zone" );
clgame.entities = NULL;
// NOTE: important stuff!
// vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS
// during LoadLibrary
VGui_Startup( name, gameui.globals->scrWidth, gameui.globals->scrHeight );
// a1ba: we need to check if client.dll has direct dependency on SDL2
// and if so, disable relative mouse mode
@ -3967,8 +3906,29 @@ qboolean CL_LoadProgs( const char *name )
clgame.client_dll_uses_sdl = true;
#endif
// NOTE: important stuff!
// vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS
// during LoadLibrary
if( !GI->internal_vgui_support && VGui_LoadProgs( NULL ))
{
VGui_Startup( refState.width, refState.height );
}
else
{
// we failed to load vgui_support, but let's probe client.dll for support anyway
GI->internal_vgui_support = true;
}
clgame.hInstance = COM_LoadLibrary( name, false, false );
if( !clgame.hInstance ) return false;
if( !clgame.hInstance )
return false;
// delayed vgui initialization for internal support
if( GI->internal_vgui_support && VGui_LoadProgs( clgame.hInstance ))
{
VGui_Startup( refState.width, refState.height );
}
// clear exports
for( func = cdll_exports; func && func->name; func++ )

View File

@ -422,54 +422,6 @@ static void UI_ConvertGameInfo( GAMEINFO *out, gameinfo_t *in )
out->flags |= GFL_RENDER_PICBUTTON_TEXT;
}
static qboolean PIC_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 )
{
float dudx, dvdy;
// clip sub rect to sprite
if(( width == 0 ) || ( height == 0 ))
return false;
if( *x + *width <= gameui.ds.scissor_x )
return false;
if( *x >= gameui.ds.scissor_x + gameui.ds.scissor_width )
return false;
if( *y + *height <= gameui.ds.scissor_y )
return false;
if( *y >= gameui.ds.scissor_y + gameui.ds.scissor_height )
return false;
dudx = (*u1 - *u0) / *width;
dvdy = (*v1 - *v0) / *height;
if( *x < gameui.ds.scissor_x )
{
*u0 += (gameui.ds.scissor_x - *x) * dudx;
*width -= gameui.ds.scissor_x - *x;
*x = gameui.ds.scissor_x;
}
if( *x + *width > gameui.ds.scissor_x + gameui.ds.scissor_width )
{
*u1 -= (*x + *width - (gameui.ds.scissor_x + gameui.ds.scissor_width)) * dudx;
*width = gameui.ds.scissor_x + gameui.ds.scissor_width - *x;
}
if( *y < gameui.ds.scissor_y )
{
*v0 += (gameui.ds.scissor_y - *y) * dvdy;
*height -= gameui.ds.scissor_y - *y;
*y = gameui.ds.scissor_y;
}
if( *y + *height > gameui.ds.scissor_y + gameui.ds.scissor_height )
{
*v1 -= (*y + *height - (gameui.ds.scissor_y + gameui.ds.scissor_height)) * dvdy;
*height = gameui.ds.scissor_y + gameui.ds.scissor_height - *y;
}
return true;
}
/*
====================
PIC_DrawGeneric
@ -488,10 +440,10 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const
if( prc )
{
// calc user-defined rectangle
s1 = (float)prc->left / (float)w;
t1 = (float)prc->top / (float)h;
s2 = (float)prc->right / (float)w;
t2 = (float)prc->bottom / (float)h;
s1 = prc->left / (float)w;
t1 = prc->top / (float)h;
s2 = prc->right / (float)w;
t2 = prc->bottom / (float)h;
if( width == -1 && height == -1 )
{
@ -512,10 +464,9 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const
}
// pass scissor test if supposed
if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
if( !CL_Scissor( &gameui.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
return;
PicAdjustSize( &x, &y, &width, &height );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, gameui.ds.gl_texturenum );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
}
@ -660,11 +611,7 @@ static void GAME_EXPORT pfnPIC_EnableScissor( int x, int y, int width, int heigh
width = bound( 0, width, gameui.globals->scrWidth - x );
height = bound( 0, height, gameui.globals->scrHeight - y );
gameui.ds.scissor_x = x;
gameui.ds.scissor_width = width;
gameui.ds.scissor_y = y;
gameui.ds.scissor_height = height;
gameui.ds.scissor_test = true;
CL_EnableScissor( &gameui.ds.scissor, x, y, width, height );
}
/*
@ -675,11 +622,7 @@ pfnPIC_DisableScissor
*/
static void GAME_EXPORT pfnPIC_DisableScissor( void )
{
gameui.ds.scissor_x = 0;
gameui.ds.scissor_width = 0;
gameui.ds.scissor_y = 0;
gameui.ds.scissor_height = 0;
gameui.ds.scissor_test = false;
CL_DisableScissor( &gameui.ds.scissor );
}
/*
@ -767,7 +710,7 @@ static void GAME_EXPORT pfnDrawCharacter( int ix, int iy, int iwidth, int iheigh
t2 = t1 + size;
// pass scissor test if supposed
if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
if( !CL_Scissor( &gameui.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 ))
return;
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
@ -1264,6 +1207,7 @@ static ui_extendedfuncs_t gExtendedfuncs =
Sys_DoubleTime,
pfnParseFileSafe,
NET_AdrToString,
NET_CompareAdrSort,
R_GetRenderDevice
};

View File

@ -22,12 +22,13 @@ GNU General Public License for more details.
#include "vgui_draw.h"
#include "library.h"
#include "vid_common.h"
#include "pm_local.h"
#define MAX_TOTAL_CMDS 32
#define MAX_CMD_BUFFER 8000
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
#define CL_CONNECTION_RETRIES 10
#define CL_TEST_RETRIES_NORESPONCE 2
#define CL_TEST_RETRIES_NORESPONCE 3
#define CL_TEST_RETRIES 5
CVAR_DEFINE_AUTO( mp_decals, "300", FCVAR_ARCHIVE, "decals limit in multiplayer" );
@ -38,8 +39,8 @@ CVAR_DEFINE_AUTO( cl_allow_upload, "1", FCVAR_ARCHIVE, "allow to uploading resou
CVAR_DEFINE_AUTO( cl_download_ingame, "1", FCVAR_ARCHIVE, "allow to downloading resources while client is active" );
CVAR_DEFINE_AUTO( cl_logofile, "lambda", FCVAR_ARCHIVE, "player logo name" );
CVAR_DEFINE_AUTO( cl_logocolor, "orange", FCVAR_ARCHIVE, "player logo color" );
CVAR_DEFINE_AUTO( cl_logoext, "bmp", FCVAR_ARCHIVE, "temporary cvar to tell engine which logo must be packed" );
CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" );
convar_t *rcon_client_password;
convar_t *rcon_address;
convar_t *cl_timeout;
convar_t *cl_nopred;
@ -56,6 +57,7 @@ convar_t *cl_nosmooth;
convar_t *cl_smoothtime;
convar_t *cl_clockreset;
convar_t *cl_fixtimerate;
convar_t *hud_fontscale;
convar_t *hud_scale;
convar_t *cl_solid_players;
convar_t *cl_draw_beams;
@ -241,11 +243,6 @@ void CL_SignonReply( void )
}
}
float CL_LerpInterval( void )
{
return Q_max( cl_interp->value, 1.f / cl_updaterate->value );
}
/*
===============
CL_LerpPoint
@ -256,55 +253,26 @@ should be put at.
*/
static float CL_LerpPoint( void )
{
float frac = 1.0f;
float server_frametime = cl_serverframetime();
double f = cl_serverframetime();
double frac;
if( server_frametime == 0.0f || cls.timedemo )
if( f == 0.0 || cls.timedemo )
{
double fgap = cl_clientframetime();
cl.time = cl.mtime[0];
// maybe don't need for Xash demos
if( cls.demoplayback )
cl.oldtime = cl.mtime[0] - fgap;
return 1.0f;
}
if( server_frametime > 0.1f )
{
// dropped packet, or start of demo
cl.mtime[1] = cl.mtime[0] - 0.1f;
server_frametime = 0.1f;
}
#if 0
/*
g-cont: this code more suitable for singleplayer
NOTE in multiplayer causes significant framerate stutter/jitter and
occuring frames with zero time delta and even with negative time delta.
game becomes more twitchy and as if without interpolation.
*/
frac = (cl.time - cl.mtime[1]) / f;
if( frac < 0.0f )
{
if( frac < -0.01f )
cl.time = cl.mtime[1];
frac = 0.0f;
}
else if( frac > 1.0f )
{
if( frac > 1.01f )
cl.time = cl.mtime[0];
frac = 1.0f;
}
#else
// for multiplayer
if( cl_interp->value > 0.001f )
{
// manual lerp value (goldsrc mode)
float td = Q_max( 0.f, cl.time - cl.mtime[0] );
frac = td / CL_LerpInterval();
}
else if( server_frametime > 0.001f )
{
// automatic lerp (classic mode)
frac = ( cl.time - cl.mtime[1] ) / server_frametime;
}
#endif
if( cl_interp->value <= 0.001 )
return 1.0f;
frac = ( cl.time - cl.mtime[0] ) / cl_interp->value;
return frac;
}
@ -346,7 +314,7 @@ Validate interpolation cvars, calc interpolation window
void CL_ComputeClientInterpolationAmount( usercmd_t *cmd )
{
const float epsilon = 0.001f; // to avoid float invalid comparision
float min_interp = MIN_EX_INTERP;
float min_interp;
float max_interp = MAX_EX_INTERP;
float interpolation_time;
@ -366,8 +334,8 @@ void CL_ComputeClientInterpolationAmount( usercmd_t *cmd )
max_interp = 0.2f;
min_interp = 1.0f / cl_updaterate->value;
interpolation_time = CL_LerpInterval( );
interpolation_time = cl_interp->value * 1000.0;
if( (cl_interp->value + epsilon) < min_interp )
{
Con_Printf( "ex_interp forced up to %.1f msec\n", min_interp * 1000.f );
@ -622,7 +590,7 @@ CL_CreateCmd
*/
void CL_CreateCmd( void )
{
usercmd_t cmd;
usercmd_t nullcmd, *cmd;
runcmd_t *pcmd;
vec3_t angles;
qboolean active;
@ -635,7 +603,6 @@ void CL_CreateCmd( void )
// store viewangles in case it's will be freeze
VectorCopy( cl.viewangles, angles );
ms = bound( 1, host.frametime * 1000, 255 );
memset( &cmd, 0, sizeof( cmd ));
input_override = 0;
CL_SetSolidEntities();
@ -654,12 +621,18 @@ void CL_CreateCmd( void )
pcmd->receivedtime = -1.0;
pcmd->heldback = false;
pcmd->sendsize = 0;
cmd = &pcmd->cmd;
}
else
{
memset( &nullcmd, 0, sizeof( nullcmd ));
cmd = &nullcmd;
}
active = (( cls.signon == SIGNONS ) && !cl.paused && !cls.demoplayback );
Platform_PreCreateMove();
clgame.dllFuncs.CL_CreateMove( host.frametime, &pcmd->cmd, active );
IN_EngineAppendMove( host.frametime, &pcmd->cmd, active );
clgame.dllFuncs.CL_CreateMove( host.frametime, cmd, active );
IN_EngineAppendMove( host.frametime, cmd, active );
CL_PopPMStates();
@ -736,15 +709,22 @@ void CL_WritePacket( void )
CL_ComputePacketLoss ();
memset( data, 0, sizeof( data ));
MSG_Init( &buf, "ClientData", data, sizeof( data ));
// Determine number of backup commands to send along
numbackup = bound( 0, cl_cmdbackup->value, MAX_BACKUP_COMMANDS );
numbackup = bound( 0, cl_cmdbackup->value, cls.legacymode ? MAX_LEGACY_BACKUP_CMDS : MAX_BACKUP_COMMANDS );
if( cls.state == ca_connected ) numbackup = 0;
// clamp cmdrate
if( cl_cmdrate->value < 0.0f ) Cvar_SetValue( "cl_cmdrate", 0.0f );
else if( cl_cmdrate->value > 100.0f ) Cvar_SetValue( "cl_cmdrate", 100.0f );
if( cl_cmdrate->value < 10.0f )
{
Cvar_SetValue( "cl_cmdrate", 10.0f );
}
else if( cl_cmdrate->value > 100.0f )
{
Cvar_SetValue( "cl_cmdrate", 100.0f );
}
// Check to see if we can actually send this command
@ -774,6 +754,7 @@ void CL_WritePacket( void )
if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME )
{
Con_NPrintf( 1, "^3Warning:^1 Connection Problem^7\n" );
Con_NPrintf( 2, "^1Auto-disconnect in %.1f seconds^7", cl_timeout->value - ( host.realtime - cls.netchan.last_received ));
cl.validsequence = 0;
}
}
@ -813,12 +794,13 @@ void CL_WritePacket( void )
newcmds = ( cls.netchan.outgoing_sequence - cls.lastoutgoingcommand );
// put an upper/lower bound on this
newcmds = bound( 0, newcmds, cls.legacymode?MAX_LEGACY_TOTAL_CMDS:MAX_TOTAL_CMDS );
newcmds = bound( 0, newcmds, cls.legacymode ? MAX_LEGACY_TOTAL_CMDS: MAX_TOTAL_CMDS );
if( cls.state == ca_connected ) newcmds = 0;
MSG_WriteByte( &buf, newcmds );
numcmds = newcmds + numbackup;
from = -1;
for( i = numcmds - 1; i >= 0; i-- )
@ -864,6 +846,9 @@ void CL_WritePacket( void )
cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].sendsize = MSG_GetNumBytesWritten( &buf );
cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].heldback = false;
// send voice data to the server
CL_AddVoiceToDatagram();
// composite the rest of the datagram..
if( MSG_GetNumBitsWritten( &cls.datagram ) <= MSG_GetNumBitsLeft( &buf ))
MSG_WriteBits( &buf, MSG_GetData( &cls.datagram ), MSG_GetNumBitsWritten( &cls.datagram ));
@ -1041,6 +1026,9 @@ void CL_SendConnectPacket( void )
input_devices = IN_CollectInputDevices();
IN_LockInputDevices( true );
Cvar_SetCheatState();
Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER );
Info_SetValueForKey( protinfo, "d", va( "%d", input_devices ), sizeof( protinfo ) );
Info_SetValueForKey( protinfo, "v", XASH_VERSION, sizeof( protinfo ) );
Info_SetValueForKey( protinfo, "b", va( "%d", Q_buildnum() ), sizeof( protinfo ) );
@ -1095,8 +1083,9 @@ Resend a connect message if the last one has timed out
*/
void CL_CheckForResend( void )
{
netadr_t adr;
netadr_t adr;
int res;
qboolean bandwidthTest;
if( cls.internetservers_wait )
CL_InternetServers_f();
@ -1161,14 +1150,18 @@ void CL_CheckForResend( void )
return;
}
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value;
cls.serveradr = adr;
cls.max_fragment_size = Q_max( FRAGMENT_MAX_SIZE, cls.max_fragment_size >> Q_min( 1, cls.connect_retry ));
cls.max_fragment_size = Q_min( FRAGMENT_MAX_SIZE, cls.max_fragment_size / (cls.connect_retry + 1));
cls.connect_time = host.realtime; // for retransmit requests
cls.connect_retry++;
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
if( bandwidthTest )
Con_Printf( "Connecting to %s... [retry #%i, max fragment size %i]\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
else
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
if( !cls.legacymode && cl_test_bandwidth.value )
if( bandwidthTest )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size );
else
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
@ -1193,7 +1186,7 @@ resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qbo
void CL_CreateResourceList( void )
{
char szFileName[MAX_OSPATH];
char szFileName[MAX_OSPATH];
byte rgucMD5_hash[16];
resource_t *pNewResource;
int nSize;
@ -1201,30 +1194,36 @@ void CL_CreateResourceList( void )
HPAK_FlushHostQueue();
cl.num_resources = 0;
Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.bmp" );
memset( rgucMD5_hash, 0, sizeof( rgucMD5_hash ));
// sanitize cvar value
if( Q_strcmp( cl_logoext.string, "bmp" ) &&
Q_strcmp( cl_logoext.string, "png" ))
Cvar_DirectSet( &cl_logoext, "bmp" );
Q_snprintf( szFileName, sizeof( szFileName ),
"logos/remapped.%s", cl_logoext.string );
fp = FS_Open( szFileName, "rb", true );
if( fp )
if( !fp )
return;
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
nSize = FS_FileLength( fp );
if( nSize != 0 )
{
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
nSize = FS_FileLength( fp );
pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 );
if( nSize != 0 )
if( pNewResource )
{
pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 );
if( pNewResource )
{
SetBits( pNewResource->ucFlags, RES_CUSTOM );
memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 );
HPAK_AddLump( false, CUSTOM_RES_PATH, pNewResource, NULL, fp );
}
SetBits( pNewResource->ucFlags, RES_CUSTOM );
memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 );
HPAK_AddLump( false, CUSTOM_RES_PATH, pNewResource, NULL, fp );
}
FS_Close( fp );
}
FS_Close( fp );
}
/*
@ -1287,7 +1286,7 @@ void CL_Rcon_f( void )
string command;
int i;
if( !COM_CheckString( rcon_client_password->string ))
if( !COM_CheckString( rcon_password.string ))
{
Con_Printf( "You must set 'rcon_password' before issuing an rcon command.\n" );
return;
@ -1302,7 +1301,7 @@ void CL_Rcon_f( void )
NET_Config( true, false ); // allow remote
Q_strcat( message, "rcon " );
Q_strcat( message, rcon_client_password->string );
Q_strcat( message, rcon_password.string );
Q_strcat( message, " " );
for( i = 1; i < Cmd_Argc(); i++ )
@ -1351,7 +1350,7 @@ void CL_ClearState( void )
CL_ClearEffects ();
CL_FreeEdicts ();
CL_ClearPhysEnts ();
PM_ClearPhysEnts( clgame.pmove );
NetAPI_CancelAllRequests();
// wipe the entire cl structure
@ -1359,6 +1358,7 @@ void CL_ClearState( void )
MSG_Clear( &cls.netchan.message );
memset( &clgame.fade, 0, sizeof( clgame.fade ));
memset( &clgame.shake, 0, sizeof( clgame.shake ));
clgame.mapname[0] = '\0';
Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY );
cl.maxclients = 1; // allow to drawing player in menu
cl.mtime[0] = cl.mtime[1] = 1.0f; // because level starts from 1.0f second
@ -1500,6 +1500,7 @@ void CL_Disconnect( void )
cls.connect_time = 0;
cls.changedemo = false;
cls.max_fragment_size = FRAGMENT_MAX_SIZE; // reset fragment size
Voice_Disconnect();
CL_Stop_f();
// send a disconnect message to the server
@ -1567,11 +1568,38 @@ void CL_LocalServers_f( void )
// send a broadcast packet
adr.type = NA_BROADCAST;
adr.port = MSG_BigShort( PORT_SERVER );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION );
adr.type = NA_MULTICAST_IP6;
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION );
}
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0"
/*
=================
CL_BuildMasterServerScanRequest
=================
*/
size_t CL_BuildMasterServerScanRequest( char *buf, size_t size, qboolean nat )
{
size_t remaining;
char *info;
if( unlikely( size < sizeof( MS_SCAN_REQUEST )))
return 0;
Q_strncpy( buf, MS_SCAN_REQUEST, size );
info = buf + sizeof( MS_SCAN_REQUEST ) - 1;
remaining = size - sizeof( MS_SCAN_REQUEST );
info[0] = 0;
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version
Info_SetValueForKey( info, "nat", nat ? "1" : "0", remaining );
return sizeof( MS_SCAN_REQUEST ) + Q_strlen( info );
}
/*
=================
@ -1580,18 +1608,17 @@ CL_InternetServers_f
*/
void CL_InternetServers_f( void )
{
char fullquery[512] = MS_SCAN_REQUEST;
char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1;
const size_t remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST );
char fullquery[512];
size_t len;
qboolean nat = cl_nat->value != 0.0f;
len = CL_BuildMasterServerScanRequest( fullquery, sizeof( fullquery ), nat );
Con_Printf( "Scanning for servers on the internet area...\n" );
NET_Config( true, true ); // allow remote
Con_Printf( "Scanning for servers on the internet area...\n" );
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version
Info_SetValueForKey( info, "nat", cl_nat->string, remaining );
cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery );
cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, len, fullquery );
cls.internetservers_pending = true;
if( !cls.internetservers_wait )
@ -1625,6 +1652,8 @@ void CL_Reconnect_f( void )
if( COM_CheckString( cls.servername ))
{
qboolean legacy = cls.legacymode;
if( cls.state >= ca_connected )
CL_Disconnect();
@ -1632,6 +1661,7 @@ void CL_Reconnect_f( void )
cls.demonum = cls.movienum = -1; // not in the demo loop now
cls.state = ca_connecting;
cls.signon = 0;
cls.legacymode = legacy; // don't change protocol
Con_Printf( "reconnecting...\n" );
}
@ -1713,16 +1743,23 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
static char infostring[MAX_INFO_STRING+8];
char *s = MSG_ReadString( msg );
int i;
const char *magic = ": wrong version\n";
size_t len = Q_strlen( s ), magiclen = Q_strlen( magic );
CL_FixupColorStringsForInfoString( s, infostring );
if( Q_strstr( infostring, "wrong version" ) )
if( len >= magiclen && !Q_strcmp( s + len - magiclen, magic ))
{
Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION );
Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring );
return;
}
if( !Info_IsValid( s ))
{
Con_Printf( "^1Server^7: %s, invalid infostring\n", NET_AdrToString( from ));
return;
}
CL_FixupColorStringsForInfoString( s, infostring );
if( !COM_CheckString( Info_ValueForKey( infostring, "gamedir" )))
{
Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring );
@ -1732,11 +1769,13 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
if( !COM_CheckString( Info_ValueForKey( infostring, "p" )))
{
Info_SetValueForKey( infostring, "legacy", "1", sizeof( infostring ) );
Con_Print("Legacy: ");
Con_Printf( "^3Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" ));
}
else
{
// more info about servers
Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" ));
}
// more info about servers
Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" ));
UI_AddServerToList( from, infostring );
}
@ -2095,6 +2134,12 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
}
else if( !Q_strcmp( c, "f" ))
{
if( !NET_IsMasterAdr( from ))
{
Con_Printf( S_WARN "unexpected server list packet from %s\n", NET_AdrToString( from ));
return;
}
// serverlist got from masterserver
while( MSG_GetNumBitsLeft( msg ) > 8 )
{
@ -2405,11 +2450,18 @@ void CL_ProcessFile( qboolean successfully_received, const char *filename )
{
if( filename[0] != '!' )
Con_Printf( "processing %s\n", filename );
if( !Q_strnicmp( filename, "downloaded/", 11 ))
{
// skip "downloaded/" part to avoid mismatch with needed resources list
filename += 11;
}
}
else if( !successfully_received )
{
Con_Printf( S_ERROR "server failed to transmit file '%s'\n", CL_CleanFileName( filename ));
}
if( cls.legacymode )
{
if( host.downloadcount > 0 )
@ -2806,8 +2858,11 @@ void CL_InitLocal( void )
Cvar_RegisterVariable( &cl_download_ingame );
Cvar_RegisterVariable( &cl_logofile );
Cvar_RegisterVariable( &cl_logocolor );
Cvar_RegisterVariable( &cl_logoext );
Cvar_RegisterVariable( &cl_test_bandwidth );
Voice_RegisterCvars();
// register our variables
cl_crosshair = Cvar_Get( "crosshair", "1", FCVAR_ARCHIVE, "show weapon chrosshair" );
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0, "disable delta-compression for server messages" );
@ -2818,7 +2873,6 @@ void CL_InitLocal( void )
cl_charset = Cvar_Get( "cl_charset", "utf-8", FCVAR_ARCHIVE, "1-byte charset to use (iconv style)" );
hud_utf8 = Cvar_Get( "hud_utf8", "0", FCVAR_ARCHIVE, "Use utf-8 encoding for hud text" );
rcon_client_password = Cvar_Get( "rcon_password", "", FCVAR_PRIVILEGED, "remote control client password" );
rcon_address = Cvar_Get( "rcon_address", "", FCVAR_PRIVILEGED, "remote control address" );
cl_trace_messages = Cvar_Get( "cl_trace_messages", "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable message names tracing (good for developers)");
@ -2842,9 +2896,9 @@ void CL_InitLocal( void )
cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", FCVAR_ARCHIVE, "disable smooth up stair climbing" );
cl_nointerp = Cvar_Get( "cl_nointerp", "0", FCVAR_CLIENTDLL, "disable interpolation of entities and players" );
cl_smoothtime = Cvar_Get( "cl_smoothtime", "0", FCVAR_ARCHIVE, "time to smooth up" );
cl_smoothtime = Cvar_Get( "cl_smoothtime", "0.1", FCVAR_ARCHIVE, "time to smooth up" );
cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "10", FCVAR_ARCHIVE, "how many additional history commands are sent" );
cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" );
cl_cmdrate = Cvar_Get( "cl_cmdrate", "60", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" );
cl_draw_particles = Cvar_Get( "r_drawparticles", "1", FCVAR_CHEAT, "render particles" );
cl_draw_tracers = Cvar_Get( "r_drawtracers", "1", FCVAR_CHEAT, "render tracers" );
cl_draw_beams = Cvar_Get( "r_drawbeams", "1", FCVAR_CHEAT, "render beams" );
@ -2853,6 +2907,7 @@ void CL_InitLocal( void )
cl_bmodelinterp = Cvar_Get( "cl_bmodelinterp", "1", FCVAR_ARCHIVE, "enable bmodel interpolation" );
cl_clockreset = Cvar_Get( "cl_clockreset", "0.1", FCVAR_ARCHIVE, "frametime delta maximum value before reset" );
cl_fixtimerate = Cvar_Get( "cl_fixtimerate", "7.5", FCVAR_ARCHIVE, "time in msec to client clock adjusting" );
hud_fontscale = Cvar_Get( "hud_fontscale", "1.0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud font texture" );
hud_scale = Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" );
Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" );
cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" );
@ -2874,6 +2929,12 @@ void CL_InitLocal( void )
Cmd_AddCommand ("god", NULL, "enable godmode" );
Cmd_AddCommand ("fov", NULL, "set client field of view" );
Cmd_AddRestrictedCommand ("ent_list", NULL, "list entities on server" );
Cmd_AddRestrictedCommand ("ent_fire", NULL, "fire entity command (be careful)" );
Cmd_AddRestrictedCommand ("ent_info", NULL, "dump entity information" );
Cmd_AddRestrictedCommand ("ent_create", NULL, "create entity with specified values (be careful)" );
Cmd_AddRestrictedCommand ("ent_getvars", NULL, "put parameters of specified entities to client's' ent_last_* cvars" );
// register our commands
Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" );
Cmd_AddCommand ("localservers", CL_LocalServers_f, "collect info about local servers" );
@ -2940,12 +3001,12 @@ void CL_AdjustClock( void )
if( fabs( cl.timedelta ) >= 0.001f )
{
double msec, adjust;
float sign;
double sign;
msec = ( cl.timedelta * 1000.0f );
sign = ( msec < 0 ) ? 1.0f : -1.0f;
msec = fabs( msec );
adjust = sign * ( cl_fixtimerate->value / 1000.0f );
msec = ( cl.timedelta * 1000.0 );
sign = ( msec < 0 ) ? 1.0 : -1.0;
msec = Q_min( cl_fixtimerate->value, fabs( msec ));
adjust = sign * ( msec / 1000.0 );
if( fabs( adjust ) < fabs( cl.timedelta ))
{
@ -3009,8 +3070,8 @@ void Host_ClientFrame( void )
// a new portion updates from server
CL_RedoPrediction ();
// TODO: implement
// Voice_Idle( host.frametime );
// update voice
Voice_Idle( host.frametime );
// emit visible entities
CL_EmitEntities ();
@ -3030,9 +3091,6 @@ void Host_ClientFrame( void )
// catch changes video settings
VID_CheckChanges();
// process VGUI
VGui_RunFrame ();
// update the screen
SCR_UpdateScreen ();
@ -3064,6 +3122,7 @@ void CL_Init( void )
VID_Init(); // init video
S_Init(); // init sound
Voice_Init( VOICE_DEFAULT_CODEC, 3 ); // init voice
// unreliable buffer. unsed for unreliable commands and voice stream
MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf ));
@ -3107,7 +3166,9 @@ void CL_Shutdown( void )
CL_UnloadProgs ();
cls.initialized = false;
VGui_Shutdown();
// for client-side VGUI support we use other order
if( !GI->internal_vgui_support )
VGui_Shutdown();
FS_Delete( "demoheader.tmp" ); // remove tmp file
SCR_FreeCinematic (); // release AVI's *after* client.dll because custom renderer may use them

View File

@ -25,6 +25,9 @@ mobile_engfuncs_t *gMobileEngfuncs;
convar_t *vibration_length;
convar_t *vibration_enable;
static cl_font_t g_scaled_font;
static float g_font_scale;
static void pfnVibrate( float life, char flags )
{
if( !vibration_enable->value )
@ -60,28 +63,28 @@ static void pfnEnableTextInput( int enable )
static int pfnDrawScaledCharacter( int x, int y, int number, int r, int g, int b, float scale )
{
int width = clgame.scrInfo.charWidths[number] * scale * hud_scale->value;
int height = clgame.scrInfo.iCharHeight * scale * hud_scale->value;
// this call is very ineffective and possibly broken!
rgba_t color = { r, g, b, 255 };
int flags = FONT_DRAW_HUD;
if( !cls.creditsFont.valid )
return 0;
if( hud_utf8->value )
SetBits( flags, FONT_DRAW_UTF8 );
x *= hud_scale->value;
y *= hud_scale->value;
if( fabs( g_font_scale - scale ) > 0.1f ||
g_scaled_font.hFontTexture != cls.creditsFont.hFontTexture )
{
int i;
number &= 255;
number = Con_UtfProcessChar( number );
g_scaled_font = cls.creditsFont;
g_scaled_font.scale *= scale;
g_scaled_font.charHeight *= scale;
for( i = 0; i < ARRAYSIZE( g_scaled_font.charWidths ); i++ )
g_scaled_font.charWidths[i] *= scale;
if( number < 32 )
return 0;
g_font_scale = scale;
}
if( y < -height )
return 0;
pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 );
pfnPIC_DrawAdditive( x, y, width, height, &cls.creditsFont.fontRc[number] );
return width;
return CL_DrawCharacter( x, y, number, color, &g_scaled_font, flags );
}
static void *pfnGetNativeObject( const char *obj )

View File

@ -281,7 +281,10 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w )
for( j = start; j < h; j++ )
{
NetGraph_DrawRect( &fill, netcolors[NETGRAPH_NET_COLORS + j + extrap_point] );
int color = NETGRAPH_NET_COLORS + j + extrap_point;
color = Q_min( color, ARRAYSIZE( netcolors ) - 1 );
NetGraph_DrawRect( &fill, netcolors[color] );
fill.top--;
}
}
@ -297,7 +300,10 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w )
for( j = 0; j < h; j++ )
{
NetGraph_DrawRect( &fill, netcolors[NETGRAPH_NET_COLORS + j + oldh] );
int color = NETGRAPH_NET_COLORS + j + oldh;
color = Q_min( color, ARRAYSIZE( netcolors ) - 1 );
NetGraph_DrawRect( &fill, netcolors[color] );
fill.top--;
}
}
@ -358,6 +364,7 @@ NetGraph_DrawTextFields
static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int count, float avg, int packet_loss, int packet_choke, int graphtype )
{
static int lastout;
cl_font_t *font = Con_GetFont( 0 );
rgba_t colors = { 0.9 * 255, 0.9 * 255, 0.7 * 255, 255 };
int ptx = Q_max( x + w - NETGRAPH_LERP_HEIGHT - 1, 1 );
int pty = Q_max( rect.top + rect.bottom - NETGRAPH_LERP_HEIGHT - 3, 1 );
@ -379,16 +386,17 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
// move rolling average
framerate = FRAMERATE_AVG_FRAC * host.frametime + ( 1.0f - FRAMERATE_AVG_FRAC ) * framerate;
Con_SetFont( 0 );
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
if( framerate > 0.0f )
{
y -= net_graphheight->value;
Con_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors );
CL_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors, font, FONT_DRAW_NORENDERMODE );
if( avg > 1.0f )
Con_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors );
CL_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
@ -396,10 +404,10 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
if( !out ) out = lastout;
else lastout = out;
Con_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors );
CL_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
Con_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors );
CL_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE );
y += 15;
if( graphtype > 2 )
@ -407,16 +415,14 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
int loss = (int)(( packet_loss + PACKETLOSS_AVG_FRAC ) - 0.01f );
int choke = (int)(( packet_choke + PACKETCHOKE_AVG_FRAC ) - 0.01f );
Con_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors );
CL_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors, font, FONT_DRAW_NORENDERMODE );
}
}
if( graphtype < 3 )
Con_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors );
CL_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors, font, FONT_DRAW_NORENDERMODE );
Con_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors );
Con_RestoreFont();
CL_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors, font, FONT_DRAW_NORENDERMODE );
}
/*

View File

@ -843,54 +843,74 @@ void CL_ParseFileTransferFailed( sizebuf_t *msg )
CL_ParseServerData
==================
*/
void CL_ParseServerData( sizebuf_t *msg )
void CL_ParseServerData( sizebuf_t *msg, qboolean legacy )
{
char gamefolder[MAX_QPATH];
qboolean background;
int i;
Con_Reportf( "Serverdata packet received.\n" );
cls.timestart = Sys_DoubleTime();
Con_Reportf( "%s packet received.\n", legacy ? "Legacy serverdata" : "Serverdata" );
cls.timestart = Sys_DoubleTime();
cls.demowaiting = false; // server is changed
// wipe the client_t struct
if( !cls.changelevel && !cls.changedemo )
CL_ClearState ();
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();
cls.state = ca_connected;
// parse protocol version number
i = MSG_ReadLong( msg );
if( i != PROTOCOL_VERSION )
Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
if( legacy )
{
if( i != PROTOCOL_LEGACY_VERSION )
Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_LEGACY_VERSION );
}
else
{
if( i != PROTOCOL_VERSION )
Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
}
cl.servercount = MSG_ReadLong( msg );
cl.checksum = MSG_ReadLong( msg );
cl.playernum = MSG_ReadByte( msg );
cl.maxclients = MSG_ReadByte( msg );
clgame.maxEntities = MSG_ReadWord( msg );
clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS );
clgame.maxModels = MSG_ReadWord( msg );
Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING );
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING );
if( legacy )
{
clgame.maxEntities = bound( MIN_LEGACY_EDICTS, clgame.maxEntities, MAX_LEGACY_EDICTS );
clgame.maxModels = 512; // ???
}
else
{
clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS );
clgame.maxModels = MSG_ReadWord( msg );
}
Q_strncpy( clgame.mapname, MSG_ReadString( msg ), sizeof( clgame.mapname ));
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle ));
background = MSG_ReadOneBit( msg );
Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_QPATH );
Q_strncpy( gamefolder, MSG_ReadString( msg ), sizeof( gamefolder ));
host.features = (uint)MSG_ReadLong( msg );
// receive the player hulls
for( i = 0; i < MAX_MAP_HULLS * 3; i++ )
if( !legacy )
{
host.player_mins[i/3][i%3] = MSG_ReadChar( msg );
host.player_maxs[i/3][i%3] = MSG_ReadChar( msg );
// receive the player hulls
for( i = 0; i < MAX_MAP_HULLS * 3; i++ )
{
host.player_mins[i/3][i%3] = MSG_ReadChar( msg );
host.player_maxs[i/3][i%3] = MSG_ReadChar( msg );
}
}
if( clgame.maxModels > MAX_MODELS )
Con_Printf( S_WARN "server model limit is above client model limit %i > %i\n", clgame.maxModels, MAX_MODELS );
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();
if( Con_FixedFont( ))
{
// seperate the printfs so the server message can have a color
@ -914,11 +934,7 @@ void CL_ParseServerData( sizebuf_t *msg )
// set the background state
if( cls.demoplayback && ( cls.demonum != -1 ))
{
// re-init mouse
host.mouse_visible = false;
cl.background = true;
}
else cl.background = background;
if( cl.background ) // tell the game parts about background state
@ -965,8 +981,11 @@ void CL_ParseServerData( sizebuf_t *msg )
COM_ClearCustomizationList( &cl.players[i].customdata, true );
CL_CreateCustomizationList();
// request resources from server
CL_ServerCommand( true, "sendres %i\n", cl.servercount );
if( !legacy )
{
// request resources from server
CL_ServerCommand( true, "sendres %i\n", cl.servercount );
}
memset( &clgame.movevars, 0, sizeof( clgame.movevars ));
memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars ));
@ -1141,7 +1160,7 @@ void CL_ParseClientData( sizebuf_t *msg )
CL_ParseBaseline
==================
*/
void CL_ParseBaseline( sizebuf_t *msg )
void CL_ParseBaseline( sizebuf_t *msg, qboolean legacy )
{
int i, newnum;
entity_state_t nullstate;
@ -1154,8 +1173,15 @@ void CL_ParseBaseline( sizebuf_t *msg )
while( 1 )
{
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
if( newnum == LAST_EDICT ) break; // end of baselines
if( legacy )
{
newnum = MSG_ReadWord( msg );
}
else
{
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
if( newnum == LAST_EDICT ) break; // end of baselines
}
player = CL_IsPlayerIndex( newnum );
if( newnum >= clgame.maxEntities )
@ -1166,14 +1192,22 @@ void CL_ParseBaseline( sizebuf_t *msg )
ent->index = newnum;
MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f );
if( legacy )
{
break; // only one baseline allowed in legacy protocol
}
}
cl.instanced_baseline_count = MSG_ReadUBitLong( msg, 6 );
for( i = 0; i < cl.instanced_baseline_count; i++ )
if( !legacy )
{
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
MSG_ReadDeltaEntity( msg, &nullstate, &cl.instanced_baseline[i], newnum, false, 1.0f );
cl.instanced_baseline_count = MSG_ReadUBitLong( msg, 6 );
for( i = 0; i < cl.instanced_baseline_count; i++ )
{
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
MSG_ReadDeltaEntity( msg, &nullstate, &cl.instanced_baseline[i], newnum, false, 1.0f );
}
}
}
@ -1321,7 +1355,7 @@ CL_UpdateUserinfo
collect userinfo from all players
================
*/
void CL_UpdateUserinfo( sizebuf_t *msg )
void CL_UpdateUserinfo( sizebuf_t *msg, qboolean legacy )
{
int slot, id;
qboolean active;
@ -1332,7 +1366,8 @@ void CL_UpdateUserinfo( sizebuf_t *msg )
if( slot >= MAX_CLIENTS )
Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" );
id = MSG_ReadLong( msg ); // unique user ID
if( !legacy )
id = MSG_ReadLong( msg ); // unique user ID
player = &cl.players[slot];
active = MSG_ReadOneBit( msg ) ? true : false;
@ -1344,11 +1379,17 @@ void CL_UpdateUserinfo( sizebuf_t *msg )
player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" ));
MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ));
if( !legacy )
MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ));
if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t ));
}
else memset( player, 0, sizeof( *player ));
else
{
COM_ClearCustomizationList( &player->customdata, true );
memset( player, 0, sizeof( *player ));
}
}
/*
@ -1680,7 +1721,10 @@ CL_ParseVoiceInit
*/
void CL_ParseVoiceInit( sizebuf_t *msg )
{
// TODO: ???
char *pszCodec = MSG_ReadString( msg );
int quality = MSG_ReadByte( msg );
Voice_Init( pszCodec, quality );
}
/*
@ -1691,7 +1735,31 @@ CL_ParseVoiceData
*/
void CL_ParseVoiceData( sizebuf_t *msg )
{
// TODO: ???
int size, idx, frames;
byte received[8192];
idx = MSG_ReadByte( msg ) + 1;
frames = MSG_ReadByte( msg );
size = MSG_ReadShort( msg );
size = Q_min( size, sizeof( received ));
MSG_ReadBytes( msg, received, size );
if ( idx <= 0 || idx > cl.maxclients )
return;
// must notify through as both local player and normal client
if( idx == cl.playernum + 1 )
Voice_StatusAck( &voice.local, VOICE_LOOPBACK_INDEX );
Voice_StatusAck( &voice.players_status[idx], idx );
if ( !size )
return;
Voice_AddIncomingData( idx, received, size, frames );
}
/*
@ -1807,7 +1875,7 @@ void CL_ParseScreenFade( sizebuf_t *msg )
duration = (float)MSG_ReadWord( msg );
holdTime = (float)MSG_ReadWord( msg );
sf->fadeFlags = MSG_ReadShort( msg );
flScale = ( sf->fadeFlags & FFADE_LONGFADE ) ? (1.0f / 256.0f) : (1.0f / 4096.0f);
flScale = FBitSet( sf->fadeFlags, FFADE_LONGFADE ) ? (1.0f / 256.0f) : (1.0f / 4096.0f);
sf->fader = MSG_ReadByte( msg );
sf->fadeg = MSG_ReadByte( msg );
@ -1820,7 +1888,7 @@ void CL_ParseScreenFade( sizebuf_t *msg )
// calc fade speed
if( duration > 0 )
{
if( sf->fadeFlags & FFADE_OUT )
if( FBitSet( sf->fadeFlags, FFADE_OUT ))
{
if( sf->fadeEnd )
{
@ -1828,6 +1896,7 @@ void CL_ParseScreenFade( sizebuf_t *msg )
}
sf->fadeEnd += cl.time;
sf->fadeTotalEnd = sf->fadeEnd;
sf->fadeReset += sf->fadeEnd;
}
else
@ -2201,13 +2270,13 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseServerData( msg );
CL_ParseServerData( msg, false );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg );
break;
case svc_updateuserinfo:
CL_UpdateUserinfo( msg );
CL_UpdateUserinfo( msg, false );
break;
case svc_deltatable:
Delta_ParseTableField( msg );
@ -2237,7 +2306,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_spawnbaseline:
CL_ParseBaseline( msg );
CL_ParseBaseline( msg, false );
break;
case svc_temp_entity:
CL_ParseTempEntity( msg );
@ -2335,6 +2404,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_voicedata:
CL_ParseVoiceData( msg );
cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
@ -2373,160 +2443,6 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
}
}
/*
==================
CL_ParseBaseline
==================
*/
void CL_LegacyParseBaseline( sizebuf_t *msg )
{
int i, newnum;
qboolean player;
cl_entity_t *ent;
Delta_InitClient (); // finalize client delta's
newnum = MSG_ReadWord( msg );
player = CL_IsPlayerIndex( newnum );
if( newnum >= clgame.maxEntities )
Host_Error( "CL_AllocEdict: no free edicts\n" );
ent = CL_EDICT_NUM( newnum );
memset( &ent->prevstate, 0, sizeof( ent->prevstate ));
ent->index = newnum;
MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f );
}
/*
==================
CL_ParseServerData
==================
*/
void CL_ParseLegacyServerData( sizebuf_t *msg )
{
string gamefolder;
qboolean background;
int i;
Con_Reportf( "Legacy serverdata packet received.\n" );
cls.timestart = Sys_DoubleTime();
cls.demowaiting = false; // server is changed
//clgame.load_sequence++; // now all hud sprites are invalid
// wipe the client_t struct
if( !cls.changelevel && !cls.changedemo )
CL_ClearState ();
cls.state = ca_connected;
// parse protocol version number
i = MSG_ReadLong( msg );
//cls.serverProtocol = i;
if( i != 48 )
Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
cl.servercount = MSG_ReadLong( msg );
cl.checksum = MSG_ReadLong( msg );
cl.playernum = MSG_ReadByte( msg );
cl.maxclients = MSG_ReadByte( msg );
clgame.maxEntities = MSG_ReadWord( msg );
clgame.maxEntities = bound( 30, clgame.maxEntities, 4096 );
clgame.maxModels = 512;
Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING );
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING );
background = MSG_ReadOneBit( msg );
Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_STRING );
host.features = (uint)MSG_ReadLong( msg );
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();
if( Con_FixedFont( ))
{
// seperate the printfs so the server message can have a color
Con_Print( "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n" );
Con_Print( va( "%c%s\n\n", 2, clgame.maptitle ));
}
// multiplayer game?
if( cl.maxclients > 1 )
{
// allow console in multiplayer games
host.allow_console = true;
// loading user settings
CSCR_LoadDefaultCVars( "user.scr" );
if( r_decals->value > mp_decals.value )
Cvar_SetValue( "r_decals", mp_decals.value );
}
else Cvar_Reset( "r_decals" );
// set the background state
if( cls.demoplayback && ( cls.demonum != -1 ))
{
// re-init mouse
host.mouse_visible = false;
cl.background = true;
}
else cl.background = background;
if( cl.background ) // tell the game parts about background state
Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY );
else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY );
if( !cls.changelevel )
{
// continue playing if we are changing level
S_StopBackgroundTrack ();
}
if( !cls.changedemo )
UI_SetActiveMenu( cl.background );
else if( !cls.demoplayback )
Key_SetKeyDest( key_menu );
// don't reset cursor in background mode
if( cl.background )
IN_MouseRestorePos();
// will be changed later
cl.viewentity = cl.playernum + 1;
gameui.globals->maxClients = cl.maxclients;
Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle ));
if( !cls.changelevel && !cls.changedemo )
CL_InitEdicts (); // re-arrange edicts
// get splash name
if( cls.demoplayback && ( cls.demonum != -1 ))
Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, refState.wideScreen ? "16x9" : "4x3" ));
else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, refState.wideScreen ? "16x9" : "4x3" ));
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background )
{
if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true ))
Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen
cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime)
}
for( i = 0; i < MAX_CLIENTS; i++ )
COM_ClearCustomizationList( &cl.players[i].customdata, true );
CL_CreateCustomizationList();
memset( &clgame.movevars, 0, sizeof( clgame.movevars ));
memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars ));
memset( &clgame.centerPrint, 0, sizeof( clgame.centerPrint ));
cl.video_prepped = false;
cl.audio_prepped = false;
}
/*
==================
CL_ParseStaticEntity
@ -2602,8 +2518,6 @@ void CL_LegacyParseStaticEntity( sizebuf_t *msg )
R_AddEfrags( ent ); // add link
}
void CL_LegacyParseSoundPacket( sizebuf_t *msg, qboolean is_ambient )
{
vec3_t pos;
@ -2735,36 +2649,6 @@ void CL_LegacyPrecacheEvent( sizebuf_t *msg )
CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex );
}
void CL_LegacyUpdateUserinfo( sizebuf_t *msg )
{
int slot, id = 0;
qboolean active;
player_info_t *player;
slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS );
if( slot >= MAX_CLIENTS )
Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" );
//id = MSG_ReadLong( msg ); // unique user ID
player = &cl.players[slot];
active = MSG_ReadOneBit( msg ) ? true : false;
if( active )
{
Q_strncpy( player->userinfo, MSG_ReadString( msg ), sizeof( player->userinfo ));
Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name ));
Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model ));
player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" ));
//MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ));
if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t ));
}
else memset( player, 0, sizeof( *player ));
}
#if XASH_LOW_MEMORY == 0
#define MAX_LEGACY_RESOURCES 2048
#elif XASH_LOW_MEMORY == 2
@ -2968,13 +2852,13 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseLegacyServerData( msg );
CL_ParseServerData( msg, true );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg );
break;
case svc_updateuserinfo:
CL_LegacyUpdateUserinfo( msg );
CL_UpdateUserinfo( msg, true );
break;
case svc_deltatable:
Delta_ParseTableField( msg );
@ -3004,7 +2888,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_spawnbaseline:
CL_LegacyParseBaseline( msg );
CL_ParseBaseline( msg, true );
break;
case svc_temp_entity:
CL_ParseTempEntity( msg );
@ -3024,7 +2908,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_legacy_modelindex:
CL_LegacyPrecacheModel( msg );
break;
case svc_legacy_soundindex:
CL_LegacyPrecacheSound( msg );
@ -3040,7 +2923,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
CL_ParseRestore( msg );
break;
case svc_legacy_eventindex:
//CL_ParseFinaleCutscene( msg, 3 );
CL_LegacyPrecacheEvent(msg);
break;
case svc_weaponanim:
@ -3117,12 +2999,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
case svc_director:
CL_ParseDirector( msg );
break;
case svc_voiceinit:
CL_ParseVoiceInit( msg );
break;
case svc_voicedata:
CL_ParseVoiceData( msg );
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
break;

View File

@ -26,20 +26,6 @@ GNU General Public License for more details.
#define MIN_PREDICTION_EPSILON 0.5f // complain if error is > this and we have cl_showerror set
#define MAX_PREDICTION_ERROR 64.0f // above this is assumed to be a teleport, don't smooth, etc.
/*
=============
CL_ClearPhysEnts
=============
*/
void CL_ClearPhysEnts( void )
{
clgame.pmove->numtouch = 0;
clgame.pmove->numvisent = 0;
clgame.pmove->nummoveent = 0;
clgame.pmove->numphysent = 0;
}
/*
=============
CL_PushPMStates
@ -208,7 +194,7 @@ check for instant movement in case
we don't want interpolate this
==================
*/
qboolean CL_PlayerTeleported( local_state_t *from, local_state_t *to )
static qboolean CL_PlayerTeleported( local_state_t *from, local_state_t *to )
{
int len, maxlen;
vec3_t delta;
@ -713,33 +699,7 @@ static int GAME_EXPORT pfnTestPlayerPosition( float *pos, pmtrace_t *ptrace )
static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr )
{
int i;
for( i = 0; i < clgame.pmove->numtouch; i++ )
{
if( clgame.pmove->touchindex[i].ent == hitent )
return;
}
if( clgame.pmove->numtouch >= MAX_PHYSENTS )
return;
VectorCopy( clgame.pmove->velocity, tr->deltavelocity );
tr->ent = hitent;
clgame.pmove->touchindex[clgame.pmove->numtouch++] = *tr;
}
static int GAME_EXPORT pfnPointContents( float *p, int *truecontents )
{
int cont, truecont;
truecont = cont = PM_PointContents( clgame.pmove, p );
if( truecontents ) *truecontents = truecont;
if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN )
cont = CONTENTS_WATER;
return cont;
PM_StuckTouch( clgame.pmove, hitent, tr );
}
static int GAME_EXPORT pfnTruePointContents( float *p )
@ -747,101 +707,19 @@ static int GAME_EXPORT pfnTruePointContents( float *p )
return PM_TruePointContents( clgame.pmove, p );
}
static int GAME_EXPORT pfnHullPointContents( struct hull_s *hull, int num, float *p )
{
return PM_HullPointContents( hull, num, p );
}
static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int traceFlags, int ignore_pe )
{
return PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL );
}
pmtrace_t *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe )
{
static pmtrace_t tr;
int old_usehull;
old_usehull = clgame.pmove->usehull;
clgame.pmove->usehull = usehull;
switch( flags )
{
case PM_TRACELINE_PHYSENTSONLY:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL );
break;
case PM_TRACELINE_ANYVISIBLE:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, ignore_pe, NULL );
break;
}
clgame.pmove->usehull = old_usehull;
return &tr;
}
static hull_t *pfnHullForBsp( physent_t *pe, float *offset )
static void *pfnHullForBsp( physent_t *pe, float *offset )
{
return PM_HullForBsp( pe, clgame.pmove, offset );
}
static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, trace_t *trace )
{
int old_usehull;
vec3_t start_l, end_l;
vec3_t offset, temp;
qboolean rotated;
matrix4x4 matrix;
hull_t *hull;
PM_InitTrace( trace, end );
old_usehull = clgame.pmove->usehull;
clgame.pmove->usehull = 2;
hull = PM_HullForBsp( pe, clgame.pmove, offset );
clgame.pmove->usehull = old_usehull;
if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles ))
rotated = true;
else rotated = false;
if( rotated )
{
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f );
Matrix4x4_VectorITransform( matrix, start, start_l );
Matrix4x4_VectorITransform( matrix, end, end_l );
}
else
{
VectorSubtract( start, offset, start_l );
VectorSubtract( end, offset, end_l );
}
PM_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, (pmtrace_t *)trace );
trace->ent = NULL;
if( rotated )
{
VectorCopy( trace->plane.normal, temp );
Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist );
}
VectorLerp( start, trace->fraction, end, trace->endpos );
return trace->fraction;
}
static const char *pfnTraceTexture( int ground, float *vstart, float *vend )
{
physent_t *pe;
if( ground < 0 || ground >= clgame.pmove->numphysent )
return NULL; // bad ground
pe = &clgame.pmove->physents[ground];
return PM_TraceTexture( pe, vstart, vend );
return PM_TraceModel( clgame.pmove, pe, start, end, trace );
}
static void GAME_EXPORT pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch )
@ -870,25 +748,7 @@ static int GAME_EXPORT pfnTestPlayerPositionEx( float *pos, pmtrace_t *ptrace, p
static pmtrace_t *pfnTraceLineEx( float *start, float *end, int flags, int usehull, pfnIgnore pmFilter )
{
static pmtrace_t tr;
int old_usehull;
old_usehull = clgame.pmove->usehull;
clgame.pmove->usehull = usehull;
switch( flags )
{
case PM_TRACELINE_PHYSENTSONLY:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, -1, pmFilter );
break;
case PM_TRACELINE_ANYVISIBLE:
tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, -1, pmFilter );
break;
}
clgame.pmove->usehull = old_usehull;
return &tr;
return PM_TraceLineEx( clgame.pmove, start, end, flags, usehull, pmFilter );
}
/*
@ -928,23 +788,23 @@ void CL_InitClientMove( void )
clgame.pmove->Con_Printf = Con_Printf;
clgame.pmove->Sys_FloatTime = Sys_DoubleTime;
clgame.pmove->PM_StuckTouch = pfnStuckTouch;
clgame.pmove->PM_PointContents = pfnPointContents;
clgame.pmove->PM_PointContents = (void*)PM_CL_PointContents;
clgame.pmove->PM_TruePointContents = pfnTruePointContents;
clgame.pmove->PM_HullPointContents = pfnHullPointContents;
clgame.pmove->PM_HullPointContents = (void*)PM_HullPointContents;
clgame.pmove->PM_PlayerTrace = pfnPlayerTrace;
clgame.pmove->PM_TraceLine = PM_TraceLine;
clgame.pmove->PM_TraceLine = PM_CL_TraceLine;
clgame.pmove->RandomLong = COM_RandomLong;
clgame.pmove->RandomFloat = COM_RandomFloat;
clgame.pmove->PM_GetModelType = pfnGetModelType;
clgame.pmove->PM_GetModelBounds = pfnGetModelBounds;
clgame.pmove->PM_HullForBsp = (void*)pfnHullForBsp;
clgame.pmove->PM_HullForBsp = pfnHullForBsp;
clgame.pmove->PM_TraceModel = pfnTraceModel;
clgame.pmove->COM_FileSize = COM_FileSize;
clgame.pmove->COM_LoadFile = COM_LoadFile;
clgame.pmove->COM_FreeFile = COM_FreeFile;
clgame.pmove->memfgets = COM_MemFgets;
clgame.pmove->PM_PlaySound = pfnPlaySound;
clgame.pmove->PM_TraceTexture = pfnTraceTexture;
clgame.pmove->PM_TraceTexture = PM_CL_TraceTexture;
clgame.pmove->PM_PlaybackEventFull = pfnPlaybackEventFull;
clgame.pmove->PM_PlayerTraceEx = pfnPlayerTraceEx;
clgame.pmove->PM_TestPlayerPositionEx = pfnTestPlayerPositionEx;
@ -955,21 +815,10 @@ void CL_InitClientMove( void )
clgame.dllFuncs.pfnPlayerMoveInit( clgame.pmove );
}
static void PM_CheckMovingGround( clientdata_t *cd, entity_state_t *state, float frametime )
static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const usercmd_t *ucmd, qboolean runfuncs, double time )
{
if(!( cd->flags & FL_BASEVELOCITY ))
{
// apply momentum (add in half of the previous frame of velocity first)
VectorMA( cd->velocity, 1.0f + (frametime * 0.5f), state->basevelocity, cd->velocity );
VectorClear( state->basevelocity );
}
cd->flags &= ~FL_BASEVELOCITY;
}
void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time )
{
entity_state_t *ps;
clientdata_t *cd;
const entity_state_t *ps;
const clientdata_t *cd;
ps = &from->playerstate;
cd = &from->client;
@ -986,13 +835,13 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q
VectorCopy( ps->basevelocity, pmove->basevelocity );
VectorCopy( cd->view_ofs, pmove->view_ofs );
VectorClear( pmove->movedir );
pmove->flDuckTime = cd->flDuckTime;
pmove->flDuckTime = (float)cd->flDuckTime;
pmove->bInDuck = cd->bInDuck;
pmove->usehull = ps->usehull;
pmove->flTimeStepSound = cd->flTimeStepSound;
pmove->iStepLeft = ps->iStepLeft;
pmove->flFallVelocity = ps->flFallVelocity;
pmove->flSwimTime = cd->flSwimTime;
pmove->flSwimTime = (float)cd->flSwimTime;
VectorCopy( cd->punchangle, pmove->punchangle );
pmove->flNextPrimaryAttack = 0.0f; // not used by PM_ code
pmove->effects = ps->effects;
@ -1000,7 +849,7 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q
pmove->gravity = ps->gravity;
pmove->friction = ps->friction;
pmove->oldbuttons = ps->oldbuttons;
pmove->waterjumptime = cd->waterjumptime;
pmove->waterjumptime = (float)cd->waterjumptime;
pmove->dead = (cl.local.health <= 0);
pmove->deadflag = cd->deadflag;
pmove->spectator = (cls.spectator != 0);
@ -1027,7 +876,7 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q
Q_strncpy( pmove->physinfo, cls.physinfo, MAX_INFO_STRING );
}
void CL_FinishPMove( playermove_t *pmove, local_state_t *to )
const void CL_FinishPMove( const playermove_t *pmove, local_state_t *to )
{
entity_state_t *ps;
clientdata_t *cd;
@ -1038,7 +887,7 @@ void CL_FinishPMove( playermove_t *pmove, local_state_t *to )
cd->flags = pmove->flags;
cd->bInDuck = pmove->bInDuck;
cd->flTimeStepSound = pmove->flTimeStepSound;
cd->flDuckTime = pmove->flDuckTime;
cd->flDuckTime = (int)pmove->flDuckTime;
cd->flSwimTime = (int)pmove->flSwimTime;
cd->waterjumptime = (int)pmove->waterjumptime;
cd->watertype = pmove->watertype;
@ -1051,7 +900,7 @@ void CL_FinishPMove( playermove_t *pmove, local_state_t *to )
VectorCopy( pmove->angles, ps->angles );
VectorCopy( pmove->basevelocity, ps->basevelocity );
VectorCopy( pmove->punchangle, cd->punchangle );
ps->oldbuttons = pmove->cmd.buttons;
ps->oldbuttons = (uint)pmove->cmd.buttons;
ps->friction = pmove->friction;
ps->movetype = pmove->movetype;
ps->onground = pmove->onground;
@ -1159,13 +1008,9 @@ void CL_PredictMovement( qboolean repredicting )
{
runcmd_t *to_cmd = NULL, *from_cmd;
local_state_t *from = NULL, *to = NULL;
uint current_command;
uint current_command_mod;
frame_t *frame = NULL;
frame_t *frame = NULL;
uint i, stoppoint;
qboolean runfuncs;
double f = 1.0;
cl_entity_t *ent;
double time;
if( cls.state != ca_active || cls.spectator )
@ -1176,7 +1021,7 @@ void CL_PredictMovement( qboolean repredicting )
CL_SetUpPlayerPrediction( false, false );
if( cls.state != ca_active || !cl.validsequence )
if( !cl.validsequence )
return;
if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK )
@ -1217,6 +1062,10 @@ void CL_PredictMovement( qboolean repredicting )
for( i = 1; i < CL_UPDATE_MASK && cls.netchan.incoming_acknowledged + i < cls.netchan.outgoing_sequence + stoppoint; i++ )
{
uint current_command;
uint current_command_mod;
qboolean runfuncs;
current_command = cls.netchan.incoming_acknowledged + i;
current_command_mod = current_command & CL_UPDATE_MASK;
@ -1293,7 +1142,7 @@ void CL_PredictMovement( qboolean repredicting )
if( FBitSet( to->client.flags, FL_ONGROUND ))
{
ent = CL_GetEntityByIndex( cl.local.lastground );
cl_entity_t *ent = CL_GetEntityByIndex( cl.local.lastground );
cl.local.onground = cl.local.lastground;
cl.local.moving = false;
@ -1315,13 +1164,13 @@ void CL_PredictMovement( qboolean repredicting )
else
{
cl.local.onground = -1;
cl.local.moving = 0;
cl.local.moving = false;
}
if( cls.correction_time > 0 && !cl_nosmooth->value && cl_smoothtime->value )
{
vec3_t delta;
float frac;
vec3_t delta;
float frac;
// only decay timer once per frame
if( !repredicting )

View File

@ -237,10 +237,6 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg )
}
else Cvar_Reset( "r_decals" );
// re-init mouse
if( cl.background )
host.mouse_visible = false;
if( cl.background ) // tell the game parts about background state
Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY );
else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY );

View File

@ -133,7 +133,7 @@ const char *CL_GenericHandle( int fileindex )
return cl.files_precache[fileindex];
}
int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef )
intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef )
{
switch( parm )
{
@ -161,9 +161,9 @@ int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef )
case PARM_WATER_ALPHA:
return FBitSet( world.flags, FWORLD_WATERALPHA );
case PARM_DELUXEDATA:
return *(int *)&world.deluxedata;
return (intptr_t)world.deluxedata;
case PARM_SHADOWDATA:
return *(int *)&world.shadowdata;
return (intptr_t)world.shadowdata;
default:
// indicates call from client.dll
if( checkRef )
@ -204,7 +204,7 @@ int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef )
return 0;
}
static int pfnRenderGetParm( int parm, int arg )
static intptr_t pfnRenderGetParm( int parm, int arg )
{
return CL_RenderGetParm( parm, arg, true );
}

View File

@ -150,8 +150,7 @@ same as r_speeds but for network channel
void SCR_NetSpeeds( void )
{
static char msg[MAX_SYSPATH];
int x, y, height;
char *p, *start, *end;
int x, y;
float time = cl.mtime[0];
static int min_svfps = 100;
static int max_svfps = 0;
@ -160,6 +159,7 @@ void SCR_NetSpeeds( void )
static int max_clfps = 0;
int cur_clfps = 0;
rgba_t color;
cl_font_t *font = Con_GetCurFont();
if( !host.allow_console )
return;
@ -196,25 +196,11 @@ void SCR_NetSpeeds( void )
Q_memprint( cls.netchan.total_sended )
);
x = refState.width - 320;
x = refState.width - 320 * font->scale;
y = 384;
Con_DrawStringLen( NULL, NULL, &height );
MakeRGBA( color, 255, 255, 255, 255 );
p = start = msg;
do
{
end = Q_strchr( p, '\n' );
if( end ) msg[end-start] = '\0';
Con_DrawString( x, y, p, color );
y += height;
if( end ) p = end + 1;
else break;
} while( 1 );
CL_DrawString( x, y, msg, color, font, FONT_DRAW_RESETCOLORONLF );
}
/*
@ -231,31 +217,15 @@ void SCR_RSpeeds( void )
if( ref.dllFuncs.R_SpeedsMessage( msg, sizeof( msg )))
{
int x, y, height;
char *p, *start, *end;
int x, y;
rgba_t color;
cl_font_t *font = Con_GetCurFont();
x = refState.width - 340;
x = refState.width - 340 * font->scale;
y = 64;
Con_DrawStringLen( NULL, NULL, &height );
MakeRGBA( color, 255, 255, 255, 255 );
p = start = msg;
do
{
end = Q_strchr( p, '\n' );
if( end ) msg[end-start] = '\0';
Con_DrawString( x, y, p, color );
y += height;
// handle '\n\n'
if( *p == '\n' )
y += height;
if( end ) p = end + 1;
else break;
} while( 1 );
CL_DrawString( x, y, msg, color, font, FONT_DRAW_RESETCOLORONLF );
}
}
@ -577,77 +547,6 @@ void SCR_UpdateScreen( void )
V_PostRender();
}
qboolean SCR_LoadFixedWidthFont( const char *fontname )
{
int i, fontWidth;
if( cls.creditsFont.valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE );
R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture );
cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = fontWidth / 16;
cls.creditsFont.type = FONT_FIXED;
cls.creditsFont.valid = true;
// build fixed rectangles
for( i = 0; i < 256; i++ )
{
cls.creditsFont.fontRc[i].left = (i * (fontWidth / 16)) % fontWidth;
cls.creditsFont.fontRc[i].right = cls.creditsFont.fontRc[i].left + fontWidth / 16;
cls.creditsFont.fontRc[i].top = (i / 16) * (fontWidth / 16);
cls.creditsFont.fontRc[i].bottom = cls.creditsFont.fontRc[i].top + fontWidth / 16;
cls.creditsFont.charWidths[i] = clgame.scrInfo.charWidths[i] = fontWidth / 16;
}
return true;
}
qboolean SCR_LoadVariableWidthFont( const char *fontname )
{
int i, fontWidth;
byte *buffer;
fs_offset_t length;
qfont_t *src;
if( cls.creditsFont.valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE );
R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture );
// half-life font with variable chars witdh
buffer = FS_LoadFile( fontname, &length, false );
// setup creditsfont
if( buffer && length >= sizeof( qfont_t ))
{
src = (qfont_t *)buffer;
cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = src->rowheight;
cls.creditsFont.type = FONT_VARIABLE;
// build rectangles
for( i = 0; i < 256; i++ )
{
cls.creditsFont.fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth;
cls.creditsFont.fontRc[i].right = cls.creditsFont.fontRc[i].left + src->fontinfo[i].charwidth;
cls.creditsFont.fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth;
cls.creditsFont.fontRc[i].bottom = cls.creditsFont.fontRc[i].top + src->rowheight;
cls.creditsFont.charWidths[i] = clgame.scrInfo.charWidths[i] = src->fontinfo[i].charwidth;
}
cls.creditsFont.valid = true;
}
if( buffer ) Mem_Free( buffer );
return true;
}
/*
================
SCR_LoadCreditsFont
@ -657,22 +556,41 @@ INTERNAL RESOURCE
*/
void SCR_LoadCreditsFont( void )
{
const char *path = "gfx/creditsfont.fnt";
dword crc;
cl_font_t *const font = &cls.creditsFont;
qboolean success = false;
float scale = hud_fontscale->value;
dword crc = 0;
// replace default gfx.wad textures by current charset's font
if( !CRC32_File( &crc, "gfx.wad" ) || crc == 0x49eb9f16 )
{
const char *path2 = va("creditsfont_%s.fnt", Cvar_VariableString( "con_charset" ) );
if( FS_FileExists( path2, false ) )
path = path2;
string charsetFnt;
if( Q_snprintf( charsetFnt, sizeof( charsetFnt ),
"creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 )
{
if( FS_FileExists( charsetFnt, false ))
success = Con_LoadVariableWidthFont( charsetFnt, font, scale, kRenderTransAdd, TF_FONT );
}
}
if( !SCR_LoadVariableWidthFont( path ))
if( !success )
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, kRenderTransAdd, TF_FONT );
if( !success )
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransAdd, TF_FONT );
// copy font size for client.dll
if( success )
{
if( !SCR_LoadFixedWidthFont( "gfx/conchars" ))
Con_DPrintf( S_ERROR "failed to load HUD font\n" );
int i;
clgame.scrInfo.iCharHeight = cls.creditsFont.charHeight;
for( i = 0; i < ARRAYSIZE( cls.creditsFont.charWidths ); i++ )
clgame.scrInfo.charWidths[i] = cls.creditsFont.charWidths[i];
}
else Con_DPrintf( S_ERROR "failed to load HUD font\n" );
}
/*
@ -780,7 +698,6 @@ SCR_VidInit
*/
void SCR_VidInit( void )
{
string libpath;
if( !ref.initialized ) // don't call VidInit too soon
return;
@ -795,8 +712,11 @@ void SCR_VidInit( void )
gameui.globals->scrHeight = refState.height;
}
COM_GetCommonLibraryPath( LIBRARY_CLIENT, libpath, sizeof( libpath ));
VGui_Startup( libpath, refState.width, refState.height );
// notify vgui about screen size change
if( clgame.hInstance )
{
VGui_Startup( refState.width, refState.height );
}
CL_ClearSpriteTextures(); // now all hud sprites are invalid
@ -827,7 +747,7 @@ void SCR_Init( void )
v_dark = Cvar_Get( "v_dark", "0", 0, "starts level from dark screen" );
scr_viewsize = Cvar_Get( "viewsize", "120", FCVAR_ARCHIVE, "screen size" );
net_speeds = Cvar_Get( "net_speeds", "0", FCVAR_ARCHIVE, "show network packets" );
cl_showfps = Cvar_Get( "cl_showfps", "1", FCVAR_ARCHIVE, "show client fps" );
cl_showfps = Cvar_Get( "cl_showfps", "0", FCVAR_ARCHIVE, "show client fps" );
cl_showpos = Cvar_Get( "cl_showpos", "0", FCVAR_ARCHIVE, "show local player position and velocity" );
// register our commands

View File

@ -1037,7 +1037,7 @@ void GAME_EXPORT R_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t
vecSpot[1] = pos[1] + COM_RandomFloat( -0.5f, 0.5f ) * size[1];
vecSpot[2] = pos[2] + COM_RandomFloat( -0.5f, 0.5f ) * size[2];
if( CL_PointContents( vecSpot ) != CONTENTS_SOLID )
if( PM_CL_PointContents( vecSpot, NULL ) != CONTENTS_SOLID )
break; // valid spot
}
@ -1974,6 +1974,9 @@ void CL_ParseTempEntity( sizebuf_t *msg )
pos[1] = MSG_ReadCoord( &buf );
pos[2] = MSG_ReadCoord( &buf );
R_BlobExplosion( pos );
hSound = S_RegisterSound( cl_explode_sounds[0] );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 );
break;
case TE_SMOKE:
pos[0] = MSG_ReadCoord( &buf );
@ -2027,7 +2030,7 @@ void CL_ParseTempEntity( sizebuf_t *msg )
dl->decay = 300;
hSound = S_RegisterSound( cl_explode_sounds[0] );
S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 );
break;
case TE_BSPDECAL:
case TE_DECAL:
@ -2928,7 +2931,13 @@ void CL_PlayerDecal( int playernum, int customIndex, int entityIndex, float *pos
{
if( !pCust->nUserData1 )
{
int sprayTextureIndex;
const char *decalname = va( "player%dlogo%d", playernum, customIndex );
sprayTextureIndex = ref.dllFuncs.GL_FindTexture( decalname );
if( sprayTextureIndex != 0 )
{
ref.dllFuncs.GL_FreeTexture( sprayTextureIndex );
}
pCust->nUserData1 = GL_LoadTextureInternal( decalname, pCust->pInfo, TF_DECAL );
}
textureIndex = pCust->nUserData1;

View File

@ -209,7 +209,7 @@ qboolean SCR_PlayCinematic( const char *arg )
if( FS_FileExists( arg, false ) && !fullpath )
{
Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", path );
Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", arg );
return false;
}

View File

@ -286,9 +286,6 @@ qboolean V_PreRender( void )
if( !ref.initialized )
return false;
if( host.status == HOST_NOFOCUS )
return false;
if( host.status == HOST_SLEEP )
return false;

View File

@ -33,6 +33,7 @@ GNU General Public License for more details.
#include "net_api.h"
#include "world.h"
#include "ref_common.h"
#include "voice.h"
// client sprite types
#define SPR_CLIENT 0 // client sprite for temp-entities or user-textures
@ -107,7 +108,6 @@ extern int CL_UPDATE_BACKUP;
#define MIN_UPDATERATE 10.0f
#define MAX_UPDATERATE 102.0f
#define MIN_EX_INTERP 0.005f
#define MAX_EX_INTERP 0.1f
#define CL_MIN_RESEND_TIME 1.5f // mininum time gap (in seconds) before a subsequent connection request is sent.
@ -138,7 +138,7 @@ typedef struct
int light_level;
int waterlevel;
int usehull;
int moving;
qboolean moving;
int pushmsec;
int weapons;
float maxspeed;
@ -210,7 +210,7 @@ typedef struct
// a lerp point for other data
double oldtime; // previous cl.time, time-oldtime is used
// to decay light values and smooth step ups
float timedelta; // floating delta between two updates
double timedelta; // floating delta between two updates
char serverinfo[MAX_SERVERINFO_STRING];
player_info_t players[MAX_CLIENTS]; // collected info about all other players include himself
@ -318,32 +318,46 @@ typedef struct
pfnEventHook func; // user-defined function
} cl_user_event_t;
#define FONT_FIXED 0
#define FONT_VARIABLE 1
#define FONT_FIXED 0
#define FONT_VARIABLE 1
#define FONT_DRAW_HUD BIT( 0 ) // pass to drawing function to apply hud_scale
#define FONT_DRAW_UTF8 BIT( 1 ) // call UtfProcessChar
#define FONT_DRAW_FORCECOL BIT( 2 ) // ignore colorcodes
#define FONT_DRAW_NORENDERMODE BIT( 3 ) // ignore font's default rendermode
#define FONT_DRAW_NOLF BIT( 4 ) // ignore \n
#define FONT_DRAW_RESETCOLORONLF BIT( 5 ) // yet another flag to simulate consecutive Con_DrawString calls...
typedef struct
{
int hFontTexture; // handle to texture
wrect_t fontRc[256]; // rectangles
byte charWidths[256];
int charHeight;
int type;
qboolean valid; // all rectangles are valid
int hFontTexture; // handle to texture
wrect_t fontRc[256]; // tex coords
float scale; // scale factor
byte charWidths[256]; // scaled widths
int charHeight; // scaled height
int type; // fixed width font or variable
int rendermode; // default rendermode
qboolean nearest; // nearest filtering enabled
qboolean valid; // all rectangles are valid
} cl_font_t;
typedef struct scissor_state_s
{
int x;
int y;
int width;
int height;
qboolean test;
} scissor_state_t;
typedef struct
{
// scissor test
scissor_state_t scissor;
// temp handle
const model_t *pSprite; // pointer to current SpriteTexture
// scissor test
int scissor_x;
int scissor_y;
int scissor_width;
int scissor_height;
qboolean scissor_test;
qboolean adjust_size; // allow to adjust scale for fonts
int renderMode; // override kRenderMode from TriAPI
TRICULLSTYLE cullMode; // override CULL FACE from TriAPI
@ -370,14 +384,10 @@ typedef struct cl_predicted_player_s
typedef struct
{
int gl_texturenum; // this is a real texnum
// scissor test
int scissor_x;
int scissor_y;
int scissor_width;
int scissor_height;
qboolean scissor_test;
scissor_state_t scissor;
int gl_texturenum; // this is a real texnum
// holds text color
rgba_t textColor;
@ -671,6 +681,7 @@ extern convar_t *cl_levelshot_name;
extern convar_t *cl_draw_beams;
extern convar_t *cl_clockreset;
extern convar_t *cl_fixtimerate;
extern convar_t *hud_fontscale;
extern convar_t *hud_scale;
extern convar_t *gl_showtextures;
extern convar_t *cl_bmodelinterp;
@ -753,6 +764,7 @@ int CL_IsDevOverviewMode( void );
void CL_PingServers_f( void );
void CL_SignonReply( void );
void CL_ClearState( void );
size_t CL_BuildMasterServerScanRequest( char *buf, size_t size, qboolean nat );
//
// cl_demo.c
@ -794,6 +806,19 @@ void CL_ResetEvent( event_info_t *ei );
word CL_EventIndex( const char *name );
void CL_FireEvents( void );
//
// cl_font.c
//
qboolean CL_FixedFont( cl_font_t *font );
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
void CL_FreeFont( cl_font_t *font );
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags );
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags );
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height );
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags );
//
// cl_game.c
//
@ -823,11 +848,15 @@ model_t *CL_LoadClientSprite( const char *filename );
model_t *CL_LoadModel( const char *modelname, int *index );
HSPRITE EXPORT pfnSPR_Load( const char *szPicName );
HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags );
void PicAdjustSize( float *x, float *y, float *w, float *h );
void SPR_AdjustSize( float *x, float *y, float *w, float *h );
void SPR_AdjustTexCoords( float width, float height, float *s1, float *t1, float *s2, float *t2 );
int CL_GetScreenInfo( SCREENINFO *pscrinfo );
void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a );
void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pmtrace_t *tr );
void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnore)( physent_t *pe ), pmtrace_t *tr );
pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe );
const char *PM_CL_TraceTexture( int ground, float *vstart, float *vend );
int PM_CL_PointContents( const float *p, int *truecontents );
void CL_SetTraceHull( int hull );
void CL_GetMousePosition( int *mx, int *my ); // TODO: move to input
cl_entity_t* CL_GetViewModel( void );
@ -835,6 +864,9 @@ void pfnGetScreenFade( struct screenfade_s *fade );
physent_t *pfnGetPhysent( int idx );
struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend );
movevars_t *pfnGetMoveVars( void );
void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int height );
void CL_DisableScissor( scissor_state_t *scissor );
qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 );
_inline cl_entity_t *CL_EDICT_NUM( int n )
{
@ -910,10 +942,8 @@ void CL_PredictMovement( qboolean repredicting );
void CL_CheckPredictionError( void );
qboolean CL_IsPredicted( void );
int CL_TruePointContents( const vec3_t p );
int CL_PointContents( const vec3_t p );
int CL_WaterEntity( const float *rgflPos );
cl_entity_t *CL_GetWaterEntity( const float *rgflPos );
void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time );
int CL_TestLine( const vec3_t start, const vec3_t end, int flags );
pmtrace_t *CL_VisTraceLine( vec3_t start, vec3_t end, int flags );
pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags );
@ -922,7 +952,6 @@ void CL_PopTraceBounds( void );
void CL_MoveSpectatorCamera( void );
void CL_SetLastUpdate( void );
void CL_RedoPrediction( void );
void CL_ClearPhysEnts( void );
void CL_PushPMStates( void );
void CL_PopPMStates( void );
void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient );
@ -963,7 +992,7 @@ void CL_ClearAllRemaps( void );
// cl_render.c
//
qboolean R_InitRenderAPI( void );
int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef );
intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef );
lightstyle_t *CL_GetLightStyle( int number );
int R_FatPVS( const vec3_t org, float radius, byte *visbuffer, qboolean merge, qboolean fullvis );
const ref_overview_t *GL_GetOverviewParms( void );
@ -1027,13 +1056,13 @@ int Con_UtfProcessChar( int in );
int Con_UtfProcessCharForce( int in );
int Con_UtfMoveLeft( char *str, int pos );
int Con_UtfMoveRight( char *str, int pos, int length );
void Con_DrawStringLen( const char *pText, int *length, int *height );
int Con_DrawString( int x, int y, const char *string, rgba_t setColor );
int Con_DrawCharacter( int x, int y, int number, rgba_t color );
void Con_DrawCharacterLen( int number, int *width, int *height );
void Con_DefaultColor( int r, int g, int b );
void Con_InvalidateFonts( void );
void Con_SetFont( int fontNum );
cl_font_t *Con_GetCurFont( void );
cl_font_t *Con_GetFont( int num );
void Con_DrawCharacterLen( int number, int *width, int *height );
int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); // legacy, use cl_font.c
void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height ); // legacy, use cl_font.c
void Con_CharEvent( int key );
void Con_RestoreFont( void );
void Key_Console( int key );

View File

@ -116,7 +116,7 @@ typedef struct
// console fonts
cl_font_t chars[CON_NUMFONTS];// fonts.wad/font1.fnt
cl_font_t *curFont, *lastUsedFont;
cl_font_t *curFont;
// console input
field_t input;
@ -555,89 +555,9 @@ Con_FixedFont
*/
qboolean Con_FixedFont( void )
{
if( con.curFont && con.curFont->valid && con.curFont->type == FONT_FIXED )
return true;
return false;
return CL_FixedFont( con.curFont );
}
static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font )
{
int i, fontWidth;
if( font->valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
// keep source to print directly into conback image
font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_KEEP_SOURCE );
R_GetTextureParms( &fontWidth, NULL, font->hFontTexture );
if( font->hFontTexture && fontWidth != 0 )
{
font->charHeight = fontWidth / 16 * con_fontscale->value;
font->type = FONT_FIXED;
// build fixed rectangles
for( i = 0; i < 256; i++ )
{
font->fontRc[i].left = (i * (fontWidth / 16)) % fontWidth;
font->fontRc[i].right = font->fontRc[i].left + fontWidth / 16;
font->fontRc[i].top = (i / 16) * (fontWidth / 16);
font->fontRc[i].bottom = font->fontRc[i].top + fontWidth / 16;
font->charWidths[i] = fontWidth / 16 * con_fontscale->value;
}
font->valid = true;
}
return true;
}
static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font )
{
int i, fontWidth;
byte *buffer;
fs_offset_t length;
qfont_t *src;
if( font->valid )
return true; // already loaded
if( !FS_FileExists( fontname, false ))
return false;
font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_NEAREST );
R_GetTextureParms( &fontWidth, NULL, font->hFontTexture );
// setup consolefont
if( font->hFontTexture && fontWidth != 0 )
{
// half-life font with variable chars witdh
buffer = FS_LoadFile( fontname, &length, false );
if( buffer && length >= sizeof( qfont_t ))
{
src = (qfont_t *)buffer;
font->charHeight = src->rowheight * con_fontscale->value;
font->type = FONT_VARIABLE;
// build rectangles
for( i = 0; i < 256; i++ )
{
font->fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth;
font->fontRc[i].right = font->fontRc[i].left + src->fontinfo[i].charwidth;
font->fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth;
font->fontRc[i].bottom = font->fontRc[i].top + src->rowheight;
font->charWidths[i] = src->fontinfo[i].charwidth * con_fontscale->value;
}
font->valid = true;
}
if( buffer ) Mem_Free( buffer );
}
return true;
}
/*
================
@ -648,32 +568,46 @@ INTERNAL RESOURCE
*/
static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
{
const char *path = NULL;
dword crc = 0;
qboolean success = false;
float scale = con_fontscale->value;
if( font->valid ) return; // already loaded
// replace default fonts.wad textures by current charset's font
if( !CRC32_File( &crc, "fonts.wad" ) || crc == 0x3c0a0029 )
{
const char *path2 = va("font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" ) );
if( FS_FileExists( path2, false ) )
path = path2;
}
if( font->valid )
return; // already loaded
// loading conchars
if( Sys_CheckParm( "-oldfont" ))
Con_LoadVariableWidthFont( "gfx/conchars.fnt", font );
{
success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
else
{
if( !path )
path = va( "fonts/font%i", fontNumber );
string path;
dword crc = 0;
Con_LoadVariableWidthFont( path, font );
// replace default fonts.wad textures by current charset's font
if( !CRC32_File( &crc, "fonts.wad" ) || crc == 0x3c0a0029 )
{
if( Q_snprintf( path, sizeof( path ),
"font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" )) > 0 )
{
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
}
if( !success )
{
Q_snprintf( path, sizeof( path ), "fonts/font%i", fontNumber );
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
}
}
// quake fixed font as fallback
if( !font->valid ) Con_LoadFixedWidthFont( "gfx/conchars", font );
if( !success )
{
// quake fixed font as fallback
// keep source to print directly into conback image
if( !Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransTexture, TF_FONT|TF_KEEP_SOURCE ))
Con_DPrintf( S_ERROR "failed to load console font\n" );
}
}
/*
@ -702,7 +636,7 @@ static void Con_LoadConchars( void )
fontSize = CON_NUMFONTS - 1;
// sets the current font
con.lastUsedFont = con.curFont = &con.chars[fontSize];
con.curFont = &con.chars[fontSize];
}
// CP1251 table
@ -875,129 +809,25 @@ static void Con_DrawCharToConback( int num, const byte *conchars, byte *dest )
/*
====================
Con_TextAdjustSize
Con_GetFont
draw charcters routine
====================
*/
static void Con_TextAdjustSize( int *x, int *y, int *w, int *h )
cl_font_t *Con_GetFont( int num )
{
float xscale, yscale;
if( !x && !y && !w && !h ) return;
// scale for screen sizes
xscale = (float)refState.width / (float)clgame.scrInfo.iWidth;
yscale = (float)refState.height / (float)clgame.scrInfo.iHeight;
if( x ) *x *= xscale;
if( y ) *y *= yscale;
if( w ) *w *= xscale;
if( h ) *h *= yscale;
num = bound( 0, num, CON_NUMFONTS - 1 );
return &con.chars[num];
}
/*
====================
Con_DrawGenericChar
Con_GetCurFont
draw console single character
====================
*/
static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
cl_font_t *Con_GetCurFont( void )
{
int width, height;
float s1, t1, s2, t2;
wrect_t *rc;
number &= 255;
if( !con.curFont || !con.curFont->valid )
return 0;
number = Con_UtfProcessChar(number);
if( !number )
return 0;
if( y < -con.curFont->charHeight )
return 0;
rc = &con.curFont->fontRc[number];
R_GetTextureParms( &width, &height, con.curFont->hFontTexture );
if( !width || !height )
return con.curFont->charWidths[number];
// don't apply color to fixed fonts it's already colored
if( con.curFont->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, con.curFont->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] );
// calc rectangle
s1 = (float)rc->left / width;
t1 = (float)rc->top / height;
s2 = (float)rc->right / width;
t2 = (float)rc->bottom / height;
width = ( rc->right - rc->left ) * con_fontscale->value;
height = ( rc->bottom - rc->top ) * con_fontscale->value;
if( clgame.ds.adjust_size )
Con_TextAdjustSize( &x, &y, &width, &height );
ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, con.curFont->hFontTexture );
ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color
return con.curFont->charWidths[number];
}
/*
====================
Con_SetFont
choose font size
====================
*/
void Con_SetFont( int fontNum )
{
fontNum = bound( 0, fontNum, CON_NUMFONTS - 1 );
con.curFont = &con.chars[fontNum];
}
/*
====================
Con_RestoreFont
restore auto-selected console font
(that based on screen resolution)
====================
*/
void Con_RestoreFont( void )
{
con.curFont = con.lastUsedFont;
}
/*
====================
Con_DrawCharacter
client version of routine
====================
*/
int Con_DrawCharacter( int x, int y, int number, rgba_t color )
{
ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture );
return Con_DrawGenericChar( x, y, number, color );
}
/*
====================
Con_DrawCharacterLen
returns character sizes in screen pixels
====================
*/
void Con_DrawCharacterLen( int number, int *width, int *height )
{
if( width && con.curFont ) *width = con.curFont->charWidths[number];
if( height && con.curFont ) *height = con.curFont->charHeight;
return con.curFont;
}
/*
@ -1009,105 +839,7 @@ compute string width and height in screen pixels
*/
void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height )
{
int curLength = 0;
if( !con.curFont )
return;
if( height )
*height = con.curFont->charHeight;
if (!length)
return;
*length = 0;
while( *pText )
{
byte c = *pText;
if( *pText == '\n' )
{
pText++;
curLength = 0;
}
// skip color strings they are not drawing
if( IsColorString( pText ))
{
pText += 2;
continue;
}
// Convert to unicode
c = Con_UtfProcessChar( c );
if( c )
curLength += con.curFont->charWidths[c];
pText++;
if( curLength > *length )
*length = curLength;
}
}
/*
==================
Con_DrawString
Draws a multi-colored string, optionally forcing
to a fixed color.
==================
*/
int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qboolean forceColor, int hideChar )
{
rgba_t color;
int drawLen = 0;
int numDraws = 0;
const char *s;
if( !con.curFont ) return 0; // no font set
Con_UtfProcessChar( 0 );
// draw the colored text
memcpy( color, setColor, sizeof( color ));
s = string;
while( *s )
{
if( *s == '\n' )
{
s++;
if( !*s ) break; // at end the string
drawLen = 0; // begin new row
y += con.curFont->charHeight;
}
if( IsColorString( s ))
{
if( !forceColor )
{
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ));
color[3] = setColor[3];
}
s += 2;
numDraws++;
continue;
}
// hide char for overstrike mode
if( hideChar == numDraws )
drawLen += con.curFont->charWidths[*s];
else drawLen += Con_DrawCharacter( x + drawLen, y, *s, color );
numDraws++;
s++;
}
ref.dllFuncs.Color4ub( 255, 255, 255, 255 );
return drawLen;
return CL_DrawStringLen( con.curFont, pText, length, height, FONT_DRAW_UTF8 );
}
/*
@ -1119,10 +851,9 @@ client version of routine
*/
int Con_DrawString( int x, int y, const char *string, rgba_t setColor )
{
return Con_DrawGenericString( x, y, string, setColor, false, -1 );
return CL_DrawString( x, y, string, setColor, con.curFont, FONT_DRAW_UTF8 );
}
/*
================
Con_Init
@ -1499,7 +1230,7 @@ void Field_KeyDownEvent( field_t *edit, int key )
return;
}
if( key == K_BACKSPACE )
if( key == K_BACKSPACE || key == K_X_BUTTON )
{
if( edit->cursor > 0 )
{
@ -1511,7 +1242,7 @@ void Field_KeyDownEvent( field_t *edit, int key )
return;
}
if( key == K_RIGHTARROW )
if( key == K_RIGHTARROW || key == K_DPAD_RIGHT )
{
if( edit->cursor < len ) edit->cursor = Con_UtfMoveRight( edit->buffer, edit->cursor, edit->widthInChars );
if( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
@ -1519,7 +1250,7 @@ void Field_KeyDownEvent( field_t *edit, int key )
return;
}
if( key == K_LEFTARROW )
if( key == K_LEFTARROW || key == K_DPAD_LEFT )
{
if( edit->cursor > 0 ) edit->cursor = Con_UtfMoveLeft( edit->buffer, edit->cursor );
if( edit->cursor < edit->scroll ) edit->scroll--;
@ -1614,7 +1345,7 @@ Field_DrawInputLine
void Field_DrawInputLine( int x, int y, field_t *edit )
{
int len, cursorChar;
int drawLen, hideChar = -1;
int drawLen;
int prestep, curPos;
char str[MAX_SYSPATH];
byte *colorDefault;
@ -1651,35 +1382,23 @@ void Field_DrawInputLine( int x, int y, field_t *edit )
// save char for overstrike
cursorChar = str[edit->cursor - prestep];
if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 ))
hideChar = edit->cursor - prestep; // skip this char
// draw it
Con_DrawGenericString( x, y, str, colorDefault, false, hideChar );
CL_DrawString( x, y, str, colorDefault, con.curFont, FONT_DRAW_UTF8 );
// draw the cursor
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
// calc cursor position
str[edit->cursor - prestep] = 0;
Con_DrawStringLen( str, &curPos, NULL );
Con_UtfProcessChar( 0 );
CL_DrawStringLen( con.curFont, str, &curPos, NULL, FONT_DRAW_UTF8 );
if( host.key_overstrike && cursorChar )
if( host.key_overstrike )
{
// overstrike cursor
#if 0
pglEnable( GL_BLEND );
pglDisable( GL_ALPHA_TEST );
pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
#endif
Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault );
CL_DrawCharacter( x + curPos, y, '|', colorDefault, con.curFont, 0 );
}
else
{
Con_UtfProcessChar( 0 );
Con_DrawCharacter( x + curPos, y, '_', colorDefault );
CL_DrawCharacter( x + curPos, y, '_', colorDefault, con.curFont, 0 );
}
}
@ -1828,8 +1547,8 @@ void Key_Console( int key )
return;
}
// enter finishes the line
if( key == K_ENTER || key == K_KP_ENTER )
// enter or A finish the line
if( key == K_ENTER || key == K_KP_ENTER || key == K_A_BUTTON )
{
// backslash text are commands, else chat
if( con.input.buffer[0] == '\\' || con.input.buffer[0] == '/' )
@ -1856,7 +1575,7 @@ void Key_Console( int key )
}
// command completion
if( key == K_TAB )
if( key == K_TAB || key == K_L2_BUTTON )
{
Con_CompleteCommand( &con.input );
Con_Bottom();
@ -1919,6 +1638,23 @@ void Key_Console( int key )
return;
}
// enable the OSK with button press
if( key == K_Y_BUTTON )
{
Key_EnableTextInput( true, true );
return;
}
// exit the console by pressing MINUS on NSwitch
// or both Back(Select)/Start buttons for everyone else
if( key == K_BACK_BUTTON || key == K_START_BUTTON )
{
if( cls.state == ca_active && !cl.background )
Key_SetKeyDest( key_game );
else UI_SetActiveMenu( true );
return;
}
// pass to the normal editline routine
Field_KeyDownEvent( &con.input, key );
}
@ -1934,14 +1670,14 @@ void Key_Message( int key )
{
char buffer[MAX_SYSPATH];
if( key == K_ESCAPE )
if( key == K_ESCAPE || key == K_BACK_BUTTON )
{
Key_SetKeyDest( key_game );
Con_ClearField( &con.chat );
return;
}
if( key == K_ENTER || key == K_KP_ENTER )
if( key == K_ENTER || key == K_KP_ENTER || key == K_A_BUTTON )
{
if( con.chat.buffer[0] && cls.state == ca_active )
{
@ -1983,7 +1719,7 @@ void Con_DrawInput( int lines )
return;
y = lines - ( con.curFont->charHeight * 2 );
Con_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7] );
CL_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7], con.curFont, 0 );
Field_DrawInputLine( con.curFont->charWidths[' ']*2, y, &con.input );
}
@ -2041,7 +1777,7 @@ void Con_DrawDebug( void )
if( scr_download->value != -1.0f )
{
Q_snprintf( dlstring, sizeof( dlstring ), "Downloading [%d remaining]: ^2%s^7 %5.1f%% time %.f secs",
host.downloadcount, host.downloadfile, scr_download->value, Sys_DoubleTime() - timeStart );
host.downloadcount, host.downloadfile, scr_download->value, Sys_DoubleTime() - timeStart );
x = refState.width - 500;
y = con.curFont->charHeight * 1.05f;
Con_DrawString( x, y, dlstring, g_color_table[7] );
@ -2051,7 +1787,7 @@ void Con_DrawDebug( void )
timeStart = Sys_DoubleTime();
}
if( !host_developer.value || Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" ))
if( !host.allow_console || Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" ))
return;
if( con.draw_notify && !Con_Visible( ))
@ -2077,7 +1813,7 @@ void Con_DrawNotify( void )
x = con.curFont->charWidths[' ']; // offset one space at left screen side
if( host_developer.value && ( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" )))
if( host.allow_console && ( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" )))
{
for( i = CON_LINES_COUNT - con.num_times; i < CON_LINES_COUNT; i++ )
{
@ -2128,7 +1864,11 @@ int Con_DrawConsoleLine( int y, int lineno )
return 0; // this string will be shown only at notify
if( y >= con.curFont->charHeight )
Con_DrawGenericString( con.curFont->charWidths[' '], y, li->start, g_color_table[7], false, -1 );
{
float x = con.curFont->charWidths[' '];
CL_DrawString( x, y, li->start, g_color_table[7], con.curFont, FONT_DRAW_UTF8 );
}
return con.curFont->charHeight;
}
@ -2195,17 +1935,15 @@ void Con_DrawSolidConsole( int lines )
// draw current version
memcpy( color, g_color_table[7], sizeof( color ));
Q_snprintf( curbuild, MAX_STRING, "%s %i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " %i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = refState.width - stringLen;
stringLen = Con_StringLength( curbuild );
start = refState.width - stringLen;
fraction = lines / (float)refState.height;
color[3] = Q_min( fraction * 2.0f, 1.0f ) * 255; // fadeout version number
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, 0, curbuild[i], color );
Con_DrawString( start, 0, curbuild, color );
// draw the text
if( CON_LINES_COUNT > 0 )
@ -2222,7 +1960,7 @@ void Con_DrawSolidConsole( int lines )
// draw red arrows to show the buffer is backscrolled
for( x = 0; x < con.linewidth; x += 4 )
Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] );
CL_DrawCharacter( ( x + 1 ) * start, y, '^', g_color_table[1], con.curFont, 0 );
y -= con.curFont->charHeight;
}
x = lastline;
@ -2325,7 +2063,7 @@ void Con_DrawVersion( void )
{
// draws the current build
byte *color = g_color_table[7];
int i, stringLen, width = 0, charH = 0;
int stringLen, charH = 0;
int start, height = refState.height;
qboolean draw_version = false;
string curbuild;
@ -2348,16 +2086,15 @@ void Con_DrawVersion( void )
host.force_draw_version = false;
if( host.force_draw_version || draw_version )
Q_snprintf( curbuild, MAX_STRING, "%s v%i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (%s-%s build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " v%i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
else Q_snprintf( curbuild, MAX_STRING, "v%i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( ));
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = refState.width - stringLen * 1.05f;
stringLen = Con_StringLength( curbuild );
height -= charH * 1.05f;
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, height, curbuild[i], color );
Con_DrawString( start, height, curbuild, color );
}
/*
@ -2397,28 +2134,35 @@ void Con_RunConsole( void )
con.vislines = con.showlines;
}
if( FBitSet( con_charset->flags, FCVAR_CHANGED ) ||
if( FBitSet( con_charset->flags, FCVAR_CHANGED ) ||
FBitSet( con_fontscale->flags, FCVAR_CHANGED ) ||
FBitSet( con_fontnum->flags, FCVAR_CHANGED ) ||
FBitSet( cl_charset->flags, FCVAR_CHANGED ) )
FBitSet( cl_charset->flags, FCVAR_CHANGED ))
{
// update codepage parameters
g_codepage = 0;
if( !Q_stricmp( con_charset->string, "cp1251" ) )
if( !Q_stricmp( con_charset->string, "cp1251" ))
{
g_codepage = 1251;
else if( !Q_stricmp( con_charset->string, "cp1252" ) )
}
else if( !Q_stricmp( con_charset->string, "cp1252" ))
{
g_codepage = 1252;
}
else
{
Con_Printf( S_WARN "Unknown charset %s, defaulting to cp1252", con_charset->string );
Cvar_DirectSet( con_charset, "cp1252" );
g_codepage = 1252;
}
g_utf8 = !Q_stricmp( cl_charset->string, "utf-8" );
Con_InvalidateFonts();
Con_LoadConchars();
cls.creditsFont.valid = false;
SCR_LoadCreditsFont();
ClearBits( con_charset->flags, FCVAR_CHANGED );
ClearBits( con_fontnum->flags, FCVAR_CHANGED );
ClearBits( con_fontscale->flags, FCVAR_CHANGED );
ClearBits( cl_charset->flags, FCVAR_CHANGED );
}
}
@ -2553,8 +2297,10 @@ Con_InvalidateFonts
*/
void Con_InvalidateFonts( void )
{
memset( con.chars, 0, sizeof( con.chars ));
con.curFont = con.lastUsedFont = NULL;
int i;
for( i = 0; i < ARRAYSIZE( con.chars ); i++ )
CL_FreeFont( &con.chars[i] );
con.curFont = NULL;
}
/*

View File

@ -244,7 +244,7 @@ void Joy_AxisMotionEvent( byte axis, short value )
return;
}
return Joy_KnownAxisMotionEvent( joyaxesmap[axis], value );
Joy_KnownAxisMotionEvent( joyaxesmap[axis], value );
}
void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value )

View File

@ -151,7 +151,6 @@ convar_t *touch_exp_mult;
convar_t *touch_grid_enable;
convar_t *touch_grid_count;
convar_t *touch_config_file;
convar_t *touch_enable;
convar_t *touch_in_menu;
convar_t *touch_joy_radius;
convar_t *touch_dpad_radius;
@ -162,7 +161,9 @@ convar_t *touch_highlight_b;
convar_t *touch_highlight_a;
convar_t *touch_precise_amount;
convar_t *touch_joy_texture;
convar_t *touch_emulate;
CVAR_DEFINE_AUTO( touch_enable, DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" );
CVAR_DEFINE_AUTO( touch_emulate, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" );
// code looks smaller with it
#define B(x) (button->x)
@ -214,6 +215,74 @@ static inline int Touch_ExportButtonToConfig( file_t *f, touch_button_t *button,
return 0;
}
/*
=================
Touch_DumpConfig
Dump config to file
=================
*/
qboolean Touch_DumpConfig( const char *name, const char *profilename )
{
file_t *f;
touch_button_t *button;
f = FS_Open( name, "w", true );
if( !f )
{
Con_Printf( S_ERROR "Couldn't write %s.\n", name );
return false;
}
FS_Printf( f, "//=======================================================================\n");
FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY ));
FS_Printf( f, "//\t\t\ttouchscreen config\n" );
FS_Printf( f, "//=======================================================================\n" );
FS_Printf( f, "\ntouch_config_file \"%s\"\n", profilename );
FS_Printf( f, "\n// touch cvars\n" );
FS_Printf( f, "\n// sensitivity settings\n" );
FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value );
FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value );
FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value );
FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value );
FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look));
FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value );
FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value );
FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value );
FS_Printf( f, "\n// grid settings\n" );
FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value );
FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable));
FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" );
FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] );
FS_Printf( f, "\n// highlight when pressed\n" );
FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value );
FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value );
FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value );
FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value );
FS_Printf( f, "\n// _joy and _dpad options\n" );
FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value );
FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value );
FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" );
FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value );
FS_Printf( f, "\n// enable/disable move indicator\n" );
FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value );
FS_Printf( f, "\n// reset menu state when execing config\n" );
FS_Printf( f, "touch_setclientonly 0\n" );
FS_Printf( f, "\n// touch buttons\n" );
FS_Printf( f, "touch_removeall\n" );
for( button = touch.list_user.first; button; button = button->next )
{
Touch_ExportButtonToConfig( f, button, false );
}
FS_Close( f );
return true;
}
/*
=================
Touch_WriteConfig
@ -237,61 +306,14 @@ void Touch_WriteConfig( void )
Q_snprintf( newconfigfile, sizeof( newconfigfile ), "%s.new", touch_config_file->string );
Q_snprintf( oldconfigfile, sizeof( oldconfigfile ), "%s.bak", touch_config_file->string );
f = FS_Open( newconfigfile, "w", true );
if( f )
if( Touch_DumpConfig( newconfigfile, touch_config_file->string ))
{
touch_button_t *button;
FS_Printf( f, "//=======================================================================\n");
FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY ));
FS_Printf( f, "//\t\t\ttouchscreen config\n" );
FS_Printf( f, "//=======================================================================\n" );
FS_Printf( f, "\ntouch_config_file \"%s\"\n", touch_config_file->string );
FS_Printf( f, "\n// touch cvars\n" );
FS_Printf( f, "\n// sensitivity settings\n" );
FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value );
FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value );
FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value );
FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value );
FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look));
FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value );
FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value );
FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value );
FS_Printf( f, "\n// grid settings\n" );
FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value );
FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable));
FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" );
FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] );
FS_Printf( f, "\n// highlight when pressed\n" );
FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value );
FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value );
FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value );
FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value );
FS_Printf( f, "\n// _joy and _dpad options\n" );
FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value );
FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value );
FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" );
FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value );
FS_Printf( f, "\n// enable/disable move indicator\n" );
FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value );
FS_Printf( f, "\n// reset menu state when execing config\n" );
FS_Printf( f, "touch_setclientonly 0\n" );
FS_Printf( f, "\n// touch buttons\n" );
FS_Printf( f, "touch_removeall\n" );
for( button = touch.list_user.first; button; button = button->next )
{
Touch_ExportButtonToConfig( f, button, false );
}
FS_Close( f );
FS_Delete( oldconfigfile );
FS_Rename( touch_config_file->string, oldconfigfile );
FS_Delete( touch_config_file->string );
FS_Rename( newconfigfile, touch_config_file->string );
}
else Con_Printf( S_ERROR "Couldn't write %s.\n", touch_config_file->string );
}
/*
@ -303,8 +325,8 @@ export current touch configuration into profile
*/
static void Touch_ExportConfig_f( void )
{
file_t *f;
const char *name;
string profilename, profilebase;
if( Cmd_Argc() != 2 )
{
@ -316,65 +338,15 @@ static void Touch_ExportConfig_f( void )
name = Cmd_Argv( 1 );
Con_Reportf( "Exporting config to %s\n", name );
f = FS_Open( name, "w", true );
if( f )
if( Q_strstr( name, "touch_presets/" ))
{
string profilename, profilebase;
touch_button_t *button;
if( Q_strstr( name, "touch_presets/" ) )
{
COM_FileBase( name, profilebase );
Q_snprintf( profilename, sizeof( profilebase ), "touch_profiles/%s (copy).cfg", profilebase );
}
else Q_strncpy( profilename, name, sizeof( profilename ));
FS_Printf( f, "//=======================================================================\n");
FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY ));
FS_Printf( f, "//\t\t\ttouchscreen preset\n" );
FS_Printf( f, "//=======================================================================\n" );
FS_Printf( f, "\ntouch_config_file \"%s\"\n", profilename );
FS_Printf( f, "\n// touch cvars\n" );
FS_Printf( f, "\n// sensitivity settings\n" );
FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value );
FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value );
FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value );
FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value );
FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look) );
FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value );
FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value );
FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value );
FS_Printf( f, "\n// grid settings\n" );
FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value );
FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable) );
FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" );
FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] );
FS_Printf( f, "\n// highlight when pressed\n" );
FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value );
FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value );
FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value );
FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value );
FS_Printf( f, "\n// _joy and _dpad options\n" );
FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value );
FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value );
FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" );
FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value );
FS_Printf( f, "\n// enable/disable move indicator\n" );
FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value );
FS_Printf( f, "\n// reset menu state when execing config\n" );
FS_Printf( f, "touch_setclientonly 0\n" );
FS_Printf( f, "\n// touch buttons\n" );
FS_Printf( f, "touch_removeall\n" );
for( button = touch.list_user.first; button; button = button->next )
{
Touch_ExportButtonToConfig( f, button, true );
}
FS_Printf( f, "\n// round button coordinates to grid\n" );
FS_Printf( f, "touch_roundall\n" );
FS_Close( f );
COM_FileBase( name, profilebase );
Q_snprintf( profilename, sizeof( profilebase ), "touch_profiles/%s (copy).cfg", profilebase );
}
else Con_Printf( S_ERROR "Couldn't write %s.\n", name );
else Q_strncpy( profilename, name, sizeof( profilename ));
Con_Reportf( "Exporting config to \"%s\", profile name \"%s\"\n", name, profilename );
Touch_DumpConfig( name, profilename );
}
/*
@ -497,8 +469,8 @@ static touch_button_t *Touch_FindFirst( touchbuttonlist_t *list, const char *nam
void Touch_SetClientOnly( byte state )
{
// TODO: fix clash with vgui cursors
touch.clientonly = state;
host.mouse_visible = state;
touch.move_finger = touch.look_finger = -1;
touch.forward = touch.side = 0;
@ -994,7 +966,7 @@ static void Touch_InitEditor( void )
Touch_ClearList( &touch.list_edit );
temp = Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close.tga", "touch_disableedit", 0, y, x, y + 0.1f, color, true );
temp = Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close", "touch_disableedit", 0, y, x, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
temp = Touch_AddButton( &touch.list_edit, "close", "#Close and save", "", x, y, x + 0.2f, y + 0.1f, color, true );
@ -1002,7 +974,7 @@ static void Touch_InitEditor( void )
y += 0.2f;
temp = Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset.tga", "touch_reloadconfig", 0, y, x, y + 0.1f, color, true );
temp = Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset", "touch_reloadconfig", 0, y, x, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
temp = Touch_AddButton( &touch.list_edit, "close", "#Cancel and reset", "", x, y, x + 0.2f, y + 0.1f, color, true );
@ -1010,7 +982,7 @@ static void Touch_InitEditor( void )
y += 0.2f;
touch.hidebutton = Touch_AddButton( &touch.list_edit, "showhide", "touch_default/edit_hide.tga", "touch_toggleselection", 0, y, x, y + 0.1f, color, true );
touch.hidebutton = Touch_AddButton( &touch.list_edit, "showhide", "touch_default/edit_hide", "touch_toggleselection", 0, y, x, y + 0.1f, color, true );
SetBits( touch.hidebutton->flags, TOUCH_FL_HIDE | TOUCH_FL_NOEDIT );
}
@ -1038,24 +1010,24 @@ void Touch_Init( void )
MakeRGBA( color, 255, 255, 255, 255 );
Touch_AddDefaultButton( "look", "", "_look", 0.500000, 0.000000, 1.000000, 1, color, 0, 0, 0 );
Touch_AddDefaultButton( "move", "", "_move", 0.000000, 0.000000, 0.500000, 1, color, 0, 0, 0 );
Touch_AddDefaultButton( "invnext", "touch_default/next_weap.tga", "invnext", 0.000000, 0.530200, 0.120000, 0.757428, color, 2, 1, 0 );
Touch_AddDefaultButton( "invprev", "touch_default/prev_weap.tga", "invprev", 0.000000, 0.075743, 0.120000, 0.302971, color, 2, 1, 0 );
Touch_AddDefaultButton( "use", "touch_default/use.tga", "+use", 0.880000, 0.454457, 1.000000, 0.681685, color, 2, 1, 0 );
Touch_AddDefaultButton( "jump", "touch_default/jump.tga", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 );
Touch_AddDefaultButton( "attack", "touch_default/shoot.tga", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 );
Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt.tga", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 );
Touch_AddDefaultButton( "loadquick", "touch_default/load.tga", "loadquick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 );
Touch_AddDefaultButton( "savequick", "touch_default/save.tga", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 );
Touch_AddDefaultButton( "messagemode", "touch_default/keyboard.tga", "messagemode", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 8 );
Touch_AddDefaultButton( "reload", "touch_default/reload.tga", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 );
Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled.tga", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 );
Touch_AddDefaultButton( "scores", "touch_default/map.tga", "+showscores", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 );
Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons.tga", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "duck", "touch_default/crouch.tga", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "tduck", "touch_default/tduck.tga", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 );
Touch_AddDefaultButton( "edit", "touch_default/settings.tga", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 );
Touch_AddDefaultButton( "menu", "touch_default/menu.tga", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "invnext", "touch_default/next_weap", "invnext", 0.000000, 0.530200, 0.120000, 0.757428, color, 2, 1, 0 );
Touch_AddDefaultButton( "invprev", "touch_default/prev_weap", "invprev", 0.000000, 0.075743, 0.120000, 0.302971, color, 2, 1, 0 );
Touch_AddDefaultButton( "use", "touch_default/use", "+use", 0.880000, 0.454457, 1.000000, 0.681685, color, 2, 1, 0 );
Touch_AddDefaultButton( "jump", "touch_default/jump", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 );
Touch_AddDefaultButton( "attack", "touch_default/shoot", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 );
Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 );
Touch_AddDefaultButton( "loadquick", "touch_default/load", "loadquick", 0.680000, 0.000000, 0.760000, 0.151486, color, 2, 1, 16 );
Touch_AddDefaultButton( "savequick", "touch_default/save", "savequick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 );
Touch_AddDefaultButton( "messagemode", "touch_default/keyboard", "messagemode", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 );
Touch_AddDefaultButton( "reload", "touch_default/reload", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 );
Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 );
Touch_AddDefaultButton( "scores", "touch_default/map", "+showscores", 0.680000, 0.000000, 0.760000, 0.151486, color, 2, 1, 8 );
Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 );
Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 );
Touch_AddDefaultButton( "menu", "touch_default/menu", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "spray", "touch_default/spray", "impulse 201", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 0 );
Cmd_AddCommand( "touch_addbutton", Touch_AddButton_f, "add native touch button" );
Cmd_AddCommand( "touch_removebutton", IN_TouchRemoveButton_f, "remove native touch button" );
@ -1106,14 +1078,19 @@ void Touch_Init( void )
touch_dpad_radius = Cvar_Get( "touch_dpad_radius", "1.0", FCVAR_FILTERABLE, "dpad radius multiplier" );
touch_joy_radius = Cvar_Get( "touch_joy_radius", "1.0", FCVAR_FILTERABLE, "joy radius multiplier" );
touch_move_indicator = Cvar_Get( "touch_move_indicator", "0.0", FCVAR_FILTERABLE, "indicate move events (0 to disable)" );
touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy.tga", FCVAR_FILTERABLE, "texture for move indicator");
touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy", FCVAR_FILTERABLE, "texture for move indicator");
// input devices cvar
touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" );
touch_emulate = Cvar_Get( "touch_emulate", "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" );
Cvar_RegisterVariable( &touch_enable );
Cvar_RegisterVariable( &touch_emulate );
/// TODO: touch sdl platform
// SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" );
// TODO: touch platform
#if SDL_VERSION_ATLEAST( 2, 0, 10 )
SDL_SetHint( SDL_HINT_MOUSE_TOUCH_EVENTS, "0" );
SDL_SetHint( SDL_HINT_TOUCH_MOUSE_EVENTS, "0" );
#else
SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" );
#endif
touch.initialized = true;
}
@ -1133,8 +1110,14 @@ static void Touch_InitConfig( void )
/// TODO: hud font
//pfnGetScreenInfo( NULL ); //HACK: update hud screen parameters like iHeight
if( FS_FileExists( touch_config_file->string, true ) )
{
Cbuf_AddText( va( "exec \"%s\"\n", touch_config_file->string ) );
else Touch_LoadDefaults_f( );
Cbuf_Execute();
}
else
{
Touch_LoadDefaults_f();
}
Touch_InitEditor();
touch.joytexture = ref.dllFuncs.GL_LoadTexture( touch_joy_texture->string, NULL, 0, TF_NOMIPMAP );
@ -1372,7 +1355,7 @@ void Touch_Draw( void )
{
touch_button_t *button;
if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) )
if( !touch.initialized || ( !touch_enable.value && !touch.clientonly ))
return;
Touch_InitConfig();
@ -1515,9 +1498,9 @@ static void Touch_EditMove( touchEventType type, int fingerID, float x, float y,
touch.hidebutton->flags &= ~TOUCH_FL_HIDE;
if( FBitSet( button->flags, TOUCH_FL_HIDE ))
Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_show.tga" );
Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_show" );
else
Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_hide.tga" );
Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_hide" );
}
}
if( type == event_motion ) // shutdown button move
@ -1966,15 +1949,15 @@ static int Touch_ControlsEvent( touchEventType type, int fingerID, float x, floa
int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy )
{
// simulate menu mouse click
if( cls.key_dest != key_game && !CVAR_TO_BOOL(touch_in_menu) )
if( cls.key_dest != key_game && !CVAR_TO_BOOL( touch_in_menu ))
{
touch.move_finger = touch.resize_finger = touch.look_finger = -1;
// Hack for keyboard, hope it help
if( cls.key_dest == key_console || cls.key_dest == key_message )
{
Key_EnableTextInput( true, true );
if ( type == event_down ) // don't pop it again on event_up
Key_EnableTextInput( true, true );
if( cls.key_dest == key_console )
{
static float y1 = 0;
@ -2011,25 +1994,20 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
{
VGui_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) );
if( type != event_motion )
VGui_KeyEvent( K_MOUSE1, type == event_down ? 1 : 0 );
// allow scoreboard scroll
if( host.mouse_visible && type == event_motion )
return 0;
switch( type )
{
case event_down:
VGui_MouseEvent( K_MOUSE1, 1 );
break;
case event_up:
VGui_MouseEvent( K_MOUSE1, 0 );
break;
default: break;
}
}
if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) )
{
#if 0
if( type == event_down )
Key_Event( K_MOUSE1, true );
if( type == event_up )
Key_Event( K_MOUSE1, false );
Android_AddMove( dx * (float)refState.width, dy * (float)refState.height );
#endif
if( !touch.initialized || ( !touch_enable.value && !touch.clientonly ))
return 0;
}
if( clgame.dllFuncs.pfnTouchEvent && clgame.dllFuncs.pfnTouchEvent( type, fingerID, x, y, dx, dy ) )
return true;
@ -2048,35 +2026,65 @@ void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch )
void Touch_KeyEvent( int key, int down )
{
int xi, yi;
float x, y;
static float lx, ly;
static int kidNamedFinger = -1;
touchEventType event;
float x, y;
int finger, xi, yi;
if( !CVAR_TO_BOOL(touch_emulate) )
if( !touch_emulate.value )
{
if( CVAR_TO_BOOL(touch_enable) )
if( touch_enable.value )
return;
if( !touch.clientonly )
return;
}
Platform_GetMousePos( &xi, &yi );
x = xi/SCR_W;
y = yi/SCR_H;
if( cls.key_dest == key_menu && down < 2 && key == K_MOUSE1 )
if( !key )
{
UI_MouseMove( xi, yi );
UI_KeyEvent( key, down );
if( kidNamedFinger < 0 )
return;
finger = kidNamedFinger;
event = event_motion;
}
else
{
finger = key == K_MOUSE1 ? 0 : 1;
if( down )
{
event = event_down;
kidNamedFinger = finger;
}
else
{
event = event_up;
kidNamedFinger = -1;
}
}
if( down == 1 )
Touch_ControlsEvent( event_down, key == K_MOUSE1?0:1, x, y, 0, 0 );
else
Touch_ControlsEvent( down? event_motion: event_up, key == K_MOUSE1?0:1, x, y, x-lx, y-ly );
lx = x, ly = y;
// don't deactivate mouse in game
// checking a case when mouse and touchscreen
// can be used simultaneously
Platform_SetCursorType( dc_arrow );
Platform_GetMousePos( &xi, &yi );
x = xi / SCR_W;
y = yi / SCR_H;
Con_DPrintf( "event %d %.2f %.2f %.2f %.2f\n",
event, x, y, x - lx, y - ly );
IN_TouchEvent( event, finger, x, y, x - lx, y - ly );
lx = x;
ly = y;
}
qboolean Touch_WantVisibleCursor( void )
{
return ( touch_enable.value && touch_emulate.value ) || touch.clientonly;
}
void Touch_Shutdown( void )

View File

@ -47,8 +47,6 @@ convar_t *cl_backspeed;
convar_t *look_filter;
convar_t *m_rawinput;
static qboolean s_bRawInput, s_bMouseGrab;
/*
================
IN_CollectInputDevices
@ -63,7 +61,7 @@ uint IN_CollectInputDevices( void )
if( !m_ignore->value ) // no way to check is mouse connected, so use cvar only
ret |= INPUT_DEVICE_MOUSE;
if( CVAR_TO_BOOL(touch_enable) )
if( touch_enable.value )
ret |= INPUT_DEVICE_TOUCH;
if( Joy_IsActive() ) // connected or enabled
@ -94,13 +92,13 @@ void IN_LockInputDevices( qboolean lock )
{
SetBits( m_ignore->flags, FCVAR_READ_ONLY );
SetBits( joy_enable->flags, FCVAR_READ_ONLY );
SetBits( touch_enable->flags, FCVAR_READ_ONLY );
SetBits( touch_enable.flags, FCVAR_READ_ONLY );
}
else
{
ClearBits( m_ignore->flags, FCVAR_READ_ONLY );
ClearBits( joy_enable->flags, FCVAR_READ_ONLY );
ClearBits( touch_enable->flags, FCVAR_READ_ONLY );
ClearBits( touch_enable.flags, FCVAR_READ_ONLY );
}
}
@ -173,19 +171,15 @@ Called when key_dest is changed
*/
void IN_ToggleClientMouse( int newstate, int oldstate )
{
if( newstate == oldstate ) return;
if( newstate == oldstate )
return;
if( oldstate == key_game )
// since SetCursorType controls cursor visibility
// execute it first, and then check mouse grab state
if( newstate == key_menu || newstate == key_console || newstate == key_message )
{
IN_DeactivateMouse();
}
else if( newstate == key_game )
{
IN_ActivateMouse();
}
Platform_SetCursorType( dc_arrow );
if( ( newstate == key_menu || newstate == key_console || newstate == key_message ) && ( !CL_IsBackgroundMap() || CL_IsBackgroundDemo( )))
{
#if XASH_ANDROID
Android_ShowMouse( true );
#endif
@ -195,6 +189,8 @@ void IN_ToggleClientMouse( int newstate, int oldstate )
}
else
{
Platform_SetCursorType( dc_none );
#if XASH_ANDROID
Android_ShowMouse( false );
#endif
@ -202,10 +198,21 @@ void IN_ToggleClientMouse( int newstate, int oldstate )
Evdev_SetGrab( true );
#endif
}
if( oldstate == key_game )
{
IN_DeactivateMouse();
}
else if( newstate == key_game )
{
IN_ActivateMouse();
}
}
void IN_CheckMouseState( qboolean active )
{
static qboolean s_bRawInput, s_bMouseGrab;
#if XASH_WIN32
qboolean useRawInput = CVAR_TO_BOOL( m_rawinput ) && clgame.client_dll_uses_sdl || clgame.dllFuncs.pfnLookEvent;
#else
@ -308,25 +315,25 @@ IN_MouseMove
*/
void IN_MouseMove( void )
{
POINT current_pos;
int x, y;
if( !in_mouseinitialized )
return;
if( touch_emulate.value )
{
// touch emulation overrides all input
Touch_KeyEvent( 0, 0 );
return;
}
// find mouse movement
Platform_GetMousePos( &current_pos.x, &current_pos.y );
Platform_GetMousePos( &x, &y );
VGui_MouseMove( current_pos.x, current_pos.y );
// HACKHACK: show cursor in UI, as mainui doesn't call
// platform-dependent SetCursor anymore
#if XASH_SDL
if( UI_IsVisible() )
SDL_ShowCursor( SDL_TRUE );
#endif
VGui_MouseMove( x, y );
// if the menu is visible, move the menu cursor
UI_MouseMove( current_pos.x, current_pos.y );
UI_MouseMove( x, y );
}
/*
@ -336,8 +343,6 @@ IN_MouseEvent
*/
void IN_MouseEvent( int key, int down )
{
int i;
if( !in_mouseinitialized )
return;
@ -345,10 +350,15 @@ void IN_MouseEvent( int key, int down )
SetBits( in_mstate, BIT( key ));
else ClearBits( in_mstate, BIT( key ));
if( cls.key_dest == key_game )
// touch emulation overrides all input
if( touch_emulate.value )
{
Touch_KeyEvent( K_MOUSE1 + key, down );
}
else if( cls.key_dest == key_game )
{
// perform button actions
VGui_KeyEvent( K_MOUSE1 + key, down );
VGui_MouseEvent( K_MOUSE1 + key, down );
// don't do Key_Event here
// client may override IN_MouseEvent
@ -363,6 +373,23 @@ void IN_MouseEvent( int key, int down )
}
}
/*
==============
IN_MWheelEvent
direction is negative for wheel down, otherwise wheel up
==============
*/
void IN_MWheelEvent( int y )
{
int b = y > 0 ? K_MWHEELUP : K_MWHEELDOWN;
VGui_MWheelEvent( y );
Key_Event( b, true );
Key_Event( b, false );
}
/*
===========
IN_Shutdown

View File

@ -34,6 +34,7 @@ void IN_Init( void );
void Host_InputFrame( void );
void IN_Shutdown( void );
void IN_MouseEvent( int key, int down );
void IN_MWheelEvent( int direction );
void IN_ActivateMouse( void );
void IN_DeactivateMouse( void );
void IN_MouseSavePos( void );
@ -57,8 +58,8 @@ typedef enum
event_motion
} touchEventType;
extern convar_t *touch_enable;
extern convar_t *touch_emulate;
extern convar_t touch_enable;
extern convar_t touch_emulate;
void Touch_Draw( void );
void Touch_SetClientOnly( byte state );
@ -73,6 +74,7 @@ void Touch_GetMove( float * forward, float *side, float *yaw, float *pitch );
void Touch_ResetDefaultButtons( void );
int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy );
void Touch_KeyEvent( int key, int down );
qboolean Touch_WantVisibleCursor( void );
//
// in_joy.c

View File

@ -104,9 +104,9 @@ keyname_t keynames[] =
{"B_BUTTON", K_B_BUTTON, "+use"},
{"X_BUTTON", K_X_BUTTON, "+reload"},
{"Y_BUTTON", K_Y_BUTTON, "impulse 100"}, // Flashlight
{"BACK", K_BACK_BUTTON, "cancelselect"}, // Menu
{"BACK", K_BACK_BUTTON, "pause"}, // Menu
{"MODE", K_MODE_BUTTON, ""},
{"START", K_START_BUTTON, "pause"},
{"START", K_START_BUTTON, "escape"},
{"STICK1", K_LSTICK, "+speed"},
{"STICK2", K_RSTICK, "+duck"},
{"L1_BUTTON", K_L1_BUTTON, "+duck"},
@ -123,13 +123,13 @@ keyname_t keynames[] =
{"JOY4" , K_JOY4 , ""},
{"C_BUTTON", K_C_BUTTON, ""},
{"Z_BUTTON", K_Z_BUTTON, ""},
{"AUX20", K_AUX20, ""}, // generic
{"AUX21", K_AUX21, ""},
{"AUX22", K_AUX22, ""},
{"AUX23", K_AUX23, ""},
{"AUX24", K_AUX24, ""},
{"AUX25", K_AUX25, ""},
{"AUX26", K_AUX26, ""},
{"MISC_BUTTON", K_MISC_BUTTON, ""},
{"PADDLE1", K_PADDLE1_BUTTON, ""},
{"PADDLE2", K_PADDLE2_BUTTON, ""},
{"PADDLE3", K_PADDLE3_BUTTON, ""},
{"PADDLE4", K_PADDLE4_BUTTON, ""},
{"TOUCHPAD", K_TOUCHPAD, ""},
{"AUX26", K_AUX26, ""}, // generic
{"AUX27", K_AUX27, ""},
{"AUX28", K_AUX28, ""},
{"AUX29", K_AUX29, ""},
@ -374,7 +374,7 @@ void Key_Unbindall_f( void )
{
int i;
for( i = 0; i < 256; i++ )
for( i = 0; i < ARRAYSIZE( keys ); i++ )
{
if( keys[i].binding )
Key_SetBinding( i, "" );
@ -382,6 +382,7 @@ void Key_Unbindall_f( void )
// set some defaults
Key_SetBinding( K_ESCAPE, "escape" );
Key_SetBinding( K_START_BUTTON, "escape" );
}
/*
@ -395,7 +396,7 @@ void Key_Reset_f( void )
int i;
// clear all keys first
for( i = 0; i < 256; i++ )
for( i = 0; i < ARRAYSIZE( keys ); i++ )
{
if( keys[i].binding )
Key_SetBinding( i, "" );
@ -712,7 +713,6 @@ void GAME_EXPORT Key_Event( int key, int down )
}
VGui_KeyEvent( key, down );
Touch_KeyEvent( key, down );
// console key is hardcoded, so the user can never unbind it
if( key == '`' || key == '~' )
@ -842,7 +842,9 @@ void GAME_EXPORT Key_SetKeyDest( int key_dest )
cls.key_dest = key_menu;
break;
case key_console:
#if !XASH_NSWITCH // if we don't disable this, pops up the keyboard during load
Key_EnableTextInput( true, false );
#endif
cls.key_dest = key_console;
break;
case key_message:
@ -1149,7 +1151,7 @@ Draw button with symbol on it
*/
static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height )
{
char str[] = {symb & 255, 0};
cl_font_t *font = Con_GetCurFont();
byte color[] = { 255, 255, 255, 255 };
int x1 = x * refState.width,
y1 = y * refState.height,
@ -1157,14 +1159,15 @@ static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float
h = height * refState.height;
if( symb == osk.curbutton.val )
{
ref.dllFuncs.FillRGBABlend( x1, y1, w, h, 255, 160, 0, 100 );
}
if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) )
return;
Con_DrawCharacter( x1 + 1, y1, symb, color );
CL_DrawCharacter(
x1 + width * 0.4 * refState.width,
y1 + height * 0.4 * refState.height,
symb, color, font, 0 );
}
/*
@ -1178,7 +1181,11 @@ static void OSK_DrawSpecialButton( const char *name, float x, float y, float wid
{
byte color[] = { 0, 255, 0, 255 };
Con_DrawString( x * refState.width, y * refState.height, name, color );
Con_DrawString(
x * refState.width + width * 0.4 * refState.width,
y * refState.height + height * 0.4 * refState.height,
name,
color );
}

View File

@ -56,12 +56,11 @@ void GL_RenderFrame( const ref_viewpass_t *rvp )
VectorCopy( rvp->vieworigin, refState.vieworg );
VectorCopy( rvp->viewangles, refState.viewangles );
AngleVectors( refState.viewangles, refState.vforward, refState.vright, refState.vup );
ref.dllFuncs.GL_RenderFrame( rvp );
}
static int pfnEngineGetParm( int parm, int arg )
static intptr_t pfnEngineGetParm( int parm, int arg )
{
return CL_RenderGetParm( parm, arg, false ); // prevent recursion
}
@ -152,7 +151,7 @@ static player_info_t *pfnPlayerInfo( int index )
if( index == -1 ) // special index for menu
return &gameui.playerinfo;
if( index < 0 || index > cl.maxclients )
if( index < 0 || index >= cl.maxclients )
return NULL;
return &cl.players[index];
@ -280,9 +279,6 @@ static ref_api_t gEngfuncs =
Mod_PointInLeaf,
Mod_CreatePolygonsForHull,
R_StudioSlerpBones,
R_StudioCalcBoneQuaternion,
R_StudioCalcBonePosition,
R_StudioGetAnim,
pfnStudioEvent,
@ -360,7 +356,7 @@ static ref_api_t gEngfuncs =
pfnGetPhysent,
pfnTraceSurface,
PM_TraceLine,
PM_CL_TraceLine,
CL_VisTraceLine,
CL_TraceLine,
pfnGetMoveVars,

View File

@ -14,6 +14,7 @@ GNU General Public License for more details.
*/
#include "common.h"
#include "client.h"
#include "sound.h"
// during registration it is possible to have more sounds
@ -28,7 +29,6 @@ static sfx_t s_knownSfx[MAX_SFX];
static sfx_t *s_sfxHashList[MAX_SFX_HASH];
static string s_sentenceImmediateName; // keep dummy sentence name
qboolean s_registering = false;
int s_registration_sequence = 0;
/*
=================
@ -199,7 +199,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache )
*pfInCache = ( sfx->cache != NULL ) ? true : false;
}
// prolonge registration
sfx->servercount = s_registration_sequence;
sfx->servercount = cl.servercount;
return sfx;
}
}
@ -219,7 +219,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache )
memset( sfx, 0, sizeof( *sfx ));
if( pfInCache ) *pfInCache = false;
Q_strncpy( sfx->name, name, MAX_STRING );
sfx->servercount = s_registration_sequence;
sfx->servercount = cl.servercount;
sfx->hashValue = COM_HashKey( sfx->name, MAX_SFX_HASH );
// link it in
@ -273,7 +273,6 @@ void S_BeginRegistration( void )
{
int i;
s_registration_sequence++;
snd_ambient = false;
// check for automatic ambient sounds
@ -309,7 +308,7 @@ void S_EndRegistration( void )
if( !sfx->name[0] || !Q_stricmp( sfx->name, "*default" ))
continue; // don't release default sound
if( sfx->servercount != s_registration_sequence )
if( sfx->servercount != cl.servercount )
S_FreeSound( sfx ); // don't need this sound
}
@ -349,7 +348,7 @@ sound_t S_RegisterSound( const char *name )
sfx = S_FindName( name, NULL );
if( !sfx ) return -1;
sfx->servercount = s_registration_sequence;
sfx->servercount = cl.servercount;
if( !s_registering ) S_LoadSound( sfx );
return sfx - s_knownSfx;

View File

@ -533,8 +533,6 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv
// spatialize
memset( target_chan, 0, sizeof( *target_chan ));
pitch *= (sys_timescale.value + 1) / 2;
VectorCopy( pos, target_chan->origin );
target_chan->staticsound = ( ent == 0 ) ? true : false;
target_chan->use_loop = (flags & SND_STOP_LOOPING) ? false : true;
@ -1129,7 +1127,7 @@ static uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend,
S_RawEntSamples
===================
*/
static void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol )
void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol )
{
rawchan_t *ch;
@ -1288,6 +1286,9 @@ static void S_FreeIdleRawChannels( void )
if( ch->s_rawend >= paintedtime )
continue;
if ( ch->entnum > 0 )
SND_ForceCloseMouth( ch->entnum );
if(( paintedtime - ch->s_rawend ) / SOUND_DMA_SPEED >= S_RAW_SOUND_IDLE_SEC )
{
@ -1850,7 +1851,7 @@ S_SoundInfo_f
*/
void S_SoundInfo_f( void )
{
Con_Printf( "Audio: DirectSound\n" );
Con_Printf( "Audio backend: %s\n", dma.backendName );
Con_Printf( "%5d channel(s)\n", 2 );
Con_Printf( "%5d samples\n", dma.samples );
Con_Printf( "%5d bits/sample\n", 16 );
@ -1860,6 +1861,33 @@ void S_SoundInfo_f( void )
S_PrintBackgroundTrackState ();
}
/*
=================
S_VoiceRecordStart_f
=================
*/
void S_VoiceRecordStart_f( void )
{
if( cls.state != ca_active || cls.legacymode )
return;
Voice_RecordStart();
}
/*
=================
S_VoiceRecordStop_f
=================
*/
void S_VoiceRecordStop_f( void )
{
if( cls.state != ca_active || !Voice_IsRecording() )
return;
CL_AddVoiceToDatagram();
Voice_RecordStop();
}
/*
================
S_Init
@ -1894,11 +1922,12 @@ qboolean S_Init( void )
Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" );
Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" );
Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" );
Cmd_AddCommand( "+voicerecord", Cmd_Null_f, "start voice recording (non-implemented)" );
Cmd_AddCommand( "-voicerecord", Cmd_Null_f, "stop voice recording (non-implemented)" );
Cmd_AddCommand( "+voicerecord", S_VoiceRecordStart_f, "start voice recording" );
Cmd_AddCommand( "-voicerecord", S_VoiceRecordStop_f, "stop voice recording" );
Cmd_AddCommand( "spk", S_SayReliable_f, "reliable play a specified sententce" );
Cmd_AddCommand( "speak", S_Say_f, "playing a specified sententce" );
dma.backendName = "None";
if( !SNDDMA_Init( ) )
{
Con_Printf( "Audio: sound system can't be initialized\n" );

View File

@ -619,7 +619,9 @@ void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate )
ch->pitch = VOX_ModifyPitch( ch, ch->basePitch * 0.01f );
else ch->pitch = ch->basePitch * 0.01f;
if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE ))
ch->pitch *= ( sys_timescale.value + 1 ) / 2;
if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM ))
{
if( pSource->width == 1 )
SND_MoveMouth8( ch, pSource, sampleCount );
@ -895,43 +897,13 @@ void S_MixUpsample( int sampleCount, int filtertype )
ppaint->ifilter++;
}
void MIX_MixStreamBuffer( int end )
{
portable_samplepair_t *pbuf;
rawchan_t *ch;
pbuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER );
ch = S_FindRawChannel( S_RAW_SOUND_BACKGROUNDTRACK, false );
// clear the paint buffer
if( s_listener.paused || !ch || ch->s_rawend < paintedtime )
{
memset( pbuf, 0, (end - paintedtime) * sizeof( portable_samplepair_t ));
}
else
{
int i, stop;
// copy from the streaming sound source
stop = (end < ch->s_rawend) ? end : ch->s_rawend;
for( i = paintedtime; i < stop; i++ )
{
pbuf[i-paintedtime].left = ( ch->rawsamples[i & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8;
pbuf[i-paintedtime].right = ( ch->rawsamples[i & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8;
}
for( ; i < end; i++ )
pbuf[i-paintedtime].left = pbuf[i-paintedtime].right = 0;
}
}
void MIX_MixRawSamplesBuffer( int end )
{
portable_samplepair_t *pbuf;
portable_samplepair_t *pbuf, *roombuf, *streambuf;
uint i, j, stop;
pbuf = MIX_GetCurrentPaintbufferPtr()->pbuf;
roombuf = MIX_GetPFrontFromIPaint( IROOMBUFFER );
streambuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER );
if( s_listener.paused ) return;
@ -939,16 +911,19 @@ void MIX_MixRawSamplesBuffer( int end )
for( i = 0; i < MAX_RAW_CHANNELS; i++ )
{
// copy from the streaming sound source
rawchan_t *ch = raw_channels[i];
rawchan_t *ch = raw_channels[i];
qboolean stream;
// background track should be mixing into another buffer
if( !ch || ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK )
if( !ch )
continue;
// not audible
if( !ch->leftvol && !ch->rightvol )
continue;
stream = ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK || CL_IsPlayerIndex( ch->entnum );
pbuf = stream ? streambuf : roombuf;
stop = (end < ch->s_rawend) ? end : ch->s_rawend;
for( j = paintedtime; j < stop; j++ )
@ -956,6 +931,9 @@ void MIX_MixRawSamplesBuffer( int end )
pbuf[j-paintedtime].left += ( ch->rawsamples[j & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8;
pbuf[j-paintedtime].right += ( ch->rawsamples[j & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8;
}
if( ch->entnum > 0 )
SND_MoveMouthRaw( ch, &ch->rawsamples[paintedtime & ( ch->max_samples - 1 )], stop - paintedtime );
}
}
@ -965,9 +943,6 @@ void MIX_MixRawSamplesBuffer( int end )
// caller also remixes all into final IPAINTBUFFER output.
void MIX_UpsampleAllPaintbuffers( int end, int count )
{
// process stream buffer
MIX_MixStreamBuffer( end );
// 11khz sounds are mixed into 3 buffers based on distance from listener, and facing direction
// These buffers are facing, facingaway, room
// These 3 mixed buffers are then each upsampled to 22khz.
@ -1009,7 +984,6 @@ void MIX_UpsampleAllPaintbuffers( int end, int count )
#endif
// mix raw samples from the video streams
MIX_SetCurrentPaintbuffer( IROOMBUFFER );
MIX_MixRawSamplesBuffer( end );
MIX_DeactivateAllPaintbuffers();

View File

@ -24,17 +24,7 @@ void SND_InitMouth( int entnum, int entchannel )
{
if(( entchannel == CHAN_VOICE || entchannel == CHAN_STREAM ) && entnum > 0 )
{
cl_entity_t *clientEntity;
// init mouth movement vars
clientEntity = CL_GetEntityByIndex( entnum );
if( clientEntity )
{
clientEntity->mouth.mouthopen = 0;
clientEntity->mouth.sndcount = 0;
clientEntity->mouth.sndavg = 0;
}
SND_ForceInitMouth( entnum );
}
}
@ -42,15 +32,7 @@ void SND_CloseMouth( channel_t *ch )
{
if( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM )
{
cl_entity_t *clientEntity;
clientEntity = CL_GetEntityByIndex( ch->entnum );
if( clientEntity )
{
// shut mouth
clientEntity->mouth.mouthopen = 0;
}
SND_ForceCloseMouth( ch->entnum );
}
}
@ -150,3 +132,68 @@ void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count )
pMouth->sndcount = 0;
}
}
void SND_ForceInitMouth( int entnum )
{
cl_entity_t *clientEntity;
clientEntity = CL_GetEntityByIndex( entnum );
if( clientEntity )
{
clientEntity->mouth.mouthopen = 0;
clientEntity->mouth.sndavg = 0;
clientEntity->mouth.sndcount = 0;
}
}
void SND_ForceCloseMouth( int entnum )
{
cl_entity_t *clientEntity;
clientEntity = CL_GetEntityByIndex( entnum );
if( clientEntity )
clientEntity->mouth.mouthopen = 0;
}
void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count )
{
cl_entity_t *clientEntity;
mouth_t *pMouth = NULL;
int savg, data;
int scount = 0;
uint i;
clientEntity = CL_GetEntityByIndex( ch->entnum );
if( !clientEntity ) return;
pMouth = &clientEntity->mouth;
if( pData == NULL )
return;
i = 0;
scount = pMouth->sndcount;
savg = 0;
while ( i < count && scount < CAVGSAMPLES )
{
data = pData[i].left; // mono sound anyway
data = ( bound( -32767, data, 0x7ffe ) >> 8 );
savg += abs( data );
i += 80 + ( (byte)data & 0x1F );
scount++;
}
pMouth->sndavg += savg;
pMouth->sndcount = (byte)scount;
if ( pMouth->sndcount >= CAVGSAMPLES )
{
pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES;
pMouth->sndavg = 0;
pMouth->sndcount = 0;
}
}

View File

@ -113,10 +113,11 @@ typedef struct snd_format_s
typedef struct
{
snd_format_t format;
int samples; // mono samples in buffer
int samplepos; // in mono samples
byte *buffer;
int samples; // mono samples in buffer
int samplepos; // in mono samples
byte *buffer;
qboolean initialized; // sound engine is active
const char *backendName;
} dma_t;
#include "vox.h"
@ -202,7 +203,7 @@ typedef struct
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
#define MAX_CHANNELS (256 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
#define MAX_RAW_CHANNELS 16
#define MAX_RAW_CHANNELS 48
#define MAX_RAW_SAMPLES 8192
extern sound_t ambient_sfx[NUM_AMBIENTS];
@ -271,6 +272,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size );
int S_GetCurrentDynamicSounds( soundlist_t *pout, int size );
sfx_t *S_GetSfxByHandle( sound_t handle );
rawchan_t *S_FindRawChannel( int entnum, qboolean create );
void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol );
void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum );
void S_StopSound( int entnum, int channel, const char *soundname );
void S_UpdateFrame( struct ref_viewpass_s *rvp );
@ -283,9 +285,12 @@ void S_FreeSounds( void );
// s_mouth.c
//
void SND_InitMouth( int entnum, int entchannel );
void SND_ForceInitMouth( int entnum );
void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count );
void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count );
void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count );
void SND_CloseMouth( channel_t *ch );
void SND_ForceCloseMouth( int entnum );
//
// s_stream.c

View File

@ -23,93 +23,101 @@ GNU General Public License for more details.
#include "input.h"
#include "platform/platform.h"
static enum VGUI_KeyCode s_pVirtualKeyTrans[256];
static VGUI_DefaultCursor s_currentCursor;
static HINSTANCE s_pVGuiSupport; // vgui_support library
static convar_t *vgui_utf8 = NULL;
CVAR_DEFINE_AUTO( vgui_utf8, "0", FCVAR_ARCHIVE, "enable utf-8 support for vgui text" );
void GAME_EXPORT *VGUI_EngineMalloc(size_t size)
static void GAME_EXPORT *VGUI_EngineMalloc( size_t size );
static void GAME_EXPORT VGUI_GetMousePos( int *, int * );
static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor );
static byte GAME_EXPORT VGUI_GetColor( int, int );
static int GAME_EXPORT VGUI_UtfProcessChar( int in );
static qboolean GAME_EXPORT VGUI_IsInGame( void );
static struct
{
qboolean initialized;
vguiapi_t dllFuncs;
VGUI_DefaultCursor cursor;
HINSTANCE hInstance;
enum VGUI_KeyCode virtualKeyTrans[256];
} vgui =
{
false,
{
false, // Not initialized yet
NULL, // VGUI_DrawInit,
NULL, // VGUI_DrawShutdown,
NULL, // VGUI_SetupDrawingText,
NULL, // VGUI_SetupDrawingRect,
NULL, // VGUI_SetupDrawingImage,
NULL, // VGUI_BindTexture,
NULL, // VGUI_EnableTexture,
NULL, // VGUI_CreateTexture,
NULL, // VGUI_UploadTexture,
NULL, // VGUI_UploadTextureBlock,
NULL, // VGUI_DrawQuad,
NULL, // VGUI_GetTextureSizes,
NULL, // VGUI_GenerateTexture,
VGUI_EngineMalloc,
VGUI_CursorSelect,
VGUI_GetColor,
VGUI_IsInGame,
Key_EnableTextInput,
VGUI_GetMousePos,
VGUI_UtfProcessChar,
Platform_GetClipboardText,
Platform_SetClipboardText,
Platform_GetKeyModifiers,
},
-1
};
static void GAME_EXPORT *VGUI_EngineMalloc( size_t size )
{
return Z_Malloc( size );
}
qboolean GAME_EXPORT VGUI_IsInGame( void )
static qboolean GAME_EXPORT VGUI_IsInGame( void )
{
return cls.state == ca_active && cls.key_dest == key_game;
}
void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y )
static void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y )
{
float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth;
float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight;
int x, y;
Platform_GetMousePos( &x, &y );
*_x = x / xscale, *_y = y / yscale;
*_x = x / xscale;
*_y = y / yscale;
}
void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor )
static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor )
{
Platform_SetCursorType( cursor );
s_currentCursor = cursor;
if( vgui.cursor != cursor )
Platform_SetCursorType( cursor );
}
byte GAME_EXPORT VGUI_GetColor( int i, int j)
static byte GAME_EXPORT VGUI_GetColor( int i, int j )
{
return g_color_table[i][j];
}
// Define and initialize vgui API
int GAME_EXPORT VGUI_UtfProcessChar( int in )
static int GAME_EXPORT VGUI_UtfProcessChar( int in )
{
if( CVAR_TO_BOOL( vgui_utf8 ))
if( vgui_utf8.value )
return Con_UtfProcessCharForce( in );
else
return in;
return in;
}
vguiapi_t vgui =
{
false, // Not initialized yet
NULL, // VGUI_DrawInit,
NULL, // VGUI_DrawShutdown,
NULL, // VGUI_SetupDrawingText,
NULL, // VGUI_SetupDrawingRect,
NULL, // VGUI_SetupDrawingImage,
NULL, // VGUI_BindTexture,
NULL, // VGUI_EnableTexture,
NULL, // VGUI_CreateTexture,
NULL, // VGUI_UploadTexture,
NULL, // VGUI_UploadTextureBlock,
NULL, // VGUI_DrawQuad,
NULL, // VGUI_GetTextureSizes,
NULL, // VGUI_GenerateTexture,
VGUI_EngineMalloc,
VGUI_CursorSelect,
VGUI_GetColor,
VGUI_IsInGame,
NULL,
VGUI_GetMousePos,
VGUI_UtfProcessChar,
Platform_GetClipboardText,
Platform_SetClipboardText,
Platform_GetKeyModifiers,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
qboolean VGui_IsActive( void )
{
return vgui.initialized;
}
void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from )
static void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from )
{
to->DrawInit = from->VGUI_DrawInit;
to->DrawShutdown = from->VGUI_DrawShutdown;
@ -126,137 +134,86 @@ void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from )
to->GenerateTexture = from->VGUI_GenerateTexture;
}
void VGui_RegisterCvars( void )
{
Cvar_RegisterVariable( &vgui_utf8 );
}
qboolean VGui_LoadProgs( HINSTANCE hInstance )
{
void (*F)( vguiapi_t* );
qboolean client = hInstance != NULL;
// not loading interface from client.dll, load vgui_support.dll instead
if( !client )
{
string vguiloader, vguilib;
// HACKHACK: try to load path from custom path
// to support having different versions of VGUI
if( Sys_GetParmFromCmdLine( "-vguilib", vguilib ) && !COM_LoadLibrary( vguilib, false, false ))
{
Con_Reportf( S_WARN "VGUI preloading failed. Default library will be used! Reason: %s", COM_GetLibraryError());
}
if( !Sys_GetParmFromCmdLine( "-vguiloader", vguiloader ))
{
Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, sizeof( vguiloader ));
}
hInstance = vgui.hInstance = COM_LoadLibrary( vguiloader, false, false );
if( !vgui.hInstance )
{
if( FS_FileExists( vguiloader, false ))
Con_Reportf( S_ERROR "Failed to load vgui_support library: %s\n", COM_GetLibraryError() );
else Con_Reportf( "vgui_support: not found\n" );
return false;
}
}
// try legacy API first
F = COM_GetProcAddress( hInstance, client ? "InitVGUISupportAPI" : "InitAPI" );
if( F )
{
VGui_FillAPIFromRef( &vgui.dllFuncs, &ref.dllFuncs );
F( &vgui.dllFuncs );
vgui.initialized = vgui.dllFuncs.initialized = true;
Con_Reportf( "vgui_support: initialized legacy API in %s module\n", client ? "client" : "support" );
return true;
}
Con_Reportf( S_ERROR "Failed to find VGUI support API entry point in %s module\n", client ? "client" : "support" );
return false;
}
/*
================
VGui_Startup
Load vgui_support library and call VGui_Startup
================
*/
void VGui_Startup( const char *clientlib, int width, int height )
void VGui_Startup( int width, int height )
{
static qboolean failed = false;
void (*F) ( vguiapi_t * );
char vguiloader[256];
char vguilib[256];
vguiloader[0] = vguilib[0] = '\0';
if( failed )
// vgui not initialized from both support and client modules, skip
if( !vgui.initialized )
return;
if( !vgui.initialized )
{
vgui_utf8 = Cvar_Get( "vgui_utf8", "0", FCVAR_ARCHIVE, "enable utf-8 support for vgui text" );
height = Q_max( 480, height );
VGui_FillAPIFromRef( &vgui, &ref.dllFuncs );
if( width <= 640 ) width = 640;
else if( width <= 800 ) width = 800;
else if( width <= 1024 ) width = 1024;
else if( width <= 1152 ) width = 1152;
else if( width <= 1280 ) width = 1280;
else if( width <= 1600 ) width = 1600;
s_pVGuiSupport = COM_LoadLibrary( clientlib, false, false );
if( s_pVGuiSupport )
{
F = COM_GetProcAddress( s_pVGuiSupport, "InitVGUISupportAPI" );
if( F )
{
F( &vgui );
vgui.initialized = true;
Con_Reportf( "vgui_support: found internal client support\n" );
}
else
{
COM_FreeLibrary( s_pVGuiSupport );
}
}
if( !vgui.initialized )
{
// HACKHACK: load vgui with correct path first if specified.
// it will be reused while resolving vgui support and client deps
if( Sys_GetParmFromCmdLine( "-vguilib", vguilib ))
{
if( Q_strstr( vguilib, ".dll" ))
Q_strncpy( vguiloader, "vgui_support.dll", 256 );
else
Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, 256 );
if( !COM_LoadLibrary( vguilib, false, false ))
Con_Reportf( S_WARN "VGUI preloading failed. Default library will be used! Reason: %s\n", COM_GetLibraryError() );
}
if( Q_strstr( clientlib, ".dll" ))
Q_strncpy( vguiloader, "vgui_support.dll", 256 );
if( !vguiloader[0] && !Sys_GetParmFromCmdLine( "-vguiloader", vguiloader ))
Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, 256 );
s_pVGuiSupport = COM_LoadLibrary( vguiloader, false, false );
if( !s_pVGuiSupport )
{
s_pVGuiSupport = COM_LoadLibrary( va( "../%s", vguiloader ), false, false );
}
if( !s_pVGuiSupport )
{
if( FS_FileExists( vguiloader, false ))
{
Con_Reportf( S_ERROR "Failed to load vgui_support library: %s\n", COM_GetLibraryError() );
}
else
{
Con_Reportf( "vgui_support: not found\n" );
}
failed = true;
}
else
{
F = COM_GetProcAddress( s_pVGuiSupport, "InitAPI" );
if( F )
{
F( &vgui );
vgui.initialized = true;
}
else
{
Con_Reportf( S_ERROR "Failed to find vgui_support library entry point!\n" );
failed = true;
COM_FreeLibrary( s_pVGuiSupport );
}
}
}
}
if( height < 480 )
height = 480;
if( width <= 640 )
width = 640;
else if( width <= 800 )
width = 800;
else if( width <= 1024 )
width = 1024;
else if( width <= 1152 )
width = 1152;
else if( width <= 1280 )
width = 1280;
else if( width <= 1600 )
width = 1600;
#ifdef XASH_DLL_LOADER
else if ( Q_strstr( vguiloader, ".dll" ) )
width = 1600;
#endif
if( vgui.initialized )
{
//host.mouse_visible = true;
vgui.Startup( width, height );
}
else if ( COM_CheckString( clientlib ) )
{
failed = true;
}
if( vgui.dllFuncs.Startup )
vgui.dllFuncs.Startup( width, height );
}
@ -270,214 +227,223 @@ Unload vgui_support library and call VGui_Shutdown
*/
void VGui_Shutdown( void )
{
if( vgui.Shutdown )
vgui.Shutdown();
if( vgui.dllFuncs.Shutdown )
vgui.dllFuncs.Shutdown();
if( s_pVGuiSupport )
COM_FreeLibrary( s_pVGuiSupport );
s_pVGuiSupport = NULL;
if( vgui.hInstance )
COM_FreeLibrary( vgui.hInstance );
vgui.hInstance = NULL;
vgui.initialized = false;
}
void VGUI_InitKeyTranslationTable( void )
static void VGUI_InitKeyTranslationTable( void )
{
static qboolean bInitted = false;
static qboolean initialized = false;
if( bInitted )
return;
bInitted = true;
if( initialized ) return;
initialized = true;
// set virtual key translation table
memset( s_pVirtualKeyTrans, -1, sizeof( s_pVirtualKeyTrans ) );
memset( vgui.virtualKeyTrans, -1, sizeof( vgui.virtualKeyTrans ) );
s_pVirtualKeyTrans['0'] = KEY_0;
s_pVirtualKeyTrans['1'] = KEY_1;
s_pVirtualKeyTrans['2'] = KEY_2;
s_pVirtualKeyTrans['3'] = KEY_3;
s_pVirtualKeyTrans['4'] = KEY_4;
s_pVirtualKeyTrans['5'] = KEY_5;
s_pVirtualKeyTrans['6'] = KEY_6;
s_pVirtualKeyTrans['7'] = KEY_7;
s_pVirtualKeyTrans['8'] = KEY_8;
s_pVirtualKeyTrans['9'] = KEY_9;
s_pVirtualKeyTrans['A'] = s_pVirtualKeyTrans['a'] = KEY_A;
s_pVirtualKeyTrans['B'] = s_pVirtualKeyTrans['b'] = KEY_B;
s_pVirtualKeyTrans['C'] = s_pVirtualKeyTrans['c'] = KEY_C;
s_pVirtualKeyTrans['D'] = s_pVirtualKeyTrans['d'] = KEY_D;
s_pVirtualKeyTrans['E'] = s_pVirtualKeyTrans['e'] = KEY_E;
s_pVirtualKeyTrans['F'] = s_pVirtualKeyTrans['f'] = KEY_F;
s_pVirtualKeyTrans['G'] = s_pVirtualKeyTrans['g'] = KEY_G;
s_pVirtualKeyTrans['H'] = s_pVirtualKeyTrans['h'] = KEY_H;
s_pVirtualKeyTrans['I'] = s_pVirtualKeyTrans['i'] = KEY_I;
s_pVirtualKeyTrans['J'] = s_pVirtualKeyTrans['j'] = KEY_J;
s_pVirtualKeyTrans['K'] = s_pVirtualKeyTrans['k'] = KEY_K;
s_pVirtualKeyTrans['L'] = s_pVirtualKeyTrans['l'] = KEY_L;
s_pVirtualKeyTrans['M'] = s_pVirtualKeyTrans['m'] = KEY_M;
s_pVirtualKeyTrans['N'] = s_pVirtualKeyTrans['n'] = KEY_N;
s_pVirtualKeyTrans['O'] = s_pVirtualKeyTrans['o'] = KEY_O;
s_pVirtualKeyTrans['P'] = s_pVirtualKeyTrans['p'] = KEY_P;
s_pVirtualKeyTrans['Q'] = s_pVirtualKeyTrans['q'] = KEY_Q;
s_pVirtualKeyTrans['R'] = s_pVirtualKeyTrans['r'] = KEY_R;
s_pVirtualKeyTrans['S'] = s_pVirtualKeyTrans['s'] = KEY_S;
s_pVirtualKeyTrans['T'] = s_pVirtualKeyTrans['t'] = KEY_T;
s_pVirtualKeyTrans['U'] = s_pVirtualKeyTrans['u'] = KEY_U;
s_pVirtualKeyTrans['V'] = s_pVirtualKeyTrans['v'] = KEY_V;
s_pVirtualKeyTrans['W'] = s_pVirtualKeyTrans['w'] = KEY_W;
s_pVirtualKeyTrans['X'] = s_pVirtualKeyTrans['x'] = KEY_X;
s_pVirtualKeyTrans['Y'] = s_pVirtualKeyTrans['y'] = KEY_Y;
s_pVirtualKeyTrans['Z'] = s_pVirtualKeyTrans['z'] = KEY_Z;
// TODO: engine keys are not enough here!
// make crossplatform way to pass SDL keys here
s_pVirtualKeyTrans[K_KP_5 - 5] = KEY_PAD_0;
s_pVirtualKeyTrans[K_KP_5 - 4] = KEY_PAD_1;
s_pVirtualKeyTrans[K_KP_5 - 3] = KEY_PAD_2;
s_pVirtualKeyTrans[K_KP_5 - 2] = KEY_PAD_3;
s_pVirtualKeyTrans[K_KP_5 - 1] = KEY_PAD_4;
s_pVirtualKeyTrans[K_KP_5 - 0] = KEY_PAD_5;
s_pVirtualKeyTrans[K_KP_5 + 1] = KEY_PAD_6;
s_pVirtualKeyTrans[K_KP_5 + 2] = KEY_PAD_7;
s_pVirtualKeyTrans[K_KP_5 + 3] = KEY_PAD_8;
s_pVirtualKeyTrans[K_KP_5 + 4] = KEY_PAD_9;
s_pVirtualKeyTrans[K_KP_SLASH] = KEY_PAD_DIVIDE;
s_pVirtualKeyTrans['*'] = KEY_PAD_MULTIPLY;
s_pVirtualKeyTrans[K_KP_MINUS] = KEY_PAD_MINUS;
s_pVirtualKeyTrans[K_KP_PLUS] = KEY_PAD_PLUS;
s_pVirtualKeyTrans[K_KP_ENTER] = KEY_PAD_ENTER;
//s_pVirtualKeyTrans[K_KP_DECIMAL] = KEY_PAD_DECIMAL;
s_pVirtualKeyTrans['['] = KEY_LBRACKET;
s_pVirtualKeyTrans[']'] = KEY_RBRACKET;
s_pVirtualKeyTrans[';'] = KEY_SEMICOLON;
s_pVirtualKeyTrans['\''] = KEY_APOSTROPHE;
s_pVirtualKeyTrans['`'] = KEY_BACKQUOTE;
s_pVirtualKeyTrans[','] = KEY_COMMA;
s_pVirtualKeyTrans['.'] = KEY_PERIOD;
s_pVirtualKeyTrans[K_KP_SLASH] = KEY_SLASH;
s_pVirtualKeyTrans['\\'] = KEY_BACKSLASH;
s_pVirtualKeyTrans['-'] = KEY_MINUS;
s_pVirtualKeyTrans['='] = KEY_EQUAL;
s_pVirtualKeyTrans[K_ENTER] = KEY_ENTER;
s_pVirtualKeyTrans[K_SPACE] = KEY_SPACE;
s_pVirtualKeyTrans[K_BACKSPACE] = KEY_BACKSPACE;
s_pVirtualKeyTrans[K_TAB] = KEY_TAB;
s_pVirtualKeyTrans[K_CAPSLOCK] = KEY_CAPSLOCK;
s_pVirtualKeyTrans[K_KP_NUMLOCK] = KEY_NUMLOCK;
s_pVirtualKeyTrans[K_ESCAPE] = KEY_ESCAPE;
//s_pVirtualKeyTrans[K_KP_SCROLLLOCK] = KEY_SCROLLLOCK;
s_pVirtualKeyTrans[K_INS] = KEY_INSERT;
s_pVirtualKeyTrans[K_DEL] = KEY_DELETE;
s_pVirtualKeyTrans[K_HOME] = KEY_HOME;
s_pVirtualKeyTrans[K_END] = KEY_END;
s_pVirtualKeyTrans[K_PGUP] = KEY_PAGEUP;
s_pVirtualKeyTrans[K_PGDN] = KEY_PAGEDOWN;
s_pVirtualKeyTrans[K_PAUSE] = KEY_BREAK;
//s_pVirtualKeyTrans[K_SHIFT] = KEY_RSHIFT;
s_pVirtualKeyTrans[K_SHIFT] = KEY_LSHIFT; // SHIFT -> left SHIFT
//s_pVirtualKeyTrans[SDLK_RALT] = KEY_RALT;
s_pVirtualKeyTrans[K_ALT] = KEY_LALT; // ALT -> left ALT
//s_pVirtualKeyTrans[SDLK_RCTRL] = KEY_RCONTROL;
s_pVirtualKeyTrans[K_CTRL] = KEY_LCONTROL; // CTRL -> left CTRL
s_pVirtualKeyTrans[K_WIN] = KEY_LWIN;
//s_pVirtualKeyTrans[SDLK_APPLICATION] = KEY_RWIN;
//s_pVirtualKeyTrans[K_WIN] = KEY_APP;
s_pVirtualKeyTrans[K_UPARROW] = KEY_UP;
s_pVirtualKeyTrans[K_LEFTARROW] = KEY_LEFT;
s_pVirtualKeyTrans[K_DOWNARROW] = KEY_DOWN;
s_pVirtualKeyTrans[K_RIGHTARROW] = KEY_RIGHT;
s_pVirtualKeyTrans[K_F1] = KEY_F1;
s_pVirtualKeyTrans[K_F2] = KEY_F2;
s_pVirtualKeyTrans[K_F3] = KEY_F3;
s_pVirtualKeyTrans[K_F4] = KEY_F4;
s_pVirtualKeyTrans[K_F5] = KEY_F5;
s_pVirtualKeyTrans[K_F6] = KEY_F6;
s_pVirtualKeyTrans[K_F7] = KEY_F7;
s_pVirtualKeyTrans[K_F8] = KEY_F8;
s_pVirtualKeyTrans[K_F9] = KEY_F9;
s_pVirtualKeyTrans[K_F10] = KEY_F10;
s_pVirtualKeyTrans[K_F11] = KEY_F11;
s_pVirtualKeyTrans[K_F12] = KEY_F12;
vgui.virtualKeyTrans['0'] = KEY_0;
vgui.virtualKeyTrans['1'] = KEY_1;
vgui.virtualKeyTrans['2'] = KEY_2;
vgui.virtualKeyTrans['3'] = KEY_3;
vgui.virtualKeyTrans['4'] = KEY_4;
vgui.virtualKeyTrans['5'] = KEY_5;
vgui.virtualKeyTrans['6'] = KEY_6;
vgui.virtualKeyTrans['7'] = KEY_7;
vgui.virtualKeyTrans['8'] = KEY_8;
vgui.virtualKeyTrans['9'] = KEY_9;
vgui.virtualKeyTrans['A'] = vgui.virtualKeyTrans['a'] = KEY_A;
vgui.virtualKeyTrans['B'] = vgui.virtualKeyTrans['b'] = KEY_B;
vgui.virtualKeyTrans['C'] = vgui.virtualKeyTrans['c'] = KEY_C;
vgui.virtualKeyTrans['D'] = vgui.virtualKeyTrans['d'] = KEY_D;
vgui.virtualKeyTrans['E'] = vgui.virtualKeyTrans['e'] = KEY_E;
vgui.virtualKeyTrans['F'] = vgui.virtualKeyTrans['f'] = KEY_F;
vgui.virtualKeyTrans['G'] = vgui.virtualKeyTrans['g'] = KEY_G;
vgui.virtualKeyTrans['H'] = vgui.virtualKeyTrans['h'] = KEY_H;
vgui.virtualKeyTrans['I'] = vgui.virtualKeyTrans['i'] = KEY_I;
vgui.virtualKeyTrans['J'] = vgui.virtualKeyTrans['j'] = KEY_J;
vgui.virtualKeyTrans['K'] = vgui.virtualKeyTrans['k'] = KEY_K;
vgui.virtualKeyTrans['L'] = vgui.virtualKeyTrans['l'] = KEY_L;
vgui.virtualKeyTrans['M'] = vgui.virtualKeyTrans['m'] = KEY_M;
vgui.virtualKeyTrans['N'] = vgui.virtualKeyTrans['n'] = KEY_N;
vgui.virtualKeyTrans['O'] = vgui.virtualKeyTrans['o'] = KEY_O;
vgui.virtualKeyTrans['P'] = vgui.virtualKeyTrans['p'] = KEY_P;
vgui.virtualKeyTrans['Q'] = vgui.virtualKeyTrans['q'] = KEY_Q;
vgui.virtualKeyTrans['R'] = vgui.virtualKeyTrans['r'] = KEY_R;
vgui.virtualKeyTrans['S'] = vgui.virtualKeyTrans['s'] = KEY_S;
vgui.virtualKeyTrans['T'] = vgui.virtualKeyTrans['t'] = KEY_T;
vgui.virtualKeyTrans['U'] = vgui.virtualKeyTrans['u'] = KEY_U;
vgui.virtualKeyTrans['V'] = vgui.virtualKeyTrans['v'] = KEY_V;
vgui.virtualKeyTrans['W'] = vgui.virtualKeyTrans['w'] = KEY_W;
vgui.virtualKeyTrans['X'] = vgui.virtualKeyTrans['x'] = KEY_X;
vgui.virtualKeyTrans['Y'] = vgui.virtualKeyTrans['y'] = KEY_Y;
vgui.virtualKeyTrans['Z'] = vgui.virtualKeyTrans['z'] = KEY_Z;
vgui.virtualKeyTrans[K_KP_5 - 5] = KEY_PAD_0;
vgui.virtualKeyTrans[K_KP_5 - 4] = KEY_PAD_1;
vgui.virtualKeyTrans[K_KP_5 - 3] = KEY_PAD_2;
vgui.virtualKeyTrans[K_KP_5 - 2] = KEY_PAD_3;
vgui.virtualKeyTrans[K_KP_5 - 1] = KEY_PAD_4;
vgui.virtualKeyTrans[K_KP_5 - 0] = KEY_PAD_5;
vgui.virtualKeyTrans[K_KP_5 + 1] = KEY_PAD_6;
vgui.virtualKeyTrans[K_KP_5 + 2] = KEY_PAD_7;
vgui.virtualKeyTrans[K_KP_5 + 3] = KEY_PAD_8;
vgui.virtualKeyTrans[K_KP_5 + 4] = KEY_PAD_9;
vgui.virtualKeyTrans[K_KP_SLASH] = KEY_PAD_DIVIDE;
vgui.virtualKeyTrans['*'] = KEY_PAD_MULTIPLY;
vgui.virtualKeyTrans[K_KP_MINUS] = KEY_PAD_MINUS;
vgui.virtualKeyTrans[K_KP_PLUS] = KEY_PAD_PLUS;
vgui.virtualKeyTrans[K_KP_ENTER] = KEY_PAD_ENTER;
vgui.virtualKeyTrans[K_KP_NUMLOCK] = KEY_NUMLOCK;
vgui.virtualKeyTrans['['] = KEY_LBRACKET;
vgui.virtualKeyTrans[']'] = KEY_RBRACKET;
vgui.virtualKeyTrans[';'] = KEY_SEMICOLON;
vgui.virtualKeyTrans['`'] = KEY_BACKQUOTE;
vgui.virtualKeyTrans[','] = KEY_COMMA;
vgui.virtualKeyTrans['.'] = KEY_PERIOD;
vgui.virtualKeyTrans['-'] = KEY_MINUS;
vgui.virtualKeyTrans['='] = KEY_EQUAL;
vgui.virtualKeyTrans['/'] = KEY_SLASH;
vgui.virtualKeyTrans['\\'] = KEY_BACKSLASH;
vgui.virtualKeyTrans['\''] = KEY_APOSTROPHE;
vgui.virtualKeyTrans[K_TAB] = KEY_TAB;
vgui.virtualKeyTrans[K_ENTER] = KEY_ENTER;
vgui.virtualKeyTrans[K_SPACE] = KEY_SPACE;
vgui.virtualKeyTrans[K_CAPSLOCK] = KEY_CAPSLOCK;
vgui.virtualKeyTrans[K_BACKSPACE] = KEY_BACKSPACE;
vgui.virtualKeyTrans[K_ESCAPE] = KEY_ESCAPE;
vgui.virtualKeyTrans[K_INS] = KEY_INSERT;
vgui.virtualKeyTrans[K_DEL] = KEY_DELETE;
vgui.virtualKeyTrans[K_HOME] = KEY_HOME;
vgui.virtualKeyTrans[K_END] = KEY_END;
vgui.virtualKeyTrans[K_PGUP] = KEY_PAGEUP;
vgui.virtualKeyTrans[K_PGDN] = KEY_PAGEDOWN;
vgui.virtualKeyTrans[K_PAUSE] = KEY_BREAK;
vgui.virtualKeyTrans[K_SHIFT] = KEY_LSHIFT; // SHIFT -> left SHIFT
vgui.virtualKeyTrans[K_ALT] = KEY_LALT; // ALT -> left ALT
vgui.virtualKeyTrans[K_CTRL] = KEY_LCONTROL; // CTRL -> left CTRL
vgui.virtualKeyTrans[K_WIN] = KEY_LWIN;
vgui.virtualKeyTrans[K_UPARROW] = KEY_UP;
vgui.virtualKeyTrans[K_LEFTARROW] = KEY_LEFT;
vgui.virtualKeyTrans[K_DOWNARROW] = KEY_DOWN;
vgui.virtualKeyTrans[K_RIGHTARROW] = KEY_RIGHT;
vgui.virtualKeyTrans[K_F1] = KEY_F1;
vgui.virtualKeyTrans[K_F2] = KEY_F2;
vgui.virtualKeyTrans[K_F3] = KEY_F3;
vgui.virtualKeyTrans[K_F4] = KEY_F4;
vgui.virtualKeyTrans[K_F5] = KEY_F5;
vgui.virtualKeyTrans[K_F6] = KEY_F6;
vgui.virtualKeyTrans[K_F7] = KEY_F7;
vgui.virtualKeyTrans[K_F8] = KEY_F8;
vgui.virtualKeyTrans[K_F9] = KEY_F9;
vgui.virtualKeyTrans[K_F10] = KEY_F10;
vgui.virtualKeyTrans[K_F11] = KEY_F11;
vgui.virtualKeyTrans[K_F12] = KEY_F12;
}
enum VGUI_KeyCode VGUI_MapKey( int keyCode )
static enum VGUI_KeyCode VGUI_MapKey( int keyCode )
{
VGUI_InitKeyTranslationTable();
if( keyCode < 0 || keyCode >= (int)sizeof( s_pVirtualKeyTrans ) / (int)sizeof( s_pVirtualKeyTrans[0] ))
{
//Assert( false );
return (enum VGUI_KeyCode)-1;
}
else
{
return s_pVirtualKeyTrans[keyCode];
}
if( keyCode >= 0 && keyCode < ARRAYSIZE( vgui.virtualKeyTrans ))
return vgui.virtualKeyTrans[keyCode];
return (enum VGUI_KeyCode)-1;
}
void VGui_KeyEvent( int key, int down )
void VGui_MouseEvent( int key, int clicks )
{
if( !vgui.initialized )
enum VGUI_MouseAction mact;
enum VGUI_MouseCode code;
if( !vgui.dllFuncs.Mouse )
return;
switch( key )
{
case K_MOUSE1:
if( down && host.mouse_visible ) {
Key_EnableTextInput(true, false);
}
vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_LEFT );
return;
case K_MOUSE2:
vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_RIGHT );
return;
case K_MOUSE3:
vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_MIDDLE );
return;
case K_MWHEELDOWN:
vgui.Mouse( MA_WHEEL, 1 );
return;
case K_MWHEELUP:
vgui.Mouse( MA_WHEEL, -1 );
return;
default:
break;
case K_MOUSE1: code = MOUSE_LEFT; break;
case K_MOUSE2: code = MOUSE_RIGHT; break;
case K_MOUSE3: code = MOUSE_MIDDLE; break;
default: return;
}
if( down == 2 )
vgui.Key( KA_TYPED, VGUI_MapKey( key ) );
if( clicks >= 2 )
mact = MA_DOUBLE;
else if( clicks == 1 )
mact = MA_PRESSED;
else
vgui.Key( down?KA_PRESSED:KA_RELEASED, VGUI_MapKey( key ) );
//Msg("VGui_KeyEvent %d %d %d\n", key, VGUI_MapKey( key ), down );
mact = MA_RELEASED;
vgui.dllFuncs.Mouse( mact, code );
}
void VGui_MWheelEvent( int y )
{
if( !vgui.dllFuncs.Mouse )
return;
vgui.dllFuncs.Mouse( MA_WHEEL, y );
}
void VGui_KeyEvent( int key, int down )
{
enum VGUI_KeyCode code;
if( !vgui.dllFuncs.Key )
return;
if(( code = VGUI_MapKey( key )) < 0 )
return;
if( down )
{
vgui.dllFuncs.Key( KA_PRESSED, code );
vgui.dllFuncs.Key( KA_TYPED, code );
}
else vgui.dllFuncs.Key( KA_RELEASED, code );
}
void VGui_MouseMove( int x, int y )
{
float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth;
float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight;
if( vgui.initialized )
vgui.MouseMove( x / xscale, y / yscale );
if( vgui.dllFuncs.MouseMove )
{
float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth;
float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight;
vgui.dllFuncs.MouseMove( x / xscale, y / yscale );
}
}
void VGui_Paint( void )
{
if( vgui.initialized )
vgui.Paint();
if( vgui.dllFuncs.Paint )
vgui.dllFuncs.Paint();
}
void VGui_RunFrame( void )
void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType )
{
//stub
vgui.cursor = cursorType;
}
void *GAME_EXPORT VGui_GetPanel( void )
{
if( vgui.initialized )
return vgui.GetPanel();
if( vgui.dllFuncs.GetPanel )
return vgui.dllFuncs.GetPanel();
return NULL;
}
void VGui_ReportTextInput( const char *text )
{
if ( vgui.initialized )
vgui.TextInput( text );
if( vgui.dllFuncs.TextInput )
vgui.dllFuncs.TextInput( text );
}

View File

@ -16,25 +16,22 @@ GNU General Public License for more details.
#ifndef VGUI_DRAW_H
#define VGUI_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
#include "port.h"
//
// vgui_draw.c
//
void VGui_Startup( const char *clientlib, int width, int height );
void VGui_RegisterCvars( void );
qboolean VGui_LoadProgs( HINSTANCE hInstance );
void VGui_Startup( int width, int height );
void VGui_Shutdown( void );
void VGui_Paint( void );
void VGui_RunFrame( void );
void VGui_MouseEvent( int key, int clicks );
void VGui_MWheelEvent( int y );
void VGui_KeyEvent( int key, int down );
void VGui_MouseMove( int x, int y );
qboolean VGui_IsActive( void );
void *VGui_GetPanel( void );
void VGui_ReportTextInput( const char *text );
#ifdef __cplusplus
}
#endif
#endif//VGUI_DRAW_H
void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType );
#endif // VGUI_DRAW_H

View File

@ -21,7 +21,6 @@ GNU General Public License for more details.
#include "platform/platform.h"
#define WINDOW_NAME XASH_ENGINE_NAME " Window" // Half-Life
convar_t *vid_displayfrequency;
convar_t *vid_fullscreen;
convar_t *vid_mode;
convar_t *vid_brightness;
@ -67,19 +66,30 @@ void VID_InitDefaultResolution( void )
R_SaveVideoMode
=================
*/
void R_SaveVideoMode( int w, int h , int render_w, int render_h )
void R_SaveVideoMode( int w, int h, int render_w, int render_h )
{
if( !w || !h || !render_w || !render_h )
{
host.renderinfo_changed = false;
return;
}
host.window_center_x = w / 2;
host.window_center_y = h / 2;
Cvar_SetValue( "width", w );
Cvar_SetValue( "height", h );
// immediately drop changed state or we may trigger
// video subsystem to reapply settings
host.renderinfo_changed = false;
if( refState.width == render_w && refState.height == render_h )
return;
refState.width = render_w;
refState.height = render_h;
host.renderinfo_changed = false;
// check for 4:3 or 5:4
if( render_w * 3 != render_h * 4 && render_w * 4 != render_h * 5 )
refState.wideScreen = true;
@ -176,12 +186,11 @@ void VID_Init( void )
Cvar_Get( "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
Cvar_Get( "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
window_xpos = Cvar_Get( "_window_xpos", "130", FCVAR_RENDERINFO, "window position by horizontal" );
window_ypos = Cvar_Get( "_window_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" );
window_xpos = Cvar_Get( "_window_xpos", "-1", FCVAR_RENDERINFO, "window position by horizontal" );
window_ypos = Cvar_Get( "_window_ypos", "-1", FCVAR_RENDERINFO, "window position by vertical" );
vid_gamma = Cvar_Get( "gamma", "2.5", FCVAR_ARCHIVE, "gamma amount" );
vid_brightness = Cvar_Get( "brightness", "0.0", FCVAR_ARCHIVE, "brightness factor" );
vid_displayfrequency = Cvar_Get ( "vid_displayfrequency", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen refresh rate" );
vid_fullscreen = Cvar_Get( "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable fullscreen mode" );
vid_mode = Cvar_Get( "vid_mode", "0", FCVAR_RENDERINFO, "current video mode index (used just for storage)" );
vid_highdpi = Cvar_Get( "vid_highdpi", "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" );

View File

@ -29,7 +29,6 @@ extern glwstate_t glw_state;
#define VID_MIN_WIDTH 320
extern convar_t *vid_fullscreen;
extern convar_t *vid_displayfrequency;
extern convar_t *vid_highdpi;
extern convar_t *vid_rotate;
extern convar_t *vid_scale;

633
engine/client/voice.c Normal file
View File

@ -0,0 +1,633 @@
/*
voice.c - voice chat implementation
Copyright (C) 2022 Velaron
Copyright (C) 2022 SNMetamorph
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#define CUSTOM_MODES 1 // required to correctly link with Opus Custom
#include <opus_custom.h>
#include "common.h"
#include "client.h"
#include "voice.h"
voice_state_t voice = { 0 };
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" );
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" );
CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "incoming voice volume scale" );
CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (average)" );
CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (maximum)" );
CVAR_DEFINE_AUTO( voice_inputfromfile, "0", FCVAR_PRIVILEGED, "input voice from voice_input.wav" );
static void Voice_ApplyGainAdjust( int16_t *samples, int count );
/*
===============================================================================
OPUS INTEGRATION
===============================================================================
*/
/*
=========================
Voice_InitOpusDecoder
=========================
*/
static qboolean Voice_InitOpusDecoder( void )
{
int err;
voice.width = sizeof( opus_int16 );
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
if( !voice.custom_mode )
{
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
return false;
}
voice.decoder = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.decoder )
{
Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err ));
return false;
}
return true;
}
/*
=========================
Voice_InitOpusEncoder
=========================
*/
static qboolean Voice_InitOpusEncoder( int quality )
{
int err;
voice.encoder = opus_custom_encoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.encoder )
{
Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err ));
return false;
}
switch( quality )
{
case 1: // 6 kbps
opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 6000 ));
break;
case 2: // 12 kbps
opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 ));
break;
case 4: // 64 kbps
opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 64000 ));
break;
case 5: // 96 kbps
opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 96000 ));
break;
default: // 36 kbps
opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 36000 ));
break;
}
return true;
}
/*
=========================
Voice_ShutdownOpusDecoder
=========================
*/
static void Voice_ShutdownOpusDecoder( void )
{
if( voice.decoder )
{
opus_custom_decoder_destroy( voice.decoder );
voice.decoder = NULL;
}
}
/*
=========================
Voice_ShutdownOpusEncoder
=========================
*/
static void Voice_ShutdownOpusEncoder( void )
{
if( voice.encoder )
{
opus_custom_encoder_destroy( voice.encoder );
voice.encoder = NULL;
}
if( voice.custom_mode )
{
opus_custom_mode_destroy( voice.custom_mode );
voice.custom_mode = NULL;
}
}
/*
=========================
Voice_GetOpusCompressedData
=========================
*/
static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames )
{
uint ofs = 0, size = 0;
uint frame_size_bytes = voice.frame_size * voice.width;
if( voice.input_file )
{
uint numbytes;
double updateInterval, curtime = Sys_DoubleTime();
updateInterval = curtime - voice.start_time;
voice.start_time = curtime;
numbytes = updateInterval * voice.samplerate * voice.width * VOICE_PCM_CHANNELS;
numbytes = Q_min( numbytes, voice.input_file->size - voice.input_file_pos );
numbytes = Q_min( numbytes, sizeof( voice.input_buffer ) - voice.input_buffer_pos );
memcpy( voice.input_buffer + voice.input_buffer_pos, voice.input_file->buffer + voice.input_file_pos, numbytes );
voice.input_buffer_pos += numbytes;
voice.input_file_pos += numbytes;
}
if( !voice.input_file )
VoiceCapture_Lock( true );
for( ofs = 0; voice.input_buffer_pos - ofs >= frame_size_bytes && ofs <= voice.input_buffer_pos; ofs += frame_size_bytes )
{
int bytes;
#if 1
if( !voice.input_file )
{
// adjust gain before encoding, but only for input from voice
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size);
}
#endif
bytes = opus_custom_encode( voice.encoder, (const opus_int16 *)( voice.input_buffer + ofs ),
voice.frame_size, out + size + sizeof( uint16_t ), maxsize );
if( bytes > 0 )
{
// write compressed frame size
*((uint16_t *)&out[size]) = bytes;
size += bytes + sizeof( uint16_t );
maxsize -= bytes + sizeof( uint16_t );
(*frames)++;
}
else
{
Con_Printf( S_ERROR "%s: failed to encode frame: %s\n", __func__, opus_strerror( bytes ));
}
}
// did we compress anything? update counters
if( ofs )
{
fs_offset_t remaining = voice.input_buffer_pos - ofs;
// move remaining samples to the beginning of buffer
memmove( voice.input_buffer, voice.input_buffer + ofs, remaining );
voice.input_buffer_pos = remaining;
}
if( !voice.input_file )
VoiceCapture_Lock( false );
return size;
}
/*
===============================================================================
VOICE CHAT INTEGRATION
===============================================================================
*/
/*
=========================
Voice_ApplyGainAdjust
=========================
*/
static void Voice_ApplyGainAdjust( int16_t *samples, int count )
{
float gain, modifiedMax;
int average, adjustedSample, blockOffset = 0;
for( ;; )
{
int i, localMax = 0, localSum = 0;
int blockSize = Q_min( count - ( blockOffset + voice.autogain.block_size ), voice.autogain.block_size );
if( blockSize < 1 )
break;
for( i = 0; i < blockSize; ++i )
{
int sample = samples[blockOffset + i];
int absSample = abs( sample );
if( absSample > localMax )
localMax = absSample;
localSum += absSample;
gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier;
adjustedSample = Q_min( SHRT_MAX, Q_max(( int )( sample * gain ), SHRT_MIN ));
samples[blockOffset + i] = adjustedSample;
}
if( blockOffset % voice.autogain.block_size == 0 )
{
average = localSum / blockSize;
modifiedMax = average + ( localMax - average ) * voice_avggain.value;
voice.autogain.current_gain = voice.autogain.next_gain * voice_scale.value;
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * voice_scale.value;
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 );
}
blockOffset += blockSize;
}
}
/*
=========================
Voice_Status
Notify user dll aboit voice transmission
=========================
*/
static void Voice_Status( int entindex, qboolean bTalking )
{
if( cls.state == ca_active && clgame.dllFuncs.pfnVoiceStatus )
clgame.dllFuncs.pfnVoiceStatus( entindex, bTalking );
}
/*
=========================
Voice_StatusTimeout
Waits few milliseconds and if there was no
voice transmission, sends notification
=========================
*/
static void Voice_StatusTimeout( voice_status_t *status, int entindex, double frametime )
{
if( status->talking_ack )
{
status->talking_timeout += frametime;
if( status->talking_timeout > 0.2 )
{
status->talking_ack = false;
Voice_Status( entindex, false );
}
}
}
/*
=========================
Voice_StatusAck
Sends notification to user dll and
zeroes timeouts for this client
=========================
*/
void Voice_StatusAck( voice_status_t *status, int playerIndex )
{
if( !status->talking_ack )
Voice_Status( playerIndex, true );
status->talking_ack = true;
status->talking_timeout = 0.0;
}
/*
=========================
Voice_IsRecording
=========================
*/
qboolean Voice_IsRecording( void )
{
return voice.is_recording;
}
/*
=========================
Voice_RecordStop
=========================
*/
void Voice_RecordStop( void )
{
if( voice.input_file )
{
FS_FreeSound( voice.input_file );
voice.input_file = NULL;
}
VoiceCapture_Activate( false );
voice.is_recording = false;
Voice_Status( VOICE_LOCALCLIENT_INDEX, false );
voice.input_buffer_pos = 0;
memset( voice.input_buffer, 0, sizeof( voice.input_buffer ));
}
/*
=========================
Voice_RecordStart
=========================
*/
void Voice_RecordStart( void )
{
Voice_RecordStop();
if( voice_inputfromfile.value )
{
voice.input_file = FS_LoadSound( "voice_input.wav", NULL, 0 );
if( voice.input_file )
{
Sound_Process( &voice.input_file, voice.samplerate, voice.width, SOUND_RESAMPLE );
voice.input_file_pos = 0;
voice.start_time = Sys_DoubleTime();
voice.is_recording = true;
}
else
{
FS_FreeSound( voice.input_file );
voice.input_file = NULL;
}
}
if( !Voice_IsRecording() )
voice.is_recording = VoiceCapture_Activate( true );
if( Voice_IsRecording() )
Voice_Status( VOICE_LOCALCLIENT_INDEX, true );
}
/*
=========================
Voice_Disconnect
We're disconnected from server
stop recording and notify user dlls
=========================
*/
void Voice_Disconnect( void )
{
int i;
Voice_RecordStop();
if( voice.local.talking_ack )
{
Voice_Status( VOICE_LOOPBACK_INDEX, false );
voice.local.talking_ack = false;
}
for( i = 0; i < MAX_CLIENTS; i++ )
{
if( voice.players_status[i].talking_ack )
{
Voice_Status( i, false );
voice.players_status[i].talking_ack = false;
}
}
}
/*
=========================
Voice_StartChannel
Feed the decoded data to engine sound subsystem
=========================
*/
static void Voice_StartChannel( uint samples, byte *data, int entnum )
{
SND_ForceInitMouth( entnum );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, 255 );
}
/*
=========================
Voice_AddIncomingData
Received encoded voice data, decode it
=========================
*/
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
{
int samples = 0;
int ofs = 0;
if( !voice.decoder )
return;
// decode frame by frame
for( ;; )
{
int frame_samples;
uint16_t compressed_size;
// no compressed size mark
if( ofs + sizeof( uint16_t ) > size )
break;
compressed_size = *(const uint16_t *)(data + ofs);
ofs += sizeof( uint16_t );
// no frame data
if( ofs + compressed_size > size )
break;
frame_samples = opus_custom_decode( voice.decoder, data + ofs, compressed_size,
(opus_int16*)voice.decompress_buffer + samples, voice.frame_size );
ofs += compressed_size;
samples += frame_samples;
}
if( samples > 0 )
Voice_StartChannel( samples, voice.decompress_buffer, ent );
}
/*
=========================
CL_AddVoiceToDatagram
Encode our voice data and send it to server
=========================
*/
void CL_AddVoiceToDatagram( void )
{
uint size, frames = 0;
if( cls.state != ca_active || !Voice_IsRecording() || !voice.encoder )
return;
size = Voice_GetOpusCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames );
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 )
{
MSG_BeginClientCmd( &cls.datagram, clc_voicedata );
MSG_WriteByte( &cls.datagram, voice_loopback.value != 0 );
MSG_WriteByte( &cls.datagram, frames );
MSG_WriteShort( &cls.datagram, size );
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size );
}
}
/*
=========================
Voice_RegisterCvars
Register voice related cvars and commands
=========================
*/
void Voice_RegisterCvars( void )
{
Cvar_RegisterVariable( &voice_enable );
Cvar_RegisterVariable( &voice_loopback );
Cvar_RegisterVariable( &voice_scale );
Cvar_RegisterVariable( &voice_avggain );
Cvar_RegisterVariable( &voice_maxgain );
Cvar_RegisterVariable( &voice_inputfromfile );
}
/*
=========================
Voice_Shutdown
Completely shutdown the voice subsystem
=========================
*/
static void Voice_Shutdown( void )
{
int i;
Voice_RecordStop();
Voice_ShutdownOpusEncoder();
Voice_ShutdownOpusDecoder();
VoiceCapture_Shutdown();
if( voice.local.talking_ack )
Voice_Status( VOICE_LOOPBACK_INDEX, false );
for( i = 0; i < MAX_CLIENTS; i++ )
{
if( voice.players_status[i].talking_ack )
Voice_Status( i, false );
}
memset( &voice, 0, sizeof( voice ));
}
/*
=========================
Voice_Idle
Run timeout for all clients
=========================
*/
void Voice_Idle( double frametime )
{
int i;
if( FBitSet( voice_enable.flags, FCVAR_CHANGED ) && !voice_enable.value )
{
Voice_Shutdown();
return;
}
// update local player status first
Voice_StatusTimeout( &voice.local, VOICE_LOOPBACK_INDEX, frametime );
for( i = 0; i < MAX_CLIENTS; i++ )
Voice_StatusTimeout( &voice.players_status[i], i, frametime );
}
/*
=========================
Voice_Init
Initialize the voice subsystem
=========================
*/
qboolean Voice_Init( const char *pszCodecName, int quality )
{
if( !voice_enable.value )
return false;
if( Q_strcmp( pszCodecName, VOICE_OPUS_CUSTOM_CODEC ))
{
Con_Printf( S_ERROR "Server requested unsupported codec: %s\n", pszCodecName );
return false;
}
// reinitialize only if codec parameters are different
if( Q_strcmp( voice.codec, pszCodecName ) && voice.quality != quality )
Voice_Shutdown();
voice.autogain.block_size = 128;
if( !Voice_InitOpusDecoder( ))
{
// no reason to init encoder and open audio device
// if we can't hear other players
Con_Printf( S_ERROR "Voice chat disabled.\n" );
Voice_Shutdown();
return false;
}
// we can hear others players, so it's fine to fail now
voice.initialized = true;
Q_strncpy( voice.codec, pszCodecName, sizeof( voice.codec ));
if( !Voice_InitOpusEncoder( quality ))
{
Con_Printf( S_WARN "Other players will not be able to hear you.\n" );
return false;
}
voice.quality = quality;
if( !VoiceCapture_Init( ))
Con_Printf( S_WARN "No microphone is available.\n" );
return true;
}

104
engine/client/voice.h Normal file
View File

@ -0,0 +1,104 @@
/*
voice.h - voice chat implementation
Copyright (C) 2022 Velaron
Copyright (C) 2022 SNMetamorph
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef VOICE_H
#define VOICE_H
#include "common.h"
#include "protocol.h" // MAX_CLIENTS
#include "sound.h"
typedef struct OpusCustomEncoder OpusCustomEncoder;
typedef struct OpusCustomDecoder OpusCustomDecoder;
typedef struct OpusCustomMode OpusCustomMode;
#define VOICE_LOOPBACK_INDEX (-2)
#define VOICE_LOCALCLIENT_INDEX (-1)
#define VOICE_PCM_CHANNELS 1 // always mono
// never change these parameters when using opuscustom
#define VOICE_OPUS_CUSTOM_SAMPLERATE SOUND_44k
// must follow opus custom requirements
// also be divisible with MAX_RAW_SAMPLES
#define VOICE_OPUS_CUSTOM_FRAME_SIZE 1024
#define VOICE_OPUS_CUSTOM_CODEC "opus_custom_44k_512"
// a1ba: do not change, we don't have any re-encoding support now
#define VOICE_DEFAULT_CODEC VOICE_OPUS_CUSTOM_CODEC
typedef struct voice_status_s
{
qboolean talking_ack;
double talking_timeout;
} voice_status_t;
typedef struct voice_state_s
{
string codec;
int quality;
qboolean initialized;
qboolean is_recording;
double start_time;
voice_status_t local;
voice_status_t players_status[MAX_CLIENTS];
// opus stuff
OpusCustomMode *custom_mode;
OpusCustomEncoder *encoder;
OpusCustomDecoder *decoder;
// audio info
uint width;
uint samplerate;
uint frame_size; // in samples
// buffers
byte input_buffer[MAX_RAW_SAMPLES];
byte output_buffer[MAX_RAW_SAMPLES];
byte decompress_buffer[MAX_RAW_SAMPLES];
fs_offset_t input_buffer_pos; // in bytes
// input from file
wavdata_t *input_file;
fs_offset_t input_file_pos; // in bytes
// automatic gain control
struct {
int block_size;
float current_gain;
float next_gain;
float gain_multiplier;
} autogain;
} voice_state_t;
extern voice_state_t voice;
void CL_AddVoiceToDatagram( void );
void Voice_RegisterCvars( void );
qboolean Voice_Init( const char *pszCodecName, int quality );
void Voice_Idle( double frametime );
qboolean Voice_IsRecording( void );
void Voice_RecordStop( void );
void Voice_RecordStart( void );
void Voice_Disconnect( void );
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames );
void Voice_StatusAck( voice_status_t *status, int playerIndex );
#endif // VOICE_H

View File

@ -984,7 +984,7 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr
cmd_condlevel = 0;
// cvar value substitution
if( CVAR_TO_BOOL( cmd_scripting ))
if( CVAR_TO_BOOL( cmd_scripting ) && isPrivileged )
{
while( *text )
{
@ -1171,17 +1171,21 @@ void Cmd_List_f( void )
{
cmd_t *cmd;
int i = 0;
const char *match;
size_t matchlen = 0;
const char *match = NULL;
if( Cmd_Argc() > 1 ) match = Cmd_Argv( 1 );
else match = NULL;
if( Cmd_Argc() > 1 )
{
match = Cmd_Argv( 1 );
matchlen = Q_strlen( match );
}
for( cmd = cmd_functions; cmd; cmd = cmd->next )
{
if( cmd->name[0] == '@' )
continue; // never show system cmds
if( match && !Q_stricmpext( match, cmd->name ))
if( match && !Q_strnicmpext( match, cmd->name, matchlen ))
continue;
Con_Printf( " %-*s ^3%s^7\n", 32, cmd->name, cmd->desc );
@ -1391,8 +1395,8 @@ void Cmd_Init( void )
#endif // XASH_DEDICATED
Cmd_AddRestrictedCommand( "alias", Cmd_Alias_f, "create a script function. Without arguments show the list of all alias" );
Cmd_AddRestrictedCommand( "unalias", Cmd_UnAlias_f, "remove a script function" );
Cmd_AddCommand( "if", Cmd_If_f, "compare and set condition bits" );
Cmd_AddCommand( "else", Cmd_Else_f, "invert condition bit" );
Cmd_AddRestrictedCommand( "if", Cmd_If_f, "compare and set condition bits" );
Cmd_AddRestrictedCommand( "else", Cmd_Else_f, "invert condition bit" );
#if defined(XASH_HASHED_VARS)
Cmd_AddCommand( "basecmd_stats", BaseCmd_Stats_f, "print info about basecmd usage" );

View File

@ -21,6 +21,7 @@ GNU General Public License for more details.
#define S_WARN "^3Warning:^7 "
#define S_ERROR "^1Error:^7 "
#define S_USAGE "Usage: "
#define S_USAGE_INDENT " "
#define S_OPENGL_NOTE "^2OpenGL Note:^7 "
#define S_OPENGL_WARN "^3OpenGL Warning:^7 "

View File

@ -159,13 +159,13 @@ extern convar_t *scr_download;
extern convar_t *cmd_scripting;
extern convar_t *sv_maxclients;
extern convar_t *cl_allow_levelshots;
extern convar_t *vid_displayfrequency;
extern convar_t host_developer;
extern convar_t *host_limitlocal;
extern convar_t *host_framerate;
extern convar_t *host_maxfps;
extern convar_t sys_timescale;
extern convar_t cl_filterstuffcmd;
extern convar_t rcon_password;
/*
==============================================================
@ -307,6 +307,16 @@ typedef struct
float scale; // curstate.scale
} tentlist_t;
typedef enum bugcomp_e
{
// default mode, we assume that user dlls are not relying on engine bugs
BUGCOMP_OFF,
// GoldSrc mode, user dlls are relying on GoldSrc specific bugs
// but fixing them may break regular Xash games
BUGCOMP_GOLDSRC,
} bugcomp_t;
typedef struct host_parm_s
{
HINSTANCE hInst;
@ -348,14 +358,13 @@ typedef struct host_parm_s
qboolean allow_cheats; // this host will allow cheating
qboolean con_showalways; // show console always (developer and dedicated)
qboolean change_game; // initialize when game is changed
qboolean mouse_visible; // vgui override cursor control
qboolean mouse_visible; // vgui override cursor control (never change outside Platform_SetCursorType!)
qboolean shutdown_issued; // engine is shutting down
qboolean force_draw_version; // used when fraps is loaded
float force_draw_version_time;
qboolean apply_game_config; // when true apply only to game cvars and ignore all other commands
qboolean apply_opengl_config;// when true apply only to opengl cvars and ignore all other commands
qboolean config_executed; // a bit who indicated was config.cfg already executed e.g. from valve.rc
int sv_cvars_restored; // count of restored server cvars
qboolean crashed; // set to true if crashed
qboolean daemonized;
qboolean enabledll;
@ -381,6 +390,14 @@ typedef struct host_parm_s
struct decallist_s *decalList; // used for keep decals, when renderer is restarted or changed
int numdecals;
// bug compatibility level, for very "special" games
bugcomp_t bugcomp;
// measure time to first frame
double starttime;
// count of sleeps can be inserted between frames
double pureframetime;
} host_parm_t;
extern host_parm_t host;
@ -726,7 +743,6 @@ char *CL_Userinfo( void );
void CL_LegacyUpdateInfo( void );
void CL_CharEvent( int key );
qboolean CL_DisableVisibility( void );
int CL_PointContents( const vec3_t point );
byte *COM_LoadFile( const char *filename, int usehunk, int *pLength );
int CL_GetDemoComment( const char *demoname, char *comment );
void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName );
@ -735,7 +751,6 @@ struct cmd_s *Cmd_GetFirstFunctionHandle( void );
struct cmd_s *Cmd_GetNextFunctionHandle( struct cmd_s *cmd );
struct cmdalias_s *Cmd_AliasGetList( void );
const char *Cmd_GetName( struct cmd_s *cmd );
struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe );
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch );
void SV_StartMusic( const char *curtrack, const char *looptrack, int position );
void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale );
@ -748,7 +763,6 @@ void R_ClearAllDecals( void );
void CL_ClearStaticEntities( void );
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position );
struct cl_entity_s *CL_GetEntityByIndex( int index );
struct player_info_s *CL_GetPlayerInfo( int playerIndex );
void CL_ServerCommand( qboolean reliable, const char *fmt, ... ) _format( 2 );
void CL_HudMessage( const char *pMessage );
const char *CL_MsgInfo( int cmd );
@ -856,6 +870,7 @@ void GAME_EXPORT ID_SetCustomClientID( const char *id );
void NET_InitMasters( void );
void NET_SaveMasters( void );
qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data );
qboolean NET_IsMasterAdr( netadr_t adr );
#ifdef REF_DLL
#error "common.h in ref_dll"

View File

@ -80,8 +80,9 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len )
if( f )
{
dheader_t *header;
dheader_t *header;
dextrahdr_t *hdrext;
dlump_t entities;
memset( buf, 0, sizeof( buf ));
FS_Read( f, buf, sizeof( buf ));
@ -89,10 +90,10 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len )
ver = header->version;
// check all the lumps and some other errors
if( Mod_TestBmodelLumps( t->filenames[i], buf, true ))
if( Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities ))
{
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
lumpofs = entities.fileofs;
lumplen = entities.filelen;
ver = header->version;
}
@ -368,7 +369,7 @@ qboolean Cmd_GetSavesList( const char *s, char *completedname, int length )
string matchbuf;
int i, numsaves;
t = FS_Search( va( "%s%s*.sav", DEFAULT_SAVE_DIRECTORY, s ), true, true ); // lookup only in gamedir
t = FS_Search( va( DEFAULT_SAVE_DIRECTORY "%s*.sav", s ), true, true ); // lookup only in gamedir
if( !t ) return false;
COM_FileBase( t->filenames[0], matchbuf );
@ -904,21 +905,22 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
{
int num_spawnpoints = 0;
dheader_t *header;
dlump_t entities;
memset( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( t->filenames[i], buf, true ))
if( !Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities ))
{
FS_Close( f );
continue;
}
// after call Mod_TestBmodelLumps we gurantee what map is valid
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
lumpofs = entities.fileofs;
lumplen = entities.filelen;
Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename ));
COM_StripExtension( entfilename );
@ -1324,8 +1326,9 @@ static void Cmd_WriteHelp(const char *name, const char *unused, const char *desc
{
int length;
if( !desc || !Q_strcmp( desc, "" ))
if( !COM_CheckString( desc ))
return; // ignore fantom cmds
if( name[0] == '+' || name[0] == '-' )
return; // key bindings
@ -1354,7 +1357,6 @@ void Host_FinalizeConfig( file_t *f, const char *config )
FS_Close( f );
FS_Delete( backup );
FS_Rename( config, backup );
FS_Delete( config );
FS_Rename( newcfg, config );
}

View File

@ -12,7 +12,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "common.h"
@ -23,9 +25,9 @@ Sys_Crash
Crash handler, called from system
================
*/
#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP || XASH_CRASHHANDLER == CRASHHANDLER_WIN32
#if XASH_WIN32
#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP
#if DBGHELP
#pragma comment( lib, "dbghelp" )
@ -187,7 +189,7 @@ static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo )
SymCleanup( process );
}
#endif /* XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP */
#endif /* DBGHELP */
LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
@ -198,7 +200,7 @@ static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
// check to avoid recursive call
host.crashed = true;
#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP
#if DBGHELP
Sys_StackTrace( pInfo );
#else
Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode );
@ -237,21 +239,21 @@ void Sys_RestoreCrashHandler( void )
if( oldFilter ) SetUnhandledExceptionFilter( oldFilter );
}
#elif XASH_CRASHHANDLER == CRASHHANDLER_UCONTEXT
#elif XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
// Posix signal handler
#include "library.h"
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
#define HAVE_UCONTEXT_H 1
#endif
#ifdef HAVE_UCONTEXT_H
#include <ucontext.h>
#endif
#include <signal.h>
#include <sys/mman.h>
#include "library.h"
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
#define STACK_DUMP_STR "Stack dump:\n"
#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 )
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
static struct sigaction oldFilter;
#ifdef XASH_DYNAMIC_DLADDR
static int d_dladdr( void *sym, Dl_info *info )
@ -282,28 +284,18 @@ static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
if( dlinfo.dli_sname )
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
else
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
}
else
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
}
struct sigaction oldFilter;
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
#define STACK_DUMP_STR "Stack dump:\n"
#define STACK_BACKTRACE_STR_LEN (sizeof( STACK_BACKTRACE_STR ) - 1)
#define STACK_DUMP_STR_LEN (sizeof( STACK_DUMP_STR ) - 1)
#define ALIGN( x, y ) (((uintptr_t) (x) + ((y)-1)) & ~((y)-1))
static void Sys_Crash( int signal, siginfo_t *si, void *context)
{
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
char message[8192];
int len, logfd, i = 0;
size_t pagesize;
#if XASH_OPENBSD
struct sigcontext *ucontext = (struct sigcontext*)context;
@ -358,10 +350,10 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
#endif
// safe actions first, stack and memory may be corrupted
len = Q_snprintf( message, sizeof( message ), "Ver: %s %s (build %i-%s, %s-%s)\n",
XASH_ENGINE_NAME, XASH_VERSION, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
#if !XASH_BSD
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
#else
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
@ -380,6 +372,7 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
if( pc && bp && sp )
{
size_t pagesize = sysconf( _SC_PAGESIZE );
// try to print backtrace
write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
@ -447,7 +440,7 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
void Sys_SetupCrashHandler( void )
{
struct sigaction act;
struct sigaction act = { 0 };
act.sa_sigaction = Sys_Crash;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigaction( SIGSEGV, &act, &oldFilter );
@ -464,7 +457,7 @@ void Sys_RestoreCrashHandler( void )
sigaction( SIGILL, &oldFilter, NULL );
}
#elif XASH_CRASHHANDLER == CRASHHANDLER_NULL
#else
void Sys_SetupCrashHandler( void )
{

View File

@ -17,9 +17,23 @@ GNU General Public License for more details.
#include "custom.h"
#include "ref_common.h"
qboolean CustomDecal_Validate( void *raw, int nFileSize )
static rgbdata_t *CustomDecal_LoadImage( const char *path, void *raw, int size )
{
rgbdata_t *test = FS_LoadImage( "#logo.bmp", raw, nFileSize );
const char *testname;
// this way we limit file types
if( !Q_stricmp( COM_FileExtension( path ), "png" ))
testname = "#logo.png";
else testname = "#logo.bmp";
Image_SetForceFlags( IL_LOAD_PLAYER_DECAL );
return FS_LoadImage( testname, raw, size );
}
static qboolean CustomDecal_Validate( const char *path, void *raw, int nFileSize )
{
rgbdata_t *test = CustomDecal_LoadImage( path, raw, nFileSize );
if( test )
{
@ -97,21 +111,25 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou
{
pCust->resource.playernum = playernumber;
if( CustomDecal_Validate( pCust->pBuffer, pResource->nDownloadSize ))
if( CustomDecal_Validate( pResource->szFileName, pCust->pBuffer, pResource->nDownloadSize ))
{
if( !FBitSet( flags, FCUST_IGNOREINIT ))
{
if( pResource->nDownloadSize >= (1 * 1024) && pResource->nDownloadSize <= ( 16 * 1024 ))
if( pResource->nDownloadSize >= (1 * 1024) && pResource->nDownloadSize <= ( 128 * 1024 ))
{
pCust->bTranslated = true;
pCust->nUserData1 = 0;
pCust->nUserData2 = 1;
if( !FBitSet( flags, FCUST_WIPEDATA ))
pCust->pInfo = FS_LoadImage( "#logo.bmp", pCust->pBuffer, pCust->resource.nDownloadSize );
pCust->pInfo = CustomDecal_LoadImage( pResource->szFileName, pCust->pBuffer, pCust->resource.nDownloadSize );
else pCust->pInfo = NULL;
if( nLumps ) *nLumps = 1;
}
else
{
Con_Printf( S_WARN "Ignoring custom decal \"%s\": wrong size (%i bytes)\n", pResource->szFileName, pResource->nDownloadSize );
}
}
}
}

View File

@ -21,7 +21,30 @@ GNU General Public License for more details.
convar_t *cvar_vars = NULL; // head of list
convar_t *cmd_scripting;
CVAR_DEFINE_AUTO( cl_filterstuffcmd, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" );
#ifdef HACKS_RELATED_HLMODS
typedef struct cvar_filter_quirks_s
{
const char *gamedir; // gamedir to enable for
const char *cvars; // list of cvars should be excluded from filter
} cvar_filter_quirks_t;
static cvar_filter_quirks_t cvar_filter_quirks[] =
{
// EXAMPLE:
//{
// "valve",
// "test;test1;test100"
//},
{
"ricochet",
"r_drawviewmodel",
},
};
static cvar_filter_quirks_t *cvar_active_filter_quirks = NULL;
#endif
CVAR_DEFINE_AUTO( cl_filterstuffcmd, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" );
/*
============
@ -220,6 +243,24 @@ const char *Cvar_ValidateString( convar_t *var, const char *value )
return pszValue;
}
/*
============
Cvar_ValidateVarName
============
*/
static qboolean Cvar_ValidateVarName( const char *s, qboolean isvalue )
{
if( !s )
return false;
if( Q_strchr( s, '\\' ) && !isvalue )
return false;
if( Q_strchr( s, '\"' ))
return false;
if( Q_strchr( s, ';' ) && !isvalue )
return false;
return true;
}
/*
============
Cvar_UnlinkVar
@ -368,7 +409,7 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char *
// which executed from the config file. So we don't need to
// change value here: we *already* have actual value from config.
// in other cases we need to rewrite them
if( Q_strcmp( var->desc, "" ))
if( COM_CheckStringEmpty( var->desc ))
{
// directly set value
freestring( var->string );
@ -495,6 +536,113 @@ void Cvar_RegisterVariable( convar_t *var )
#endif
}
/*
============
Cvar_Set2
============
*/
static convar_t *Cvar_Set2( const char *var_name, const char *value )
{
convar_t *var;
const char *pszValue;
qboolean dll_variable = false;
qboolean force = false;
if( !Cvar_ValidateVarName( var_name, false ))
{
Con_DPrintf( S_ERROR "Invalid cvar name string: %s\n", var_name );
return NULL;
}
var = Cvar_FindVar( var_name );
if( !var )
{
// if cvar not found, create it
return Cvar_Get( var_name, value, FCVAR_USER_CREATED, NULL );
}
else
{
if( !Cmd_CurrentCommandIsPrivileged( ))
{
if( FBitSet( var->flags, FCVAR_PRIVILEGED ))
{
Con_Printf( "%s is priveleged.\n", var->name );
return var;
}
if( cl_filterstuffcmd.value > 0.0f && FBitSet( var->flags, FCVAR_FILTERABLE ))
{
Con_Printf( "%s is filterable.\n", var->name );
return var;
}
}
}
// use this check to prevent acessing for unexisting fields
// for cvar_t: latched_string, description, etc
dll_variable = FBitSet( var->flags, FCVAR_EXTDLL );
// check value
if( !value )
{
if( !FBitSet( var->flags, FCVAR_EXTENDED|FCVAR_ALLOCATED ))
{
Con_Printf( "%s has no default value and can't be reset.\n", var->name );
return var;
}
if( dll_variable )
value = "0";
else
value = var->def_string; // reset to default value
}
if( !Q_strcmp( value, var->string ))
return var;
// any latched values not allowed for game cvars
if( dll_variable )
force = true;
if( !force )
{
if( FBitSet( var->flags, FCVAR_READ_ONLY ))
{
Con_Printf( "%s is read-only.\n", var->name );
return var;
}
if( FBitSet( var->flags, FCVAR_CHEAT ) && !host.allow_cheats )
{
Con_Printf( "%s is cheat protected.\n", var->name );
return var;
}
// just tell user about deferred changes
if( FBitSet( var->flags, FCVAR_LATCH ) && ( SV_Active() || CL_Active( )))
Con_Printf( "%s will be changed upon restarting.\n", var->name );
}
pszValue = Cvar_ValidateString( var, value );
// nothing to change
if( !Q_strcmp( pszValue, var->string ))
return var;
// fill it cls.userinfo, svs.serverinfo
if( !Cvar_UpdateInfo( var, pszValue, true ))
return var;
// and finally changed the cvar itself
freestring( var->string );
var->string = copystring( pszValue );
var->value = Q_atof( var->string );
// tell engine about changes
Cvar_Changed( var );
return var;
}
/*
============
Cvar_DirectSet
@ -777,6 +925,39 @@ static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged )
if( cl_filterstuffcmd.value <= 0.0f )
return true;
#ifdef HACKS_RELATED_HLMODS
// check if game-specific filter exceptions should be applied
// TODO: for cmd exceptions, make generic function
if( cvar_active_filter_quirks )
{
const char *cur, *next;
cur = cvar_active_filter_quirks->cvars;
next = Q_strchr( cur, ';' );
// TODO: implement Q_strchrnul
while( cur && *cur )
{
size_t len = next ? next - cur : Q_strlen( cur );
// found, quit
if( !Q_strnicmp( cur, v->name, len ))
return true;
if( next )
{
cur = next + 1;
next = Q_strchr( cur, ';' );
}
else
{
// stop
cur = NULL;
}
}
}
#endif
if( FBitSet( v->flags, FCVAR_FILTERABLE ))
return false;
@ -840,8 +1021,6 @@ qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged )
else
{
Cvar_DirectSet( v, Cmd_Argv( 1 ));
if( host.apply_game_config )
host.sv_cvars_restored++;
return true;
}
}
@ -887,6 +1066,40 @@ void Cvar_Toggle_f( void )
Cvar_Set( Cmd_Argv( 1 ), va( "%i", v ));
}
/*
============
Cvar_Set_f
Allows setting and defining of arbitrary cvars from console, even if they
weren't declared in C code.
============
*/
void Cvar_Set_f( void )
{
int i, c, l = 0, len;
char combined[MAX_CMD_TOKENS];
c = Cmd_Argc();
if( c < 3 )
{
Msg( S_USAGE "set <variable> <value>\n" );
return;
}
combined[0] = 0;
for( i = 2; i < c; i++ )
{
len = Q_strlen( Cmd_Argv(i) + 1 );
if( l + len >= MAX_CMD_TOKENS - 2 )
break;
Q_strcat( combined, Cmd_Argv( i ));
if( i != c-1 ) Q_strcat( combined, " " );
l += len;
}
Cvar_Set2( Cmd_Argv( 1 ), combined );
}
/*
============
Cvar_SetGL_f
@ -934,16 +1147,20 @@ void Cvar_List_f( void )
const char *match = NULL;
char *value;
int count = 0;
size_t matchlen = 0;
if( Cmd_Argc() > 1 )
{
match = Cmd_Argv( 1 );
matchlen = Q_strlen( match );
}
for( var = cvar_vars; var; var = var->next )
{
if( var->name[0] == '@' )
continue; // never shows system cvars
if( match && !Q_stricmpext( match, var->name ))
if( match && !Q_strnicmpext( match, var->name, matchlen ))
continue;
if( Q_colorstr( var->string ))
@ -994,16 +1211,37 @@ Reads in all archived cvars
void Cvar_Init( void )
{
cvar_vars = NULL;
cvar_active_filter_quirks = NULL;
cmd_scripting = Cvar_Get( "cmd_scripting", "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED, "enable simple condition checking and variable operations" );
Cvar_RegisterVariable (&host_developer); // early registering for dev
Cvar_RegisterVariable( &host_developer ); // early registering for dev
Cvar_RegisterVariable( &cl_filterstuffcmd );
Cmd_AddRestrictedCommand( "setgl", Cvar_SetGL_f, "change the value of a opengl variable" ); // OBSOLETE
Cmd_AddRestrictedCommand( "toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" );
Cmd_AddRestrictedCommand( "reset", Cvar_Reset_f, "reset any type variable to initial value" );
Cmd_AddCommand( "set", Cvar_Set_f, "create or change the value of a console variable" );
Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" );
}
/*
============
Cvar_PostFSInit
============
*/
void Cvar_PostFSInit( void )
{
int i;
for( i = 0; i < ARRAYSIZE( cvar_filter_quirks ); i++ )
{
if( !Q_stricmp( cvar_filter_quirks[i].gamedir, GI->gamefolder ))
{
cvar_active_filter_quirks = &cvar_filter_quirks[i];
break;
}
}
}
#if XASH_ENGINE_TESTS
#include "tests.h"

View File

@ -48,6 +48,7 @@ typedef struct convar_s
#define FCVAR_VIDRESTART (1<<20) // recreate the window is cvar with this flag was changed
#define FCVAR_TEMPORARY (1<<21) // these cvars holds their values and can be unlink in any time
#define FCVAR_MOVEVARS (1<<22) // this cvar is a part of movevars_t struct that shared between client and server
#define FCVAR_USER_CREATED (1<<23) // created by a set command (dll's used)
#define CVAR_DEFINE( cv, cvname, cvstr, cvflags, cvdesc ) \
convar_t cv = { (char*)cvname, (char*)cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, (char*)cvdesc, NULL }
@ -77,6 +78,7 @@ void Cvar_Reset( const char *var_name );
void Cvar_SetCheatState( void );
qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged );
void Cvar_Init( void );
void Cvar_PostFSInit( void );
void Cvar_Unlink( int group );
#endif//CVAR_H

View File

@ -19,74 +19,6 @@ GNU General Public License for more details.
ref_globals_t refState;
const char *svc_strings[256] =
{
"svc_bad",
"svc_nop",
"svc_disconnect",
"svc_changing",
"svc_version",
"svc_setview",
"svc_sound",
"svc_time",
"svc_print",
"svc_stufftext",
"svc_setangle",
"svc_serverdata",
"svc_lightstyle",
"svc_updateuserinfo",
"svc_deltatable",
"svc_clientdata",
"svc_stopsound",
"svc_updatepings",
"svc_particle",
"svc_restoresound",
"svc_spawnstatic",
"svc_event_reliable",
"svc_spawnbaseline",
"svc_temp_entity",
"svc_setpause",
"svc_signonnum",
"svc_centerprint",
"svc_event",
"svc_soundindex",
"svc_ambientsound",
"svc_intermission",
"svc_modelindex",
"svc_cdtrack",
"svc_serverinfo",
"svc_eventindex",
"svc_weaponanim",
"svc_bspdecal",
"svc_roomtype",
"svc_addangle",
"svc_usermessage",
"svc_packetentities",
"svc_deltapacketentities",
"svc_chokecount",
"svc_resourcelist",
"svc_deltamovevars",
"svc_customization",
"svc_unused46",
"svc_crosshairangle",
"svc_soundfade",
"svc_unused49",
"svc_unused50",
"svc_director",
"svc_studiodecal",
"svc_unused53",
"svc_unused54",
"svc_unused55",
"svc_unused56",
"svc_querycvarvalue",
"svc_querycvarvalue2",
"svc_exec",
"svc_unused60",
"svc_unused61",
"svc_unused62",
"svc_unused63",
};
void CL_ProcessFile( qboolean successfully_received, const char *filename )
{
@ -325,4 +257,9 @@ void CL_Crashed( void )
{
}
void CL_HudMessage( const char *pMessage )
{
}
#endif // XASH_DEDICATED

View File

@ -74,7 +74,7 @@ void Sys_PrintUsage( void )
#if XASH_MESSAGEBOX == MSGBOX_STDERR
"\n" // dirty hack to not have Xash Error: Usage: on same line
#endif // XASH_MESSAGEBOX == MSGBOX_STDERR
"Usage:\n"
S_USAGE "\n"
#if !XASH_MOBILE_PLATFORM
#if XASH_WIN32
O("<xash>.exe [options] [+command1] [+command2 arg]","")
@ -151,6 +151,8 @@ void Sys_PrintUsage( void )
O("-clientlib <path>","override client DLL path")
#endif
O("-rodir <path> ","set read-only base directory, experimental")
O("-bugcomp ","enable precise bug compatibility. Will break games that don't require it")
O(" ","Refer to engine documentation for more info")
O("-ip <ip> ","set custom ip")
O("-port <port> ","set custom host port")
@ -267,36 +269,35 @@ void Host_AbortCurrentFrame( void )
/*
==================
Host_CheckSleep
Host_CalcSleep
==================
*/
void Host_CheckSleep( void )
static int Host_CalcSleep( void )
{
int sleeptime = host_sleeptime->value;
#ifndef XASH_DEDICATED
// never sleep in timedemo for benchmarking purposes
// also don't sleep with vsync for less lag
if( CL_IsTimeDemo( ) || CVAR_TO_BOOL( gl_vsync ))
return 0;
#endif
if( Host_IsDedicated() )
{
// let the dedicated server some sleep
Sys_Sleep( sleeptime );
return host_sleeptime->value;
}
else
switch( host.status )
{
if( host.status == HOST_NOFOCUS )
{
if( SV_Active() && CL_IsInGame( ))
Sys_Sleep( sleeptime ); // listenserver
else Sys_Sleep( 20 ); // sleep 20 ms otherwise
}
else if( host.status == HOST_SLEEP )
{
// completely sleep in minimized state
Sys_Sleep( 20 );
}
else
{
Sys_Sleep( sleeptime );
}
case HOST_NOFOCUS:
if( SV_Active() && CL_IsInGame())
return host_sleeptime->value;
// fallthrough
case HOST_SLEEP:
return 20;
}
return host_sleeptime->value;
}
void Host_NewInstance( const char *name, const char *finalmsg )
@ -344,7 +345,7 @@ void Host_ChangeGame_f( void )
}
else
{
const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 ));
const char *arg1 = va( "%s", Cmd_Argv( 1 ));
const char *arg2 = va( "change game to '%s'", FI->games[i]->title );
Host_NewInstance( arg1, arg2 );
@ -595,24 +596,16 @@ double Host_CalcFPS( void )
}
else if( Host_IsLocalGame( ))
{
fps = host_maxfps->value;
if( !CVAR_TO_BOOL( gl_vsync ))
fps = host_maxfps->value;
}
else
{
fps = host_maxfps->value;
if( fps == 0.0 ) fps = MAX_FPS;
fps = bound( MIN_FPS, fps, MAX_FPS );
}
// probably left part of this condition is redundant :-)
if( host.type != HOST_DEDICATED && Host_IsLocalGame( ) && !CL_IsTimeDemo( ))
{
// ajdust fps for vertical synchronization
if( CVAR_TO_BOOL( gl_vsync ))
if( !CVAR_TO_BOOL( gl_vsync ))
{
if( vid_displayfrequency->value != 0.0f )
fps = vid_displayfrequency->value;
else fps = 60.0; // default
fps = host_maxfps->value;
if( fps == 0.0 ) fps = MAX_FPS;
fps = bound( MIN_FPS, fps, MAX_FPS );
}
}
#endif
@ -630,27 +623,53 @@ Returns false if the time is too short to run a frame
qboolean Host_FilterTime( float time )
{
static double oldtime;
double fps;
double scale = sys_timescale.value;
double fps, scale = sys_timescale.value;
host.realtime += time * scale;
fps = Host_CalcFPS( );
fps = Host_CalcFPS();
// clamp the fps in multiplayer games
if( fps != 0.0 )
{
static int sleeps;
double targetframetime;
int sleeptime = Host_CalcSleep();
// limit fps to withing tolerable range
fps = bound( MIN_FPS, fps, MAX_FPS );
if( Host_IsDedicated() )
if( Host_IsDedicated( ))
targetframetime = ( 1.0 / ( fps + 1.0 ));
else targetframetime = ( 1.0 / fps );
if(( host.realtime - oldtime ) < targetframetime * scale )
{
if(( host.realtime - oldtime ) < ( 1.0 / ( fps + 1.0 )) * scale)
return false;
if( sleeptime > 0 && sleeps > 0 )
{
Sys_Sleep( sleeptime );
sleeps--;
}
return false;
}
else
if( sleeptime > 0 && sleeps <= 0 )
{
if(( host.realtime - oldtime ) < ( 1.0 / fps ) * scale )
return false;
if( host.status == HOST_FRAME )
{
// give few sleeps this frame with small margin
double targetsleeptime = targetframetime - host.pureframetime * 2;
// don't sleep if we can't keep up with the framerate
if( targetsleeptime > 0 )
sleeps = targetsleeptime / ( sleeptime * 0.001 );
else sleeps = 0;
}
else
{
// always sleep at least once in minimized/nofocus state
sleeps = 1;
}
}
}
@ -673,12 +692,17 @@ Host_Frame
*/
void Host_Frame( float time )
{
Host_CheckSleep();
double t1, t2;
// decide the simulation time
if( !Host_FilterTime( time ))
return;
t1 = Sys_DoubleTime();
if( host.framecount == 0 )
Con_DPrintf( "Time to first frame: %.3f seconds\n", t1 - host.starttime );
Host_InputFrame (); // input frame
Host_ClientBegin (); // begin client
Host_GetCommands (); // dedicated in
@ -686,6 +710,10 @@ void Host_Frame( float time )
Host_ClientFrame (); // client frame
HTTP_Run(); // both server and client
t2 = Sys_DoubleTime();
host.pureframetime = t2 - t1;
host.framecount++;
}
@ -701,15 +729,6 @@ void GAME_EXPORT Host_Error( const char *error, ... )
static qboolean recursive = false;
va_list argptr;
if( host.mouse_visible && !CL_IsInMenu( ))
{
// hide VGUI mouse
#ifdef XASH_SDL
SDL_ShowCursor( 0 );
#endif
host.mouse_visible = false;
}
va_start( argptr, error );
Q_vsprintf( hosterror1, error, argptr );
va_end( argptr );
@ -820,18 +839,15 @@ static void Host_RunTests( int stage )
{
case 0: // early engine load
memset( &tests_stats, 0, sizeof( tests_stats ));
Test_RunLibCommon();
Test_RunCommon();
Test_RunCmd();
Test_RunCvar();
TEST_LIST_0;
#if !XASH_DEDICATED
Test_RunCon();
TEST_LIST_0_CLIENT;
#endif /* XASH_DEDICATED */
break;
case 1: // after FS load
Test_RunImagelib();
TEST_LIST_1;
#if !XASH_DEDICATED
Test_RunVOX();
TEST_LIST_1_CLIENT;
#endif
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
Sys_Quit();
@ -859,10 +875,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
if( !Sys_CheckParm( "-disablehelp" ) )
{
if( Sys_CheckParm( "-help" ) || Sys_CheckParm( "-h" ) || Sys_CheckParm( "--help" ) )
{
if( Sys_CheckParm( "-help" ) || Sys_CheckParm( "-h" ) || Sys_CheckParm( "--help" ) )
{
Sys_PrintUsage();
}
}
}
if( !Sys_CheckParm( "-noch" ) )
@ -878,9 +894,11 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
host.mempool = Mem_AllocPool( "Zone Engine" );
host.allow_console = DEFAULT_ALLOWCONSOLE;
// HACKHACK: Quake console is always allowed
// TODO: determine if we are running QWrap more reliable
if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" ))
if( !host.allow_console && ( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" )))
host.allow_console = true;
if( Sys_CheckParm( "-dev" ))
@ -941,6 +959,13 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
// member console allowing
host.allow_console_init = host.allow_console;
if( Sys_CheckParm( "-bugcomp" ))
{
// add argument check here when we add other levels
// of bugcompatibility
host.bugcomp = BUGCOMP_GOLDSRC;
}
// timeBeginPeriod( 1 ); // a1ba: Do we need this?
// NOTE: this message couldn't be passed into game console but it doesn't matter
@ -983,22 +1008,22 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
if( COM_CheckString( baseDir ) )
{
Q_strncpy( host.rootdir, baseDir, sizeof(host.rootdir) );
Q_strncpy( host.rootdir, baseDir, sizeof( host.rootdir ));
}
else
{
#if TARGET_OS_IOS
const char *IOS_GetDocsDir();
Q_strncpy( host.rootdir, IOS_GetDocsDir(), sizeof(host.rootdir) );
#elif XASH_SDL == 2
#elif (XASH_SDL == 2) && !XASH_NSWITCH // GetBasePath not impl'd in switch-sdl2
char *szBasePath;
if( !( szBasePath = SDL_GetBasePath() ) )
Sys_Error( "couldn't determine current directory: %s", SDL_GetError() );
Q_strncpy( host.rootdir, szBasePath, sizeof( host.rootdir ) );
Q_strncpy( host.rootdir, szBasePath, sizeof( host.rootdir ));
SDL_free( szBasePath );
#else
if( !getcwd( host.rootdir, sizeof(host.rootdir) ) )
if( !getcwd( host.rootdir, sizeof( host.rootdir )))
{
Sys_Error( "couldn't determine current directory: %s", strerror( errno ) );
host.rootdir[0] = 0;
@ -1006,6 +1031,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
#endif
}
#if XASH_WIN32
COM_FixSlashes( host.rootdir );
#endif
len = Q_strlen( host.rootdir );
if( len && host.rootdir[len - 1] == '/' )
@ -1022,6 +1051,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
Q_strncpy( host.rodir, roDir, sizeof( host.rodir ));
}
#if XASH_WIN32
COM_FixSlashes( host.rootdir );
#endif
len = Q_strlen( host.rodir );
if( len && host.rodir[len - 1] == '/' )
@ -1044,6 +1077,12 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
Sys_InitLog();
// print bugcompatibility level here, after log was initialized
if( host.bugcomp == BUGCOMP_GOLDSRC )
{
Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" );
}
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" );
@ -1057,6 +1096,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
#endif
FS_LoadGameInfo( NULL );
Cvar_PostFSInit();
if( FS_FileExists( va( "%s.rc", SI.basedirName ), false ))
Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc
@ -1102,6 +1142,8 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
{
static double oldtime, newtime;
host.starttime = Sys_DoubleTime();
pChangeGame = func; // may be NULL
Host_InitCommon( argc, argv, progname, bChangeGame );
@ -1126,7 +1168,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
build = Cvar_Get( "buildnum", va( "%i", Q_buildnum_compat()), FCVAR_READ_ONLY, "returns a current build number" );
ver = Cvar_Get( "ver", va( "%i/%s (hw build %i)", PROTOCOL_VERSION, XASH_COMPAT_VERSION, Q_buildnum_compat()), FCVAR_READ_ONLY, "shows an engine version" );
Cvar_Get( "host_ver", va( "%i %s %s %s %s", Q_buildnum(), XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildcommit() ), FCVAR_READ_ONLY, "detailed info about this build" );
Cvar_Get( "host_ver", va( "%i " XASH_VERSION " %s %s %s", Q_buildnum(), Q_buildos(), Q_buildarch(), Q_buildcommit() ), FCVAR_READ_ONLY, "detailed info about this build" );
Cvar_Get( "host_lowmemorymode", va( "%i", XASH_LOW_MEMORY ), FCVAR_READ_ONLY, "indicates if engine compiled for low RAM consumption (0 - normal, 1 - low engine limits, 2 - low protocol limits)" );
Mod_Init();
@ -1195,11 +1237,17 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
oldtime = Sys_DoubleTime() - 0.1;
if( Host_IsDedicated() && GameState->nextstate == STATE_RUNFRAME )
if( Host_IsDedicated( ))
{
// in dedicated server input system can't set HOST_FRAME status
// so set it here as we're finished initializing
host.status = HOST_FRAME;
if( GameState->nextstate == STATE_RUNFRAME )
Con_Printf( "Type 'map <mapname>' to start game... (TAB-autocomplete is working too)\n" );
// execute server.cfg after commandline
// so we have a chance to set servercfgfile
Con_Printf( "Type 'map <mapname>' to start game... (TAB-autocomplete is working too)\n" );
Cbuf_AddText( va( "exec %s\n", Cvar_VariableString( "servercfgfile" )));
Cbuf_Execute();
}

View File

@ -17,8 +17,8 @@ GNU General Public License for more details.
#include "hpak.h"
#define HPAK_MAX_ENTRIES 0x8000
#define HPAK_MIN_SIZE (1 * 1024)
#define HPAK_MAX_SIZE (128 * 1024)
#define HPAK_ENTRY_MIN_SIZE (512)
#define HPAK_ENTRY_MAX_SIZE (128 * 1024)
typedef struct hash_pack_queue_s
{
@ -49,6 +49,18 @@ const char *HPAK_TypeFromIndex( int type )
return "?";
}
static inline void HPAK_ResourceToCompat( dresource_t *dest, resource_t *src )
{
memcpy( dest, src, sizeof( *dest ));
dest->pNext = dest->pPrev = 0xDEADBEEF;
}
static inline void HPAK_ResourceFromCompat( resource_t *dest, dresource_t *src )
{
memcpy( dest, src, sizeof( *src ));
dest->pNext = dest->pPrev = (void*)0xDEADBEEF;
}
static void HPAK_AddToQueue( const char *name, resource_t *pResource, void *data, file_t *f )
{
hash_pack_queue_t *p;
@ -89,6 +101,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f
byte md5[16];
file_t *fout;
MD5Context_t ctx;
dresource_t dresource;
if( !COM_CheckString( filename ))
return;
@ -101,7 +114,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f
Con_Printf( "creating HPAK %s.\n", pakname );
fout = FS_Open( pakname, "wb", false );
fout = FS_Open( pakname, "wb", true );
if( !fout )
{
Con_DPrintf( S_ERROR "HPAK_CreatePak: can't write %s.\n", pakname );
@ -145,7 +158,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f
hash_pack_info.count = 1;
hash_pack_info.entries = Z_Malloc( sizeof( hpak_lump_t ));
hash_pack_info.entries[0].resource = *pResource;
HPAK_ResourceToCompat( &hash_pack_info.entries[0].resource, pResource );
hash_pack_info.entries[0].filepos = FS_Tell( fout );
hash_pack_info.entries[0].disksize = pResource->nDownloadSize;
@ -181,7 +194,7 @@ static qboolean HPAK_FindResource( hpak_info_t *hpk, byte *hash, resource_t *pRe
if( !memcmp( hpk->entries[i].resource.rgucMD5_hash, hash, 16 ))
{
if( pResource )
*pResource = hpk->entries[i].resource;
HPAK_ResourceFromCompat( pResource, &hpk->entries[i].resource );
return true;
}
}
@ -203,7 +216,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
if( pData == NULL && pFile == NULL )
return;
if( pResource->nDownloadSize < HPAK_MIN_SIZE || pResource->nDownloadSize > HPAK_MAX_SIZE )
if( pResource->nDownloadSize < HPAK_ENTRY_MIN_SIZE || pResource->nDownloadSize > HPAK_ENTRY_MAX_SIZE )
{
Con_Printf( S_ERROR "%s: invalid size %s\n", name, Q_pretifymem( pResource->nDownloadSize, 2 ));
return;
@ -213,7 +226,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
memset( &ctx, 0, sizeof( MD5Context_t ));
MD5Init( &ctx );
if( pData == NULL )
if( !pData )
{
byte *temp;
@ -247,7 +260,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
Q_strncpy( srcname, name, sizeof( srcname ));
COM_ReplaceExtension( srcname, ".hpk" );
file_src = FS_Open( srcname, "rb", false );
file_src = FS_Open( srcname, "rb", true );
if( !file_src )
{
@ -259,7 +272,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
Q_strncpy( dstname, srcname, sizeof( dstname ));
COM_ReplaceExtension( dstname, ".hp2" );
file_dst = FS_Open( dstname, "wb", false );
file_dst = FS_Open( dstname, "wb", true );
if( !file_dst )
{
@ -312,11 +325,12 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
// make a new container
dstpak.count = srcpak.count + 1;
dstpak.entries = Z_Malloc( sizeof( hpak_lump_t ) * dstpak.count );
memcpy( dstpak.entries, srcpak.entries, srcpak.count );
memcpy( dstpak.entries, srcpak.entries, sizeof( hpak_lump_t ) * srcpak.count );
// check is there are entry with same hash
for( i = 0; i < srcpak.count; i++ )
{
if( memcmp( md5, srcpak.entries[i].resource.rgucMD5_hash, 16 ))
if( memcmp( md5, srcpak.entries[i].resource.rgucMD5_hash, 16 ) == 0 )
{
pCurrentEntry = &dstpak.entries[i];
@ -330,12 +344,14 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource,
memset( pCurrentEntry, 0, sizeof( hpak_lump_t ));
FS_Seek( file_dst, hash_pack_header.infotableofs, SEEK_SET );
pCurrentEntry->resource = *pResource;
HPAK_ResourceToCompat( &pCurrentEntry->resource, pResource );
pCurrentEntry->filepos = FS_Tell( file_dst );
pCurrentEntry->disksize = pResource->nDownloadSize;
if( !pData ) FS_FileCopy( file_dst, file_src, pCurrentEntry->disksize );
else FS_Write( file_dst, pData, pCurrentEntry->disksize );
if( !pData )
FS_FileCopy( file_dst, pFile, pCurrentEntry->disksize );
else
FS_Write( file_dst, pData, pCurrentEntry->disksize );
hash_pack_header.infotableofs = FS_Tell( file_dst );
FS_Write( file_dst, &dstpak.count, sizeof( dstpak.count ));
@ -370,7 +386,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet )
int i, num_lumps;
MD5Context_t MD5_Hash;
string pakname;
resource_t *pRes;
dresource_t *pRes;
byte md5[16];
if( quiet ) HPAK_FlushHostQueue();
@ -382,7 +398,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet )
Q_strncpy( pakname, filename, sizeof( pakname ));
COM_ReplaceExtension( pakname, ".hpk" );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
@ -418,7 +434,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet )
for( i = 0; i < num_lumps; i++ )
{
if( dataDir[i].disksize < 1 || dataDir[i].disksize > 131071 )
if( dataDir[i].disksize < HPAK_ENTRY_MIN_SIZE || dataDir[i].disksize > HPAK_ENTRY_MAX_SIZE )
{
// odd max size
Con_DPrintf( S_ERROR "HPAK_ValidatePak: lump %i has invalid size %s\n", i, Q_pretifymem( dataDir[i].disksize, 2 ));
@ -499,7 +515,7 @@ void HPAK_CheckSize( const char *filename )
Q_strncpy( pakname, filename, sizeof( pakname ));
COM_ReplaceExtension( pakname, ".hpk" );
if( FS_FileSize( pakname, false ) > ( maxsize * 1000000 ))
if( FS_FileSize( pakname, false ) > ( maxsize * 1048576 ))
{
Con_Printf( "Server: Size of %s > %f MB, deleting.\n", filename, hpk_maxsize->value );
Log_Printf( "Server: Size of %s > %f MB, deleting.\n", filename, hpk_maxsize->value );
@ -532,7 +548,7 @@ qboolean HPAK_ResourceForHash( const char *filename, byte *hash, resource_t *pRe
Q_strncpy( pakname, filename, sizeof( pakname ));
COM_ReplaceExtension( pakname, ".hpk" );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f ) return false;
FS_Read( f, &header, sizeof( header ));
@ -580,7 +596,7 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource
Q_strncpy( pakname, filename, sizeof( pakname ));
COM_ReplaceExtension( pakname, ".hpk" );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname );
@ -621,7 +637,7 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource
directory.entries = Z_Malloc( sizeof( hpak_lump_t ) * directory.count );
FS_Read( f, directory.entries, sizeof( hpak_lump_t ) * directory.count );
*pResource = directory.entries[index-1].resource;
HPAK_ResourceFromCompat( pResource, &directory.entries[index-1].resource );
Z_Free( directory.entries );
FS_Close( f );
@ -647,7 +663,7 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte
for( p = gp_hpak_queue; p != NULL; p = p->next )
{
if( !Q_stricmp(p->name, filename ) && !memcmp( p->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 ))
if( !Q_stricmp( p->name, filename ) && !memcmp( p->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 ))
{
if( buffer )
{
@ -666,7 +682,7 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte
Q_strncpy( pakname, filename, sizeof( pakname ));
COM_ReplaceExtension( pakname, ".hpk" );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f ) return false;
FS_Read( f, &header, sizeof( header ));
@ -702,11 +718,13 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte
{
entry = &directory.entries[i];
if( !memcmp( entry->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 ))
if( entry->filepos > 0 &&
entry->disksize > 0 &&
!memcmp( entry->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 ))
{
FS_Seek( f, entry->filepos, SEEK_SET );
if( buffer && entry->disksize > 0 )
if( buffer )
{
tmpbuf = Z_Malloc( entry->disksize );
FS_Read( f, tmpbuf, entry->disksize );
@ -747,7 +765,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource )
Q_strncpy( read_path, name, sizeof( read_path ));
COM_ReplaceExtension( read_path, ".hpk" );
file_src = FS_Open( read_path, "rb", false );
file_src = FS_Open( read_path, "rb", true );
if( !file_src )
{
Con_DPrintf( S_ERROR "%s couldn't open.\n", read_path );
@ -756,7 +774,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource )
Q_strncpy( save_path, read_path, sizeof( save_path ));
COM_ReplaceExtension( save_path, ".hp2" );
file_dst = FS_Open( save_path, "wb", false );
file_dst = FS_Open( save_path, "wb", true );
if( !file_dst )
{
@ -877,7 +895,7 @@ void HPAK_List_f( void )
COM_ReplaceExtension( pakname, ".hpk" );
Con_Printf( "Contents for %s.\n", pakname );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname );
@ -968,7 +986,7 @@ void HPAK_Extract_f( void )
COM_ReplaceExtension( pakname, ".hpk" );
Con_Printf( "Contents for %s.\n", pakname );
f = FS_Open( pakname, "rb", false );
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname );
@ -1020,7 +1038,7 @@ void HPAK_Extract_f( void )
Con_Printf( "Extracting %i: %10s %s %s\n", nCurrent + 1, type, size, lumpname );
if( entry->disksize <= 0 || entry->disksize >= HPAK_MAX_SIZE )
if( entry->disksize < HPAK_ENTRY_MIN_SIZE || entry->disksize > HPAK_ENTRY_MAX_SIZE )
{
Con_DPrintf( S_WARN "Unable to extract data, size invalid: %s\n", Q_memprint( entry->disksize ));
continue;

View File

@ -37,24 +37,53 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps]
#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK"
#define IDHPAK_VERSION 1
// a1ba: because Valve for some reason writes resource_t to file
// I had to make it crossplatform version
#pragma pack( push, 8 )
typedef struct dresource_s
{
char szFileName[64]; /* 0 64 */
/* --- cacheline 1 boundary (64 bytes) --- */
resourcetype_t type; /* 64 4 */
int nIndex; /* 68 4 */
int nDownloadSize; /* 72 4 */
unsigned char ucFlags; /* 76 1 */
unsigned char rgucMD5_hash[16]; /* 77 16 */
unsigned char playernum; /* 93 1 */
unsigned char rguc_reserved[32]; /* 94 32 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 2 boundary (128 bytes) --- */
uint32_t pNext; /* 128 4 */
uint32_t pPrev; /* 132 4 */
/* size: 136, cachelines: 3, members: 10 */
/* sum members: 134, holes: 1, sum holes: 2 */
/* last cacheline: 8 bytes */
} dresource_t;
#pragma pack( pop )
STATIC_ASSERT( sizeof( dresource_t ) == 136, "invalid dresource_t size, HPAKs won't be compatible (no custom logo in multiplayer!)" );
typedef struct
{
int ident; // should be equal HPAK
int version;
int infotableofs;
int ident; // should be equal HPAK
int version;
int infotableofs;
} hpak_header_t;
typedef struct
{
resource_t resource;
int filepos;
int disksize;
dresource_t resource;
int filepos;
int disksize;
} hpak_lump_t;
typedef struct
{
int count;
hpak_lump_t *entries; // variable sized.
int count;
hpak_lump_t *entries; // variable sized.
} hpak_info_t;
#endif // HPAK_H

View File

@ -91,7 +91,7 @@ typedef struct imglib_s
// global parms
rgba_t fogParams; // some water textures has info about underwater fog
image_hint_t hint; // hint for some loaders
int hint; // hint for some loaders
byte *tempbuffer; // for convert operations
int cmd_flags; // global imglib flags
int force_flags; // override cmd_flags
@ -103,6 +103,8 @@ typedef struct imglib_s
#define IMAGE_MAXHEIGHT 8192
#define LUMP_MAXWIDTH 1024 // WorldCraft limits
#define LUMP_MAXHEIGHT 1024
#define PLDECAL_MAXWIDTH 512
#define PLDECAL_MAXHEIGHT 512
enum
{
@ -171,7 +173,6 @@ rgbdata_t *Image_Quantize( rgbdata_t *pic );
// img_utils.c
//
void Image_Reset( void );
rgbdata_t *ImagePack( void );
byte *Image_Copy( size_t size );
void Image_CopyParms( rgbdata_t *src );
qboolean Image_ValidSize( const char *name );

View File

@ -25,7 +25,7 @@ Image_LoadBMP
qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesize )
{
byte *buf_p, *pixbuf;
rgba_t palette[256];
rgba_t palette[256] = { 0 };
int i, columns, column, rows, row, bpp = 1;
int cbPalBytes = 0, padSize = 0, bps = 0;
int reflectivity[3] = { 0, 0, 0 };
@ -69,8 +69,11 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi
// bogus compression? Only non-compressed supported.
if( bhdr.compression != BI_RGB )
{
Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name );
return false;
if( bhdr.bitsPerPixel != 32 || bhdr.compression != BI_BITFIELDS )
{
Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name );
return false;
}
}
image.width = columns = bhdr.width;
@ -174,7 +177,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi
break;
}
estimatedSize = ( buf_p - buffer ) + ( image.width + padSize ) * image.height * ( bhdr.bitsPerPixel >> 3 );
estimatedSize = ( buf_p - buffer ) + image.width * image.height * ( bhdr.bitsPerPixel >> 3 );
if( filesize < estimatedSize )
{
if( image.palette )

View File

@ -25,7 +25,12 @@ GNU General Public License for more details.
#define BI_FILE_HEADER_SIZE 14
#define BI_SIZE 40 // size of bitmap info header.
#if !defined(BI_RGB)
#define BI_RGB 0 // uncompressed RGB bitmap(defined in wingdi.h)
#define BI_RGB 0 // uncompressed RGB bitmap (defined in wingdi.h)
#define BI_RLE8 1
#define BI_RLE4 2
#define BI_BITFIELDS 3
#define BI_JPEG 4
#define BI_PNG 5
#endif
#pragma pack( push, 1 )

View File

@ -110,20 +110,21 @@ void Image_Reset( void )
image.size = 0;
}
rgbdata_t *ImagePack( void )
static rgbdata_t *ImagePack( void )
{
rgbdata_t *pack = Mem_Calloc( host.imagepool, sizeof( rgbdata_t ));
rgbdata_t *pack;
// clear any force flags
image.force_flags = 0;
if( image.cubemap && image.num_sides != 6 )
{
// this never be happens, just in case
FS_FreeImage( pack );
// this never can happen, just in case
return NULL;
}
pack = Mem_Calloc( host.imagepool, sizeof( *pack ));
if( image.cubemap )
{
image.flags |= IMAGE_CUBEMAP;
@ -163,7 +164,7 @@ FS_AddSideToPack
================
*/
qboolean FS_AddSideToPack( const char *name, int adjust_flags )
static qboolean FS_AddSideToPack( int adjust_flags )
{
byte *out, *flipped;
qboolean resampled = false;
@ -203,6 +204,88 @@ qboolean FS_AddSideToPack( const char *name, int adjust_flags )
return true;
}
static const loadpixformat_t *Image_GetLoadFormatForExtension( const char *ext )
{
const loadpixformat_t *format;
if( !COM_CheckStringEmpty( ext ))
return NULL;
for( format = image.loadformats; format->formatstring; format++ )
{
if( !Q_stricmp( ext, format->ext ))
return format;
}
return NULL;
}
static qboolean Image_ProbeLoadBuffer_( const loadpixformat_t *fmt, const char *name, const byte *buf, size_t size, int override_hint )
{
if( override_hint > 0 )
image.hint = override_hint;
else image.hint = fmt->hint;
return fmt->loadfunc( name, buf, size );
}
static qboolean Image_ProbeLoadBuffer( const loadpixformat_t *fmt, const char *name, const byte *buf, size_t size, int override_hint )
{
if( size <= 0 )
return false;
// bruteforce all loaders
if( !fmt )
{
for( fmt = image.loadformats; fmt->formatstring; fmt++ )
{
if( Image_ProbeLoadBuffer_( fmt, name, buf, size, override_hint ))
return true;
}
return false;
}
return Image_ProbeLoadBuffer_( fmt, name, buf, size, override_hint );
}
static qboolean Image_ProbeLoad_( const loadpixformat_t *fmt, const char *name, const char *suffix, int override_hint )
{
qboolean success = false;
fs_offset_t filesize;
string path;
byte *f;
Q_snprintf( path, sizeof( path ), fmt->formatstring, name, suffix, fmt->ext );
f = FS_LoadFile( path, &filesize, false );
if( f )
{
success = Image_ProbeLoadBuffer( fmt, path, f, filesize, override_hint );
Mem_Free( f );
}
return success;
}
static qboolean Image_ProbeLoad( const loadpixformat_t *fmt, const char *name, const char *suffix, int override_hint )
{
if( !fmt )
{
// bruteforce all formats to allow implicit extension
for( fmt = image.loadformats; fmt->formatstring; fmt++ )
{
if( Image_ProbeLoad_( fmt, name, suffix, override_hint ))
return true;
}
return false;
}
return Image_ProbeLoad_( fmt, name, suffix, override_hint );
}
/*
================
FS_LoadImage
@ -213,87 +296,35 @@ loading and unpack to rgba any known image
rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
{
const char *ext = COM_FileExtension( filename );
string path, loadname, sidename;
qboolean anyformat = true;
string loadname;
int i;
fs_offset_t filesize = 0;
const loadpixformat_t *format;
const loadpixformat_t *extfmt;
const cubepack_t *cmap;
byte *f;
Q_strncpy( loadname, filename, sizeof( loadname ));
Image_Reset(); // clear old image
if( Q_stricmp( ext, "" ))
{
// we needs to compare file extension with list of supported formats
// and be sure what is real extension, not a filename with dot
for( format = image.loadformats; format && format->formatstring; format++ )
{
if( !Q_stricmp( format->ext, ext ))
{
COM_StripExtension( loadname );
anyformat = false;
break;
}
}
}
// we needs to compare file extension with list of supported formats
// and be sure what is real extension, not a filename with dot
if(( extfmt = Image_GetLoadFormatForExtension( ext )))
COM_StripExtension( loadname );
// special mode: skip any checks, load file from buffer
if( filename[0] == '#' && buffer && size )
goto load_internal;
// now try all the formats in the selected list
for( format = image.loadformats; format && format->formatstring; format++)
{
if( anyformat || !Q_stricmp( ext, format->ext ))
{
Q_sprintf( path, format->formatstring, loadname, "", format->ext );
image.hint = format->hint;
f = FS_LoadFile( path, &filesize, false );
if( f && filesize > 0 )
{
if( format->loadfunc( path, f, filesize ))
{
Mem_Free( f ); // release buffer
return ImagePack(); // loaded
}
else Mem_Free( f ); // release buffer
}
}
}
if( Image_ProbeLoad( extfmt, loadname, "", -1 ))
return ImagePack();
// check all cubemap sides with package suffix
for( cmap = load_cubemap; cmap && cmap->type; cmap++ )
{
for( i = 0; i < 6; i++ )
{
// for support mixed cubemaps e.g. sky_ft.bmp, sky_rt.tga
// NOTE: all loaders must keep sides in one format for all
for( format = image.loadformats; format && format->formatstring; format++ )
if( Image_ProbeLoad( extfmt, loadname, cmap->type[i].suf, cmap->type[i].hint ) &&
FS_AddSideToPack( cmap->type[i].flags )) // process flags to flip some sides
{
if( anyformat || !Q_stricmp( ext, format->ext ))
{
Q_sprintf( path, format->formatstring, loadname, cmap->type[i].suf, format->ext );
image.hint = (image_hint_t)cmap->type[i].hint; // side hint
f = FS_LoadFile( path, &filesize, false );
if( f && filesize > 0 )
{
// this name will be used only for tell user about problems
if( format->loadfunc( path, f, filesize ))
{
Q_snprintf( sidename, sizeof( sidename ), "%s%s.%s", loadname, cmap->type[i].suf, format->ext );
if( FS_AddSideToPack( sidename, cmap->type[i].flags )) // process flags to flip some sides
{
Mem_Free( f );
break; // loaded
}
}
Mem_Free( f );
}
}
break;
}
if( image.num_sides != i + 1 ) // check side
@ -323,20 +354,13 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
return ImagePack(); // all done
load_internal:
for( format = image.loadformats; format && format->formatstring; format++ )
if( buffer && size )
{
if( anyformat || !Q_stricmp( ext, format->ext ))
{
image.hint = format->hint;
if( buffer && size > 0 )
{
if( format->loadfunc( loadname, buffer, size ))
return ImagePack(); // loaded
}
}
if( Image_ProbeLoadBuffer( extfmt, loadname, buffer, size, -1 ))
return ImagePack();
}
if( filename[0] != '#' )
if( loadname[0] != '#' )
Con_Reportf( S_WARN "FS_LoadImage: couldn't load \"%s\"\n", loadname );
// clear any force flags
@ -345,6 +369,8 @@ load_internal:
return NULL;
}
/*
================
Image_Save
@ -355,7 +381,7 @@ writes image as any known format
qboolean FS_SaveImage( const char *filename, rgbdata_t *pix )
{
const char *ext = COM_FileExtension( filename );
qboolean anyformat = !Q_stricmp( ext, "" ) ? true : false;
qboolean anyformat = !COM_CheckStringEmpty( ext );
string path, savename;
const savepixformat_t *format;

View File

@ -20,12 +20,17 @@ GNU General Public License for more details.
#if defined(XASH_NO_NETWORK)
#include "platform/stub/net_stub.h"
#elif XASH_NSWITCH
// our ntohl is here
#include <arpa/inet.h>
#elif !XASH_WIN32
#include <netinet/in.h>
#endif
static const char png_sign[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'};
static const char ihdr_sign[] = {'I', 'H', 'D', 'R'};
static const char trns_sign[] = {'t', 'R', 'N', 'S'};
static const char plte_sign[] = {'P', 'L', 'T', 'E'};
static const char idat_sign[] = {'I', 'D', 'A', 'T'};
static const char iend_sign[] = {'I', 'E', 'N', 'D'};
static const int iend_crc32 = 0xAE426082;
@ -39,9 +44,10 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
{
int ret;
short p, a, b, c, pa, pb, pc;
byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL, *rowend;
uint chunk_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize;
uint uncompressed_size, pixel_size, i, y, filter_type, chunk_sign;
byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL;
byte *pallete = NULL, *trns = NULL;
uint chunk_len, trns_len, plte_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize;
uint uncompressed_size, pixel_size, pixel_count, i, y, filter_type, chunk_sign, r_alpha, g_alpha, b_alpha;
qboolean has_iend_chunk = false;
z_stream stream = {0};
png_t png_hdr;
@ -67,7 +73,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
// check IHDR chunk length (valid value - 13)
if( png_hdr.ihdr_len != sizeof( png_ihdr_t ) )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid IHDR chunk size (%s)\n", name );
Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid IHDR chunk size %u (%s)\n", png_hdr.ihdr_len, name );
return false;
}
@ -79,36 +85,43 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
}
// convert image width and height to little endian
png_hdr.ihdr_chunk.height = ntohl( png_hdr.ihdr_chunk.height );
png_hdr.ihdr_chunk.width = ntohl( png_hdr.ihdr_chunk.width );
image.height = png_hdr.ihdr_chunk.height = ntohl( png_hdr.ihdr_chunk.height );
image.width = png_hdr.ihdr_chunk.width = ntohl( png_hdr.ihdr_chunk.width );
if( png_hdr.ihdr_chunk.height == 0 || png_hdr.ihdr_chunk.width == 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid image size %dx%d (%s)\n", png_hdr.ihdr_chunk.width, png_hdr.ihdr_chunk.height, name );
Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid image size %ux%u (%s)\n", png_hdr.ihdr_chunk.width, png_hdr.ihdr_chunk.height, name );
return false;
}
if( !Image_ValidSize( name ))
return false;
if( png_hdr.ihdr_chunk.bitdepth != 8 )
{
Con_DPrintf( S_WARN "Image_LoadPNG: Only 8-bit images is supported (%s)\n", name );
return false;
}
if( png_hdr.ihdr_chunk.colortype != PNG_CT_RGB && png_hdr.ihdr_chunk.colortype != PNG_CT_RGBA )
if( !( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB
|| png_hdr.ihdr_chunk.colortype == PNG_CT_RGBA
|| png_hdr.ihdr_chunk.colortype == PNG_CT_GREY
|| png_hdr.ihdr_chunk.colortype == PNG_CT_ALPHA
|| png_hdr.ihdr_chunk.colortype == PNG_CT_PALLETE ) )
{
Con_DPrintf( S_WARN "Image_LoadPNG: Only 8-bit RGB and RGBA images is supported (%s)\n", name );
Con_DPrintf( S_WARN "Image_LoadPNG: Unknown color type %u (%s)\n", png_hdr.ihdr_chunk.colortype, name );
return false;
}
if( png_hdr.ihdr_chunk.compression > 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown compression method (%s)\n", name );
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown compression method %u (%s)\n", png_hdr.ihdr_chunk.compression, name );
return false;
}
if( png_hdr.ihdr_chunk.filter > 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown filter type (%s)\n", name );
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown filter type %u (%s)\n", png_hdr.ihdr_chunk.filter, name );
return false;
}
@ -120,7 +133,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
if( png_hdr.ihdr_chunk.interlace > 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown interlacing type (%s)\n", name );
Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown interlacing type %u (%s)\n", png_hdr.ihdr_chunk.interlace, name );
return false;
}
@ -151,15 +164,34 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
if( chunk_len > INT_MAX )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with wrong size (%s)\n", name );
Mem_Free( idat_buf );
if( idat_buf ) Mem_Free( idat_buf );
return false;
}
if( chunk_len > filesize - ( buf_p - buffer ))
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with size past file size (%s)\n", name );
if( idat_buf ) Mem_Free( idat_buf );
return false;
}
// move pointer
buf_p += sizeof( chunk_sign );
buf_p += sizeof( chunk_len );
// find transparency
if( !memcmp( buf_p, trns_sign, sizeof( trns_sign ) ) )
{
trns = buf_p + sizeof( trns_sign );
trns_len = chunk_len;
}
// find pallete for indexed image
else if( !memcmp( buf_p, plte_sign, sizeof( plte_sign ) ) )
{
pallete = buf_p + sizeof( plte_sign );
plte_len = chunk_len / 3;
}
// get all IDAT chunks data
if( !memcmp( buf_p, idat_sign, sizeof( idat_sign ) ) )
else if( !memcmp( buf_p, idat_sign, sizeof( idat_sign ) ) )
{
newsize = oldsize + chunk_len;
idat_buf = (byte *)Mem_Realloc( host.imagepool, idat_buf, newsize );
@ -185,7 +217,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
if( ntohl( crc32 ) != crc32_check )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with wrong CRC32 sum (%s)\n", name );
Mem_Free( idat_buf );
if( idat_buf ) Mem_Free( idat_buf );
return false;
}
@ -193,6 +225,19 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
buf_p += sizeof( crc32 );
}
if( oldsize == 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Couldn't find IDAT chunks (%s)\n", name );
return false;
}
if( png_hdr.ihdr_chunk.colortype == PNG_CT_PALLETE && !pallete )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: PLTE chunk not found (%s)\n", name );
Mem_Free( idat_buf );
return false;
}
if( !has_iend_chunk )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk not found (%s)\n", name );
@ -202,19 +247,20 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
if( chunk_len != 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk has wrong size (%s)\n", name );
Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk has wrong size %u (%s)\n", chunk_len, name );
Mem_Free( idat_buf );
return false;
}
if( oldsize == 0 )
{
Con_DPrintf( S_ERROR "Image_LoadPNG: Couldn't find IDAT chunks (%s)\n", name );
return false;
}
switch( png_hdr.ihdr_chunk.colortype )
{
case PNG_CT_GREY:
case PNG_CT_PALLETE:
pixel_size = 1;
break;
case PNG_CT_ALPHA:
pixel_size = 2;
break;
case PNG_CT_RGB:
pixel_size = 3;
break;
@ -228,12 +274,13 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
}
image.type = PF_RGBA_32; // always exctracted to 32-bit buffer
image.width = png_hdr.ihdr_chunk.width;
image.height = png_hdr.ihdr_chunk.height;
image.size = image.height * image.width * 4;
image.flags |= IMAGE_HAS_COLOR;
pixel_count = image.height * image.width;
image.size = pixel_count * 4;
if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGBA )
if( png_hdr.ihdr_chunk.colortype & PNG_CT_RGB )
image.flags |= IMAGE_HAS_COLOR;
if( trns || ( png_hdr.ihdr_chunk.colortype & PNG_CT_ALPHA ) )
image.flags |= IMAGE_HAS_ALPHA;
image.depth = 1;
@ -275,7 +322,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
raw = uncompressed_buffer;
if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB )
if( png_hdr.ihdr_chunk.colortype != PNG_CT_RGBA )
prior = pixbuf = raw;
filter_type = *raw++;
@ -377,23 +424,83 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
prior = pixbuf;
}
// convert RGB-to-RGBA
if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB )
{
pixbuf = image.rgba;
raw = uncompressed_buffer;
pixbuf = image.rgba;
raw = uncompressed_buffer;
for( y = 0; y < image.height; y++ )
switch( png_hdr.ihdr_chunk.colortype )
{
case PNG_CT_RGB:
if( trns )
{
rowend = raw + rowsize;
for( ; raw < rowend; raw += pixel_size )
r_alpha = trns[0] << 8 | trns[1];
g_alpha = trns[2] << 8 | trns[3];
b_alpha = trns[4] << 8 | trns[5];
}
for( y = 0; y < pixel_count; y++, raw += pixel_size )
{
*pixbuf++ = raw[0];
*pixbuf++ = raw[1];
*pixbuf++ = raw[2];
if( trns && r_alpha == raw[0]
&& g_alpha == raw[1]
&& b_alpha == raw[2] )
*pixbuf++ = 0;
else
*pixbuf++ = 0xFF;
}
break;
case PNG_CT_GREY:
if( trns )
r_alpha = trns[0] << 8 | trns[1];
for( y = 0; y < pixel_count; y++, raw += pixel_size )
{
*pixbuf++ = raw[0];
*pixbuf++ = raw[0];
*pixbuf++ = raw[0];
if( trns && r_alpha == raw[0] )
*pixbuf++ = 0;
else
*pixbuf++ = 0xFF;
}
break;
case PNG_CT_ALPHA:
for( y = 0; y < pixel_count; y++, raw += pixel_size )
{
*pixbuf++ = raw[0];
*pixbuf++ = raw[0];
*pixbuf++ = raw[0];
*pixbuf++ = raw[1];
}
break;
case PNG_CT_PALLETE:
for( y = 0; y < pixel_count; y++, raw += pixel_size )
{
if( raw[0] < plte_len )
{
*pixbuf++ = raw[0];
*pixbuf++ = raw[1];
*pixbuf++ = raw[2];
*pixbuf++ = pallete[3 * raw[0] + 0];
*pixbuf++ = pallete[3 * raw[0] + 1];
*pixbuf++ = pallete[3 * raw[0] + 2];
if( trns && raw[0] < trns_len )
*pixbuf++ = trns[raw[0]];
else
*pixbuf++ = 0xFF;
}
else
{
*pixbuf++ = 0;
*pixbuf++ = 0;
*pixbuf++ = 0;
*pixbuf++ = 0xFF;
}
}
break;
default:
break;
}
Mem_Free( uncompressed_buffer );

View File

@ -25,8 +25,8 @@ GNU General Public License for more details.
enum png_colortype
{
PNG_CT_GREY,
PNG_CT_PALLETE = BIT(0),
PNG_CT_RGB = BIT(1),
PNG_CT_PALLETE = (PNG_CT_RGB|BIT(0)),
PNG_CT_ALPHA = BIT(2),
PNG_CT_RGBA = (PNG_CT_RGB|PNG_CT_ALPHA)
};

View File

@ -408,7 +408,7 @@ void learn( void )
if( rad ) alterneigh( rad, j, r, g, b ); // alter neighbours
p += step;
if( p >= lim ) p -= lengthcount;
while( p >= lim ) p -= lengthcount;
i++;

View File

@ -237,7 +237,16 @@ void Image_AddCmdFlags( uint flags )
qboolean Image_ValidSize( const char *name )
{
if( image.width > IMAGE_MAXWIDTH || image.height > IMAGE_MAXHEIGHT || image.width <= 0 || image.height <= 0 )
int max_width = IMAGE_MAXWIDTH;
int max_height = IMAGE_MAXHEIGHT;
if( Image_CheckFlag( IL_LOAD_PLAYER_DECAL ))
{
max_width = PLDECAL_MAXWIDTH;
max_height = PLDECAL_MAXHEIGHT;
}
if( image.width > max_width || image.height > max_height || image.width <= 0 || image.height <= 0 )
{
Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width, image.height );
return false;

View File

@ -311,13 +311,17 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesi
{
int numcolors;
for( i = 0; i < pixels; i++ )
// HACKHACK: console background image shouldn't be transparent
if( !Q_stristr( name, "conback" ))
{
if( fin[i] == 255 )
for( i = 0; i < pixels; i++ )
{
image.flags |= IMAGE_HAS_ALPHA;
rendermode = LUMP_MASKED;
break;
if( fin[i] == 255 )
{
image.flags |= IMAGE_HAS_ALPHA;
rendermode = LUMP_MASKED;
break;
}
}
}
pal = fin + pixels;

391
engine/common/ipv6text.c Normal file
View File

@ -0,0 +1,391 @@
#include <stdio.h>
#include <string.h>
#include "ipv6text.h"
#include "xash3d_types.h"
#ifdef _WIN32
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
void IPv6IPToString( char *pszOutText, const unsigned char *ip )
{
// Find the longest run of consecutive zero quads.
// If there's a tie, we want the leftmost one.
int idxLongestRunStart = -1;
int nLongestRun = 1; // It must be at least 2 quads in a row, a single 0 must not be compressed
int nCurrentRun = 0, idxQuad;
char *p;
qboolean bNeedColon;
for ( idxQuad = 0 ; idxQuad < 8 ; ++idxQuad )
{
// Zero
if ( ip[idxQuad*2] || ip[idxQuad*2 + 1] )
{
// Terminate run
nCurrentRun = 0;
}
else
{
// Extend (or begin) run
++nCurrentRun;
// Longer than previously found run?
if ( nCurrentRun > nLongestRun )
{
nLongestRun = nCurrentRun;
idxLongestRunStart = idxQuad - nCurrentRun + 1;
}
}
}
// Print the quads
p = pszOutText;
idxQuad = 0;
bNeedColon = false;
while ( idxQuad < 8 )
{
// Run of compressed zeros?
if ( idxQuad == idxLongestRunStart )
{
*(p++) = ':';
*(p++) = ':';
bNeedColon = false;
idxQuad += nLongestRun;
}
else
{
// Lowercase hex digits, with leading zeros omitted
static const char hexdigits[] = "0123456789abcdef";
unsigned quad;
// Colon to separate from previous, unless
// we are first or immediately follow compressed zero "::"
if ( bNeedColon )
*(p++) = ':';
// Next quad should should print a separator
bNeedColon = true;
// Assemble 16-bit quad value from the two bytes
quad = ( (unsigned)ip[idxQuad*2] << 8U ) | ip[idxQuad*2 + 1];
// Manually do the hex number formatting.
if ( quad >= 0x0010 )
{
if ( quad >= 0x0100 )
{
if ( quad >= 0x1000 )
*(p++) = hexdigits[ quad >> 12U ];
*(p++) = hexdigits[ ( quad >> 8U ) & 0xf ];
}
*(p++) = hexdigits[ ( quad >> 4U ) & 0xf ];
}
// Least significant digit, which is always printed
*(p++) = hexdigits[ quad & 0xf ];
// On to the next one
++idxQuad;
}
}
// String terminator
*p = '\0';
}
void IPv6AddrToString( char *pszOutText, const unsigned char *ip, uint16_t port, uint32_t scope )
{
char *p = pszOutText;
// Open bracket
*(p++) = '[';
// Print in the IP
IPv6IPToString( p, ip );
// Find the end of the string
while (*p)
++p;
if ( scope )
{
// And now the scope. Max 32-digit scope number is 10 digits
snprintf( p, 12, "%%%d", scope );
// Find the end of the string
while (*p)
++p;
}
// And now the rest. Max 16-digit port number is 6 digits
snprintf( p, 8, "]:%u", (unsigned int)port );
}
static inline int ParseIPv6Addr_HexDigitVal( char c )
{
if ( c >= '0' && c <= '9' ) return c - '0';
if ( c >= 'a' && c <= 'f' ) return c - ('a' - 0xa);
if ( c >= 'A' && c <= 'F' ) return c - ('A' - 0xa);
return -1;
}
static inline int ParseIPv6Addr_DecimalDigitVal( char c )
{
if ( c >= '0' && c <= '9' ) return c - '0';
return -1;
}
static bool ParseIPv6Addr_IsSpace( char c )
{
// Newlines don't count, intentionally
return c == ' ' || c == '\t';
}
bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope )
{
unsigned char *d, *pZeroFill, *pEndIP;
const char *s;
qboolean bQuadMustFollow;
int nPort;
while ( ParseIPv6Addr_IsSpace( *pszText ) )
++pszText;
s = pszText;
// Skip opening bracket, if present
if ( *s == '[' )
{
++s;
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
}
// Special case for leading "::"
bQuadMustFollow = true;
d = pOutIP;
pZeroFill = NULL;
pEndIP = pOutIP + 16;
if ( s[0] == ':' && s[1] == ':' )
{
pZeroFill = d;
s += 2;
bQuadMustFollow = false;
}
// Parse quads until we get to the end
for (;;)
{
// Next thing must be a quad, or end of input. Is it a quad?
int quadDigit = ParseIPv6Addr_HexDigitVal( *s );
int quad;
if ( quadDigit < 0 )
{
if ( bQuadMustFollow )
return false;
break;
}
// No room for more quads?
if ( d >= pEndIP )
return false;
++s;
quad = quadDigit;
// Now parse up to three additional characters
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
}
}
}
// Stash it in the next slot, ignoring for now the issue
// of compressed zeros
*(d++) = (unsigned char)( quad >> 8 );
*(d++) = (unsigned char)quad;
// Only valid character for the IP portion is a colon.
// Anything else ends the IP portion
if ( *s != ':' )
break;
// Compressed zeros?
if ( s[1] == ':' )
{
// Eat '::'
s += 2;
// Can only have one range of compressed zeros
if ( pZeroFill )
return false;
// Remember where to insert the compressed zeros
pZeroFill = d;
// An IP can end with '::'
bQuadMustFollow = false;
}
else
{
// If they have filed the entire IP with no compressed zeros,
// then this is unambiguously a port number. That's not
// necessarily the best style, but it *is* unambiguous
// what it should mean, so let's allow it. If there
// are compressed zeros, then this is ambiguous, and we will
// always interpret it as a quad.
if ( !pZeroFill && d >= pEndIP )
break; // leave ':' as next character, for below
// Eat ':'
++s;
// A single colon must be followed by another quad
bQuadMustFollow = true;
}
}
// End of the IP. Do we have compressed zeros?
if ( pZeroFill )
{
// How many zeros do we need to fill?
intptr_t nZeros = pEndIP - d;
if ( nZeros <= 0 )
return false;
// Shift the quads after the bytes to the end
memmove( pZeroFill+nZeros, pZeroFill, d-pZeroFill );
// And now fill the zeros
memset( pZeroFill, 0, nZeros );
}
else
{
// No compressed zeros. Just make sure we filled the IP exactly
if ( d != pEndIP )
return false;
}
if ( *s == '%' )
{
// Parse scope number
uint32_t unScope = 0;
int nScopeDigit;
++s;
nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nScopeDigit < 0 )
return false;
unScope = (uint32_t)nScopeDigit;
for (;;)
{
++s;
if ( *s == '\0' || *s == ']' || ParseIPv6Addr_IsSpace( *s ) )
break;
nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nScopeDigit < 0 )
return false;
unScope = unScope * 10 + nScopeDigit;
}
if ( pOutScope )
*pOutScope = unScope;
}
else
{
if ( pOutScope )
*pOutScope = 0;
}
// If we started with a bracket, then the next character MUST be a bracket.
// (And this is the only circumstance in which a closing bracket would be legal)
if ( *pszText == '[' )
{
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s != ']' )
return false;
++s;
}
// Now we are definitely at the end of the IP. Do we have a port?
// We support all of the syntaxes mentioned in RFC5952 section 6 other
// than the ambiguous case
if ( *s == ':' || *s == '#' || *s == '.' || *s == 'p' || *s == 'P' )
{
++s;
}
else
{
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s == '\0' )
{
// Parsed IP without port OK
if ( pOutPort )
*pOutPort = -1;
return true;
}
if ( strncmp( s, "port", 4 ) == 0 )
{
s += 4;
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
}
else
{
// Extra stuff after the IP which isn't whitespace or a port
return false;
}
}
// We have a port. If they didn't ask for it, that's considered a parse failure.
if ( !pOutPort )
return false;
// Parse port number
nPort = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nPort < 0 )
return false;
for (;;)
{
int portDigit;
++s;
if ( *s == '\0' || ParseIPv6Addr_IsSpace( *s ) )
break;
portDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( portDigit < 0 )
return false;
nPort = nPort * 10 + portDigit;
if ( nPort > 0xffff )
return false;
}
// Consume trailing whitespace; confirm nothing else in the input
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s != '\0' )
return false;
*pOutPort = nPort;
return true;
}

54
engine/common/ipv6text.h Normal file
View File

@ -0,0 +1,54 @@
/// Standalone plain C utilities for parsing and printing IPv6 addresses
#pragma once
#include <stdint.h>
#include <stdbool.h>
/// Max length of an IPv6 string, with scope, WITHOUT port number, including \0':
/// 0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295
#define k_ncchMaxIPV6AddrStringWithoutPort 51
/// Max number of bytes output by IPv6AddrToString, including '\0':
/// [0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295]:12345
/// There are other strings that are acceptable to ParseIPv6Addr
/// that are longer than this, but this is the longest canonical
/// string.
#define k_ncchMaxIPV6AddrStringWithPort 59
#ifdef __cplusplus
extern "C" {
#endif
/// Format an IPv6 address to the canonical form according to RFC5952.
/// The address should be 16 bytes (e.g. same as in6_addr::s6_addr).
/// Your buffer MUST be at least k_ncchMaxIPV6AddrStringWithoutPort bytes.
extern void IPv6IPToString( char *pszOutText, const unsigned char *ip );
/// Format IPv6 IP and port to string. This uses the recommended
/// bracket notation, eg [1234::1]:12345. Your buffer must be
/// at least k_ncchMaxIPV6AddrStringWithPort bytes.
extern void IPv6AddrToString( char *pszOutText, const unsigned char *ip, uint16_t port, uint32_t scope );
/// Parse IPv6 address string. Returns true if parsed OK. Returns false
/// if input cannot be parsed, or if input specifies a port but pOutPort is NULL.
/// If input does not specify a port, and pOutPort is non-NULL, then *pOutPort is
/// set to -1.
///
/// Parsing is tolerant of any unambiguous IPv6 representation, the input
/// need not be the canonical RFC5952 representation.
///
/// IPv6 zones are not supported.
///
/// Leading and trailing whitespace is OK around the entire string,
/// but not internal whitespace. The different methods for separating the
/// port in RFC5952 are supported section 6, except the ambiguous case
/// of a colon to separate the port, when the IP contains a double-colon.
/// Brackets around an IP are OK, even if there is no port.
///
/// Address must point to a 16-byte buffer (e.g. same as in6_addr::s6_addr)
/// Port is returned in host byte order.
extern bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope );
#ifdef __cplusplus
}
#endif

View File

@ -35,7 +35,9 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#endif
#define E_GAME "XASH3D_GAME" // default env dir to start from
#define GAME_PATH "valve" // default dir to start from
#ifndef XASH_GAMEDIR
#define XASH_GAMEDIR "valve"
#endif
static char szGameDir[128]; // safe place to keep gamedir
static int szArgc;
@ -56,7 +58,7 @@ _inline int Sys_Start( void )
const char *game = getenv( E_GAME );
if( !game )
game = GAME_PATH;
game = XASH_GAMEDIR;
Q_strncpy( szGameDir, game, sizeof( szGameDir ));
#if XASH_EMSCRIPTEN

View File

@ -65,7 +65,7 @@ void *COM_FunctionFromName_SR( void *hInstance, const char *pName )
if( f ) return f;
}
#elif XASH_MSVC
#elif _MSC_VER
// TODO: COM_ConvertToLocalPlatform doesn't support MSVC yet
// also custom loader strips always MSVC mangling, so Win32
// platforms already use platform-neutral names
@ -158,6 +158,22 @@ static void COM_GenerateClientLibraryPath( const char *name, char *out, size_t s
#endif
}
/*
==============
COM_StripIntelSuffix
Some modders use _i?86 suffix in game library name
So strip it to follow library naming for non-Intel CPUs
==============
*/
static void COM_StripIntelSuffix( char *out )
{
char *suffix = Q_strrchr( out, '_' );
if( suffix && Q_stricmpext( "_i?86", suffix ))
*suffix = 0;
}
/*
==============
COM_GenerateServerLibraryPath
@ -193,6 +209,7 @@ static void COM_GenerateServerLibraryPath( char *out, size_t size )
ext = COM_FileExtension( dllpath );
COM_StripExtension( dllpath );
COM_StripIntelSuffix( dllpath );
COM_GenerateCommonLibraryName( dllpath, ext, out, size );
#endif
@ -234,7 +251,7 @@ void COM_GetCommonLibraryPath( ECommonLibraryType eLibType, char *out, size_t si
}
break;
default:
ASSERT( true );
ASSERT( 0 );
out[0] = 0;
break;
}

View File

@ -21,6 +21,7 @@ typedef struct master_s
qboolean sent;
qboolean save;
string address;
netadr_t adr; // temporary, rewritten after each send
} master_t;
struct masterlist_s
@ -44,31 +45,32 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data )
for( list = ml.list; list; list = list->next )
{
netadr_t adr;
int res;
if( list->sent )
continue;
res = NET_StringToAdrNB( list->address, &adr );
res = NET_StringToAdrNB( list->address, &list->adr );
if( !res )
{
Con_Reportf( "Can't resolve adr: %s\n", list->address );
list->sent = true;
list->adr.type = NA_UNUSED;
continue;
}
if( res == 2 )
{
list->sent = false;
list->adr.type = NA_UNUSED;
wait = true;
continue;
}
list->sent = true;
NET_SendPacket( sock, len, data, adr );
NET_SendPacket( sock, len, data, list->adr );
}
if( !wait )
@ -85,6 +87,25 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data )
return wait;
}
/*
========================
NET_IsMasterAdr
========================
*/
qboolean NET_IsMasterAdr( netadr_t adr )
{
master_t *master;
for( master = ml.list; master; master = master->next )
{
if( NET_CompareAdr( adr, master->adr ))
return true;
}
return false;
}
/*
========================
NET_AddMaster
@ -107,6 +128,7 @@ static void NET_AddMaster( const char *addr, qboolean save )
master->sent = false;
master->save = save;
master->next = NULL;
master->adr.type = NA_UNUSED;
// link in
if( last )
@ -161,7 +183,10 @@ static void NET_ListMasters_f( void )
for( i = 1, list = ml.list; list; i++, list = list->next )
{
Msg( "%d\t%s\n", i, list->address );
Msg( "%d\t%s", i, list->address );
if( list->adr.type != NA_UNUSED )
Msg( "\t%s\n", NET_AdrToString( list->adr ));
else Msg( "\n" );
}
}

View File

@ -134,6 +134,7 @@ typedef struct
int lightmap_samples; // samples per lightmap (1 or 3)
int version; // model version
qboolean isworld;
qboolean isbsp30ext;
} dbspmodel_t;
typedef struct
@ -159,6 +160,7 @@ typedef struct
#define LUMP_SAVESTATS BIT( 0 )
#define LUMP_TESTONLY BIT( 1 )
#define LUMP_SILENT BIT( 2 )
#define LUMP_BSP30EXT BIT( 3 ) // extra marker for Mod_LoadLump
typedef struct
{
@ -251,13 +253,12 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat,
// always use alternate entrysize for BSP2
real_entrysize = info->entrysize32;
}
else if( info->lumpnumber == LUMP_CLIPNODES && version != Q1BSP_VERSION )
else if( version == HLBSP_VERSION && FBitSet( flags, LUMP_BSP30EXT ) && info->lumpnumber == LUMP_CLIPNODES )
{
// never run this check for BSP29 because Arguire QBSP 'broken' clipnodes!
if(( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize ) >= MAX_MAP_CLIPNODES )
// if this map is bsp30ext, try to guess extended clipnodes
if((( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize32 ) >= MAX_MAP_CLIPNODES_HLBSP ))
{
real_entrysize = info->entrysize32;
SetBits( flags, LUMP_SILENT ); // shut up warning
}
}
@ -434,9 +435,9 @@ void Mod_PrintWorldStats_f( void )
Con_Printf( "Lighting: %s\n", FBitSet( w->flags, MODEL_COLORED_LIGHTING ) ? "colored" : "monochrome" );
Con_Printf( "World total leafs: %d\n", worldmodel->numleafs + 1 );
Con_Printf( "original name: ^1%s\n", worldmodel->name );
Con_Printf( "internal name: %s\n", (world.message[0]) ? va( "^2%s", world.message ) : "none" );
Con_Printf( "map compiler: %s\n", (world.compiler[0]) ? va( "^3%s", world.compiler ) : "unknown" );
Con_Printf( "map editor: %s\n", (world.generator[0]) ? va( "^2%s", world.generator ) : "unknown" );
Con_Printf( "internal name: ^2%s\n", world.message[0] ? world.message : "none" );
Con_Printf( "map compiler: ^3%s\n", world.compiler[0] ? world.compiler : "unknown" );
Con_Printf( "map editor: ^2%s\n", world.generator[0] ? world.generator : "unknown" );
}
/*
@ -2578,7 +2579,7 @@ static void Mod_LoadClipnodes( dbspmodel_t *bmod )
bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Malloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out ));
if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES ))
if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->isbsp30ext && bmod->numclipnodes >= MAX_MAP_CLIPNODES_HLBSP ))
{
dclipnode32_t *in = bmod->clipnodes32;
@ -2738,33 +2739,14 @@ static void Mod_LoadLighting( dbspmodel_t *bmod )
/*
=================
Mod_LumpLooksLikePlanes
Mod_LumpLooksLikeEntities
=================
*/
static qboolean Mod_LumpLooksLikePlanes( const byte *in, dlump_t *lump, qboolean fast )
static int Mod_LumpLooksLikeEntities( const char *lump, const size_t lumplen )
{
int numplanes, i;
const dplane_t *planes;
if( lump->filelen < sizeof( dplane_t ) &&
lump->filelen % sizeof( dplane_t ) != 0 )
return false;
if( fast )
return true;
numplanes = lump->filelen / sizeof( dplane_t );
planes = (const dplane_t*)(in + lump->fileofs);
for( i = 0; i < numplanes; i++ )
{
// planes can only be from 0 to 5: PLANE_X, Y, Z and PLANE_ANYX, Y and Z
if( planes[i].type < 0 || planes[i].type > 5 )
return false;
}
return true;
// look for "classname" string
return Q_memmem( lump, lumplen, "\"classname\"", sizeof( "\"classname\"" ) - 1 ) != NULL ? 1 : 0;
}
/*
@ -2776,12 +2758,13 @@ loading and processing bmodel
*/
qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
{
dheader_t *header = (dheader_t *)mod_base;
dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)mod_base + sizeof( dheader_t ));
const dheader_t *header = (const dheader_t *)mod_base;
const dextrahdr_t *extrahdr = (const dextrahdr_t *)(mod_base + sizeof( dheader_t ));
dbspmodel_t *bmod = &srcmodel;
model_t *mod = loadmodel;
char wadvalue[2048];
int i;
size_t len = 0;
int i, ret, flags = 0;
// always reset the intermediate struct
memset( bmod, 0, sizeof( dbspmodel_t ));
@ -2799,9 +2782,26 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
#endif
switch( header->version )
{
case Q1BSP_VERSION:
case HLBSP_VERSION:
if( extrahdr->id == IDEXTRAHEADER )
{
SetBits( flags, LUMP_BSP30EXT );
}
// only relevant for half-life maps
else if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) &&
Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen ))
{
// blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
break;
}
// intended fallthrough
case Q1BSP_VERSION:
case QBSP2_VERSION:
// everything else
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
break;
default:
Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION );
@ -2810,40 +2810,21 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
}
bmod->version = header->version; // share up global
if( isworld ) world.flags = 0; // clear world settings
if( isworld )
{
world.flags = 0; // clear world settings
SetBits( flags, LUMP_SAVESTATS|LUMP_SILENT );
}
bmod->isworld = isworld;
if( header->version == HLBSP_VERSION )
{
// only relevant for half-life maps
if( !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], false ) &&
Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], false ))
{
// blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
}
else
{
// everything else
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
}
}
else
{
// everything else
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
}
bmod->isbsp30ext = FBitSet( flags, LUMP_BSP30EXT );
// loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 );
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags );
// loading extralumps
for( i = 0; i < ARRAYSIZE( extlumps ); i++ )
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 );
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], flags );
if( !bmod->isworld && loadstat.numerrors )
{
@ -2888,7 +2869,13 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
{
if( !bmod->wadlist.wadusage[i] )
continue;
Q_strncat( wadvalue, va( "%s.wad; ", bmod->wadlist.wadnames[i] ), sizeof( wadvalue ));
ret = Q_snprintf( &wadvalue[len], sizeof( wadvalue ), "%s.wad; ", bmod->wadlist.wadnames[i] );
if( ret == -1 )
{
Con_DPrintf( S_WARN "Too many wad files for output!\n" );
break;
}
len += ret;
}
if( COM_CheckString( wadvalue ))
@ -2900,16 +2887,45 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld )
return true;
}
static int Mod_LumpLooksLikeEntitiesFile( file_t *f, const dlump_t *l, int flags, const char *msg )
{
char *buf;
int ret;
if( FS_Seek( f, l->fileofs, SEEK_SET ) < 0 )
{
if( !FBitSet( flags, LUMP_SILENT ))
Con_DPrintf( S_ERROR "map ^2%s^7 %s lump past end of file\n", loadstat.name, msg );
return -1;
}
buf = Z_Malloc( l->filelen + 1 );
if( FS_Read( f, buf, l->filelen ) != l->filelen )
{
if( !FBitSet( flags, LUMP_SILENT ))
Con_DPrintf( S_ERROR "can't read %s lump of map ^2%s^7", msg, loadstat.name );
Z_Free( buf );
return -1;
}
ret = Mod_LumpLooksLikeEntities( buf, l->filelen );
Z_Free( buf );
return ret;
}
/*
=================
Mod_TestBmodelLumps
check for possible errors
return real entities lump (for bshift swapped lumps)
=================
*/
qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent )
qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities )
{
dheader_t *header = (dheader_t *)mod_base;
const dheader_t *header = (const dheader_t *)mod_base;
const dextrahdr_t *extrahdr = (const dextrahdr_t *)( mod_base + sizeof( dheader_t ));
int i, flags = LUMP_TESTONLY;
// always reset the intermediate struct
@ -2917,7 +2933,8 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s
// store the name to correct show errors and warnings
Q_strncpy( loadstat.name, name, sizeof( loadstat.name ));
if( silent ) SetBits( flags, LUMP_SILENT );
if( silent )
SetBits( flags, LUMP_SILENT );
#ifndef SUPPORT_BSP2_FORMAT
if( header->version == QBSP2_VERSION )
@ -2927,11 +2944,42 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s
return false;
}
#endif
switch( header->version )
{
case Q1BSP_VERSION:
case HLBSP_VERSION:
if( extrahdr->id == IDEXTRAHEADER )
{
SetBits( flags, LUMP_BSP30EXT );
}
else
{
// only relevant for half-life maps
int ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_ENTITIES], flags, "entities" );
if( ret < 0 ) return false;
if( !ret )
{
ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_PLANES], flags, "planes" );
if( ret < 0 ) return false;
if( ret )
{
// blue-shift swapped lumps
*entities = header->lumps[LUMP_PLANES];
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
break;
}
}
}
// intended fallthrough
case Q1BSP_VERSION:
case QBSP2_VERSION:
// everything else
*entities = header->lumps[LUMP_ENTITIES];
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
break;
default:
// don't early out: let me analyze errors
@ -2941,30 +2989,6 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s
break;
}
if( header->version == HLBSP_VERSION )
{
// only relevant for half-life maps
if( Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], true ) &&
!Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], true ))
{
// blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
}
else
{
// everything else
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
}
}
else
{
// everything else
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
}
// loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags );

View File

@ -148,7 +148,7 @@ void Mod_FreeUnused( void );
// mod_bmodel.c
//
void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded );
qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent );
qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities );
qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf );
int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis );
qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits );
@ -182,9 +182,6 @@ qboolean Mod_GetStudioBounds( const char *name, vec3_t mins, vec3_t maxs );
void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *org, float *ang );
void Mod_GetBonePosition( const edict_t *e, int iBone, float *org, float *ang );
hull_t *Mod_HullForStudio( model_t *m, float frame, int seq, vec3_t ang, vec3_t org, vec3_t size, byte *pcnt, byte *pbl, int *hitboxes, edict_t *ed );
void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s );
void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q );
void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t adj, vec3_t pos );
void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc );
void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean ignore_sequences );
int Mod_HitgroupForStudioHull( int index );

View File

@ -116,7 +116,7 @@ void Mod_ClearStudioCache( void )
AddToStudioCache
====================
*/
void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, model_t *model, hull_t *hull, int numhitboxes )
static void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, model_t *model, hull_t *hull, int numhitboxes )
{
mstudiocache_t *pCache;
@ -153,7 +153,7 @@ void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t orig
CheckStudioCache
====================
*/
mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *controller, byte *blending )
static mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *controller, byte *blending )
{
mstudiocache_t *pCached;
int i;
@ -204,7 +204,7 @@ mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence,
SetStudioHullPlane
====================
*/
void Mod_SetStudioHullPlane( int planenum, int bone, int axis, float offset, const vec3_t size )
static void Mod_SetStudioHullPlane( int planenum, int bone, int axis, float offset, const vec3_t size )
{
mplane_t *pl = &studio_planes[planenum];
@ -397,196 +397,6 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p
if( pseqdesc->motiontype & STUDIO_Z ) pos[pseqdesc->motionbone][2] = 0.0f;
}
/*
====================
StudioCalcBoneQuaternion
====================
*/
void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q )
{
vec3_t angles1;
vec3_t angles2;
int j, k;
for( j = 0; j < 3; j++ )
{
if( !panim || panim->offset[j+3] == 0 )
{
angles2[j] = angles1[j] = pbone->value[j+3]; // default;
}
else
{
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]);
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
// find span of values that includes the frame we want
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// bah, missing blend!
if( panimvalue->num.valid > k )
{
angles1[j] = panimvalue[k+1].value;
if( panimvalue->num.valid > k + 1 )
{
angles2[j] = panimvalue[k+2].value;
}
else
{
if( panimvalue->num.total > k + 1 )
angles2[j] = angles1[j];
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
else
{
angles1[j] = panimvalue[panimvalue->num.valid].value;
if( panimvalue->num.total > k + 1 )
angles2[j] = angles1[j];
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
}
angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3];
angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3];
}
if( pbone->bonecontroller[j+3] != -1 && adj != NULL )
{
angles1[j] += adj[pbone->bonecontroller[j+3]];
angles2[j] += adj[pbone->bonecontroller[j+3]];
}
}
if( !VectorCompare( angles1, angles2 ))
{
vec4_t q1, q2;
AngleQuaternion( angles1, q1, true );
AngleQuaternion( angles2, q2, true );
QuaternionSlerp( q1, q2, s, q );
}
else
{
AngleQuaternion( angles1, q, true );
}
}
/*
====================
StudioCalcBonePosition
====================
*/
void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec3_t pos )
{
vec3_t origin1;
vec3_t origin2;
int j, k;
for( j = 0; j < 3; j++ )
{
if( !panim || panim->offset[j] == 0 )
{
origin2[j] = origin1[j] = pbone->value[j]; // default;
}
else
{
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]);
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
// find span of values that includes the frame we want
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// bah, missing blend!
if( panimvalue->num.valid > k )
{
origin1[j] = panimvalue[k+1].value;
if( panimvalue->num.valid > k + 1 )
{
origin2[j] = panimvalue[k+2].value;
}
else
{
if( panimvalue->num.total > k + 1 )
origin2[j] = origin1[j];
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
else
{
origin1[j] = panimvalue[panimvalue->num.valid].value;
if( panimvalue->num.total > k + 1 )
origin2[j] = origin1[j];
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
}
origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j];
origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j];
}
if( pbone->bonecontroller[j] != -1 && adj != NULL )
{
origin1[j] += adj[pbone->bonecontroller[j]];
origin2[j] += adj[pbone->bonecontroller[j]];
}
}
if( !VectorCompare( origin1, origin2 ))
{
VectorLerp( origin1, s, origin2, pos );
}
else
{
VectorCopy( origin1, pos );
}
}
/*
====================
StudioSlerpBones
====================
*/
void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s )
{
int i;
s = bound( 0.0f, s, 1.0f );
for( i = 0; i < numbones; i++ )
{
QuaternionSlerp( q1[i], q2[i], s, q1[i] );
VectorLerp( pos1[i], s, pos2[i], pos1[i] );
}
}
/*
====================
@ -823,7 +633,7 @@ int Mod_HitgroupForStudioHull( int index )
StudioBoundVertex
====================
*/
void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_t vertex )
static void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_t vertex )
{
if((*numverts) == 0 )
ClearBounds( mins, maxs );
@ -837,7 +647,7 @@ void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_
StudioAccumulateBoneVerts
====================
*/
void Mod_StudioAccumulateBoneVerts( vec3_t mins, vec3_t maxs, int *numverts, vec3_t bone_mins, vec3_t bone_maxs, int *numbones )
static void Mod_StudioAccumulateBoneVerts( vec3_t mins, vec3_t maxs, int *numverts, vec3_t bone_mins, vec3_t bone_maxs, int *numbones )
{
vec3_t delta;
vec3_t point;
@ -1009,7 +819,7 @@ static int Mod_StudioBodyVariations( model_t *mod )
R_StudioLoadHeader
=================
*/
studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer )
static studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer )
{
byte *pin;
studiohdr_t *phdr;

View File

@ -17,7 +17,6 @@ GNU General Public License for more details.
#include "protocol.h"
#include "net_buffer.h"
#include "xash3d_mathlib.h"
//#define DEBUG_NET_MESSAGES_SEND
//#define DEBUG_NET_MESSAGES_READ
@ -26,11 +25,69 @@ GNU General Public License for more details.
// gives a 33% speedup in WriteUBitLong.
static dword BitWriteMasks[32][33];
static dword ExtraMasks[32];
unsigned short MSG_BigShort( unsigned short swap )
const char *svc_strings[svc_lastmsg+1] =
{
return (swap >> 8)|(swap << 8);
}
"svc_bad",
"svc_nop",
"svc_disconnect",
"svc_event",
"svc_changing",
"svc_setview",
"svc_sound",
"svc_time",
"svc_print",
"svc_stufftext",
"svc_setangle",
"svc_serverdata",
"svc_lightstyle",
"svc_updateuserinfo",
"svc_deltatable",
"svc_clientdata",
"svc_resource",
"svc_pings",
"svc_particle",
"svc_restoresound",
"svc_spawnstatic",
"svc_event_reliable",
"svc_spawnbaseline",
"svc_temp_entity",
"svc_setpause",
"svc_signonnum",
"svc_centerprint",
"svc_unused27",
"svc_unused28",
"svc_unused29",
"svc_intermission",
"svc_finale",
"svc_cdtrack",
"svc_restore",
"svc_cutscene",
"svc_weaponanim",
"svc_bspdecal",
"svc_roomtype",
"svc_addangle",
"svc_usermessage",
"svc_packetentities",
"svc_deltapacketentities",
"svc_choke",
"svc_resourcelist",
"svc_deltamovevars",
"svc_resourcerequest",
"svc_customization",
"svc_crosshairangle",
"svc_soundfade",
"svc_filetxferfailed",
"svc_hltv",
"svc_director",
"svc_voiceinit",
"svc_voicedata",
"svc_deltapacketbones",
"svc_unused55",
"svc_resourcelocation",
"svc_querycvarvalue",
"svc_querycvarvalue2",
"svc_exec",
};
void MSG_InitMasks( void )
{

View File

@ -63,7 +63,15 @@ void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
_inline int MSG_TellBit( sizebuf_t *sb ) { return sb->iCurBit; }
_inline const char *MSG_GetName( sizebuf_t *sb ) { return sb->pDebugName; }
qboolean MSG_CheckOverflow( sizebuf_t *sb );
unsigned short MSG_BigShort( unsigned short swap );
#if XASH_BIG_ENDIAN
#define MSG_BigShort( x ) ( x )
#else
static inline uint16_t MSG_BigShort( const uint16_t x )
{
return (x >> 8) | (x << 8);
}
#endif
// init writing
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits );

View File

@ -414,6 +414,7 @@ void Netchan_ClearFragbufs( fragbuf_t **ppbuf )
while( buf )
{
n = buf->next;
Mem_Free( buf->frag_message_buf );
Mem_Free( buf );
buf = n;
}
@ -535,12 +536,13 @@ Netchan_AllocFragbuf
==============================
*/
fragbuf_t *Netchan_AllocFragbuf( void )
fragbuf_t *Netchan_AllocFragbuf( int fragment_size )
{
fragbuf_t *buf;
buf = (fragbuf_t *)Mem_Calloc( net_mempool, sizeof( fragbuf_t ));
MSG_Init( &buf->frag_message, "Frag Message", buf->frag_message_buf, sizeof( buf->frag_message_buf ));
buf->frag_message_buf = (byte *)Mem_Calloc( net_mempool, fragment_size );
MSG_Init( &buf->frag_message, "Frag Message", buf->frag_message_buf, fragment_size );
return buf;
}
@ -736,7 +738,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg )
bytes = Q_min( remaining, chunksize );
remaining -= bytes;
buf = Netchan_AllocFragbuf();
buf = Netchan_AllocFragbuf( bytes );
buf->bufferid = bufferid++;
// Copy in data
@ -803,7 +805,7 @@ fragbuf_t *Netchan_FindBufferById( fragbuf_t **pplist, int id, qboolean allocate
return NULL;
// create new entry
pnewbuf = Netchan_AllocFragbuf();
pnewbuf = Netchan_AllocFragbuf( NET_MAX_FRAGMENT );
pnewbuf->bufferid = id;
Netchan_AddBufferToList( pplist, pnewbuf );
@ -894,7 +896,7 @@ void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filenam
{
send = Q_min( remaining, chunksize );
buf = Netchan_AllocFragbuf();
buf = Netchan_AllocFragbuf( send );
buf->bufferid = bufferid++;
// copy in data
@ -959,7 +961,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
qboolean bCompressed = false;
fragbufwaiting_t *wait, *p;
fragbuf_t *buf;
if(( filesize = FS_FileSize( filename, false )) <= 0 )
{
Con_Printf( S_WARN "Unable to open %s for transfer\n", filename );
@ -978,8 +980,12 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
if( compressedFileTime >= fileTime )
{
// if compressed file already created and newer than source
if( FS_FileSize( compressedfilename, false ) != -1 )
fs_offset_t compressedSize = FS_FileSize( compressedfilename, false );
if( compressedSize != -1 )
{
bCompressed = true;
filesize = compressedSize;
}
}
else
{
@ -1009,7 +1015,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
{
send = Q_min( remaining, chunksize );
buf = Netchan_AllocFragbuf();
buf = Netchan_AllocFragbuf( send );
buf->bufferid = bufferid++;
// copy in data
@ -1188,6 +1194,13 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
return false;
}
if( filename[0] != '!' )
{
string temp_filename;
Q_snprintf( temp_filename, sizeof( temp_filename ), "downloaded/%s", filename );
Q_strncpy( filename, temp_filename, sizeof( filename ));
}
Q_strncpy( chan->incomingfilename, filename, sizeof( chan->incomingfilename ));
if( filename[0] != '!' && FS_FileExists( filename, false ))
@ -1576,6 +1589,7 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data )
}
}
memset( send_buf, 0, sizeof( send_buf ));
MSG_Init( &send, "NetSend", send_buf, sizeof( send_buf ));
// prepare the packet header

Some files were not shown because too many files have changed in this diff Show More