Merge master to ipv6
This commit is contained in:
commit
64e97124c3
215 changed files with 10628 additions and 6033 deletions
6
.github/workflows/c-cpp.yml
vendored
6
.github/workflows/c-cpp.yml
vendored
|
@ -48,11 +48,11 @@ 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
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: FWGS/xash-extras
|
||||
path: xash-extras
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
- 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/*
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -331,4 +331,5 @@ __pycache__
|
|||
.vscode/*
|
||||
*.code-workspace
|
||||
.history/*
|
||||
.cache/*
|
||||
.cache/*
|
||||
enc_temp_folder/
|
||||
|
|
20
.gitmodules
vendored
20
.gitmodules
vendored
|
@ -1,15 +1,21 @@
|
|||
[submodule "mainui"]
|
||||
path = mainui
|
||||
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 "vgui-dev"]
|
||||
path = vgui-dev
|
||||
url = https://github.com/FWGS/vgui-dev
|
||||
[submodule "ref_gl/gl4es"]
|
||||
path = ref_gl/gl4es
|
||||
path = 3rdparty/gl4es/gl4es
|
||||
url = https://github.com/ptitSeb/gl4es
|
||||
[submodule "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
29
3rdparty/extras/wscript
vendored
Normal 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
1
3rdparty/extras/xash-extras
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e16b9826c2a0960a537139737241f498f9c0e938
|
0
ref_gl/gl-wes-v2 → 3rdparty/gl-wes-v2
vendored
0
ref_gl/gl-wes-v2 → 3rdparty/gl-wes-v2
vendored
1
3rdparty/gl4es/gl4es
vendored
Submodule
1
3rdparty/gl4es/gl4es
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5c28420a384c93345a7a5d060a56a0de5f2ac871
|
24
3rdparty/gl4es/wscript
vendored
Normal file
24
3rdparty/gl4es/wscript
vendored
Normal 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
1
3rdparty/mainui
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 0f31c646d623d75b46a9b16f887ac29a167319a3
|
0
ref_gl/nanogl → 3rdparty/nanogl
vendored
0
ref_gl/nanogl → 3rdparty/nanogl
vendored
1
3rdparty/opus/opus
vendored
Submodule
1
3rdparty/opus/opus
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 997fdf54e781ae1c04dee42018f35388a04fe483
|
40
3rdparty/opus/wscript
vendored
Normal file
40
3rdparty/opus/wscript
vendored
Normal 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
1
3rdparty/vgui_support
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 63c134f188e7c0891927f5a4149f4444b43b0be8
|
17
Documentation/bug-compatibility.md
Normal file
17
Documentation/bug-compatibility.md
Normal 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
|
9
Documentation/cross-compiling-for-windows-with-wine.md
Normal file
9
Documentation/cross-compiling-for-windows-with-wine.md
Normal 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 ...
|
150
Documentation/entity-tools.md
Normal file
150
Documentation/entity-tools.md
Normal 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
|
|
@ -7,6 +7,5 @@
|
|||
|Counter Strike: Condition Zero |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface
|
||||
|Counter Strike: Condition Zero - Deleted scenes |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface
|
||||
|Day of Defeat |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface
|
||||
|Sven-Coop |5.0+ |Uses filesystem_stdio library which xash3d does not support |filesystem_stdio replacement already was made: https://github.com/FWGS/filesystem_stdio_xash
|
||||
|XDM |3.0.4.0 | |
|
||||
|Sven-Coop |5.0+ |Uses custom GoldSrc engine |
|
||||
|
||||
|
|
|
@ -17,13 +17,16 @@ Mirrored on github - https://github.com/JoelTroch/am_src_rebirth
|
|||
Mirrored on github - https://github.com/nekonomicon/BattleGrounds
|
||||
|
||||
## Bubblemod
|
||||
Download page on official site - http://www.bubblemod.org/dl_default.php
|
||||
Download page on official site - http://www.bubblemod.org/dl_default.php (dead link!)
|
||||
|
||||
Mirrored on github - https://github.com/HLSources/BubbleMod
|
||||
|
||||
## Chicken Fortress
|
||||
Official github repository - https://github.com/CKFDevPowered/CKF3Alpha
|
||||
|
||||
## Cold Ice
|
||||
Version 1.9 is available on ModDB - https://www.moddb.com/mods/cold-ice/downloads/cold-ice-sdk
|
||||
|
||||
Version 1.9 mirrored on github - https://github.com/solidi/hl-mods/tree/master/ci
|
||||
|
||||
## Cold Ice Ressurection
|
||||
|
@ -42,11 +45,13 @@ Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ
|
|||
Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/Half-Life_2D_(Flat-Life)
|
||||
|
||||
## Gang Wars
|
||||
Mirrored on github - https://github.com/hammermaps/gw1.45src
|
||||
Mirrored on github - https://github.com/nekonomicon/gw1.45src
|
||||
|
||||
## Go-mod
|
||||
Versions 2.0 and 3.0, available in mod archives on ModDB - https://www.moddb.com/mods/go-mod/downloads
|
||||
|
||||
Version 3.0, mirrored on github - https://github.com/nekonomicon/Go-mod30
|
||||
|
||||
## GT mod
|
||||
Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/GT_mod_(Polnie_ishodniki)
|
||||
|
||||
|
@ -63,10 +68,10 @@ Available on ModDB - https://www.moddb.com/mods/half-life-echoes
|
|||
Available on ModDB - https://www.moddb.com/mods/half-life-expanded-arsenal
|
||||
|
||||
## Half-Life: Gravgun mod
|
||||
Branch **gravgun** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/gravgun
|
||||
Branch **gravgun** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/gravgun
|
||||
|
||||
## Half-Life: Invasion
|
||||
official github repository - https://github.com/jlecorre/hlinvasion
|
||||
Official github repository - https://github.com/jlecorre/hlinvasion
|
||||
|
||||
## Half-Life: Pong
|
||||
Mirrored on github - https://github.com/solidi/hl-mods/tree/master/pong
|
||||
|
@ -105,7 +110,7 @@ Official github repository - https://github.com/desukuran/half-screwed
|
|||
Version 1.3 on ModDB - https://www.moddb.com/mods/headcrab-frenzy/downloads/headcrab-frenzy-13-beta-source-code
|
||||
|
||||
## Heart of Evil
|
||||
Available on ModDB - https://www.moddb.com/mods/heart-of-evil/downloads/heart-of-evil-dlls-source-code
|
||||
Mirrored on github - https://github.com/nekonomicon/HeartOfEvil
|
||||
|
||||
## Ingram Chillin' Mod
|
||||
Official SourceForge repository - https://sourceforge.net/projects/icm-hl/
|
||||
|
@ -122,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
|
||||
|
||||
|
@ -168,7 +176,7 @@ Mirrored on github - https://github.com/ZXCmod/ZXCmod
|
|||
|
||||
# Reimplementation
|
||||
## Absolute Redemption
|
||||
Branch **redempt** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/redempt
|
||||
Branch **redempt** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/redempt
|
||||
|
||||
## Adrenaline Gamer
|
||||
OpenAG by YaLTeR - https://github.com/YaLTeR/OpenAG
|
||||
|
@ -176,34 +184,34 @@ OpenAG by YaLTeR - https://github.com/YaLTeR/OpenAG
|
|||
## Afraid of Monsters
|
||||
malortie's recreation - https://github.com/malortie/hl-aom
|
||||
|
||||
Branch **aom** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aom
|
||||
Branch **aom** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aom
|
||||
|
||||
## Afraid of Monsters: Director's cut
|
||||
Reverse-engineered code, branch **aomdc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aomdc
|
||||
Reverse-engineered code, branch **aomdc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aomdc
|
||||
|
||||
## Azure Sheep
|
||||
malortie's recreation - https://github.com/malortie/hl-asheep
|
||||
|
||||
Reverse-engineered code, branch **asheep** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/asheep
|
||||
Reverse-engineered code, branch **asheep** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/asheep
|
||||
|
||||
## Big Lolly
|
||||
malortie's recreation - https://github.com/malortie/hl-biglolly
|
||||
|
||||
Branch **biglolly** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/biglolly
|
||||
Branch **biglolly** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/biglolly
|
||||
|
||||
## Black Ops
|
||||
malortie's recreation - https://github.com/malortie/hl-blackops
|
||||
|
||||
Branch **blackops** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/blackops
|
||||
Branch **blackops** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/blackops
|
||||
|
||||
## Bloody Pizza: Vendetta
|
||||
Branch **caseclosed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/caseclosed
|
||||
Branch **caseclosed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/caseclosed
|
||||
|
||||
## Case Closed
|
||||
Branch **caseclosed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/caseclosed
|
||||
Branch **caseclosed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/caseclosed
|
||||
|
||||
## Cleaner's Adventures
|
||||
Branch **CAd** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/CAd
|
||||
Branch **CAd** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/CAd
|
||||
|
||||
## Counter Strike
|
||||
Reverse-engineered code of client part by a1batross - https://github.com/FWGS/cs16-client/tree/v1.32
|
||||
|
@ -221,54 +229,60 @@ Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/crack_lif
|
|||
## Escape from the Darkness
|
||||
malortie's recreation - https://github.com/malortie/hl-eftd
|
||||
|
||||
Reverse-engineered code, branch **eftd** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/eftd
|
||||
Reverse-engineered code, branch **eftd** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/eftd
|
||||
|
||||
## Half-Life: Blue Shift
|
||||
Unkle Mike's recreation - https://hlfx.ru/forum/showthread.php?s=&threadid=5253
|
||||
|
||||
Reverse-engineered code, branch **bshift** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/bshift
|
||||
Reverse-engineered code, branch **bshift** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bshift
|
||||
|
||||
## Half-Life: Induction
|
||||
Branch **induction** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/induction
|
||||
Branch **induction** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/induction
|
||||
|
||||
## Half-Life: Opposing Force
|
||||
Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/hlsdk-xash3d
|
||||
|
||||
Reverse-engineered code, clean branch **opfor** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/opfor
|
||||
Reverse-engineered code, clean branch **opfor** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/opfor
|
||||
|
||||
Spirit of Half Life: Opposing-Force Edition - https://github.com/Hammermaps-DEV/SOHL-V1.9-Opposing-Force-Edition
|
||||
|
||||
## Half-Life: Rebellion
|
||||
Reverse-engineered code, branch **rebellion** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/rebellion
|
||||
Reverse-engineered code, branch **rebellion** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/rebellion
|
||||
|
||||
## Half-Life: Urbicide
|
||||
Branch **hl_urbicide** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/hl_urbicide
|
||||
|
||||
## Half-Life: Visitors
|
||||
malortie's recreation - https://github.com/malortie/hl-visitors
|
||||
|
||||
Reverse-engineered code, branch **visitors** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/visitors
|
||||
Reverse-engineered code, branch **visitors** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/visitors
|
||||
|
||||
## Half-Secret
|
||||
Branch **half-secret** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/half-secret
|
||||
Branch **half-secret** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/half-secret
|
||||
|
||||
## Night at the Office
|
||||
malortie's recreation - https://github.com/malortie/hl-nato
|
||||
|
||||
Branch **noffice** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/noffice
|
||||
Branch **noffice** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/noffice
|
||||
|
||||
## Poke 646
|
||||
malortie's recreation - https://github.com/malortie/hl-poke646
|
||||
|
||||
Reverse-engineered code, branch **poke646** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/poke646
|
||||
Reverse-engineered code, branch **poke646** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/poke646
|
||||
|
||||
## Poke 646: Vendetta
|
||||
malortie's recreation - https://github.com/malortie/hl-poke646-vendetta
|
||||
|
||||
Reverse-engineered code, branch **poke646_vendetta** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/poke646_vendetta
|
||||
Reverse-engineered code, branch **poke646_vendetta** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/poke646_vendetta
|
||||
|
||||
## Residual Life
|
||||
Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/residual_point
|
||||
Reverse-engineered code, branch **residual_point** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/residual_point
|
||||
|
||||
## Residual Point
|
||||
Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/residual_point
|
||||
Reverse-engineered code, branch **residual_point** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/residual_point
|
||||
|
||||
## Sewer Beta
|
||||
Branch **sewer_beta** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sewer_beta
|
||||
|
||||
## Team Fortress Classic
|
||||
Reverse-engineered code by Velaron - https://github.com/Velaron/tf15-client
|
||||
|
@ -276,59 +290,64 @@ Reverse-engineered code by Velaron - https://github.com/Velaron/tf15-client
|
|||
## The Gate
|
||||
malortie's recreation - https://github.com/malortie/hl-thegate
|
||||
|
||||
Reverse-engineered code, branch **thegate** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/thegate
|
||||
Reverse-engineered code, branch **thegate** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/thegate
|
||||
|
||||
## They Hunger
|
||||
malortie's recreation - https://github.com/malortie/hl-theyhunger
|
||||
|
||||
Reverse-engineered code, branch **theyhunger** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/theyhunger
|
||||
Reverse-engineered code, branch **theyhunger** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/theyhunger
|
||||
|
||||
They Hunger: Tactical - https://www.moddb.com/mods/they-hunger-tactical/downloads/tht-source-code-documentation
|
||||
|
||||
## Times of Troubles
|
||||
malortie's recreation - https://github.com/malortie/hl-tot
|
||||
|
||||
Branch **tot** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/tot
|
||||
Branch **tot** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/tot
|
||||
|
||||
# Derived work
|
||||
## Adrenaline Gamer
|
||||
Branch **aghl** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aghl
|
||||
Branch **aghl** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aghl
|
||||
|
||||
## Bubblemod
|
||||
Branch **bubblemod** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/bubblemod
|
||||
Branch **bubblemod** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bubblemod
|
||||
|
||||
## Deathmatch Classic
|
||||
Deathmatch Classic: Adrenaline Gamer Edition - https://github.com/martinwebrant/agmod/tree/master/src/dmc
|
||||
|
||||
Deathmatch Quaked - https://www.moddb.com/games/deathmatch-classic/downloads/dmq2
|
||||
|
||||
Branch **dmc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/dmc
|
||||
Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc
|
||||
|
||||
## Half-Life: Echoes
|
||||
Branch **echoes** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/echoes
|
||||
Branch **echoes** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/echoes
|
||||
|
||||
## Half-Life: Invasion
|
||||
Port to HLSDK 2.4 by malortie - https://github.com/malortie/hl-invasion
|
||||
|
||||
Port to Linux by fmoraw - https://github.com/fmoraw/hlinvasion
|
||||
|
||||
Branch **invasion** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/invasion
|
||||
|
||||
## Half-Life: Top-Down
|
||||
Branch **hltopdown** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/hltopdown
|
||||
Branch **hltopdown** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/hltopdown
|
||||
|
||||
## Half-Screwed
|
||||
Branch **half-screwed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/half-screwed
|
||||
Branch **half-screwed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/half-screwed
|
||||
|
||||
## Natural Selection
|
||||
Port to Linux - https://github.com/fmoraw/NS
|
||||
|
||||
## Spirit of Half-Life
|
||||
Version 1.2, branch **sohl1.2** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sohl1.2
|
||||
|
||||
## Swiss Cheese Halloween 2002
|
||||
Just more playable version by malortie - https://github.com/malortie/hl-shall
|
||||
|
||||
Branch **halloween** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/halloween
|
||||
Branch **halloween** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/halloween
|
||||
|
||||
## Threewave CTF
|
||||
Branch **dmc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/dmc
|
||||
Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc
|
||||
|
||||
## Zombie-X
|
||||
Branch **zombie-x** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/zombie-x
|
||||
Branch **zombie-x** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/zombie-x
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
16. [Betrayal](http://www.moddb.com/mods/betrayal) (this mod has inner bugs; set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas)
|
||||
17. [Between Two Worlds](https://www.runthinkshootlive.com/posts/between-two-worlds/)
|
||||
18. [Black Mesa Energy Testing Chamber](http://twhl.info/competitions.php?results=17)
|
||||
19. [Black Mesa Sideline](http://www.isolated-design.de/half-life-mods/black-mesa-sideline/) (set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas)
|
||||
19. [Black Mesa Sideline](https://www.moddb.com/mods/black-mesa-sideline) (set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas)
|
||||
20. [BlackMesa 2007( Black Mesa Missions 2007 )](http://www.moddb.com/addons/blackmesa-2007)
|
||||
21. [Blood and Bones](https://www.runthinkshootlive.com/posts/blood-and-bones/)
|
||||
22. Boom (Huknenn) v1.0 & [HL Boom: Gold Edition v1.1](http://www.moddb.com/mods/boom)
|
||||
|
@ -103,8 +103,8 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
72. [Freeman](https://www.runthinkshootlive.com/posts/freeman/)
|
||||
73. [Freeman's Escape](http://www.moddb.com/mods/freeman-escape-french) (2 maps by *JiggZ*)
|
||||
74. [Freeman's Fight](https://www.runthinkshootlive.com/posts/freemans-fight/)
|
||||
75. [Freeman's Return](http://jpmaps.wz.cz/epizody.htm)
|
||||
76. [Freeman's Return 2](http://jpmaps.wz.cz/epizody.htm)
|
||||
75. [Freeman's Return](https://www.moddb.com/games/half-life/addons/freemans-return-v12)
|
||||
76. [Freeman's Return 2](https://www.moddb.com/games/half-life/addons/freemans-return-2-v11)
|
||||
77. [Freeman's Revenge](https://www.runthinkshootlive.com/posts/freemans-revenge/)
|
||||
78. [Freeman's Tomb( The Crypt )](http://twhl.info/vault.php?map=3787)
|
||||
79. [G-Invasion v2.5](http://www.moddb.com/mods/g-man-invasion) aka G-Man Invasion (there is an inner crash bug on final titles: some text lines are too long and cannot be properly displayed)
|
||||
|
@ -129,7 +129,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
96. [Haunted](https://www.runthinkshootlive.com/posts/haunted/) (set **sv_validate_changelevel** to **0** to avoid a problem with level change between maps **pagan7** and **pagan8**)
|
||||
97. [Hazardous Materials: Episode 1 & 2](http://www.moddb.com/mods/half-life-hazardous-materials)
|
||||
98. [Hazardous-Course 2](http://www.moddb.com/mods/hazardous-course-2)
|
||||
99. [Help Wanted](http://maps.mrsucko.org/work/)
|
||||
99. [Help Wanted](https://www.moddb.com/games/half-life/addons/help-wanted)
|
||||
100. [Hidden Evil v1.01](https://www.runthinkshootlive.com/posts/hidden-evil/)
|
||||
101. [High Speed](http://www.moddb.com/games/half-life/addons/high-speed-english-version)
|
||||
102. [hlife_hotdog_compo26](http://twhl.info/vault.php?map=5181)
|
||||
|
@ -155,7 +155,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
122. [Mario Keys](http://www.moddb.com/mods/mario-keys)
|
||||
123. [McBeth](https://www.runthinkshootlive.com/posts/mcbeth/)
|
||||
124. [Medieval World](http://www.moddb.com/mods/medieval-world)
|
||||
125. [Mel Soaring 2: Star Rancor](http://www.etherealhell.com/etherealhell/index.php/my-levels/half-life)
|
||||
125. [Mel Soaring 2: Star Rancor](https://www.moddb.com/addons/mel-soaring-2-star-rancor)
|
||||
126. [MINIMICUS](http://twhl.info/vault.php?map=163)
|
||||
127. [Mission Failed](http://www.moddb.com/mods/mission-failed)
|
||||
128. [Mission MC Poker](http://www.moddb.com/mods/half-life-mission-mc-poker)
|
||||
|
@ -176,7 +176,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
143. [Operation: Nova](http://www.moddb.com/mods/half-life-operation-nova) (there is an inner bug with an M60 machinegun on a map with Osprey: it can be activated and used if you are staying in front of it, not behind it)
|
||||
144. [Optimum Fear](https://www.fileplanet.com/7472/0/fileinfo/Optimum-Fear)
|
||||
145. [Outrun](http://www.moddb.com/mods/outrun)
|
||||
146. [Outwards (Day One)](http://www.visgi.info/maps/)
|
||||
146. [Outwards (Day One)](https://drive.google.com/file/d/1mjQ8wUazYbwTq9Ocg0Vs0raq4avbTRne/view?usp=sharing)
|
||||
147. [Overhaul Pack](http://www.moddb.com/mods/half-life-overhaul-pack) (Just HD pack for Half-Life)
|
||||
148. [P.I.Z.D.E.C.](https://www.runthinkshootlive.com/posts/pizdec/)
|
||||
149. [pagoda](http://www.moddb.com/mods/pagoda1)
|
||||
|
@ -184,7 +184,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
151. [Phobos IV](http://www.moddb.com/mods/phobos-iv)
|
||||
152. [Pimp My Car](https://www.runthinkshootlive.com/posts/pimp-my-car/) (there is an inner issue with incorrect responses of buttons on combination locks)
|
||||
153. [Point Blank – Stackdeath Complex](https://www.runthinkshootlive.com/posts/half-life-point-blank-stackdeath-complex/)
|
||||
154. [Prisoner Escaped](http://four0four.org/downloads/maps/)
|
||||
154. [Prisoner Escaped](https://www.moddb.com/mods/prisoner-escaped-1-2)
|
||||
155. [Prisoner of Event](http://wp.vondur.net/?page_id=40)
|
||||
156. [Prisoner of War](https://www.runthinkshootlive.com/posts/prisoner-of-war/)
|
||||
157. [Project Quantum Leap](http://www.moddb.com/mods/project-quantum-leap)
|
||||
|
@ -282,7 +282,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
6. [Catacombs: Part 1](https://www.runthinkshootlive.com/posts/catacombs/)
|
||||
7. [Cro-man's Office Mappack](http://twhl.info/vault.php?map=5547)
|
||||
8. [Half-Life Episode Two Demo Alpha v1.0](http://hl.loess.ru/?mod=451)
|
||||
9. [Hazardous-Course (first version of Hazardous-Course 2)](http://www.richmans-maps.ch.vu/) (link dead!)
|
||||
9. [Hazardous-Course (first version of Hazardous-Course 2)](https://drive.google.com/file/d/16djJZSpNWKyScSxSyMqX-u_S-2i400rl/view?usp=sharing)
|
||||
10. [High Tech v0.2](http://hl.loess.ru/?mod=499)
|
||||
11. [HL Shadows, Part 1](https://www.fileplanet.com/7466/0/fileinfo/'HL-Shadows',-Part-1)
|
||||
12. [HLNewEnd](http://twhl.info/vault.php?map=2275)
|
||||
|
@ -305,11 +305,11 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv
|
|||
29. [Shortcut v1.0 Beta](https://www.runthinkshootlive.com/posts/shortcut/)
|
||||
30. [Stoka](https://www.ceskemody.cz/mapy.php?lng=1&clanek=222&razeni_hra=1&pn=stoka)
|
||||
31. [Striker's Compo 26](http://twhl.info/vault.php?map=5190) (buggy)
|
||||
32. [Technology Test 2](http://www.richmans-maps.ch.vu/) (link dead!)
|
||||
32. [Technology Test 2](https://drive.google.com/file/d/1LfldzsMIN5j07bgtNkfY5v0Xl0S9de_G/view?usp=sharing)
|
||||
33. [The Gate Playable Demo](https://www.fileplanet.com/116347/110000/fileinfo/The-Gate-Playable-Demo)
|
||||
34. [They are Back](https://www.runthinkshootlive.com/posts/they-are-back/)
|
||||
35. [Tiefseelabor( Deep Sea Laboratory )](https://www.runthinkshootlive.com/posts/deep-sea-laboratory/)
|
||||
36. [Time-Shift](http://www.richmans-maps.ch.vu/) (link dead!)
|
||||
36. [Time-Shift](https://drive.google.com/file/d/1pK1s-2SIlSMQHVRPMLdC_94wTa__8_DX/view?usp=sharing)
|
||||
37. [Train Single Beta](https://cs-mapping.com.ua/forum/showthread.php?t=36394) (Remove **gfx.wad** from **TrainSingle** folder)
|
||||
38. [WAR: The Killer Beta 0.1](http://www.moddb.com/mods/war)
|
||||
39. [White Force Beta( Residual Point prototype )](http://www.moddb.com/mods/hl-residual-point/downloads/white-force-beta-2002)
|
||||
|
@ -379,7 +379,7 @@ Mods:
|
|||
5. [Half-Life Baby v1.4](http://www.moddb.com/games/half-life/addons/half-life-baby) (it's an unfinished but playable mod; after installing of the mod open **liblist.gam** or **gameinfo.txt** file in the mod's folder and correct the line **gamedll "..\hlbaby\dlls\hl.dll"** for **gamedll "dlls\hl.dll"**, otherwise you'll not be able to start a game)
|
||||
6. [Half-Secret](http://www.moddb.com/mods/half-secret) (this mod has custom **weapon_snark** code)
|
||||
7. [Induction](http://www.moddb.com/mods/half-life-induction) (this mod has new item - **item_flashlight**)
|
||||
8. [Lost in Black Mesa(first version without HLFX)](http://half-life.ru/forum/showthread.php?threadid=13959)
|
||||
8. [Lost in Black Mesa(first version without HLFX)](https://drive.google.com/file/d/1bEnm_AxJs-ly8hTEZIQBw_v2BsXdSiGa/view?usp=sharing)
|
||||
9. [Soldier](http://www.moddb.com/mods/half-life-soldier)
|
||||
10. [Solo Operations](http://www.moddb.com/mods/solo-operations)
|
||||
11. [The Blood v1.1](http://hl.loess.ru/?mods=&search=The+Blood) (there are some inner bugs in the mod, but they don't interfere with a game progress)
|
||||
|
@ -440,7 +440,7 @@ Mods:
|
|||
51. [Data-base](https://www.runthinkshootlive.com/posts/database/)
|
||||
52. [de_dust2_azabetfeN](https://cs-mapping.com.ua/forum/showthread.php?t=36394) (remove **cl_dlls** & **dlls** folders from inside of mod's directory before you start the game)
|
||||
53. [De-railed](http://twhl.info/competitions.php?results=7)
|
||||
54. [Dead Shift Beta](http://www.moddb.com/mods/dead-shift) - [Demo 1](https://www.gamewatcher.com/mods/half-life-mod/dead-shift-1-0-beta) & [Demo 2](https://www.gamefront.com/files/13532512) (3rd link dead!)
|
||||
54. [Dead Shift Beta](http://www.moddb.com/mods/dead-shift) - [Demo 1](https://www.gamewatcher.com/mods/half-life-mod/dead-shift-1-0-beta) & [Demo 2](https://drive.google.com/file/d/1uHvr8xONogTTNOy-8X6G4ehFV6Eawvbq/view?usp=sharing)
|
||||
55. [Deep](http://twhl.info/vault.php?map=1669)
|
||||
56. [Desert Attack](http://fyzzer.narod.ru/index.html)
|
||||
57. [Desert Combat Demo](https://www.fileplanet.com/190487/190000/fileinfo/Half-Life---Desert-Combat-Mod) (despite a big file size there's only one small unfinished map)
|
||||
|
@ -469,7 +469,7 @@ Mods:
|
|||
80. [Extinct Lifeform Hunt](https://www.fileplanet.com/13501/10000/fileinfo/Extinct-Lifeform-Hunt)
|
||||
81. [Facility](http://twhl.info/vault.php?map=5482)
|
||||
82. [Facility Escape](http://twhl.info/vault.php?map=3673)
|
||||
83. [Fallout](http://www.thewall.de/forum/thread/hl1-sp-48h-mapping-contest/64975.4.html) (map by *simb*)
|
||||
83. [Fallout](http://www.thewall.de/forum/thread/hl1-sp-48h-mapping-contest/64975.4.html) (map by *simb*) (link dead!)
|
||||
84. [Final Assault](http://twhl.info/vault.php?map=4500)
|
||||
85. [Flat](http://hl.loess.ru/?mods=&search=Flat)
|
||||
86. [Freeman's Allegiance](https://www.runthinkshootlive.com/posts/freemans-allegiance/)
|
||||
|
@ -498,8 +498,8 @@ Mods:
|
|||
109. [Hospital](https://gamebanana.com/maps/167446) (you need to edit **liblist.gam** file in the mod's folder - delete *gamedll & type* strings from it before you start to play)
|
||||
110. [Hostage](https://www.runthinkshootlive.com/posts/hostage-2/)
|
||||
111. [House](http://www.artpeter.net/Data/HalfLife/Hl.php)
|
||||
112. [Impulse 101 Fun - The Train](http://web.archive.org/web/20070305075816/http://www.twhl.co.za/mapvault/2817.zip) (map from *TWHL* by *Archie* aka *The Hunter*)
|
||||
113. [In America](http://www.lambda-force.org/load/half_life/karty/half_life_in_america/18-1-0-476)
|
||||
112. [Impulse 101 Fun - The Train](https://drive.google.com/file/d/1jAuMCCmREH0mfAEAscqaG8FQGa2uNknb/view?usp=sharing) (map from *TWHL* by *Archie* aka *The Hunter*)
|
||||
113. [In America](https://drive.google.com/file/d/1gOC8zfnUvBxRy8Rr8juNiUNbLYjoZpnn/view?usp=sharing)
|
||||
114. [In the Kitchen](http://twhl.info/vault.php?map=4314)
|
||||
115. [Infiltration](https://www.fileplanet.com/7467/0/fileinfo/Infiltration)
|
||||
116. [Interactivity & Lots of Entities](http://twhl.info/vault.php?map=5744) (link dead!)
|
||||
|
@ -668,7 +668,7 @@ Mods:
|
|||
279. [X-treme Violence](http://www.moddb.com/mods/x-treme-violence)
|
||||
280. [XargoL's Entry for Vassy's Compo](http://twhl.info/vault.php?map=769)
|
||||
281. [Xen Again](https://www.fileplanet.com/9132/0/fileinfo/XEN-AGAIN)
|
||||
282. [Xen World](http://www.lambda-force.org/load/half_life/karty/half_life_xen_world/18-1-0-481)
|
||||
282. [Xen World](http://www.lambda-force.org/load/half_life/karty/half_life_xen_world/18-1-0-481) (link dead!)
|
||||
283. [XUnil](https://www.fileplanet.com/7883/0/fileinfo/XUNIL)
|
||||
284. [Zeeba-G's TWHL Compo 26 Entry](http://twhl.info/competitions.php?results=26)
|
||||
285. [Zombies!](https://gamebanana.com/maps/160812)
|
||||
|
@ -694,7 +694,7 @@ For Linux and OS X you must download crossbuild from [here](http://www.moddb.com
|
|||
2. [Big Scientists](http://www.moddb.com/mods/big-scientists)
|
||||
3. [Black Silla Assault DEMO v1.2](http://www.moddb.com/mods/black-silla-assault)
|
||||
4. [Blbej Den](http://www.moddb.com/mods/blbej-den)
|
||||
5. [Cold Experiment v1](http://jpmaps.wz.cz/epizody.htm) (you will need some files from SoHL to play it properly; download SoHL 1.2, extract files and copy following folders into **coldexv1** folder: cl_dlls, dlls, models, sprites)
|
||||
5. [Cold Experiment v1](https://www.moddb.com/games/half-life/addons/cold-experiment) (you will need some files from SoHL to play it properly; download SoHL 1.2, extract files and copy following folders into **coldexv1** folder: cl_dlls, dlls, models, sprites)
|
||||
6. [Dead Sector v1.0a](http://www.moddb.com/mods/dead-sector)
|
||||
7. [Death Is Dead](http://www.moddb.com/games/half-life/addons/death-is-dead) (there is a fog-related inner bug at the first map)
|
||||
8. [Escape from Black Mesa Alpha](http://www.moddb.com/mods/escape-from-black-mesa)
|
||||
|
@ -710,9 +710,9 @@ For Linux and OS X you must download crossbuild from [here](http://www.moddb.com
|
|||
18. [Santa's Revenge](http://twhl.info/vault.php?map=4332) (set **fps_max** to **60** to avoid an inner problem of the last map with final scripted sequence, otherwise the mod can not be finished properly)
|
||||
19. [Sector 6](https://www.moddb.com/mods/sector-6/)
|
||||
20. [Space Prisoner v1.1](http://www.moddb.com/games/half-life/addons/space-prisoner-v11) (after installing of the mod open **liblist.gam** or **gameinfo.txt** file in the mod's folder and correct the line **gamedll "..\prison\dlls\spirit.dll"** for **gamedll "dlls\spirit.dll"**, otherwise you'll not be able to start a game; there is also a scripting error on a third map of the mod, so you'll be forced to use **noclip** to pass around bugged place)
|
||||
21. [Terrorist Attack 2](http://terroristattack.wz.cz/mody.html) (link dead!)
|
||||
21. [Terrorist Attack 2](https://www.moddb.com/games/half-life/addons/terrorist-attack-2)
|
||||
22. [Timeline III: The Heart of Darkness](http://www.moddb.com/mods/timeline-series)
|
||||
23. [Underground 2 Demo](http://www.isolated-design.de/half-life-mods/underground-2/)
|
||||
23. [Underground 2 Demo](https://www.moddb.com/games/half-life/addons/underground-2-demo)
|
||||
|
||||
## Mods which based on modified Spirit of Half-Life 1.2 or older(Partially playable with SoHL 1.2 libraries or not playable at all)
|
||||
1. [Black Death](http://www.moddb.com/mods/half-life-black-death)
|
||||
|
@ -825,10 +825,10 @@ Mods:
|
|||
3. [Bomb Squad](http://www.snarkpit.net/index.php?s=maps&map=3471)
|
||||
4. [Corruption](http://www.snarkpit.net/index.php?s=maps&map=3154)
|
||||
5. [Critical Mass](http://www.moddb.com/mods/half-life-critical-mass) [C3M1 Build 1 Demo](https://www.fileplanet.com/125746/120000/fileinfo/C3M1-Build-1)
|
||||
6. [EPRST!](http://www.planetphillip.com/posts/ep-rst-opposing-force/) (link dead!)
|
||||
6. [EPRST!](https://www.runthinkshootlive.com/posts/ep-rst/)
|
||||
7. [Firing Range](https://www.runthinkshootlive.com/posts/firing-range/)
|
||||
8. [Friendly Fire](https://www.runthinkshootlive.com/posts/friendly-fire/)
|
||||
9. [Guitar Star Pre-Alpha](http://hl-lab.ru/eng/mods_goldsrc/Guitar_Star_(Prealpha)) (link dead!)
|
||||
9. [Guitar Star Pre-Alpha](http://gamer-lab.com/rus/mods_goldsrc/Guitar_Star_(Prealpha))
|
||||
10. [J2000](https://www.runthinkshootlive.com/posts/j2000/)
|
||||
11. [Killing House for OF](https://www.runthinkshootlive.com/posts/scientist-rescue-aka-cqb/)
|
||||
12. [Klabautermann](https://www.runthinkshootlive.com/posts/klabautermann/) (though the map has some inner issues, it can be properly finished, just don't let the welder soldier die and don't press the fifth button until you mount a special gun in its' place)
|
||||
|
|
17
README.md
17
README.md
|
@ -18,19 +18,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).
|
||||
|
@ -49,10 +52,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)
|
||||
|
|
|
@ -73,7 +73,8 @@ BRUSH MODELS
|
|||
#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
|
||||
|
|
|
@ -530,7 +530,7 @@ typedef struct
|
|||
#define MAX_DEMOS 32
|
||||
#define MAX_MOVIES 8
|
||||
#define MAX_CDTRACKS 32
|
||||
#define MAX_CLIENT_SPRITES 256 // SpriteTextures
|
||||
#define MAX_CLIENT_SPRITES 512 // SpriteTextures (0-256 hud, 256-512 client)
|
||||
#define MAX_EFRAGS 8192 // Arcane Dimensions required
|
||||
#define MAX_REQUESTS 64
|
||||
|
||||
|
|
|
@ -47,14 +47,8 @@ GNU General Public License for more details.
|
|||
#define O_BINARY 0
|
||||
#define O_TEXT 0
|
||||
#define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH )
|
||||
#define LoadLibrary( x ) dlopen( x, RTLD_NOW )
|
||||
#define GetProcAddress( x, y ) dlsym( x, y )
|
||||
#define FreeLibrary( x ) dlclose( x )
|
||||
#elif XASH_DOS4GW
|
||||
#define PATH_SPLITTER "\\"
|
||||
#define LoadLibrary( x ) (0)
|
||||
#define GetProcAddress( x, y ) (0)
|
||||
#define FreeLibrary( x ) (0)
|
||||
#endif
|
||||
|
||||
typedef void* HANDLE;
|
||||
|
|
|
@ -161,7 +161,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 );
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <sys/types.h> // off_t
|
||||
#include STDINT_H
|
||||
#include <assert.h>
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef int sound_t;
|
||||
|
@ -87,6 +88,27 @@ typedef uint64_t longtime_t;
|
|||
#define NORETURN
|
||||
#endif
|
||||
|
||||
#if ( __GNUC__ >= 3 )
|
||||
#define unlikely(x) __builtin_expect(x, 0)
|
||||
#define likely(x) __builtin_expect(x, 1)
|
||||
#elif defined( __has_builtin )
|
||||
#if __has_builtin( __builtin_expect )
|
||||
#define unlikely(x) __builtin_expect(x, 0)
|
||||
#define likely(x) __builtin_expect(x, 1)
|
||||
#else
|
||||
#define unlikely(x) (x)
|
||||
#define likely(x) (x)
|
||||
#endif
|
||||
#else
|
||||
#define unlikely(x) (x)
|
||||
#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))
|
||||
|
@ -122,7 +144,6 @@ typedef unsigned int dword;
|
|||
typedef unsigned int uint;
|
||||
typedef char string[MAX_STRING];
|
||||
typedef struct file_s file_t; // normal file
|
||||
typedef struct wfile_s wfile_t; // wad file
|
||||
typedef struct stream_s stream_t; // sound stream for background music playing
|
||||
typedef off_t fs_offset_t;
|
||||
#if XASH_WIN32
|
||||
|
|
|
@ -888,7 +888,7 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length )
|
|||
qboolean swallowmessages = true;
|
||||
static int tdlastdemoframe = 0;
|
||||
byte *userbuf = NULL;
|
||||
size_t size;
|
||||
size_t size = 0;
|
||||
byte cmd;
|
||||
|
||||
if( !cls.demofile )
|
||||
|
@ -1408,7 +1408,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 +1535,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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
@ -1235,6 +1220,10 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla
|
|||
model_t *mod;
|
||||
int i;
|
||||
|
||||
// use high indices for client sprites
|
||||
// for GoldSrc bug-compatibility
|
||||
const int start = type != SPR_HUDSPRITE ? MAX_CLIENT_SPRITES / 2 : 0;
|
||||
|
||||
if( !COM_CheckString( filename ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "CL_LoadSpriteModel: bad name!\n" );
|
||||
|
@ -1244,8 +1233,7 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla
|
|||
Q_strncpy( name, filename, sizeof( name ));
|
||||
COM_FixSlashes( name );
|
||||
|
||||
// slot 0 isn't used
|
||||
for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ )
|
||||
for( i = 0, mod = clgame.sprites + start; i < MAX_CLIENT_SPRITES / 2; i++, mod++ )
|
||||
{
|
||||
if( !Q_stricmp( mod->name, name ))
|
||||
{
|
||||
|
@ -1262,12 +1250,12 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla
|
|||
}
|
||||
|
||||
// find a free model slot spot
|
||||
for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ )
|
||||
for( i = 0, mod = clgame.sprites + start; i < MAX_CLIENT_SPRITES / 2; i++, mod++ )
|
||||
if( !mod->name[0] ) break; // this is a valid spot
|
||||
|
||||
if( i == MAX_CLIENT_SPRITES )
|
||||
if( i == MAX_CLIENT_SPRITES / 2 )
|
||||
{
|
||||
Con_Printf( S_ERROR "MAX_CLIENT_SPRITES limit exceeded (%d)\n", MAX_CLIENT_SPRITES );
|
||||
Con_Printf( S_ERROR "MAX_CLIENT_SPRITES limit exceeded (%d)\n", MAX_CLIENT_SPRITES / 2 );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1308,7 +1296,7 @@ HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags )
|
|||
if(( spr = CL_LoadSpriteModel( szPicName, SPR_CLIENT, texFlags )) == NULL )
|
||||
return 0;
|
||||
|
||||
return (spr - clgame.sprites); // return index
|
||||
return (spr - clgame.sprites) + 1; // return index
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1324,7 +1312,7 @@ HSPRITE EXPORT pfnSPR_Load( const char *szPicName )
|
|||
if(( spr = CL_LoadSpriteModel( szPicName, SPR_HUDSPRITE, 0 )) == NULL )
|
||||
return 0;
|
||||
|
||||
return (spr - clgame.sprites); // return index
|
||||
return (spr - clgame.sprites) + 1; // return index
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1336,10 +1324,11 @@ CL_GetSpritePointer
|
|||
const model_t *CL_GetSpritePointer( HSPRITE hSprite )
|
||||
{
|
||||
model_t *mod;
|
||||
int index = hSprite - 1;
|
||||
|
||||
if( hSprite <= 0 || hSprite >= MAX_CLIENT_SPRITES )
|
||||
if( index < 0 || index >= MAX_CLIENT_SPRITES )
|
||||
return NULL; // bad image
|
||||
mod = &clgame.sprites[hSprite];
|
||||
mod = &clgame.sprites[index];
|
||||
|
||||
if( mod->needload == NL_NEEDS_LOADED )
|
||||
{
|
||||
|
@ -1947,7 +1936,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 );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2666,7 +2662,14 @@ pfnLoadMapSprite
|
|||
*/
|
||||
model_t *pfnLoadMapSprite( const char *filename )
|
||||
{
|
||||
return CL_LoadSpriteModel( filename, SPR_MAPSPRITE, 0 );
|
||||
model_t *mod;
|
||||
|
||||
mod = Mod_FindName( filename, false );
|
||||
|
||||
if( CL_LoadHudSprite( filename, mod, SPR_MAPSPRITE, 0 ))
|
||||
return mod;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3087,11 +3090,7 @@ handle colon separately
|
|||
*/
|
||||
char *pfnParseFile( char *data, char *token )
|
||||
{
|
||||
char *out;
|
||||
|
||||
out = _COM_ParseFileSafe( data, token, PFILE_TOKEN_MAX_LENGTH, PFILE_HANDLECOLON, NULL );
|
||||
|
||||
return out;
|
||||
return COM_ParseFileSafe( data, token, PFILE_TOKEN_MAX_LENGTH, PFILE_HANDLECOLON, NULL, NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3312,7 +3311,7 @@ NetAPI_InitNetworking
|
|||
*/
|
||||
void GAME_EXPORT NetAPI_InitNetworking( void )
|
||||
{
|
||||
NET_Config( true ); // allow remote
|
||||
NET_Config( true, false ); // allow remote
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3865,7 +3864,7 @@ static cl_enginefunc_t gEngfuncs =
|
|||
(void*)Cmd_GetName,
|
||||
pfnGetClientOldTime,
|
||||
pfnGetGravity,
|
||||
Mod_Handle,
|
||||
CL_ModelHandle,
|
||||
pfnEnableTexSort,
|
||||
pfnSetLightmapColor,
|
||||
pfnSetLightmapScale,
|
||||
|
@ -3910,11 +3909,13 @@ 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 );
|
||||
|
||||
COM_FreeLibrary( clgame.hInstance );
|
||||
VGui_Shutdown();
|
||||
Mem_FreePool( &cls.mempool );
|
||||
Mem_FreePool( &clgame.mempool );
|
||||
memset( &clgame, 0, sizeof( clgame ));
|
||||
|
@ -3939,10 +3940,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
|
||||
|
@ -3961,8 +3958,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++ )
|
||||
|
|
|
@ -980,7 +980,7 @@ pfnGetGamesList
|
|||
*/
|
||||
static GAMEINFO ** GAME_EXPORT pfnGetGamesList( int *numGames )
|
||||
{
|
||||
if( numGames ) *numGames = SI.numgames;
|
||||
if( numGames ) *numGames = FI->numgames;
|
||||
return gameui.modsInfo;
|
||||
}
|
||||
|
||||
|
@ -1060,10 +1060,7 @@ pfnChangeInstance
|
|||
*/
|
||||
static void GAME_EXPORT pfnChangeInstance( const char *newInstance, const char *szFinalMessage )
|
||||
{
|
||||
if( !szFinalMessage ) szFinalMessage = "";
|
||||
if( !newInstance || !*newInstance ) return;
|
||||
|
||||
Host_NewInstance( newInstance, szFinalMessage );
|
||||
Con_Reportf( S_ERROR "ChangeInstance menu call is deprecated!\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1120,6 +1117,30 @@ static char *pfnParseFile( char *buf, char *token )
|
|||
return COM_ParseFile( buf, token, INT_MAX );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnFileExists
|
||||
|
||||
legacy wrapper
|
||||
=============
|
||||
*/
|
||||
static int pfnFileExists( const char *path, int gamedironly )
|
||||
{
|
||||
return FS_FileExists( path, gamedironly );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnDelete
|
||||
|
||||
legacy wrapper
|
||||
=============
|
||||
*/
|
||||
static int pfnDelete( const char *path )
|
||||
{
|
||||
return FS_Delete( path );
|
||||
}
|
||||
|
||||
// engine callbacks
|
||||
static ui_enginefuncs_t gEngfuncs =
|
||||
{
|
||||
|
@ -1166,7 +1187,7 @@ static ui_enginefuncs_t gEngfuncs =
|
|||
pfnRenderScene,
|
||||
pfnAddEntity,
|
||||
Host_Error,
|
||||
FS_FileExists,
|
||||
pfnFileExists,
|
||||
pfnGetGameDir,
|
||||
Cmd_CheckMapsList,
|
||||
CL_Active,
|
||||
|
@ -1205,7 +1226,7 @@ static ui_enginefuncs_t gEngfuncs =
|
|||
COM_CompareFileTime,
|
||||
VID_GetModeString,
|
||||
(void*)COM_SaveFile,
|
||||
(void*)FS_Delete
|
||||
pfnDelete
|
||||
};
|
||||
|
||||
static void pfnEnableTextInput( int enable )
|
||||
|
@ -1227,6 +1248,11 @@ static int pfnGetRenderers( unsigned int num, char *shortName, size_t size1, cha
|
|||
return 1;
|
||||
}
|
||||
|
||||
static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned int flags, int *len )
|
||||
{
|
||||
return COM_ParseFileSafe( data, buf, size, flags, len, NULL );
|
||||
}
|
||||
|
||||
static ui_extendedfuncs_t gExtendedfuncs =
|
||||
{
|
||||
pfnEnableTextInput,
|
||||
|
@ -1235,7 +1261,7 @@ static ui_extendedfuncs_t gExtendedfuncs =
|
|||
Con_UtfMoveRight,
|
||||
pfnGetRenderers,
|
||||
Sys_DoubleTime,
|
||||
_COM_ParseFileSafe,
|
||||
pfnParseFileSafe,
|
||||
NET_AdrToString
|
||||
};
|
||||
|
||||
|
@ -1356,13 +1382,13 @@ qboolean UI_LoadProgs( void )
|
|||
Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY );
|
||||
|
||||
// setup gameinfo
|
||||
for( i = 0; i < SI.numgames; i++ )
|
||||
for( i = 0; i < FI->numgames; i++ )
|
||||
{
|
||||
gameui.modsInfo[i] = Mem_Calloc( gameui.mempool, sizeof( GAMEINFO ));
|
||||
UI_ConvertGameInfo( gameui.modsInfo[i], SI.games[i] );
|
||||
UI_ConvertGameInfo( gameui.modsInfo[i], FI->games[i] );
|
||||
}
|
||||
|
||||
UI_ConvertGameInfo( &gameui.gameInfo, SI.GameInfo ); // current gameinfo
|
||||
UI_ConvertGameInfo( &gameui.gameInfo, FI->GameInfo ); // current gameinfo
|
||||
|
||||
// setup globals
|
||||
gameui.globals->developer = host.allow_console;
|
||||
|
|
|
@ -70,6 +70,7 @@ convar_t *cl_upmax;
|
|||
convar_t *cl_lw;
|
||||
convar_t *cl_charset;
|
||||
convar_t *cl_trace_messages;
|
||||
convar_t *cl_nat;
|
||||
convar_t *hud_utf8;
|
||||
convar_t *ui_renderworld;
|
||||
|
||||
|
@ -195,8 +196,6 @@ void CL_CheckClientState( void )
|
|||
Netchan_ReportFlow( &cls.netchan );
|
||||
|
||||
Con_DPrintf( "client connected at %.2f sec\n", Sys_DoubleTime() - cls.timestart );
|
||||
if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped )
|
||||
SCR_EndLoadingPlaque(); // get rid of loading plaque
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +233,7 @@ void CL_SignonReply( void )
|
|||
Mem_PrintStats();
|
||||
break;
|
||||
case 2:
|
||||
SCR_EndLoadingPlaque();
|
||||
if( cl.proxy_redirect && !cls.spectator )
|
||||
CL_Disconnect();
|
||||
cl.proxy_redirect = false;
|
||||
|
@ -302,7 +302,7 @@ static float CL_LerpPoint( void )
|
|||
else if( server_frametime > 0.001f )
|
||||
{
|
||||
// automatic lerp (classic mode)
|
||||
frac = ( cl.time - cl.mtime[1] ) / server_frametime;
|
||||
frac = ( cl.time - cl.mtime[1] ) / server_frametime;
|
||||
}
|
||||
#endif
|
||||
return frac;
|
||||
|
@ -367,7 +367,7 @@ void CL_ComputeClientInterpolationAmount( usercmd_t *cmd )
|
|||
|
||||
min_interp = 1.0f / cl_updaterate->value;
|
||||
interpolation_time = CL_LerpInterval( );
|
||||
|
||||
|
||||
if( (cl_interp->value + epsilon) < min_interp )
|
||||
{
|
||||
Con_Printf( "ex_interp forced up to %.1f msec\n", min_interp * 1000.f );
|
||||
|
@ -743,8 +743,14 @@ void CL_WritePacket( void )
|
|||
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
|
||||
|
||||
|
@ -864,6 +870,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 ));
|
||||
|
@ -1253,7 +1262,7 @@ void CL_Connect_f( void )
|
|||
|
||||
// if running a local server, kill it and reissue
|
||||
if( SV_Active( )) Host_ShutdownServer();
|
||||
NET_Config( true ); // allow remote
|
||||
NET_Config( true, !CVAR_TO_BOOL( cl_nat )); // allow remote
|
||||
|
||||
Con_Printf( "server %s\n", server );
|
||||
CL_Disconnect();
|
||||
|
@ -1299,7 +1308,7 @@ void CL_Rcon_f( void )
|
|||
message[3] = (char)255;
|
||||
message[4] = 0;
|
||||
|
||||
NET_Config( true ); // allow remote
|
||||
NET_Config( true, false ); // allow remote
|
||||
|
||||
Q_strcat( message, "rcon " );
|
||||
Q_strcat( message, rcon_client_password->string );
|
||||
|
@ -1500,6 +1509,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
|
||||
|
@ -1562,7 +1572,7 @@ void CL_LocalServers_f( void )
|
|||
netadr_t adr;
|
||||
|
||||
Con_Printf( "Scanning for servers on the local network area...\n" );
|
||||
NET_Config( true ); // allow remote
|
||||
NET_Config( true, true ); // allow remote
|
||||
|
||||
// send a broadcast packet
|
||||
adr.type = NA_BROADCAST;
|
||||
|
@ -1586,12 +1596,12 @@ void CL_InternetServers_f( void )
|
|||
char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1;
|
||||
const size_t remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST );
|
||||
|
||||
NET_Config( true ); // allow remote
|
||||
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 );
|
||||
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_pending = true;
|
||||
|
@ -1715,16 +1725,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 );
|
||||
|
@ -1734,11 +1751,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 );
|
||||
}
|
||||
|
@ -2097,6 +2116,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 )
|
||||
{
|
||||
|
@ -2155,7 +2180,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
|||
}
|
||||
else if( clgame.request_type == NET_REQUEST_GAMEUI )
|
||||
{
|
||||
NET_Config( true ); // allow remote
|
||||
NET_Config( true, false ); // allow remote
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
|
||||
}
|
||||
}
|
||||
|
@ -2810,6 +2835,8 @@ void CL_InitLocal( void )
|
|||
Cvar_RegisterVariable( &cl_logocolor );
|
||||
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" );
|
||||
|
@ -2832,6 +2859,7 @@ void CL_InitLocal( void )
|
|||
cl_updaterate = Cvar_Get( "cl_updaterate", "20", FCVAR_USERINFO|FCVAR_ARCHIVE, "refresh rate of server messages" );
|
||||
cl_dlmax = Cvar_Get( "cl_dlmax", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "max allowed outcoming fragment size" );
|
||||
cl_upmax = Cvar_Get( "cl_upmax", "1200", FCVAR_ARCHIVE, "max allowed incoming fragment size" );
|
||||
cl_nat = Cvar_Get( "cl_nat", "0", 0, "show servers running under NAT" );
|
||||
rate = Cvar_Get( "rate", "3500", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player network rate" );
|
||||
topcolor = Cvar_Get( "topcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player top color" );
|
||||
bottomcolor = Cvar_Get( "bottomcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player bottom color" );
|
||||
|
@ -2843,9 +2871,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" );
|
||||
|
@ -2874,7 +2902,12 @@ void CL_InitLocal( void )
|
|||
Cmd_AddRestrictedCommand ("kill", NULL, "die instantly" );
|
||||
Cmd_AddCommand ("god", NULL, "enable godmode" );
|
||||
Cmd_AddCommand ("fov", NULL, "set client field of view" );
|
||||
Cmd_AddCommand ("log", NULL, "logging server events" );
|
||||
|
||||
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)" );
|
||||
|
@ -3011,8 +3044,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 ();
|
||||
|
@ -3032,9 +3065,6 @@ void Host_ClientFrame( void )
|
|||
// catch changes video settings
|
||||
VID_CheckChanges();
|
||||
|
||||
// process VGUI
|
||||
VGui_RunFrame ();
|
||||
|
||||
// update the screen
|
||||
SCR_UpdateScreen ();
|
||||
|
||||
|
@ -3066,6 +3096,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 ));
|
||||
|
@ -3091,13 +3122,9 @@ CL_Shutdown
|
|||
*/
|
||||
void CL_Shutdown( void )
|
||||
{
|
||||
// already freed
|
||||
if( !cls.initialized ) return;
|
||||
cls.initialized = false;
|
||||
|
||||
Con_Printf( "CL_Shutdown()\n" );
|
||||
|
||||
if( !host.crashed )
|
||||
if( !host.crashed && cls.initialized )
|
||||
{
|
||||
Host_WriteOpenGLConfig ();
|
||||
Host_WriteVideoConfig ();
|
||||
|
@ -3111,6 +3138,11 @@ void CL_Shutdown( void )
|
|||
Mobile_Shutdown ();
|
||||
SCR_Shutdown ();
|
||||
CL_UnloadProgs ();
|
||||
cls.initialized = false;
|
||||
|
||||
// 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
|
||||
|
@ -3118,4 +3150,5 @@ void CL_Shutdown( void )
|
|||
R_Shutdown ();
|
||||
|
||||
Con_Shutdown ();
|
||||
|
||||
}
|
||||
|
|
|
@ -103,6 +103,11 @@ static void pfnTouch_RemoveButton( const char *name )
|
|||
Touch_RemoveButton( name, true );
|
||||
}
|
||||
|
||||
static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned int flags, int *len )
|
||||
{
|
||||
return COM_ParseFileSafe( data, buf, size, flags, len, NULL );
|
||||
}
|
||||
|
||||
static mobile_engfuncs_t gpMobileEngfuncs =
|
||||
{
|
||||
MOBILITY_API_VERSION,
|
||||
|
@ -118,7 +123,7 @@ static mobile_engfuncs_t gpMobileEngfuncs =
|
|||
Sys_Warn,
|
||||
pfnGetNativeObject,
|
||||
ID_SetCustomClientID,
|
||||
_COM_ParseFileSafe
|
||||
pfnParseFileSafe
|
||||
};
|
||||
|
||||
qboolean Mobile_Init( void )
|
||||
|
|
|
@ -158,6 +158,7 @@ static void NetGraph_InitColors( void )
|
|||
f = (float)(i - hfrac) / (float)(NETGRAPH_LERP_HEIGHT - hfrac );
|
||||
VectorMA( mincolor[1], f, dc[1], netcolors[NETGRAPH_NET_COLORS + i] );
|
||||
}
|
||||
netcolors[NETGRAPH_NET_COLORS + i][3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +260,7 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w )
|
|||
for( a = 0; a < w; a++ )
|
||||
{
|
||||
i = ( cls.netchan.outgoing_sequence - a ) & NET_TIMINGS_MASK;
|
||||
h = ( netstat_cmdinfo[i].cmd_lerp / 3.0f ) * NETGRAPH_LERP_HEIGHT;
|
||||
h = Q_min(( netstat_cmdinfo[i].cmd_lerp / 3.0f ) * NETGRAPH_LERP_HEIGHT, net_graphheight->value * 0.7f);
|
||||
|
||||
fill.left = x + w - a - 1;
|
||||
fill.right = fill.bottom = 1;
|
||||
|
@ -395,10 +396,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 k/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors );
|
||||
Con_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors );
|
||||
y += 15;
|
||||
|
||||
Con_DrawString( x, y, va( "out: %i %.2f k/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors );
|
||||
Con_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors );
|
||||
y += 15;
|
||||
|
||||
if( graphtype > 2 )
|
||||
|
@ -615,7 +616,7 @@ static void NetGraph_GetScreenPos( wrect_t *rect, int *w, int *x, int *y )
|
|||
*x = rect->left + rect->right - 5 - *w;
|
||||
break;
|
||||
case 2: // center
|
||||
*x = rect->left + ( rect->right - 10 - *w ) / 2;
|
||||
*x = ( rect->left + ( rect->right - 10 - *w )) / 2;
|
||||
break;
|
||||
default: // left sided
|
||||
*x = rect->left + 5;
|
||||
|
@ -672,7 +673,7 @@ void SCR_DrawNetGraph( void )
|
|||
|
||||
if( graphtype < 3 )
|
||||
{
|
||||
ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd );
|
||||
ref.dllFuncs.GL_SetRenderMode( kRenderTransColor );
|
||||
ref.dllFuncs.GL_Bind( XASH_TEXTURE0, R_GetBuiltinTexture( REF_WHITE_TEXTURE ) );
|
||||
ref.dllFuncs.Begin( TRI_QUADS ); // draw all the fills as a long solid sequence of quads for speedup reasons
|
||||
|
||||
|
|
|
@ -914,11 +914,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
|
||||
|
@ -1348,7 +1344,12 @@ void CL_UpdateUserinfo( sizebuf_t *msg )
|
|||
|
||||
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 +1681,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 +1695,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 );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2335,6 +2363,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 );
|
||||
|
@ -2427,8 +2456,8 @@ void CL_ParseLegacyServerData( sizebuf_t *msg )
|
|||
i = MSG_ReadLong( msg );
|
||||
//cls.serverProtocol = i;
|
||||
|
||||
if( i != 48 )
|
||||
Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
|
||||
if( i != PROTOCOL_LEGACY_VERSION )
|
||||
Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_LEGACY_VERSION );
|
||||
|
||||
cl.servercount = MSG_ReadLong( msg );
|
||||
cl.checksum = MSG_ReadLong( msg );
|
||||
|
@ -2469,11 +2498,7 @@ void CL_ParseLegacyServerData( 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
|
||||
|
@ -3117,12 +3142,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;
|
||||
|
|
|
@ -794,6 +794,8 @@ static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end,
|
|||
matrix4x4 matrix;
|
||||
hull_t *hull;
|
||||
|
||||
PM_InitTrace( trace, end );
|
||||
|
||||
old_usehull = clgame.pmove->usehull;
|
||||
clgame.pmove->usehull = 2;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
@ -864,7 +860,7 @@ void CL_QuakeExecStuff( void )
|
|||
|
||||
if( !*text ) break;
|
||||
|
||||
text = _COM_ParseFileSafe( text, token, sizeof( token ), PFILE_IGNOREBRACKET, NULL );
|
||||
text = COM_ParseFileSafe( text, token, sizeof( token ), PFILE_IGNOREBRACKET, NULL, NULL );
|
||||
|
||||
if( !text ) break;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -418,7 +418,6 @@ void SCR_BeginLoadingPlaque( qboolean is_background )
|
|||
cls.draw_changelevel = !is_background;
|
||||
SCR_UpdateScreen();
|
||||
cls.disable_screen = host.realtime;
|
||||
cls.disable_servercount = cl.servercount;
|
||||
cl.background = is_background; // set right state before svc_serverdata is came
|
||||
|
||||
if( !Host_IsDedicated() )
|
||||
|
@ -781,7 +780,6 @@ SCR_VidInit
|
|||
*/
|
||||
void SCR_VidInit( void )
|
||||
{
|
||||
string libpath;
|
||||
if( !ref.initialized ) // don't call VidInit too soon
|
||||
return;
|
||||
|
||||
|
@ -796,8 +794,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
|
||||
|
||||
|
|
|
@ -1904,7 +1904,7 @@ handle temp-entity messages
|
|||
void CL_ParseTempEntity( sizebuf_t *msg )
|
||||
{
|
||||
sizebuf_t buf;
|
||||
byte pbuf[256];
|
||||
byte pbuf[2048];
|
||||
int iSize;
|
||||
int type, color, count, flags;
|
||||
int decalIndex, modelIndex, entityIndex;
|
||||
|
@ -1923,6 +1923,10 @@ void CL_ParseTempEntity( sizebuf_t *msg )
|
|||
|
||||
decalIndex = modelIndex = entityIndex = 0;
|
||||
|
||||
// this will probably be fatal anyway
|
||||
if( iSize > sizeof( pbuf ))
|
||||
Con_Printf( S_ERROR "%s: Temp buffer overflow!\n", __FUNCTION__ );
|
||||
|
||||
// parse user message into buffer
|
||||
MSG_ReadBytes( msg, pbuf, iSize );
|
||||
|
||||
|
@ -1970,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 );
|
||||
|
@ -2023,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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -465,7 +466,7 @@ typedef struct
|
|||
|
||||
string cdtracks[MAX_CDTRACKS]; // 32 cd-tracks read from cdaudio.txt
|
||||
|
||||
model_t sprites[MAX_CLIENT_SPRITES]; // client spritetextures
|
||||
model_t sprites[MAX_CLIENT_SPRITES]; // hud&client spritetexturesz
|
||||
int viewport[4]; // viewport sizes
|
||||
|
||||
client_draw_t ds; // draw2d stuff (hud, weaponmenu etc)
|
||||
|
@ -527,9 +528,6 @@ typedef struct
|
|||
float disable_screen; // showing loading plaque between levels
|
||||
// or changing rendering dlls
|
||||
// if time gets > 30 seconds ahead, break it
|
||||
int disable_servercount; // when we receive a frame and cl.servercount
|
||||
// > cls.disable_servercount, clear disable_screen
|
||||
|
||||
qboolean draw_changelevel; // draw changelevel image 'Loading...'
|
||||
|
||||
keydest_t key_dest;
|
||||
|
@ -705,7 +703,7 @@ dlight_t *CL_GetEntityLight( int number );
|
|||
//
|
||||
// cl_cmds.c
|
||||
//
|
||||
void CL_Quit_f( void ) NORETURN;
|
||||
void CL_Quit_f( void );
|
||||
void CL_ScreenShot_f( void );
|
||||
void CL_SnapShot_f( void );
|
||||
void CL_PlayCDTrack_f( void );
|
||||
|
@ -966,7 +964,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 );
|
||||
|
|
|
@ -283,6 +283,8 @@ void Con_ToggleConsole_f( void )
|
|||
if( !host.allow_console || UI_CreditsActive( ))
|
||||
return; // disabled
|
||||
|
||||
SCR_EndLoadingPlaque();
|
||||
|
||||
// show console only in game or by special call from menu
|
||||
if( cls.state != ca_active || cls.key_dest == key_menu )
|
||||
return;
|
||||
|
@ -912,7 +914,7 @@ static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
|
|||
if( !con.curFont || !con.curFont->valid )
|
||||
return 0;
|
||||
|
||||
number = Con_UtfProcessChar(number);
|
||||
number = Con_UtfProcessChar( number );
|
||||
if( !number )
|
||||
return 0;
|
||||
|
||||
|
@ -2039,7 +2041,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] );
|
||||
|
@ -2049,7 +2051,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( ))
|
||||
|
@ -2075,7 +2077,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++ )
|
||||
{
|
||||
|
@ -2401,11 +2403,21 @@ void Con_RunConsole( void )
|
|||
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();
|
||||
|
|
|
@ -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,23 +1010,23 @@ 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.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 );
|
||||
Touch_AddDefaultButton( "savequick", "touch_default/save", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 );
|
||||
Touch_AddDefaultButton( "messagemode", "touch_default/keyboard", "messagemode", 0.840000, 0.000000, 0.920000, 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.760000, 0.000000, 0.840000, 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 );
|
||||
|
||||
|
||||
Cmd_AddCommand( "touch_addbutton", Touch_AddButton_f, "add native touch button" );
|
||||
|
@ -1106,11 +1078,11 @@ 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" );
|
||||
|
@ -1372,7 +1344,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 +1487,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,9 +1938,8 @@ 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
|
||||
|
@ -2011,25 +1982,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 +2014,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 )
|
||||
|
|
|
@ -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
|
||||
|
@ -276,7 +283,8 @@ void IN_ActivateMouse( void )
|
|||
return;
|
||||
|
||||
IN_CheckMouseState( true );
|
||||
clgame.dllFuncs.IN_ActivateMouse();
|
||||
if( clgame.dllFuncs.IN_ActivateMouse )
|
||||
clgame.dllFuncs.IN_ActivateMouse();
|
||||
in_mouseactive = true;
|
||||
}
|
||||
|
||||
|
@ -293,7 +301,8 @@ void IN_DeactivateMouse( void )
|
|||
return;
|
||||
|
||||
IN_CheckMouseState( false );
|
||||
clgame.dllFuncs.IN_DeactivateMouse();
|
||||
if( clgame.dllFuncs.IN_DeactivateMouse )
|
||||
clgame.dllFuncs.IN_DeactivateMouse();
|
||||
in_mouseactive = false;
|
||||
}
|
||||
|
||||
|
@ -306,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( ¤t_pos.x, ¤t_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 );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -334,8 +343,6 @@ IN_MouseEvent
|
|||
*/
|
||||
void IN_MouseEvent( int key, int down )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( !in_mouseinitialized )
|
||||
return;
|
||||
|
||||
|
@ -343,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
|
||||
|
@ -361,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -712,7 +712,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 == '~' )
|
||||
|
|
|
@ -60,7 +60,7 @@ void GL_RenderFrame( const ref_viewpass_t *rvp )
|
|||
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
|
||||
}
|
||||
|
@ -151,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];
|
||||
|
@ -334,10 +334,6 @@ static ref_api_t gEngfuncs =
|
|||
COM_FreeLibrary,
|
||||
COM_GetProcAddress,
|
||||
|
||||
FS_LoadFile,
|
||||
FS_FileExists,
|
||||
FS_AllowDirectPaths,
|
||||
|
||||
R_Init_Video_,
|
||||
R_Free_Video,
|
||||
|
||||
|
@ -383,7 +379,9 @@ static ref_api_t gEngfuncs =
|
|||
|
||||
pfnDrawNormalTriangles,
|
||||
pfnDrawTransparentTriangles,
|
||||
&clgame.drawFuncs
|
||||
&clgame.drawFuncs,
|
||||
|
||||
&g_fsapi,
|
||||
};
|
||||
|
||||
static void R_UnloadProgs( void )
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -619,6 +619,8 @@ 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;
|
||||
|
||||
ch->pitch *= ( sys_timescale.value + 1 ) / 2;
|
||||
|
||||
if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE ))
|
||||
{
|
||||
if( pSource->width == 1 )
|
||||
|
@ -956,6 +958,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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,3 +150,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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
NULL,
|
||||
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,128 +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" );
|
||||
}
|
||||
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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -181,7 +180,6 @@ void VID_Init( void )
|
|||
|
||||
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" );
|
||||
|
|
|
@ -31,7 +31,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
633
engine/client/voice.c
Normal 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
104
engine/client/voice.h
Normal 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
|
|
@ -647,7 +647,7 @@ void Cmd_TokenizeString( const char *text )
|
|||
if( cmd_argc == 1 )
|
||||
cmd_args = text;
|
||||
|
||||
text = _COM_ParseFileSafe( (char*)text, cmd_token, sizeof( cmd_token ), PFILE_IGNOREBRACKET, NULL );
|
||||
text = COM_ParseFileSafe( (char*)text, cmd_token, sizeof( cmd_token ), PFILE_IGNOREBRACKET, NULL, NULL );
|
||||
|
||||
if( !text ) return;
|
||||
|
||||
|
@ -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 );
|
||||
|
|
|
@ -65,6 +65,8 @@ GNU General Public License for more details.
|
|||
#define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest"
|
||||
|
||||
#define XASH_ENGINE_NAME "Xash3D FWGS"
|
||||
#define XASH_VERSION "0.20" // engine current version
|
||||
#define XASH_COMPAT_VERSION "0.99" // version we are based on
|
||||
|
||||
// renderers order is important, software is always a last chance fallback
|
||||
#define DEFAULT_RENDERERS { "gl", "gles1", "gles2", "gl4es", "soft" }
|
||||
|
|
|
@ -585,23 +585,6 @@ void COM_TrimSpace( const char *source, char *dest )
|
|||
dest[length] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_FixSlashes
|
||||
|
||||
Changes all '/' characters into '\' characters, in place.
|
||||
============
|
||||
*/
|
||||
void COM_FixSlashes( char *pname )
|
||||
{
|
||||
while( *pname )
|
||||
{
|
||||
if( *pname == '\\' )
|
||||
*pname = '/';
|
||||
pname++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
COM_Nibble
|
||||
|
@ -1182,22 +1165,22 @@ void Test_RunCommon( void )
|
|||
|
||||
Msg( "Checking COM_ParseFile...\n" );
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "q" ) && len == 1);
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "asdf" ) && len == 4);
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "qwer" ) && len == -1);
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "f \"f" ) && len == 4);
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "meow" ) && len == -1);
|
||||
|
||||
file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len );
|
||||
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL );
|
||||
TASSERT( !Q_strcmp( buf, "bark" ) && len == 4);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -76,13 +76,6 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc
|
|||
|
||||
#define HACKS_RELATED_HLMODS // some HL-mods works differently under Xash and can't be fixed without some hacks at least at current time
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numfilenames;
|
||||
char **filenames;
|
||||
char *filenamesbuffer;
|
||||
} search_t;
|
||||
|
||||
enum
|
||||
{
|
||||
DEV_NONE = 0,
|
||||
|
@ -118,9 +111,8 @@ typedef enum
|
|||
#include "cvar.h"
|
||||
#include "con_nprint.h"
|
||||
#include "crclib.h"
|
||||
|
||||
#define XASH_VERSION "0.20" // engine current version
|
||||
#define XASH_COMPAT_VERSION "0.99" // version we are based on
|
||||
#include "ref_api.h"
|
||||
#include "fscallback.h"
|
||||
|
||||
// PERFORMANCE INFO
|
||||
#define MIN_FPS 20.0f // host minimum fps value for maxfps.
|
||||
|
@ -150,18 +142,6 @@ typedef enum
|
|||
#define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn
|
||||
#endif
|
||||
|
||||
// filesystem flags
|
||||
#define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path
|
||||
#define FS_NOWRITE_PATH ( 1U << 1 ) // default behavior - last added gamedir set as writedir. This flag disables it
|
||||
#define FS_GAMEDIR_PATH ( 1U << 2 ) // just a marker for gamedir path
|
||||
#define FS_CUSTOM_PATH ( 1U << 3 ) // custom directory
|
||||
#define FS_GAMERODIR_PATH ( 1U << 4 ) // caseinsensitive
|
||||
|
||||
#define FS_GAMEDIRONLY_SEARCH_FLAGS ( FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH )
|
||||
|
||||
#define GI SI.GameInfo
|
||||
#define FS_Gamedir() SI.GameInfo->gamefolder
|
||||
#define FS_Title() SI.GameInfo->title
|
||||
#define GameState (&host.game)
|
||||
|
||||
#define FORCE_DRAW_VERSION_TIME 5.0f // draw version for 5 seconds
|
||||
|
@ -179,7 +159,6 @@ 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;
|
||||
|
@ -202,61 +181,6 @@ GAMEINFO stuff
|
|||
internal shared gameinfo structure (readonly for engine parts)
|
||||
========================================================================
|
||||
*/
|
||||
typedef struct gameinfo_s
|
||||
{
|
||||
// filesystem info
|
||||
char gamefolder[MAX_QPATH]; // used for change game '-game x'
|
||||
char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life)
|
||||
char falldir[MAX_QPATH]; // used as second basedir
|
||||
char startmap[MAX_QPATH];// map to start singleplayer game
|
||||
char trainmap[MAX_QPATH];// map to start hazard course (if specified)
|
||||
char title[64]; // Game Main Title
|
||||
float version; // game version (optional)
|
||||
|
||||
// .dll pathes
|
||||
char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls"
|
||||
char game_dll[MAX_QPATH]; // custom path for game.dll
|
||||
|
||||
// .ico path
|
||||
char iconpath[MAX_QPATH]; // "game.ico" by default
|
||||
|
||||
// about mod info
|
||||
string game_url; // link to a developer's site
|
||||
string update_url; // link to updates page
|
||||
char type[MAX_QPATH]; // single, toolkit, multiplayer etc
|
||||
char date[MAX_QPATH];
|
||||
size_t size;
|
||||
|
||||
int gamemode;
|
||||
qboolean secure; // prevent to console acess
|
||||
qboolean nomodels; // don't let player to choose model (use player.mdl always)
|
||||
qboolean noskills; // disable skill menu selection
|
||||
qboolean render_picbutton_text; // use font renderer to render WON buttons
|
||||
|
||||
char sp_entity[32]; // e.g. info_player_start
|
||||
char mp_entity[32]; // e.g. info_player_deathmatch
|
||||
char mp_filter[32]; // filtering multiplayer-maps
|
||||
|
||||
char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds
|
||||
|
||||
int max_edicts; // min edicts is 600, max edicts is 8196
|
||||
int max_tents; // min temp ents is 300, max is 2048
|
||||
int max_beams; // min beams is 64, max beams is 512
|
||||
int max_particles; // min particles is 4096, max particles is 32768
|
||||
|
||||
char game_dll_linux[64]; // custom path for game.dll
|
||||
char game_dll_osx[64]; // custom path for game.dll
|
||||
|
||||
qboolean added;
|
||||
} gameinfo_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GAME_NORMAL,
|
||||
GAME_SINGLEPLAYER_ONLY,
|
||||
GAME_MULTIPLAYER_ONLY
|
||||
} gametype_t;
|
||||
|
||||
typedef struct sysinfo_s
|
||||
{
|
||||
string exeName; // exe.filename
|
||||
|
@ -264,9 +188,6 @@ typedef struct sysinfo_s
|
|||
string basedirName; // name of base directory
|
||||
string gamedll;
|
||||
string clientlib;
|
||||
gameinfo_t *GameInfo; // current GameInfo
|
||||
gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start)
|
||||
int numgames;
|
||||
} sysinfo_t;
|
||||
|
||||
typedef enum
|
||||
|
@ -385,6 +306,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;
|
||||
|
@ -426,7 +357,7 @@ 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;
|
||||
|
@ -459,6 +390,9 @@ 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;
|
||||
|
||||
} host_parm_t;
|
||||
|
||||
extern host_parm_t host;
|
||||
|
@ -473,6 +407,13 @@ extern sysinfo_t SI;
|
|||
|
||||
typedef void (*xcommand_t)( void );
|
||||
|
||||
//
|
||||
// filesystem_engine.c
|
||||
//
|
||||
qboolean FS_LoadProgs( void );
|
||||
void FS_Init( void );
|
||||
void FS_Shutdown( void );
|
||||
|
||||
//
|
||||
// cmd.c
|
||||
//
|
||||
|
@ -532,56 +473,6 @@ void Mem_PrintStats( void );
|
|||
#define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem )
|
||||
#define Mem_Check() _Mem_Check( __FILE__, __LINE__ )
|
||||
|
||||
//
|
||||
// filesystem.c
|
||||
//
|
||||
void FS_Init( void );
|
||||
void FS_Path( void );
|
||||
void FS_Rescan( void );
|
||||
void FS_Shutdown( void );
|
||||
void FS_ClearSearchPath( void );
|
||||
void FS_AllowDirectPaths( qboolean enable );
|
||||
void FS_AddGameDirectory( const char *dir, uint flags );
|
||||
void FS_AddGameHierarchy( const char *dir, uint flags );
|
||||
void FS_LoadGameInfo( const char *rootfolder );
|
||||
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
|
||||
byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type );
|
||||
void W_Close( wfile_t *wad );
|
||||
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
|
||||
qboolean CRC32_File( dword *crcvalue, const char *filename );
|
||||
qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] );
|
||||
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr );
|
||||
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
|
||||
qboolean COM_ParseVector( char **pfile, float *v, size_t size );
|
||||
void COM_NormalizeAngles( vec3_t angles );
|
||||
int COM_FileSize( const char *filename );
|
||||
void COM_FixSlashes( char *pname );
|
||||
void COM_FreeFile( void *buffer );
|
||||
int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare );
|
||||
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly );
|
||||
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly );
|
||||
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
|
||||
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
|
||||
int FS_VPrintf( file_t *file, const char *format, va_list ap );
|
||||
int FS_Seek( file_t *file, fs_offset_t offset, int whence );
|
||||
int FS_Gets( file_t *file, byte *string, size_t bufsize );
|
||||
int FS_Printf( file_t *file, const char *format, ... ) _format( 2 );
|
||||
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
|
||||
int FS_FileTime( const char *filename, qboolean gamedironly );
|
||||
int FS_Print( file_t *file, const char *msg );
|
||||
qboolean FS_Rename( const char *oldname, const char *newname );
|
||||
int FS_FileExists( const char *filename, int gamedironly );
|
||||
int FS_SetCurrentDirectory( const char *path );
|
||||
qboolean FS_SysFileExists( const char *path, qboolean casesensitive );
|
||||
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
|
||||
qboolean FS_Delete( const char *path );
|
||||
int FS_UnGetc( file_t *file, byte c );
|
||||
fs_offset_t FS_Tell( file_t *file );
|
||||
qboolean FS_Eof( file_t *file );
|
||||
int FS_Close( file_t *file );
|
||||
int FS_Getc( file_t *file );
|
||||
fs_offset_t FS_FileLength( file_t *f );
|
||||
|
||||
//
|
||||
// imagelib
|
||||
//
|
||||
|
@ -670,16 +561,6 @@ void FS_FreeStream( stream_t *stream );
|
|||
qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags );
|
||||
uint Sound_GetApproxWavePlayLen( const char *filepath );
|
||||
|
||||
//
|
||||
// build.c
|
||||
//
|
||||
int Q_buildnum( void );
|
||||
int Q_buildnum_compat( void );
|
||||
const char *Q_buildos( void );
|
||||
const char *Q_buildarch( void );
|
||||
const char *Q_buildcommit( void );
|
||||
|
||||
|
||||
//
|
||||
// host.c
|
||||
//
|
||||
|
@ -699,7 +580,7 @@ void Host_WriteConfig( void );
|
|||
qboolean Host_IsLocalGame( void );
|
||||
qboolean Host_IsLocalClient( void );
|
||||
void Host_ShutdownServer( void );
|
||||
void Host_Error( const char *error, ... ) _format( 1 ) NORETURN;
|
||||
void Host_Error( const char *error, ... ) _format( 1 );
|
||||
void Host_PrintEngineFeatures( void );
|
||||
void Host_Frame( float time );
|
||||
void Host_InitDecals( void );
|
||||
|
@ -879,7 +760,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 );
|
||||
|
@ -955,6 +835,11 @@ void UI_SetActiveMenu( qboolean fActive );
|
|||
void UI_ShowConnectionWarning( void );
|
||||
void Cmd_Null_f( void );
|
||||
void Rcon_Print( const char *pMsg );
|
||||
qboolean COM_ParseVector( char **pfile, float *v, size_t size );
|
||||
void COM_NormalizeAngles( vec3_t angles );
|
||||
int COM_FileSize( const char *filename );
|
||||
void COM_FreeFile( void *buffer );
|
||||
int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare );
|
||||
|
||||
// soundlib shared exports
|
||||
qboolean S_Init( void );
|
||||
|
@ -982,6 +867,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"
|
||||
|
|
|
@ -761,10 +761,10 @@ qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
|
|||
// compare gamelist with current keyword
|
||||
len = Q_strlen( s );
|
||||
|
||||
for( i = 0, numgamedirs = 0; i < SI.numgames; i++ )
|
||||
for( i = 0, numgamedirs = 0; i < FI->numgames; i++ )
|
||||
{
|
||||
if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, len))
|
||||
Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder );
|
||||
if(( *s == '*' ) || !Q_strnicmp( FI->games[i]->gamefolder, s, len))
|
||||
Q_strcpy( gamedirs[numgamedirs++], FI->games[i]->gamefolder );
|
||||
}
|
||||
|
||||
if( !numgamedirs ) return false;
|
||||
|
|
|
@ -21,7 +21,7 @@ GNU General Public License for more details.
|
|||
convar_t *cvar_vars = NULL; // head of list
|
||||
convar_t *cmd_scripting;
|
||||
|
||||
CVAR_DEFINE_AUTO( cl_filterstuffcmd, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" );
|
||||
CVAR_DEFINE_AUTO( cl_filterstuffcmd, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" );
|
||||
|
||||
/*
|
||||
============
|
||||
|
@ -220,6 +220,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
|
||||
|
@ -495,6 +513,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
|
||||
|
@ -887,6 +1012,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 +1093,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 ))
|
||||
|
@ -995,12 +1158,12 @@ void Cvar_Init( void )
|
|||
{
|
||||
cvar_vars = 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" );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
filesystem.h - engine FS
|
||||
Copyright (C) 2007 Uncle Mike
|
||||
|
||||
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 FILESYSTEM_H
|
||||
#define FILESYSTEM_H
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
PAK FILES
|
||||
|
||||
The .pak files are just a linear collapse of a directory tree
|
||||
========================================================================
|
||||
*/
|
||||
// header
|
||||
#define IDPACKV1HEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') // little-endian "PACK"
|
||||
|
||||
#define MAX_FILES_IN_PACK 65536 // pak
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident;
|
||||
int dirofs;
|
||||
int dirlen;
|
||||
} dpackheader_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[56]; // total 64 bytes
|
||||
int filepos;
|
||||
int filelen;
|
||||
} dpackfile_t;
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
.WAD archive format (WhereAllData - WAD)
|
||||
|
||||
List of compressed files, that can be identify only by TYPE_*
|
||||
|
||||
<format>
|
||||
header: dwadinfo_t[dwadinfo_t]
|
||||
file_1: byte[dwadinfo_t[num]->disksize]
|
||||
file_2: byte[dwadinfo_t[num]->disksize]
|
||||
file_3: byte[dwadinfo_t[num]->disksize]
|
||||
...
|
||||
file_n: byte[dwadinfo_t[num]->disksize]
|
||||
infotable dlumpinfo_t[dwadinfo_t->numlumps]
|
||||
========================================================================
|
||||
*/
|
||||
#define WAD3_NAMELEN 16
|
||||
#define HINT_NAMELEN 5 // e.g. _mask, _norm
|
||||
#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount
|
||||
|
||||
#include "const.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident; // should be WAD3
|
||||
int numlumps; // num files
|
||||
int infotableofs; // LUT offset
|
||||
} dwadinfo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int filepos; // file offset in WAD
|
||||
int disksize; // compressed or uncompressed
|
||||
int size; // uncompressed
|
||||
signed char type; // TYP_*
|
||||
signed char attribs; // file attribs
|
||||
signed char pad0;
|
||||
signed char pad1;
|
||||
char name[WAD3_NAMELEN]; // must be null terminated
|
||||
} dlumpinfo_t;
|
||||
|
||||
#include "custom.h"
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
.HPK archive format (Hash PAK - HPK)
|
||||
|
||||
List of compressed files, that can be identify only by TYPE_*
|
||||
|
||||
<format>
|
||||
header: dwadinfo_t[dwadinfo_t]
|
||||
file_1: byte[dwadinfo_t[num]->disksize]
|
||||
file_2: byte[dwadinfo_t[num]->disksize]
|
||||
file_3: byte[dwadinfo_t[num]->disksize]
|
||||
...
|
||||
file_n: byte[dwadinfo_t[num]->disksize]
|
||||
infotable dlumpinfo_t[dwadinfo_t->numlumps]
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK"
|
||||
#define IDHPAK_VERSION 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident; // should be equal HPAK
|
||||
int version;
|
||||
int infotableofs;
|
||||
} hpak_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
resource_t resource;
|
||||
int filepos;
|
||||
int disksize;
|
||||
} hpak_lump_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int count;
|
||||
hpak_lump_t *entries; // variable sized.
|
||||
} hpak_info_t;
|
||||
|
||||
#define ZIP_HEADER_LF (('K'<<8)+('P')+(0x03<<16)+(0x04<<24))
|
||||
#define ZIP_HEADER_SPANNED ((0x08<<24)+(0x07<<16)+('K'<<8)+'P')
|
||||
|
||||
#define ZIP_HEADER_CDF ((0x02<<24)+(0x01<<16)+('K'<<8)+'P')
|
||||
#define ZIP_HEADER_EOCD ((0x06<<24)+(0x05<<16)+('K'<<8)+'P')
|
||||
|
||||
#define ZIP_COMPRESSION_NO_COMPRESSION 0
|
||||
#define ZIP_COMPRESSION_DEFLATED 8
|
||||
|
||||
#define ZIP_ZIP64 0xffffffff
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
typedef struct zip_header_s
|
||||
{
|
||||
unsigned int signature; // little endian ZIP_HEADER
|
||||
unsigned short version; // version of pkzip need to unpack
|
||||
unsigned short flags; // flags (16 bits == 16 flags)
|
||||
unsigned short compression_flags; // compression flags (bits)
|
||||
unsigned int dos_date; // file modification time and file modification date
|
||||
unsigned int crc32; //crc32
|
||||
unsigned int compressed_size;
|
||||
unsigned int uncompressed_size;
|
||||
unsigned short filename_len;
|
||||
unsigned short extrafield_len;
|
||||
} zip_header_t;
|
||||
|
||||
/*
|
||||
in zip64 comp and uncompr size == 0xffffffff remeber this
|
||||
compressed and uncompress filesize stored in extra field
|
||||
*/
|
||||
|
||||
typedef struct zip_header_extra_s
|
||||
{
|
||||
unsigned int signature; // ZIP_HEADER_SPANNED
|
||||
unsigned int crc32;
|
||||
unsigned int compressed_size;
|
||||
unsigned int uncompressed_size;
|
||||
} zip_header_extra_t;
|
||||
|
||||
typedef struct zip_cdf_header_s
|
||||
{
|
||||
unsigned int signature;
|
||||
unsigned short version;
|
||||
unsigned short version_need;
|
||||
unsigned short generalPurposeBitFlag;
|
||||
unsigned short flags;
|
||||
unsigned short modification_time;
|
||||
unsigned short modification_date;
|
||||
unsigned int crc32;
|
||||
unsigned int compressed_size;
|
||||
unsigned int uncompressed_size;
|
||||
unsigned short filename_len;
|
||||
unsigned short extrafield_len;
|
||||
unsigned short file_commentary_len;
|
||||
unsigned short disk_start;
|
||||
unsigned short internal_attr;
|
||||
unsigned int external_attr;
|
||||
unsigned int local_header_offset;
|
||||
} zip_cdf_header_t;
|
||||
|
||||
typedef struct zip_header_eocd_s
|
||||
{
|
||||
unsigned short disk_number;
|
||||
unsigned short start_disk_number;
|
||||
unsigned short number_central_directory_record;
|
||||
unsigned short total_central_directory_record;
|
||||
unsigned int size_of_central_directory;
|
||||
unsigned int central_directory_offset;
|
||||
unsigned short commentary_len;
|
||||
} zip_header_eocd_t;
|
||||
#pragma pack( pop )
|
||||
|
||||
#endif//FILESYSTEM_H
|
153
engine/common/filesystem_engine.c
Normal file
153
engine/common/filesystem_engine.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
filesystem.c - game filesystem based on DP fs
|
||||
Copyright (C) 2007 Uncle Mike
|
||||
|
||||
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 "library.h"
|
||||
|
||||
fs_api_t g_fsapi;
|
||||
fs_globals_t *FI;
|
||||
|
||||
static HINSTANCE fs_hInstance;
|
||||
|
||||
static void FS_Rescan_f( void )
|
||||
{
|
||||
FS_Rescan();
|
||||
}
|
||||
|
||||
static void FS_ClearPaths_f( void )
|
||||
{
|
||||
FS_ClearSearchPath();
|
||||
}
|
||||
|
||||
static void FS_Path_f_( void )
|
||||
{
|
||||
FS_Path_f();
|
||||
}
|
||||
|
||||
static fs_interface_t fs_memfuncs =
|
||||
{
|
||||
Con_Printf,
|
||||
Con_DPrintf,
|
||||
Con_Reportf,
|
||||
Sys_Error,
|
||||
|
||||
_Mem_AllocPool,
|
||||
_Mem_FreePool,
|
||||
_Mem_Alloc,
|
||||
_Mem_Realloc,
|
||||
_Mem_Free,
|
||||
};
|
||||
|
||||
static void FS_UnloadProgs( void )
|
||||
{
|
||||
COM_FreeLibrary( fs_hInstance );
|
||||
fs_hInstance = 0;
|
||||
}
|
||||
|
||||
#ifdef XASH_INTERNAL_GAMELIBS
|
||||
#define FILESYSTEM_STDIO_DLL "filesystem_stdio"
|
||||
#else
|
||||
#define FILESYSTEM_STDIO_DLL "filesystem_stdio." OS_LIB_EXT
|
||||
#endif
|
||||
|
||||
qboolean FS_LoadProgs( void )
|
||||
{
|
||||
const char *name = FILESYSTEM_STDIO_DLL;
|
||||
FSAPI GetFSAPI;
|
||||
|
||||
fs_hInstance = COM_LoadLibrary( name, false, true );
|
||||
|
||||
if( !fs_hInstance )
|
||||
{
|
||||
Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API )))
|
||||
{
|
||||
FS_UnloadProgs();
|
||||
Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ))
|
||||
{
|
||||
FS_UnloadProgs();
|
||||
Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_Init
|
||||
================
|
||||
*/
|
||||
void FS_Init( void )
|
||||
{
|
||||
qboolean hasBaseDir = false;
|
||||
qboolean hasGameDir = false;
|
||||
qboolean caseinsensitive = true;
|
||||
int i;
|
||||
string gamedir;
|
||||
|
||||
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
||||
|
||||
#if !XASH_WIN32
|
||||
if( Sys_CheckParm( "-casesensitive" ) )
|
||||
caseinsensitive = false;
|
||||
#endif
|
||||
|
||||
if( !Sys_GetParmFromCmdLine( "-game", gamedir ))
|
||||
Q_strncpy( gamedir, SI.basedirName, sizeof( gamedir )); // gamedir == basedir
|
||||
|
||||
if( !FS_InitStdio( caseinsensitive, host.rootdir, SI.basedirName, gamedir, host.rodir ))
|
||||
{
|
||||
Host_Error( "Can't init filesystem_stdio!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !Sys_GetParmFromCmdLine( "-dll", SI.gamedll ))
|
||||
SI.gamedll[0] = 0;
|
||||
|
||||
if( !Sys_GetParmFromCmdLine( "-clientlib", SI.clientlib ))
|
||||
SI.clientlib[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_Shutdown
|
||||
================
|
||||
*/
|
||||
void FS_Shutdown( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
FS_ShutdownStdio();
|
||||
|
||||
memset( &SI, 0, sizeof( sysinfo_t ));
|
||||
|
||||
FS_UnloadProgs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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")
|
||||
|
@ -274,6 +276,13 @@ void Host_CheckSleep( 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;
|
||||
#endif
|
||||
|
||||
if( Host_IsDedicated() )
|
||||
{
|
||||
// let the dedicated server some sleep
|
||||
|
@ -305,7 +314,9 @@ void Host_NewInstance( const char *name, const char *finalmsg )
|
|||
|
||||
host.change_game = true;
|
||||
Q_strncpy( host.finalmsg, finalmsg, sizeof( host.finalmsg ));
|
||||
pChangeGame( name ); // call from hl.exe
|
||||
|
||||
if( !Sys_NewInstance( name ))
|
||||
pChangeGame( name ); // call from hl.exe
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -326,13 +337,13 @@ void Host_ChangeGame_f( void )
|
|||
}
|
||||
|
||||
// validate gamedir
|
||||
for( i = 0; i < SI.numgames; i++ )
|
||||
for( i = 0; i < FI->numgames; i++ )
|
||||
{
|
||||
if( !Q_stricmp( SI.games[i]->gamefolder, Cmd_Argv( 1 )))
|
||||
if( !Q_stricmp( FI->games[i]->gamefolder, Cmd_Argv( 1 )))
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == SI.numgames )
|
||||
if( i == FI->numgames )
|
||||
{
|
||||
Con_Printf( "%s not exist\n", Cmd_Argv( 1 ));
|
||||
}
|
||||
|
@ -343,7 +354,7 @@ void Host_ChangeGame_f( void )
|
|||
else
|
||||
{
|
||||
const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 ));
|
||||
const char *arg2 = va( "change game to '%s'", SI.games[i]->title );
|
||||
const char *arg2 = va( "change game to '%s'", FI->games[i]->title );
|
||||
|
||||
Host_NewInstance( arg1, arg2 );
|
||||
}
|
||||
|
@ -593,24 +604,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
|
||||
|
@ -671,11 +674,19 @@ Host_Frame
|
|||
*/
|
||||
void Host_Frame( float time )
|
||||
{
|
||||
Host_CheckSleep();
|
||||
static qboolean slept = false;
|
||||
|
||||
// decide the simulation time
|
||||
if( !Host_FilterTime( time ))
|
||||
{
|
||||
if( !slept )
|
||||
{
|
||||
Host_CheckSleep();
|
||||
slept = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
slept = false;
|
||||
|
||||
Host_InputFrame (); // input frame
|
||||
Host_ClientBegin (); // begin client
|
||||
|
@ -699,15 +710,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 );
|
||||
|
@ -854,10 +856,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" ) )
|
||||
|
@ -865,7 +867,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
|
|||
|
||||
host.enabledll = !Sys_CheckParm( "-nodll" );
|
||||
|
||||
host.change_game = bChangeGame;
|
||||
host.change_game = bChangeGame || Sys_CheckParm( "-changegame" );
|
||||
host.config_executed = false;
|
||||
host.status = HOST_INIT; // initialzation started
|
||||
|
||||
|
@ -936,6 +938,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
|
||||
|
@ -1022,18 +1031,33 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
|
|||
if( len && host.rodir[len - 1] == '/' )
|
||||
host.rodir[len - 1] = 0;
|
||||
|
||||
if( !COM_CheckStringEmpty( host.rootdir ) || FS_SetCurrentDirectory( host.rootdir ) != 0 )
|
||||
if( !COM_CheckStringEmpty( host.rootdir ))
|
||||
{
|
||||
Sys_Error( "Changing working directory failed (empty working directory)\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
FS_LoadProgs();
|
||||
|
||||
if( FS_SetCurrentDirectory( host.rootdir ) != 0 )
|
||||
Con_Reportf( "%s is working directory now\n", host.rootdir );
|
||||
else
|
||||
Sys_Error( "Changing working directory to %s failed.\n", host.rootdir );
|
||||
|
||||
FS_Init();
|
||||
|
||||
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" );
|
||||
|
||||
FS_Init();
|
||||
Image_Init();
|
||||
Sound_Init();
|
||||
|
||||
|
@ -1043,8 +1067,16 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
|
|||
#endif
|
||||
|
||||
FS_LoadGameInfo( NULL );
|
||||
|
||||
if( FS_FileExists( va( "%s.rc", SI.basedirName ), false ))
|
||||
Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc
|
||||
else Q_strncpy( SI.rcName, SI.exeName, sizeof( SI.rcName )); // e.g. quake.rc
|
||||
|
||||
Q_strncpy( host.gamefolder, GI->gamefolder, sizeof( host.gamefolder ));
|
||||
|
||||
Image_CheckPaletteQ1 ();
|
||||
Host_InitDecals (); // reload decals
|
||||
|
||||
// DEPRECATED: by FWGS fork
|
||||
#if 0
|
||||
if( GI->secure )
|
||||
|
|
|
@ -14,7 +14,7 @@ GNU General Public License for more details.
|
|||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "filesystem.h"
|
||||
#include "hpak.h"
|
||||
|
||||
#define HPAK_MAX_ENTRIES 0x8000
|
||||
#define HPAK_MIN_SIZE (1 * 1024)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +343,7 @@ 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;
|
||||
|
||||
|
@ -370,7 +383,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();
|
||||
|
@ -402,7 +415,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet )
|
|||
FS_Seek( f, hdr.infotableofs, SEEK_SET );
|
||||
FS_Read( f, &num_lumps, sizeof( num_lumps ));
|
||||
|
||||
if( num_lumps < 1 || num_lumps > MAX_FILES_IN_WAD )
|
||||
if( num_lumps < 1 || num_lumps > HPAK_MAX_ENTRIES )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps );
|
||||
FS_Close( f );
|
||||
|
@ -621,7 +634,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 +660,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 )
|
||||
{
|
||||
|
@ -702,11 +715,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 );
|
||||
|
|
89
engine/common/hpak.h
Normal file
89
engine/common/hpak.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
hpak.c - custom user package to send other clients
|
||||
Copyright (C) 2010 Uncle Mike
|
||||
|
||||
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 HPAK_H
|
||||
#define HPAK_H
|
||||
|
||||
#include "custom.h"
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
.HPK archive format (Hash PAK - HPK)
|
||||
|
||||
List of compressed files, that can be identify only by TYPE_*
|
||||
|
||||
<format>
|
||||
header: dwadinfo_t[dwadinfo_t]
|
||||
file_1: byte[dwadinfo_t[num]->disksize]
|
||||
file_2: byte[dwadinfo_t[num]->disksize]
|
||||
file_3: byte[dwadinfo_t[num]->disksize]
|
||||
...
|
||||
file_n: byte[dwadinfo_t[num]->disksize]
|
||||
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;
|
||||
} hpak_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
dresource_t resource;
|
||||
int filepos;
|
||||
int disksize;
|
||||
} hpak_lump_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int count;
|
||||
hpak_lump_t *entries; // variable sized.
|
||||
} hpak_info_t;
|
||||
|
||||
#endif // HPAK_H
|
|
@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.h"
|
||||
#include "imagelib.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
|
@ -27,6 +26,8 @@ GNU General Public License for more details.
|
|||
|
||||
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;
|
||||
|
@ -40,9 +41,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, 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;
|
||||
|
@ -68,7 +70,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;
|
||||
}
|
||||
|
||||
|
@ -85,7 +87,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -95,21 +97,25 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -121,7 +127,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;
|
||||
}
|
||||
|
||||
|
@ -159,8 +165,19 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
|
|||
// move pointer
|
||||
buf_p += sizeof( chunk_sign );
|
||||
|
||||
// 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 );
|
||||
}
|
||||
// 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 );
|
||||
|
@ -194,6 +211,13 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
|
|||
buf_p += sizeof( crc32 );
|
||||
}
|
||||
|
||||
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 );
|
||||
|
@ -203,7 +227,7 @@ 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;
|
||||
}
|
||||
|
@ -216,6 +240,13 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi
|
|||
|
||||
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;
|
||||
|
@ -231,10 +262,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;
|
||||
|
@ -276,7 +310,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++;
|
||||
|
@ -378,23 +412,73 @@ 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 )
|
||||
{
|
||||
*pixbuf++ = raw[0];
|
||||
*pixbuf++ = raw[1];
|
||||
*pixbuf++ = raw[2];
|
||||
*pixbuf++ = 0xFF;
|
||||
}
|
||||
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 )
|
||||
{
|
||||
*pixbuf++ = pallete[raw[0] + 2];
|
||||
*pixbuf++ = pallete[raw[0] + 1];
|
||||
*pixbuf++ = pallete[raw[0] + 0];
|
||||
|
||||
if( trns && raw[0] < trns_len )
|
||||
*pixbuf++ = trns[raw[0]];
|
||||
else
|
||||
*pixbuf++ = 0xFF;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Mem_Free( uncompressed_buffer );
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -96,8 +96,8 @@ static const loadpixformat_t load_null[] =
|
|||
static const loadpixformat_t load_game[] =
|
||||
{
|
||||
{ "%s%s.%s", "dds", Image_LoadDDS, IL_HINT_NO }, // dds for world and studio models
|
||||
{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus
|
||||
{ "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // WON menu images
|
||||
{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus
|
||||
{ "%s%s.%s", "png", Image_LoadPNG, IL_HINT_NO }, // NightFire 007 menus
|
||||
{ "%s%s.%s", "mip", Image_LoadMIP, IL_HINT_NO }, // hl textures from wad or buffer
|
||||
{ "%s%s.%s", "mdl", Image_LoadMDL, IL_HINT_HL }, // hl studio model skins
|
||||
|
|
|
@ -23,66 +23,44 @@ GNU General Public License for more details.
|
|||
|
||||
#if XASH_EMSCRIPTEN
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#if XASH_WIN32
|
||||
#define XASH_NOCONHOST 1
|
||||
#endif
|
||||
|
||||
static char szGameDir[128]; // safe place to keep gamedir
|
||||
static int g_iArgc;
|
||||
static char **g_pszArgv;
|
||||
|
||||
void Launcher_ChangeGame( const char *progname )
|
||||
#elif XASH_WIN32
|
||||
extern "C"
|
||||
{
|
||||
// Enable NVIDIA High Performance Graphics while using Integrated Graphics.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
|
||||
// Enable AMD High Performance Graphics while using Integrated Graphics.
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define E_GAME "XASH3D_GAME" // default env dir to start from
|
||||
#ifndef XASH_GAMEDIR
|
||||
#define XASH_GAMEDIR "valve"
|
||||
#endif
|
||||
|
||||
static char szGameDir[128]; // safe place to keep gamedir
|
||||
static int szArgc;
|
||||
static char **szArgv;
|
||||
|
||||
static void Sys_ChangeGame( const char *progname )
|
||||
{
|
||||
// a1ba: may never be called within engine
|
||||
// if platform supports execv() function
|
||||
Q_strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 );
|
||||
Host_Shutdown( );
|
||||
exit( Host_Main( g_iArgc, g_pszArgv, szGameDir, 1, &Launcher_ChangeGame ) );
|
||||
exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Sys_ChangeGame ) );
|
||||
}
|
||||
|
||||
#if XASH_NOCONHOST
|
||||
#include <windows.h>
|
||||
#include <shellapi.h> // CommandLineToArgvW
|
||||
int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow)
|
||||
_inline int Sys_Start( void )
|
||||
{
|
||||
int szArgc;
|
||||
char **szArgv;
|
||||
LPWSTR* lpArgv = CommandLineToArgvW(GetCommandLineW(), &szArgc);
|
||||
int i = 0;
|
||||
int ret;
|
||||
const char *game = getenv( E_GAME );
|
||||
|
||||
szArgv = (char**)malloc(szArgc*sizeof(char*));
|
||||
for (; i < szArgc; ++i)
|
||||
{
|
||||
size_t size = wcslen(lpArgv[i]) + 1;
|
||||
szArgv[i] = (char*)malloc(size);
|
||||
wcstombs(szArgv[i], lpArgv[i], size);
|
||||
}
|
||||
szArgv[i] = NULL;
|
||||
|
||||
LocalFree(lpArgv);
|
||||
|
||||
main( szArgc, szArgv );
|
||||
|
||||
for( i = 0; i < szArgc; ++i )
|
||||
free( szArgv[i] );
|
||||
free( szArgv );
|
||||
}
|
||||
#endif
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
char gamedir_buf[32] = "";
|
||||
const char *gamedir = getenv( "XASH3D_GAMEDIR" );
|
||||
|
||||
if( !COM_CheckString( gamedir ) )
|
||||
{
|
||||
gamedir = "valve";
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpy( gamedir_buf, gamedir, 32 );
|
||||
gamedir = gamedir_buf;
|
||||
}
|
||||
if( !game )
|
||||
game = XASH_GAMEDIR;
|
||||
|
||||
Q_strncpy( szGameDir, game, sizeof( szGameDir ));
|
||||
#if XASH_EMSCRIPTEN
|
||||
#ifdef EMSCRIPTEN_LIB_FS
|
||||
// For some unknown reason emscripten refusing to load libraries later
|
||||
|
@ -98,17 +76,56 @@ int main( int argc, char** argv )
|
|||
FS.chdir('/xash');
|
||||
}catch(e){};);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
g_iArgc = argc;
|
||||
g_pszArgv = argv;
|
||||
#if XASH_IOS
|
||||
#elif XASH_IOS
|
||||
{
|
||||
void IOS_LaunchDialog( void );
|
||||
IOS_LaunchDialog();
|
||||
}
|
||||
#endif
|
||||
return Host_Main( g_iArgc, g_pszArgv, gamedir, 0, &Launcher_ChangeGame );
|
||||
|
||||
ret = Host_Main( szArgc, szArgv, game, 0, Sys_ChangeGame );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !XASH_WIN32
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
szArgc = argc;
|
||||
szArgv = argv;
|
||||
|
||||
return Sys_Start();
|
||||
}
|
||||
#else
|
||||
int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow)
|
||||
{
|
||||
LPWSTR* lpArgv;
|
||||
int ret, i;
|
||||
|
||||
lpArgv = CommandLineToArgvW( GetCommandLineW(), &szArgc );
|
||||
szArgv = ( char** )malloc( (szArgc + 1) * sizeof( char* ));
|
||||
|
||||
for( i = 0; i < szArgc; ++i )
|
||||
{
|
||||
size_t size = wcslen(lpArgv[i]) + 1;
|
||||
|
||||
// just in case, allocate some more memory
|
||||
szArgv[i] = ( char * )malloc( size * sizeof( wchar_t ));
|
||||
wcstombs( szArgv[i], lpArgv[i], size );
|
||||
}
|
||||
szArgv[szArgc] = 0;
|
||||
|
||||
LocalFree( lpArgv );
|
||||
|
||||
ret = Sys_Start();
|
||||
|
||||
for( ; i < szArgc; ++i )
|
||||
free( szArgv[i] );
|
||||
free( szArgv );
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // XASH_WIN32
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -86,6 +86,38 @@ const char *COM_OffsetNameForFunction( void *function )
|
|||
return sname;
|
||||
}
|
||||
|
||||
dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath )
|
||||
{
|
||||
dll_user_t *p;
|
||||
fs_dllinfo_t dllInfo;
|
||||
|
||||
// no fs loaded yet, but let engine find fs
|
||||
if( !g_fsapi.FindLibrary )
|
||||
{
|
||||
p = Mem_Calloc( host.mempool, sizeof( dll_user_t ));
|
||||
Q_strncpy( p->shortPath, dllname, sizeof( p->shortPath ));
|
||||
Q_strncpy( p->fullPath, dllname, sizeof( p->fullPath ));
|
||||
Q_strncpy( p->dllName, dllname, sizeof( p->dllName ));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// fs can't find library
|
||||
if( !g_fsapi.FindLibrary( dllname, directpath, &dllInfo ))
|
||||
return NULL;
|
||||
|
||||
// NOTE: for libraries we not fail even if search is NULL
|
||||
// let the OS find library himself
|
||||
p = Mem_Calloc( host.mempool, sizeof( dll_user_t ));
|
||||
Q_strncpy( p->shortPath, dllInfo.shortPath, sizeof( p->shortPath ));
|
||||
Q_strncpy( p->fullPath, dllInfo.fullPath, sizeof( p->fullPath ));
|
||||
Q_strncpy( p->dllName, dllname, sizeof( p->dllName ));
|
||||
p->custom_loader = dllInfo.custom_loader;
|
||||
p->encrypted = dllInfo.encrypted;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -143,7 +143,6 @@ model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC );
|
|||
qboolean Mod_ValidateCRC( const char *name, CRC32_t crc );
|
||||
void Mod_NeedCRC( const char *name, qboolean needCRC );
|
||||
void Mod_FreeUnused( void );
|
||||
model_t *Mod_Handle( int handle );
|
||||
|
||||
//
|
||||
// mod_bmodel.c
|
||||
|
|
|
@ -600,20 +600,3 @@ void Mod_NeedCRC( const char *name, qboolean needCRC )
|
|||
if( needCRC ) SetBits( p->flags, FCRC_SHOULD_CHECKSUM );
|
||||
else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
Mod_Handle
|
||||
|
||||
==================
|
||||
*/
|
||||
model_t *GAME_EXPORT Mod_Handle( int handle )
|
||||
{
|
||||
if( handle < 0 || handle >= MAX_MODELS )
|
||||
{
|
||||
Con_Reportf( "Mod_Handle: bad handle #%i\n", handle );
|
||||
return NULL;
|
||||
}
|
||||
return &mod_known[handle];
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ GNU General Public License for more details.
|
|||
#define MAX_ROUTEABLE_PACKET 1400
|
||||
#define SPLITPACKET_MIN_SIZE 508 // RFC 791: 576(min ip packet) - 60 (ip header) - 8 (udp header)
|
||||
#define SPLITPACKET_MAX_SIZE 64000
|
||||
#define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )) )
|
||||
#define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )))
|
||||
|
||||
// ff02:1
|
||||
static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] =
|
||||
|
@ -219,7 +219,7 @@ void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr )
|
|||
void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 )
|
||||
{
|
||||
#if XASH_LITTLE_ENDIAN
|
||||
memcpy( adr->ip6, ip6, sizeof( adr->ip6 ) );
|
||||
memcpy( adr->ip6, ip6, sizeof( adr->ip6 ));
|
||||
#elif XASH_BIG_ENDIAN
|
||||
memcpy( adr->ip6_0, ip6, sizeof( adr->ip6_0 ));
|
||||
memcpy( adr->ip6_2, ip6 + sizeof( adr->ip6_0 ), sizeof( adr->ip6_2 ));
|
||||
|
@ -356,7 +356,7 @@ qboolean NET_GetHostByName( const char *hostname, int family, struct sockaddr_st
|
|||
#endif
|
||||
}
|
||||
|
||||
#if !defined XASH_NO_ASYNC_NS_RESOLVE && ( XASH_WIN32 || !(XASH_EMSCRIPTEN || XASH_DOS4GW) )
|
||||
#if !defined XASH_NO_ASYNC_NS_RESOLVE && ( XASH_WIN32 || !( XASH_EMSCRIPTEN || XASH_DOS4GW ))
|
||||
#define CAN_ASYNC_NS_RESOLVE
|
||||
#endif
|
||||
|
||||
|
@ -516,7 +516,7 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, q
|
|||
return 2;
|
||||
}
|
||||
|
||||
if( !Q_strcmp( copy, nsthread.hostname ) )
|
||||
if( !Q_strcmp( copy, nsthread.hostname ))
|
||||
{
|
||||
ret = nsthread.result;
|
||||
|
||||
|
@ -534,7 +534,7 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, q
|
|||
nsthread.busy = true;
|
||||
mutex_unlock( &nsthread.mutexres );
|
||||
|
||||
if( create_thread( NET_ThreadStart ) )
|
||||
if( create_thread( NET_ThreadStart ))
|
||||
{
|
||||
asyncfailed = false;
|
||||
return 2;
|
||||
|
@ -874,17 +874,20 @@ qboolean NET_IsReservedAdr( netadr_t a )
|
|||
|
||||
if( a.type6 == NA_IP6 )
|
||||
{
|
||||
uint8_t ip6[16];
|
||||
|
||||
NET_NetadrToIP6Bytes( ip6, &a );
|
||||
|
||||
// Private addresses, fc00::/7
|
||||
// Range is fc00:: to fdff:ffff:etc
|
||||
if ( a.ipx[0] >= 0xFC && a.ipx[1] <= 0xFD )
|
||||
if( ip6[0] >= 0xFC && ip6[1] <= 0xFD )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Link-local fe80::/10
|
||||
// Range is fe80:: to febf::
|
||||
if ( a.ipx[0] == 0xFE
|
||||
&& ( a.ipx[1] >= 0x80 && a.ipx[1] <= 0xBF ) )
|
||||
if( ip6[0] == 0xFE && ( ip6[1] >= 0x80 && ip6[1] <= 0xBF ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -950,7 +953,7 @@ qboolean NET_StringToAdrEx( const char *string, netadr_t *adr, int family )
|
|||
|
||||
memset( adr, 0, sizeof( netadr_t ));
|
||||
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ) )
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ))
|
||||
{
|
||||
adr->type = NA_LOOPBACK;
|
||||
return true;
|
||||
|
@ -975,7 +978,7 @@ int NET_StringToAdrNB( const char *string, netadr_t *adr )
|
|||
int res;
|
||||
|
||||
memset( adr, 0, sizeof( netadr_t ));
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ) )
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ))
|
||||
{
|
||||
adr->type = NA_LOOPBACK;
|
||||
return true;
|
||||
|
@ -1393,13 +1396,13 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size
|
|||
memcpy( data, buf, ret );
|
||||
*length = ret;
|
||||
#if !XASH_DEDICATED
|
||||
if( CL_LegacyMode() )
|
||||
if( CL_LegacyMode( ))
|
||||
return NET_LagPacket( true, sock, from, length, data );
|
||||
|
||||
// check for split message
|
||||
if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET )
|
||||
{
|
||||
return NET_GetLong( data, ret, length, CL_GetSplitSize() );
|
||||
return NET_GetLong( data, ret, length, CL_GetSplitSize( ));
|
||||
}
|
||||
#endif
|
||||
// lag the packet, if needed
|
||||
|
@ -1572,7 +1575,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t
|
|||
if( err == WSAEADDRNOTAVAIL && ( to.type == NA_BROADCAST || to.type6 == NA_MULTICAST_IP6 ))
|
||||
return;
|
||||
|
||||
if( Host_IsDedicated() )
|
||||
if( Host_IsDedicated( ))
|
||||
{
|
||||
Con_DPrintf( S_ERROR "NET_SendPacket: %s to %s\n", NET_ErrorString(), NET_AdrToString( to ));
|
||||
}
|
||||
|
@ -1678,7 +1681,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
|
|||
else if( family == AF_INET )
|
||||
pfamily = PF_INET;
|
||||
|
||||
if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP )) ) )
|
||||
if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP ))))
|
||||
{
|
||||
err = WSAGetLastError();
|
||||
if( err != WSAEAFNOSUPPORT )
|
||||
|
@ -1686,7 +1689,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
|
|||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if( NET_IsSocketError( ioctlsocket( net_socket, FIONBIO, (void*)&_true ) ) )
|
||||
if( NET_IsSocketError( ioctlsocket( net_socket, FIONBIO, (void*)&_true )))
|
||||
{
|
||||
struct timeval timeout;
|
||||
|
||||
|
@ -1697,12 +1700,12 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
|
|||
}
|
||||
|
||||
// make it broadcast capable
|
||||
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) ) )
|
||||
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ))))
|
||||
{
|
||||
Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( ));
|
||||
}
|
||||
|
||||
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) )
|
||||
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval ))))
|
||||
{
|
||||
Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( ));
|
||||
closesocket( net_socket );
|
||||
|
@ -1786,29 +1789,67 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
|
|||
NET_OpenIP
|
||||
====================
|
||||
*/
|
||||
static void NET_OpenIP( int *sockets, const char *net_iface, int hostport, int clientport, int family )
|
||||
static void NET_OpenIP( qboolean change_port, int *sockets, const char *net_iface, int hostport, int clientport, int family )
|
||||
{
|
||||
int port;
|
||||
qboolean sv_nat = Cvar_VariableInteger("sv_nat");
|
||||
qboolean cl_nat = Cvar_VariableInteger("cl_nat");
|
||||
|
||||
if( change_port && ( FBitSet( net_hostport->flags, FCVAR_CHANGED ) || sv_nat ))
|
||||
{
|
||||
// reopen socket to set random port
|
||||
if( NET_IsSocketValid( sockets[NS_SERVER] ))
|
||||
closesocket( sockets[NS_SERVER] );
|
||||
|
||||
sockets[NS_SERVER] = INVALID_SOCKET;
|
||||
ClearBits( net_hostport->flags, FCVAR_CHANGED );
|
||||
}
|
||||
|
||||
if( !NET_IsSocketValid( sockets[NS_SERVER] ))
|
||||
{
|
||||
port = hostport;
|
||||
if( !port ) port = net_hostport->value;
|
||||
if( !port ) port = PORT_SERVER; // forcing to default
|
||||
port = net_iphostport->value;
|
||||
if( !port )
|
||||
{
|
||||
if( sv_nat )
|
||||
port = PORT_ANY;
|
||||
else
|
||||
port = net_hostport->value;
|
||||
|
||||
if( !port )
|
||||
port = PORT_SERVER; // forcing to default
|
||||
}
|
||||
sockets[NS_SERVER] = NET_IPSocket( net_iface, port, family );
|
||||
|
||||
if( !NET_IsSocketValid( sockets[NS_SERVER] ) && Host_IsDedicated() )
|
||||
if( !NET_IsSocketValid( sockets[NS_SERVER] ) && Host_IsDedicated( ))
|
||||
Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port );
|
||||
}
|
||||
|
||||
// dedicated servers don't need client ports
|
||||
if( Host_IsDedicated() ) return;
|
||||
if( Host_IsDedicated( )) return;
|
||||
|
||||
if( change_port && ( FBitSet( net_clientport->flags, FCVAR_CHANGED ) || cl_nat ))
|
||||
{
|
||||
// reopen socket to set random port
|
||||
if( NET_IsSocketValid( sockets[NS_CLIENT] ))
|
||||
closesocket( sockets[NS_CLIENT] );
|
||||
|
||||
sockets[NS_CLIENT] = INVALID_SOCKET;
|
||||
ClearBits( net_clientport->flags, FCVAR_CHANGED );
|
||||
}
|
||||
|
||||
if( !NET_IsSocketValid( sockets[NS_CLIENT] ))
|
||||
{
|
||||
port = clientport;
|
||||
if( !port ) port = net_clientport->value;
|
||||
if( !port ) port = PORT_ANY; // forcing to default
|
||||
port = net_ipclientport->value;
|
||||
if( !port )
|
||||
{
|
||||
if( cl_nat )
|
||||
port = PORT_ANY;
|
||||
else
|
||||
port = net_clientport->value;
|
||||
|
||||
if( !port )
|
||||
port = PORT_ANY; // forcing to default
|
||||
}
|
||||
sockets[NS_CLIENT] = NET_IPSocket( net_iface, port, family );
|
||||
|
||||
if( !NET_IsSocketValid( sockets[NS_CLIENT] ))
|
||||
|
@ -1894,7 +1935,7 @@ NET_Config
|
|||
A single player game will only use the loopback code
|
||||
====================
|
||||
*/
|
||||
void NET_Config( qboolean multiplayer )
|
||||
void NET_Config( qboolean multiplayer, qboolean changeport )
|
||||
{
|
||||
static qboolean bFirst = true;
|
||||
static qboolean old_config;
|
||||
|
@ -1911,10 +1952,10 @@ void NET_Config( qboolean multiplayer )
|
|||
{
|
||||
// open sockets
|
||||
if( net.allow_ip )
|
||||
NET_OpenIP( net.ip_sockets, net_ipname->string, net_iphostport->value, net_ipclientport->value, AF_INET );
|
||||
NET_OpenIP( changeport, net.ip_sockets, net_ipname->string, net_iphostport->value, net_ipclientport->value, AF_INET );
|
||||
|
||||
if( net.allow_ip6 )
|
||||
NET_OpenIP( net.ip6_sockets, net_ip6name->string, net_ip6hostport->value, net_ip6clientport->value, AF_INET6 );
|
||||
NET_OpenIP( changeport, net.ip6_sockets, net_ip6name->string, net_ip6hostport->value, net_ip6clientport->value, AF_INET6 );
|
||||
|
||||
// get our local address, if possible
|
||||
if( bFirst )
|
||||
|
@ -2029,7 +2070,7 @@ void NET_Init( void )
|
|||
|
||||
net_clockwindow = Cvar_Get( "clockwindow", "0.5", FCVAR_PRIVILEGED, "timewindow to execute client moves" );
|
||||
net_address = Cvar_Get( "net_address", "0", FCVAR_READ_ONLY, "contain local address of current client" );
|
||||
net_ipname = Cvar_Get( "ip", "localhost", FCVAR_READ_ONLY, "network ip address" );
|
||||
net_ipname = Cvar_Get( "ip", "localhost", 0, "network ip address" );
|
||||
net_iphostport = Cvar_Get( "ip_hostport", "0", FCVAR_READ_ONLY, "network ip host port" );
|
||||
net_hostport = Cvar_Get( "hostport", va( "%i", PORT_SERVER ), FCVAR_READ_ONLY, "network default host port" );
|
||||
net_ipclientport = Cvar_Get( "ip_clientport", "0", FCVAR_READ_ONLY, "network ip client port" );
|
||||
|
@ -2053,7 +2094,7 @@ void NET_Init( void )
|
|||
}
|
||||
|
||||
#if XASH_WIN32
|
||||
if( WSAStartup( MAKEWORD( 1, 1 ), &net.winsockdata ) )
|
||||
if( WSAStartup( MAKEWORD( 1, 1 ), &net.winsockdata ))
|
||||
{
|
||||
Con_DPrintf( S_ERROR "network initialization failed.\n" );
|
||||
return;
|
||||
|
@ -2104,7 +2145,7 @@ void NET_Shutdown( void )
|
|||
|
||||
NET_ClearLagData( true, true );
|
||||
|
||||
NET_Config( false );
|
||||
NET_Config( false, false );
|
||||
#if XASH_WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
@ -2222,7 +2263,7 @@ static void HTTP_FreeFile( httpfile_t *file, qboolean error )
|
|||
if( error )
|
||||
{
|
||||
// Switch to next fastdl server if present
|
||||
if( file->server && ( file->state > HTTP_QUEUE ) && (file->state != HTTP_FREE ) )
|
||||
if( file->server && ( file->state > HTTP_QUEUE ) && ( file->state != HTTP_FREE ))
|
||||
{
|
||||
file->server = file->server->next;
|
||||
file->state = HTTP_QUEUE; // Reset download state, HTTP_Run() will open file again
|
||||
|
@ -2316,7 +2357,7 @@ static qboolean HTTP_ProcessStream( httpfile_t *curfile )
|
|||
return false;
|
||||
}
|
||||
|
||||
while( ( res = recv( curfile->socket, buf, BUFSIZ - curfile->header_size, 0 ) ) > 0) // if we got there, we are receiving data
|
||||
while( ( res = recv( curfile->socket, buf, BUFSIZ - curfile->header_size, 0 )) > 0) // if we got there, we are receiving data
|
||||
{
|
||||
curfile->blocktime = 0;
|
||||
|
||||
|
@ -2333,7 +2374,7 @@ static qboolean HTTP_ProcessStream( httpfile_t *curfile )
|
|||
|
||||
Con_Reportf( "HTTP: Got response!\n" );
|
||||
|
||||
if( !Q_strstr( curfile->buf, "200 OK" ) )
|
||||
if( !Q_strstr( curfile->buf, "200 OK" ))
|
||||
{
|
||||
*begin = 0; // cut string to print out response
|
||||
begin = Q_strchr( curfile->buf, '\r' );
|
||||
|
@ -2355,7 +2396,7 @@ static qboolean HTTP_ProcessStream( httpfile_t *curfile )
|
|||
|
||||
Con_Reportf( "HTTP: File size is %d\n", size );
|
||||
|
||||
if( ( curfile->size != -1 ) && ( curfile->size != size ) ) // check size if specified, not used
|
||||
if( ( curfile->size != -1 ) && ( curfile->size != size )) // check size if specified, not used
|
||||
Con_Reportf( S_WARN "Server reports wrong file size!\n" );
|
||||
|
||||
curfile->size = size;
|
||||
|
@ -2525,7 +2566,7 @@ void HTTP_Run( void )
|
|||
|
||||
if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished
|
||||
{
|
||||
res = connect( curfile->socket, (struct sockaddr*)&addr, sizeof( addr ) );
|
||||
res = connect( curfile->socket, (struct sockaddr*)&addr, sizeof( addr ));
|
||||
|
||||
if( res )
|
||||
{
|
||||
|
@ -2533,7 +2574,7 @@ void HTTP_Run( void )
|
|||
curfile->state = HTTP_CONNECTED;
|
||||
else
|
||||
{
|
||||
Con_Printf( S_ERROR "cannot connect to server: %s\n", NET_ErrorString( ) );
|
||||
Con_Printf( S_ERROR "cannot connect to server: %s\n", NET_ErrorString( ));
|
||||
HTTP_FreeFile( curfile, true ); // Cannot connect
|
||||
break;
|
||||
}
|
||||
|
@ -2567,7 +2608,7 @@ void HTTP_Run( void )
|
|||
{
|
||||
if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAENOTCONN )
|
||||
{
|
||||
Con_Printf( S_ERROR "failed to send request: %s\n", NET_ErrorString() );
|
||||
Con_Printf( S_ERROR "failed to send request: %s\n", NET_ErrorString( ));
|
||||
HTTP_FreeFile( curfile, true );
|
||||
wait = true;
|
||||
break;
|
||||
|
@ -2596,11 +2637,11 @@ void HTTP_Run( void )
|
|||
continue;
|
||||
|
||||
Con_Reportf( "HTTP: Request sent!\n");
|
||||
memset( curfile->buf, 0, sizeof( curfile->buf ) );
|
||||
memset( curfile->buf, 0, sizeof( curfile->buf ));
|
||||
curfile->state = HTTP_REQUEST_SENT;
|
||||
}
|
||||
|
||||
if( !HTTP_ProcessStream( curfile ) )
|
||||
if( !HTTP_ProcessStream( curfile ))
|
||||
break;
|
||||
|
||||
if( curfile->size > 0 )
|
||||
|
@ -2614,8 +2655,8 @@ void HTTP_Run( void )
|
|||
HTTP_FreeFile( curfile, false ); // success
|
||||
break;
|
||||
}
|
||||
else if( (WSAGetLastError() != WSAEWOULDBLOCK) && (WSAGetLastError() != WSAEINPROGRESS) )
|
||||
Con_Reportf( "problem downloading %s:\n%s\n", curfile->path, NET_ErrorString() );
|
||||
else if(( WSAGetLastError( ) != WSAEWOULDBLOCK ) && ( WSAGetLastError( ) != WSAEINPROGRESS ))
|
||||
Con_Reportf( "problem downloading %s:\n%s\n", curfile->path, NET_ErrorString( ));
|
||||
else
|
||||
curfile->blocktime += host.frametime;
|
||||
|
||||
|
@ -2628,7 +2669,7 @@ void HTTP_Run( void )
|
|||
}
|
||||
|
||||
// update progress
|
||||
if( !Host_IsDedicated() )
|
||||
if( !Host_IsDedicated() && iProgressCount != 0 )
|
||||
Cvar_SetValue( "scr_download", flProgress/iProgressCount * 100 );
|
||||
|
||||
HTTP_AutoClean();
|
||||
|
@ -2643,14 +2684,14 @@ Add new download to end of queue
|
|||
*/
|
||||
void HTTP_AddDownload( const char *path, int size, qboolean process )
|
||||
{
|
||||
httpfile_t *httpfile = Z_Calloc( sizeof( httpfile_t ) );
|
||||
httpfile_t *httpfile = Z_Calloc( sizeof( httpfile_t ));
|
||||
|
||||
Con_Reportf( "File %s queued to download\n", path );
|
||||
|
||||
httpfile->size = size;
|
||||
httpfile->downloaded = 0;
|
||||
httpfile->socket = -1;
|
||||
Q_strncpy ( httpfile->path, path, sizeof( httpfile->path ) );
|
||||
Q_strncpy ( httpfile->path, path, sizeof( httpfile->path ));
|
||||
|
||||
if( http.last_file )
|
||||
{
|
||||
|
@ -2707,12 +2748,12 @@ static httpserver_t *HTTP_ParseURL( const char *url )
|
|||
return NULL;
|
||||
|
||||
url += 7;
|
||||
server = Z_Calloc( sizeof( httpserver_t ) );
|
||||
server = Z_Calloc( sizeof( httpserver_t ));
|
||||
i = 0;
|
||||
|
||||
while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ))
|
||||
{
|
||||
if( i > sizeof( server->host ) )
|
||||
if( i > sizeof( server->host ))
|
||||
return NULL;
|
||||
|
||||
server->host[i++] = *url++;
|
||||
|
@ -2724,7 +2765,7 @@ static httpserver_t *HTTP_ParseURL( const char *url )
|
|||
{
|
||||
server->port = Q_atoi( ++url );
|
||||
|
||||
while( *url && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
while( *url && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ))
|
||||
url++;
|
||||
}
|
||||
else
|
||||
|
@ -2732,9 +2773,9 @@ static httpserver_t *HTTP_ParseURL( const char *url )
|
|||
|
||||
i = 0;
|
||||
|
||||
while( *url && ( *url != '\r' ) && ( *url != '\n' ) )
|
||||
while( *url && ( *url != '\r' ) && ( *url != '\n' ))
|
||||
{
|
||||
if( i > sizeof( server->path ) )
|
||||
if( i > sizeof( server->path ))
|
||||
return NULL;
|
||||
|
||||
server->path[i++] = *url++;
|
||||
|
@ -2776,7 +2817,7 @@ static void HTTP_AddCustomServer_f( void )
|
|||
{
|
||||
if( Cmd_Argc() == 2 )
|
||||
{
|
||||
HTTP_AddCustomServer( Cmd_Argv( 1 ) );
|
||||
HTTP_AddCustomServer( Cmd_Argv( 1 ));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2907,7 +2948,7 @@ void HTTP_Init( void )
|
|||
|
||||
if( serverfile )
|
||||
{
|
||||
while( ( line = COM_ParseFile( line, token, sizeof( token ) ) ) )
|
||||
while(( line = COM_ParseFile( line, token, sizeof( token ))))
|
||||
{
|
||||
httpserver_t *server = HTTP_ParseURL( token );
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ typedef enum
|
|||
|
||||
|
||||
#if !XASH_LOW_MEMORY
|
||||
#define MAX_INIT_MSG 0x20000 // max length of possible message
|
||||
#define MAX_INIT_MSG 0x30000 // max length of possible message
|
||||
#else
|
||||
#define MAX_INIT_MSG 0x8000
|
||||
#define MAX_INIT_MSG 0x8000
|
||||
#endif
|
||||
// net packets type
|
||||
#define NET_HEADER_OUTOFBANDPACKET -1
|
||||
|
@ -51,7 +51,7 @@ void NET_Shutdown( void );
|
|||
void NET_Sleep( int msec );
|
||||
qboolean NET_IsActive( void );
|
||||
qboolean NET_IsConfigured( void );
|
||||
void NET_Config( qboolean net_enable );
|
||||
void NET_Config( qboolean net_enable, qboolean changeport );
|
||||
qboolean NET_IsLocalAddress( netadr_t adr );
|
||||
const char *NET_AdrToString( const netadr_t a );
|
||||
const char *NET_BaseAdrToString( const netadr_t a );
|
||||
|
|
|
@ -40,6 +40,22 @@ int PM_TruePointContents( playermove_t *pmove, const vec3_t p );
|
|||
int PM_PointContents( playermove_t *pmove, const vec3_t p );
|
||||
void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent );
|
||||
|
||||
static inline void PM_InitTrace( trace_t *trace, const vec3_t end )
|
||||
{
|
||||
memset( trace, 0, sizeof( *trace ));
|
||||
VectorCopy( end, trace->endpos );
|
||||
trace->allsolid = true;
|
||||
trace->fraction = 1.0f;
|
||||
}
|
||||
|
||||
static inline void PM_InitPMTrace( pmtrace_t *trace, const vec3_t end )
|
||||
{
|
||||
memset( trace, 0, sizeof( *trace ));
|
||||
VectorCopy( end, trace->endpos );
|
||||
trace->allsolid = true;
|
||||
trace->fraction = 1.0f;
|
||||
}
|
||||
|
||||
//
|
||||
// pm_surface.c
|
||||
//
|
||||
|
|
|
@ -111,9 +111,17 @@ hull_t *PM_HullForBox( const vec3_t mins, const vec3_t maxs )
|
|||
|
||||
void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent )
|
||||
{
|
||||
memcpy( out, in, 48 ); // matched
|
||||
out->allsolid = in->allsolid;
|
||||
out->startsolid = in->startsolid;
|
||||
out->inopen = in->inopen;
|
||||
out->inwater = in->inwater;
|
||||
out->fraction = in->fraction;
|
||||
out->plane.dist = in->plane.dist;
|
||||
out->hitgroup = in->hitgroup;
|
||||
out->ent = ent;
|
||||
|
||||
VectorCopy( in->endpos, out->endpos );
|
||||
VectorCopy( in->plane.normal, out->plane.normal );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -446,10 +454,7 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int
|
|||
VectorSubtract( end, offset, end_l );
|
||||
}
|
||||
|
||||
memset( &trace_bbox, 0, sizeof( trace_bbox ));
|
||||
VectorCopy( end, trace_bbox.endpos );
|
||||
trace_bbox.allsolid = true;
|
||||
trace_bbox.fraction = 1.0f;
|
||||
PM_InitPMTrace( &trace_bbox, end );
|
||||
|
||||
if( hullcount < 1 )
|
||||
{
|
||||
|
@ -475,10 +480,7 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int
|
|||
|
||||
for( last_hitgroup = 0, j = 0; j < hullcount; j++ )
|
||||
{
|
||||
memset( &trace_hitbox, 0, sizeof( trace_hitbox ));
|
||||
VectorCopy( end, trace_hitbox.endpos );
|
||||
trace_hitbox.allsolid = true;
|
||||
trace_hitbox.fraction = 1.0f;
|
||||
PM_InitPMTrace( &trace_hitbox, end );
|
||||
|
||||
PM_RecursiveHullCheck( &hull[j], hull[j].firstclipnode, 0, 1, start_l, end_l, &trace_hitbox );
|
||||
|
||||
|
@ -622,10 +624,7 @@ int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, p
|
|||
{
|
||||
pmtrace_t trace;
|
||||
|
||||
memset( &trace, 0, sizeof( trace ));
|
||||
VectorCopy( pos, trace.endpos );
|
||||
trace.allsolid = true;
|
||||
trace.fraction = 1.0f;
|
||||
PM_InitPMTrace( &trace, pos );
|
||||
|
||||
// run custom sweep callback
|
||||
if( pmove->server || Host_IsLocalClient( ))
|
||||
|
|
|
@ -126,31 +126,7 @@ uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath )
|
|||
return msecs;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Sound_ConvertToSigned
|
||||
|
||||
Convert unsigned data to signed
|
||||
================
|
||||
*/
|
||||
void Sound_ConvertToSigned( const byte *data, int channels, int samples )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( channels == 2 )
|
||||
{
|
||||
for( i = 0; i < samples; i++ )
|
||||
{
|
||||
((signed char *)sound.tempbuffer)[i*2+0] = (int)((byte)(data[i*2+0]) - 128);
|
||||
((signed char *)sound.tempbuffer)[i*2+1] = (int)((byte)(data[i*2+1]) - 128);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < samples; i++ )
|
||||
((signed char *)sound.tempbuffer)[i] = (int)((unsigned char)(data[i]) - 128);
|
||||
}
|
||||
}
|
||||
#define drint( v ) (int)( v + 0.5 )
|
||||
|
||||
/*
|
||||
================
|
||||
|
@ -161,13 +137,15 @@ We need convert sound to signed even if nothing to resample
|
|||
*/
|
||||
qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int outrate, int outwidth )
|
||||
{
|
||||
float stepscale;
|
||||
int outcount, srcsample;
|
||||
int i, sample, sample2, samplefrac, fracstep;
|
||||
byte *data;
|
||||
double stepscale, j;
|
||||
int outcount;
|
||||
int i;
|
||||
qboolean handled = false;
|
||||
|
||||
data = sc->buffer;
|
||||
stepscale = (float)inrate / outrate; // this is usually 0.5, 1, or 2
|
||||
if( inrate == outrate && inwidth == outwidth )
|
||||
return false;
|
||||
|
||||
stepscale = (double)inrate / outrate; // this is usually 0.5, 1, or 2
|
||||
outcount = sc->samples / stepscale;
|
||||
sc->size = outcount * outwidth * sc->channels;
|
||||
|
||||
|
@ -177,69 +155,112 @@ qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int out
|
|||
if( sc->loopStart != -1 )
|
||||
sc->loopStart = sc->loopStart / stepscale;
|
||||
|
||||
// resample / decimate to the current source rate
|
||||
if( stepscale == 1.0f && inwidth == 1 && outwidth == 1 )
|
||||
if( inrate == outrate )
|
||||
{
|
||||
Sound_ConvertToSigned( data, sc->channels, outcount );
|
||||
if( inwidth == 1 && outwidth == 2 ) // S8 to S16
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int16_t*)sound.tempbuffer)[i] = ((int8_t *)sc->buffer)[i] * 256;
|
||||
handled = true;
|
||||
}
|
||||
else if( inwidth == 2 && outwidth == 1 ) // S16 to S8
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int8_t*)sound.tempbuffer)[i] = ((int16_t *)sc->buffer)[i] / 256;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else // resample case
|
||||
{
|
||||
if( inwidth == 1 )
|
||||
{
|
||||
int8_t *data = (int8_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
|
||||
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int8_t*)sound.tempbuffer)[i] = data[(int)j];
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if( outwidth == 2 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] * 256;
|
||||
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] * 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int16_t*)sound.tempbuffer)[i] = data[(int)j] * 256;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if( inwidth == 2 )
|
||||
{
|
||||
int16_t *data = (int16_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] / 256;
|
||||
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] / 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int8_t*)sound.tempbuffer)[i] = data[(int)j] / 256;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if( outwidth == 2 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
|
||||
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int16_t*)sound.tempbuffer)[i] = data[(int)j];
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( handled )
|
||||
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
else
|
||||
{
|
||||
// general case
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
|
||||
if( inwidth == 2 )
|
||||
{
|
||||
sample = ((short *)data)[srcsample*2+0];
|
||||
sample2 = ((short *)data)[srcsample*2+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
sample = (int)((char)(data[srcsample*2+0])) << 8;
|
||||
sample2 = (int)((char)(data[srcsample*2+1])) << 8;
|
||||
}
|
||||
|
||||
if( outwidth == 2 )
|
||||
{
|
||||
((short *)sound.tempbuffer)[i*2+0] = sample;
|
||||
((short *)sound.tempbuffer)[i*2+1] = sample2;
|
||||
}
|
||||
else
|
||||
{
|
||||
((signed char *)sound.tempbuffer)[i*2+0] = sample >> 8;
|
||||
((signed char *)sound.tempbuffer)[i*2+1] = sample2 >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
|
||||
if( inwidth == 2 ) sample = ((short *)data)[srcsample];
|
||||
else sample = (int)( (char)(data[srcsample])) << 8;
|
||||
|
||||
if( outwidth == 2 ) ((short *)sound.tempbuffer)[i] = sample;
|
||||
else ((signed char *)sound.tempbuffer)[i] = sample >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
Con_Reportf( "Sound_Resample: from[%d bit %d kHz] to [%d bit %d kHz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
}
|
||||
Con_Reportf( S_ERROR "Sound_Resample: unsupported from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
|
||||
sc->rate = outrate;
|
||||
sc->width = outwidth;
|
||||
|
||||
return true;
|
||||
return handled;
|
||||
}
|
||||
|
||||
qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags )
|
||||
|
|
|
@ -14,17 +14,23 @@ GNU General Public License for more details.
|
|||
*/
|
||||
|
||||
#include "common.h"
|
||||
#if XASH_ANDROID
|
||||
#if XASH_WIN32
|
||||
#define STDOUT_FILENO 1
|
||||
#include <io.h>
|
||||
#elif XASH_ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM
|
||||
#define XASH_COLORIZE_CONSOLE
|
||||
// do not waste precious CPU cycles on mobiles or low memory devices
|
||||
#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
|
||||
#define XASH_COLORIZE_CONSOLE true
|
||||
// use with caution, running engine in Qt Creator may cause a freeze in read() call
|
||||
// I was never encountered this bug anywhere else, so still enable by default
|
||||
// #define XASH_USE_SELECT 1
|
||||
#else
|
||||
#define XASH_COLORIZE_CONSOLE false
|
||||
#endif
|
||||
|
||||
#if XASH_USE_SELECT
|
||||
|
@ -129,16 +135,18 @@ void Sys_InitLog( void )
|
|||
if( s_ld.log_active )
|
||||
{
|
||||
s_ld.logfile = fopen( s_ld.log_path, mode );
|
||||
if( !s_ld.logfile )
|
||||
|
||||
if ( !s_ld.logfile )
|
||||
{
|
||||
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL ));
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
}
|
||||
|
||||
s_ld.logfileno = fileno( s_ld.logfile );
|
||||
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL ) );
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,53 +184,92 @@ void Sys_CloseLog( void )
|
|||
}
|
||||
}
|
||||
|
||||
static void Sys_PrintColorized( const char *logtime, const char *msg )
|
||||
#if XASH_COLORIZE_CONSOLE == true
|
||||
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
{
|
||||
char colored[4096];
|
||||
int len = 0;
|
||||
|
||||
while( *msg && ( len < 4090 ) )
|
||||
static const char *q3ToAnsi[ 8 ] =
|
||||
{
|
||||
static char q3ToAnsi[ 8 ] =
|
||||
{
|
||||
'0', // COLOR_BLACK
|
||||
'1', // COLOR_RED
|
||||
'2', // COLOR_GREEN
|
||||
'3', // COLOR_YELLOW
|
||||
'4', // COLOR_BLUE
|
||||
'6', // COLOR_CYAN
|
||||
'5', // COLOR_MAGENTA
|
||||
0 // COLOR_WHITE
|
||||
};
|
||||
"\033[30m", // COLOR_BLACK
|
||||
"\033[31m", // COLOR_RED
|
||||
"\033[32m", // COLOR_GREEN
|
||||
"\033[33m", // COLOR_YELLOW
|
||||
"\033[34m", // COLOR_BLUE
|
||||
"\033[36m", // COLOR_CYAN
|
||||
"\033[35m", // COLOR_MAGENTA
|
||||
"\033[0m", // COLOR_WHITE
|
||||
};
|
||||
const char *esc = q3ToAnsi[c];
|
||||
|
||||
if( IsColorString( msg ) )
|
||||
{
|
||||
int color;
|
||||
if( c == 7 )
|
||||
write( fd, esc, 4 );
|
||||
else write( fd, esc, 5 );
|
||||
}
|
||||
#else
|
||||
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) {}
|
||||
#endif
|
||||
|
||||
msg++;
|
||||
color = q3ToAnsi[ *msg++ % 8 ];
|
||||
colored[len++] = '\033';
|
||||
colored[len++] = '[';
|
||||
if( color )
|
||||
{
|
||||
colored[len++] = '3';
|
||||
colored[len++] = color;
|
||||
}
|
||||
else
|
||||
colored[len++] = '0';
|
||||
colored[len++] = 'm';
|
||||
static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg, const qboolean colorize )
|
||||
{
|
||||
const char *p = msg;
|
||||
|
||||
write( fd, logtime, Q_strlen( logtime ) );
|
||||
|
||||
while( p && *p )
|
||||
{
|
||||
p = Q_strchr( msg, '^' );
|
||||
|
||||
if( p == NULL )
|
||||
{
|
||||
write( fd, msg, Q_strlen( msg ));
|
||||
break;
|
||||
}
|
||||
else if( IsColorString( p ))
|
||||
{
|
||||
if( p != msg )
|
||||
write( fd, msg, p - msg );
|
||||
msg = p + 2;
|
||||
|
||||
if( colorize )
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, ColorIndex( p[1] ));
|
||||
}
|
||||
else
|
||||
colored[len++] = *msg++;
|
||||
{
|
||||
write( fd, msg, p - msg + 1 );
|
||||
msg = p + 1;
|
||||
}
|
||||
}
|
||||
colored[len] = 0;
|
||||
|
||||
printf( "\033[34m%s\033[0m%s\033[0m", logtime, colored );
|
||||
// flush the color
|
||||
if( colorize )
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, 7 );
|
||||
}
|
||||
|
||||
static void Sys_PrintStdout( const char *logtime, const char *msg )
|
||||
{
|
||||
#if XASH_MOBILE_PLATFORM
|
||||
static char buf[MAX_PRINT_MSG];
|
||||
|
||||
// strip color codes
|
||||
COM_StripColors( msg, buf );
|
||||
|
||||
// platform-specific output
|
||||
#if XASH_ANDROID && !XASH_DEDICATED
|
||||
__android_log_write( ANDROID_LOG_DEBUG, "Xash", buf );
|
||||
#endif // XASH_ANDROID && !XASH_DEDICATED
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
void IOS_Log( const char * );
|
||||
IOS_Log( buf );
|
||||
#endif // TARGET_OS_IOS
|
||||
#elif !XASH_WIN32 // Wcon does the job
|
||||
Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE );
|
||||
Sys_FlushStdout();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sys_PrintLog( const char *pMsg )
|
||||
{
|
||||
time_t crt_time;
|
||||
time_t crt_time;
|
||||
const struct tm *crt_tm;
|
||||
char logtime[32] = "";
|
||||
static char lastchar;
|
||||
|
@ -230,39 +277,26 @@ void Sys_PrintLog( const char *pMsg )
|
|||
time( &crt_time );
|
||||
crt_tm = localtime( &crt_time );
|
||||
|
||||
// platform-specific output
|
||||
#if XASH_ANDROID && !XASH_DEDICATED
|
||||
__android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", pMsg );
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
void IOS_Log(const char*);
|
||||
IOS_Log(pMsg);
|
||||
#endif
|
||||
|
||||
if( !lastchar || lastchar == '\n')
|
||||
strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time
|
||||
|
||||
// spew to stdout, except mobiles
|
||||
#if !XASH_MOBILE_PLATFORM
|
||||
#ifdef XASH_COLORIZE_CONSOLE
|
||||
Sys_PrintColorized( logtime, pMsg );
|
||||
#else
|
||||
printf( "%s %s", logtime, pMsg );
|
||||
#endif
|
||||
Sys_FlushStdout();
|
||||
#endif
|
||||
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = pMsg[strlen(pMsg)-1];
|
||||
// spew to stdout
|
||||
Sys_PrintStdout( logtime, pMsg );
|
||||
|
||||
if( !s_ld.logfile )
|
||||
{
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = pMsg[Q_strlen( pMsg ) - 1];
|
||||
return;
|
||||
}
|
||||
|
||||
if( !lastchar || lastchar == '\n')
|
||||
strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S]", crt_tm ); //full time
|
||||
strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
|
||||
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = pMsg[Q_strlen( pMsg ) - 1];
|
||||
|
||||
fprintf( s_ld.logfile, "%s %s", logtime, pMsg );
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ GNU General Public License for more details.
|
|||
#include "xash3d_mathlib.h"
|
||||
#include "platform/platform.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef XASH_SDL
|
||||
#include <SDL.h>
|
||||
|
@ -32,10 +33,16 @@ GNU General Public License for more details.
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if XASH_WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include "menu_int.h" // _UPDATE_PAGE macro
|
||||
|
||||
#include "library.h"
|
||||
#include "whereami.h"
|
||||
|
||||
qboolean error_on_exit = false; // arg for exit();
|
||||
#define DEBUG_BREAK
|
||||
|
||||
/*
|
||||
================
|
||||
|
@ -46,23 +53,28 @@ double GAME_EXPORT Sys_DoubleTime( void )
|
|||
{
|
||||
return Platform_DoubleTime();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Sys_DebugBreak
|
||||
================
|
||||
*/
|
||||
void Sys_DebugBreak( void )
|
||||
{
|
||||
#if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT )
|
||||
#undef DEBUG_BREAK
|
||||
qboolean Sys_DebuggerPresent( void ); // see sys_linux.c
|
||||
#if XASH_MSVC
|
||||
#define DEBUG_BREAK \
|
||||
if( Sys_DebuggerPresent() ) \
|
||||
_asm{ int 3 }
|
||||
#elif XASH_X86
|
||||
#define DEBUG_BREAK \
|
||||
if( Sys_DebuggerPresent() ) \
|
||||
asm volatile("int $3;")
|
||||
#else
|
||||
#define DEBUG_BREAK \
|
||||
if( Sys_DebuggerPresent() ) \
|
||||
raise( SIGINT )
|
||||
#endif
|
||||
#if XASH_MSVC
|
||||
if( Sys_DebuggerPresent() )
|
||||
_asm { int 3 }
|
||||
#elif XASH_X86
|
||||
if( Sys_DebuggerPresent() )
|
||||
asm volatile( "int $3;" );
|
||||
#else
|
||||
if( Sys_DebuggerPresent() )
|
||||
raise( SIGINT );
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
/*
|
||||
================
|
||||
|
@ -272,7 +284,8 @@ qboolean Sys_LoadLibrary( dll_info_t *dll )
|
|||
*func->func = NULL;
|
||||
}
|
||||
|
||||
if( !dll->link ) dll->link = LoadLibrary ( dll->name ); // environment pathes
|
||||
if( !dll->link )
|
||||
dll->link = COM_LoadLibrary( dll->name, false, true ); // environment pathes
|
||||
|
||||
// no DLL found
|
||||
if( !dll->link )
|
||||
|
@ -307,7 +320,7 @@ void* Sys_GetProcAddress( dll_info_t *dll, const char* name )
|
|||
if( !dll || !dll->link ) // invalid desc
|
||||
return NULL;
|
||||
|
||||
return (void *)GetProcAddress( dll->link, name );
|
||||
return (void *)COM_GetProcAddress( dll->link, name );
|
||||
}
|
||||
|
||||
qboolean Sys_FreeLibrary( dll_info_t *dll )
|
||||
|
@ -324,7 +337,7 @@ qboolean Sys_FreeLibrary( dll_info_t *dll )
|
|||
}
|
||||
else Con_Reportf( "Sys_FreeLibrary: Unloading %s\n", dll->name );
|
||||
|
||||
FreeLibrary( dll->link );
|
||||
COM_FreeLibrary( dll->link );
|
||||
dll->link = NULL;
|
||||
|
||||
return true;
|
||||
|
@ -368,12 +381,14 @@ void Sys_Warn( const char *format, ... )
|
|||
va_list argptr;
|
||||
char text[MAX_PRINT_MSG];
|
||||
|
||||
DEBUG_BREAK;
|
||||
|
||||
va_start( argptr, format );
|
||||
Q_vsnprintf( text, MAX_PRINT_MSG, format, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
Sys_DebugBreak();
|
||||
|
||||
Msg( "Sys_Warn: %s\n", text );
|
||||
|
||||
if( !Host_IsDedicated() ) // dedicated server should not hang on messagebox
|
||||
MSGBOX(text);
|
||||
}
|
||||
|
@ -391,12 +406,14 @@ void Sys_Error( const char *error, ... )
|
|||
va_list argptr;
|
||||
char text[MAX_PRINT_MSG];
|
||||
|
||||
DEBUG_BREAK;
|
||||
// enable cursor before debugger call
|
||||
if( !Host_IsDedicated( ))
|
||||
Platform_SetCursorType( dc_arrow );
|
||||
|
||||
if( host.status == HOST_ERR_FATAL )
|
||||
return; // don't multiple executes
|
||||
|
||||
// make sure what console received last message
|
||||
// make sure that console received last message
|
||||
if( host.change_game ) Sys_Sleep( 200 );
|
||||
|
||||
error_on_exit = true;
|
||||
|
@ -405,6 +422,8 @@ void Sys_Error( const char *error, ... )
|
|||
Q_vsnprintf( text, MAX_PRINT_MSG, error, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
Sys_DebugBreak();
|
||||
|
||||
SV_SysError( text );
|
||||
|
||||
if( !Host_IsDedicated() )
|
||||
|
@ -412,9 +431,12 @@ void Sys_Error( const char *error, ... )
|
|||
#if XASH_SDL == 2
|
||||
if( host.hWnd ) SDL_HideWindow( host.hWnd );
|
||||
#endif
|
||||
#if XASH_WIN32
|
||||
Wcon_ShowConsole( false );
|
||||
#endif
|
||||
MSGBOX( text );
|
||||
}
|
||||
|
||||
if( host_developer.value )
|
||||
else
|
||||
{
|
||||
#if XASH_WIN32
|
||||
Wcon_ShowConsole( true );
|
||||
|
@ -423,14 +445,7 @@ void Sys_Error( const char *error, ... )
|
|||
Sys_Print( text ); // print error message
|
||||
Sys_WaitForQuit();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if XASH_WIN32
|
||||
Wcon_ShowConsole( false );
|
||||
#endif
|
||||
MSGBOX( text );
|
||||
}
|
||||
|
||||
|
||||
Sys_Quit();
|
||||
}
|
||||
|
||||
|
@ -536,3 +551,67 @@ void Sys_Print( const char *pMsg )
|
|||
|
||||
Rcon_Print( pMsg );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_ChangeGame
|
||||
|
||||
This is a special function
|
||||
|
||||
Here we restart engine with new -game parameter
|
||||
but since engine will be unloaded during this call
|
||||
it explicitly doesn't use internal allocation or string copy utils
|
||||
==================
|
||||
*/
|
||||
qboolean Sys_NewInstance( const char *gamedir )
|
||||
{
|
||||
int i = 0;
|
||||
qboolean replacedArg = false;
|
||||
size_t exelen;
|
||||
char *exe, **newargs;
|
||||
|
||||
// don't use engine allocation utils here
|
||||
// they will be freed after Host_Shutdown
|
||||
newargs = calloc( host.argc + 4, sizeof( *newargs ));
|
||||
while( i < host.argc )
|
||||
{
|
||||
newargs[i] = strdup( host.argv[i] );
|
||||
|
||||
// replace existing -game argument
|
||||
if( !Q_stricmp( newargs[i], "-game" ))
|
||||
{
|
||||
newargs[i + 1] = strdup( gamedir );
|
||||
replacedArg = true;
|
||||
i += 2;
|
||||
}
|
||||
else i++;
|
||||
}
|
||||
|
||||
if( !replacedArg )
|
||||
{
|
||||
newargs[i++] = strdup( "-game" );
|
||||
newargs[i++] = strdup( gamedir );
|
||||
}
|
||||
|
||||
newargs[i++] = strdup( "-changegame" );
|
||||
newargs[i] = NULL;
|
||||
|
||||
exelen = wai_getExecutablePath( NULL, 0, NULL );
|
||||
exe = malloc( exelen + 1 );
|
||||
wai_getExecutablePath( exe, exelen, NULL );
|
||||
exe[exelen] = 0;
|
||||
|
||||
Host_Shutdown();
|
||||
|
||||
execv( exe, newargs );
|
||||
|
||||
// if execv returned, it's probably an error
|
||||
printf( "execv failed: %s", strerror( errno ));
|
||||
|
||||
for( ; i >= 0; i-- )
|
||||
free( newargs[i] );
|
||||
free( newargs );
|
||||
free( exe );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ char *Sys_GetClipboardData( void );
|
|||
const char *Sys_GetCurrentUser( void );
|
||||
int Sys_CheckParm( const char *parm );
|
||||
void Sys_Warn( const char *format, ... ) _format( 1 );
|
||||
void Sys_Error( const char *error, ... ) _format( 1 ) NORETURN;
|
||||
void Sys_Error( const char *error, ... ) _format( 1 );
|
||||
qboolean Sys_LoadLibrary( dll_info_t *dll );
|
||||
void* Sys_GetProcAddress( dll_info_t *dll, const char* name );
|
||||
qboolean Sys_FreeLibrary( dll_info_t *dll );
|
||||
|
@ -59,6 +59,7 @@ void Sys_ParseCommandLine( int argc, char **argv );
|
|||
void Sys_MergeCommandLine( void );
|
||||
void Sys_SetupCrashHandler( void );
|
||||
void Sys_RestoreCrashHandler( void );
|
||||
void Sys_DebugBreak( void );
|
||||
#define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out ))
|
||||
qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size );
|
||||
qboolean Sys_GetIntFromCmdLine( const char *parm, int *out );
|
||||
|
@ -68,6 +69,7 @@ void Sys_PrintLog( const char *pMsg );
|
|||
void Sys_InitLog( void );
|
||||
void Sys_CloseLog( void );
|
||||
void Sys_Quit( void ) NORETURN;
|
||||
qboolean Sys_NewInstance( const char *gamedir );
|
||||
|
||||
//
|
||||
// sys_con.c
|
||||
|
|
806
engine/common/whereami.c
Normal file
806
engine/common/whereami.c
Normal file
|
@ -0,0 +1,806 @@
|
|||
// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
|
||||
// without any warranty.
|
||||
// by Gregory Pakosz (@gpakosz)
|
||||
// https://github.com/gpakosz/whereami
|
||||
|
||||
// in case you want to #include "whereami.c" in a larger compilation unit
|
||||
#if !defined(WHEREAMI_H)
|
||||
#include <whereami.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
#undef _DEFAULT_SOURCE
|
||||
#define _DEFAULT_SOURCE
|
||||
#elif defined(__APPLE__)
|
||||
#undef _DARWIN_C_SOURCE
|
||||
#define _DARWIN_C_SOURCE
|
||||
#define _DARWIN_BETTER_REALPATH
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_MALLOC)
|
||||
#define WAI_MALLOC(size) malloc(size)
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_FREE)
|
||||
#define WAI_FREE(p) free(p)
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_REALLOC)
|
||||
#define WAI_REALLOC(p, size) realloc(p, size)
|
||||
#endif
|
||||
|
||||
#ifndef WAI_NOINLINE
|
||||
#if defined(_MSC_VER)
|
||||
#define WAI_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#define WAI_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#error unsupported compiler
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define WAI_RETURN_ADDRESS() _ReturnAddress()
|
||||
#elif defined(__GNUC__)
|
||||
#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
#else
|
||||
#error unsupported compiler
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push, 3)
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
wchar_t buffer1[MAX_PATH];
|
||||
wchar_t buffer2[MAX_PATH];
|
||||
wchar_t* path = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
DWORD size;
|
||||
int length_, length__;
|
||||
|
||||
size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
|
||||
|
||||
if (size == 0)
|
||||
break;
|
||||
else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
|
||||
{
|
||||
DWORD size_ = size;
|
||||
do
|
||||
{
|
||||
wchar_t* path_;
|
||||
|
||||
path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
|
||||
if (!path_)
|
||||
break;
|
||||
size_ *= 2;
|
||||
path = path_;
|
||||
size = GetModuleFileNameW(module, path, size_);
|
||||
}
|
||||
while (size == size_);
|
||||
|
||||
if (size == size_)
|
||||
break;
|
||||
}
|
||||
else
|
||||
path = buffer1;
|
||||
|
||||
if (!_wfullpath(buffer2, path, MAX_PATH))
|
||||
break;
|
||||
length_ = (int)wcslen(buffer2);
|
||||
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
|
||||
|
||||
if (length__ == 0)
|
||||
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
|
||||
if (length__ == 0)
|
||||
break;
|
||||
|
||||
if (length__ <= capacity && dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length__ - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '\\')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
length = length__;
|
||||
}
|
||||
|
||||
if (path != buffer1)
|
||||
WAI_FREE(path);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
HMODULE module;
|
||||
int length = -1;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4054)
|
||||
#endif
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
{
|
||||
length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if !defined(WAI_PROC_SELF_EXE)
|
||||
#if defined(__sun)
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
|
||||
#else
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/exe"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
resolved = realpath(WAI_PROC_SELF_EXE, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#if !defined(WAI_PROC_SELF_MAPS_RETRY)
|
||||
#define WAI_PROC_SELF_MAPS_RETRY 5
|
||||
#endif
|
||||
|
||||
#if !defined(WAI_PROC_SELF_MAPS)
|
||||
#if defined(__sun)
|
||||
#define WAI_PROC_SELF_MAPS "/proc/self/map"
|
||||
#else
|
||||
#define WAI_PROC_SELF_MAPS "/proc/self/maps"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
int length = -1;
|
||||
FILE* maps = NULL;
|
||||
int r;
|
||||
|
||||
for (r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
|
||||
{
|
||||
maps = fopen(WAI_PROC_SELF_MAPS, "r");
|
||||
if (!maps)
|
||||
break;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
|
||||
uint64_t low, high;
|
||||
char perms[5];
|
||||
uint64_t offset;
|
||||
uint32_t major, minor;
|
||||
char path[PATH_MAX];
|
||||
uint32_t inode;
|
||||
|
||||
if (!fgets(buffer, sizeof(buffer), maps))
|
||||
break;
|
||||
|
||||
if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
|
||||
{
|
||||
uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
|
||||
if (low <= addr && addr <= high)
|
||||
{
|
||||
char* resolved;
|
||||
|
||||
resolved = realpath(path, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
if (length > 4
|
||||
&&buffer[length - 1] == 'k'
|
||||
&&buffer[length - 2] == 'p'
|
||||
&&buffer[length - 3] == 'a'
|
||||
&&buffer[length - 4] == '.')
|
||||
{
|
||||
char *begin, *p;
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
length = -1; // retry
|
||||
break;
|
||||
}
|
||||
|
||||
begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (begin == MAP_FAILED)
|
||||
{
|
||||
close(fd);
|
||||
length = -1; // retry
|
||||
break;
|
||||
}
|
||||
|
||||
p = begin + offset - 30; // minimum size of local file header
|
||||
while (p >= begin) // scan backwards
|
||||
{
|
||||
if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found
|
||||
{
|
||||
uint16_t length_ = *((uint16_t*)(p + 26));
|
||||
|
||||
if (length + 2 + length_ < (int)sizeof(buffer))
|
||||
{
|
||||
memcpy(&buffer[length], "!/", 2);
|
||||
memcpy(&buffer[length + 2], p + 30, length_);
|
||||
length += 2 + length_;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
--p;
|
||||
}
|
||||
|
||||
munmap(begin, offset);
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(maps);
|
||||
maps = NULL;
|
||||
|
||||
if (length != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* path = buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
uint32_t size = (uint32_t)sizeof(buffer1);
|
||||
if (_NSGetExecutablePath(path, &size) == -1)
|
||||
{
|
||||
path = (char*)WAI_MALLOC(size);
|
||||
if (!_NSGetExecutablePath(path, &size))
|
||||
break;
|
||||
}
|
||||
|
||||
resolved = realpath(path, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path != buffer1)
|
||||
WAI_FREE(path);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__QNXNTO__)
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if !defined(WAI_PROC_SELF_EXE)
|
||||
#define WAI_PROC_SELF_EXE "/proc/self/exefile"
|
||||
#endif
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
FILE* self_exe = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
self_exe = fopen(WAI_PROC_SELF_EXE, "r");
|
||||
if (!self_exe)
|
||||
break;
|
||||
|
||||
if (!fgets(buffer1, sizeof(buffer1), self_exe))
|
||||
break;
|
||||
|
||||
resolved = realpath(buffer1, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(self_exe);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||
defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[4096];
|
||||
char buffer2[PATH_MAX];
|
||||
char buffer3[PATH_MAX];
|
||||
char** argv = (char**)buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
|
||||
size_t size;
|
||||
|
||||
if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
if (size > sizeof(buffer1))
|
||||
{
|
||||
argv = (char**)WAI_MALLOC(size);
|
||||
if (!argv)
|
||||
break;
|
||||
}
|
||||
|
||||
if (sysctl(mib, 4, argv, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
if (strchr(argv[0], '/'))
|
||||
{
|
||||
resolved = realpath(argv[0], buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* PATH = getenv("PATH");
|
||||
if (!PATH)
|
||||
break;
|
||||
|
||||
size_t argv0_length = strlen(argv[0]);
|
||||
|
||||
const char* begin = PATH;
|
||||
while (1)
|
||||
{
|
||||
const char* separator = strchr(begin, ':');
|
||||
const char* end = separator ? separator : begin + strlen(begin);
|
||||
|
||||
if (end - begin > 0)
|
||||
{
|
||||
if (*(end -1) == '/')
|
||||
--end;
|
||||
|
||||
if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2))
|
||||
{
|
||||
memcpy(buffer2, begin, end - begin);
|
||||
buffer2[end - begin] = '/';
|
||||
memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1);
|
||||
|
||||
resolved = realpath(buffer2, buffer3);
|
||||
if (resolved)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!separator)
|
||||
break;
|
||||
|
||||
begin = ++separator;
|
||||
}
|
||||
|
||||
if (!resolved)
|
||||
break;
|
||||
}
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argv != (char**)buffer1)
|
||||
WAI_FREE(argv);
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer1[PATH_MAX];
|
||||
char buffer2[PATH_MAX];
|
||||
char* path = buffer1;
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
bool ok;
|
||||
|
||||
for (ok = false; !ok; ok = true)
|
||||
{
|
||||
#if defined(__NetBSD__)
|
||||
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
|
||||
#else
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||
#endif
|
||||
size_t size = sizeof(buffer1);
|
||||
|
||||
if (sysctl(mib, 4, path, &size, NULL, 0) != 0)
|
||||
break;
|
||||
|
||||
resolved = realpath(path, buffer2);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok ? length : -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
WAI_NOINLINE WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char* resolved = NULL;
|
||||
int length = -1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||
{
|
||||
resolved = realpath(info.dli_fname, buffer);
|
||||
if (!resolved)
|
||||
break;
|
||||
|
||||
length = (int)strlen(resolved);
|
||||
if (length <= capacity)
|
||||
{
|
||||
memcpy(out, resolved, length);
|
||||
|
||||
if (dirname_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = length - 1; i >= 0; --i)
|
||||
{
|
||||
if (out[i] == '/')
|
||||
{
|
||||
*dirname_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error unsupported platform
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
67
engine/common/whereami.h
Normal file
67
engine/common/whereami.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
|
||||
// without any warranty.
|
||||
// by Gregory Pakosz (@gpakosz)
|
||||
// https://github.com/gpakosz/whereami
|
||||
|
||||
#ifndef WHEREAMI_H
|
||||
#define WHEREAMI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef WAI_FUNCSPEC
|
||||
#define WAI_FUNCSPEC
|
||||
#endif
|
||||
#ifndef WAI_PREFIX
|
||||
#define WAI_PREFIX(function) wai_##function
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the path to the current executable.
|
||||
*
|
||||
* Usage:
|
||||
* - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
|
||||
* retrieve the length of the path
|
||||
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||
* - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
|
||||
* path
|
||||
* - add a terminal NUL character with `path[length] = '\0';`
|
||||
*
|
||||
* @param out destination buffer, optional
|
||||
* @param capacity destination buffer capacity
|
||||
* @param dirname_length optional recipient for the length of the dirname part
|
||||
* of the path.
|
||||
*
|
||||
* @return the length of the executable path on success (without a terminal NUL
|
||||
* character), otherwise `-1`
|
||||
*/
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
|
||||
|
||||
/**
|
||||
* Returns the path to the current module
|
||||
*
|
||||
* Usage:
|
||||
* - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
|
||||
* the length of the path
|
||||
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||
* - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
|
||||
* - add a terminal NUL character with `path[length] = '\0';`
|
||||
*
|
||||
* @param out destination buffer, optional
|
||||
* @param capacity destination buffer capacity
|
||||
* @param dirname_length optional recipient for the length of the dirname part
|
||||
* of the path.
|
||||
*
|
||||
* @return the length of the module path on success (without a terminal NUL
|
||||
* character), otherwise `-1`
|
||||
*/
|
||||
WAI_FUNCSPEC
|
||||
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef WHEREAMI_H
|
|
@ -91,8 +91,8 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
|
|||
pool->totalsize += size;
|
||||
|
||||
// big allocations are not clumped
|
||||
pool->realsize += sizeof( memheader_t ) + size + sizeof( int );
|
||||
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( int ));
|
||||
pool->realsize += sizeof( memheader_t ) + size + sizeof( size_t );
|
||||
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( size_t ));
|
||||
if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline );
|
||||
|
||||
mem->filename = filename;
|
||||
|
@ -162,7 +162,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline
|
|||
// memheader has been unlinked, do the actual free now
|
||||
pool->totalsize -= mem->size;
|
||||
|
||||
pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int );
|
||||
pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( size_t );
|
||||
Q_free( mem );
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ typedef int HIMAGE; // handle to a graphic
|
|||
#define PIC_NEAREST (1<<0) // disable texfilter
|
||||
#define PIC_KEEP_SOURCE (1<<1) // some images keep source
|
||||
#define PIC_NOFLIP_TGA (1<<2) // Steam background completely ignore tga attribute 0x20
|
||||
#define PIC_EXPAND_SOURCE (1<<3) // don't keep as 8-bit source, expand to RGBA
|
||||
|
||||
// flags for COM_ParseFileSafe
|
||||
#define PFILE_IGNOREBRACKET (1<<0)
|
||||
|
@ -116,7 +117,7 @@ typedef struct ui_enginefuncs_s
|
|||
int (*CL_CreateVisibleEntity)( int type, struct cl_entity_s *ent );
|
||||
|
||||
// misc handlers
|
||||
void (*pfnHostError)( const char *szFmt, ... ) _format( 1 ) NORETURN;
|
||||
void (*pfnHostError)( const char *szFmt, ... ) _format( 1 );
|
||||
int (*pfnFileExists)( const char *filename, int gamedironly );
|
||||
void (*pfnGetGameDir)( char *szGetGameDir );
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ qboolean SNDDMA_Init( void )
|
|||
}
|
||||
|
||||
Msg( "OpenSL ES audio initialized.\n" );
|
||||
|
||||
dma.backendName = "OpenSL ES";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -254,4 +254,24 @@ void SNDDMA_BeginPainting( void )
|
|||
{
|
||||
pthread_mutex_lock( &snddma_android_mutex );
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Init( void )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Activate( qboolean activate )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Lock( qboolean lock )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoiceCapture_Shutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -343,23 +343,16 @@ void IN_EvdevFrame ( void )
|
|||
{
|
||||
switch ( ev.code )
|
||||
{
|
||||
case REL_X: dx += ev.value;
|
||||
case REL_X:
|
||||
dx += ev.value;
|
||||
break;
|
||||
|
||||
case REL_Y: dy += ev.value;
|
||||
case REL_Y:
|
||||
dy += ev.value;
|
||||
break;
|
||||
|
||||
case REL_WHEEL:
|
||||
if( ev.value > 0)
|
||||
{
|
||||
Key_Event( K_MWHEELDOWN, 1 );
|
||||
Key_Event( K_MWHEELDOWN, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Key_Event( K_MWHEELUP, 1 );
|
||||
Key_Event( K_MWHEELUP, 0 );
|
||||
}
|
||||
case REL_WHEEL:
|
||||
IN_MWheelEvent( ev.value );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -367,7 +360,7 @@ void IN_EvdevFrame ( void )
|
|||
{
|
||||
int key = KeycodeFromEvdev( ev.code, ev.value );
|
||||
|
||||
if( CVAR_TO_BOOL(evdev_keydebug) )
|
||||
if( CVAR_TO_BOOL( evdev_keydebug ))
|
||||
Con_Printf( "key %d %d %d\n", ev.code, key, ev.value );
|
||||
|
||||
Key_Event( key , ev.value );
|
||||
|
|
|
@ -185,12 +185,12 @@ qboolean SNDDMA_Init( void )
|
|||
}
|
||||
|
||||
dma.buffer = Z_Malloc( samples * 2 ); //allocate pcm frame buffer
|
||||
|
||||
dma.samplepos = 0;
|
||||
|
||||
dma.samples = samples;
|
||||
dma.format.width = 2;
|
||||
dma.initialized = 1;
|
||||
dma.backendName = "ALSA";
|
||||
|
||||
snd_pcm_prepare( s_alsa.pcm_handle );
|
||||
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, 2 * s_alsa.period_size );
|
||||
snd_pcm_start( s_alsa.pcm_handle );
|
||||
|
@ -341,4 +341,24 @@ void SNDDMA_Activate( qboolean active )
|
|||
}
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Init( void )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Activate( qboolean activate )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Lock( qboolean lock )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoiceCapture_Shutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,9 +37,7 @@ double Platform_DoubleTime( void );
|
|||
void Platform_Sleep( int msec );
|
||||
void Platform_ShellExecute( const char *path, const char *parms );
|
||||
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow );
|
||||
// commented out, as this is an optional feature or maybe implemented in system API directly
|
||||
// see system.c
|
||||
// qboolean Sys_DebuggerPresent( void );
|
||||
qboolean Sys_DebuggerPresent( void ); // optional, see Sys_DebugBreak
|
||||
|
||||
#if XASH_ANDROID
|
||||
const char *Android_GetAndroidID( void );
|
||||
|
@ -162,4 +160,9 @@ void SNDDMA_Activate( qboolean active ); // pause audio
|
|||
// void SNDDMA_LockSound( void ); // unused
|
||||
// void SNDDMA_UnlockSound( void ); // unused
|
||||
|
||||
qboolean VoiceCapture_Init( void );
|
||||
void VoiceCapture_Shutdown( void );
|
||||
qboolean VoiceCapture_Activate( qboolean activate );
|
||||
qboolean VoiceCapture_Lock( qboolean lock );
|
||||
|
||||
#endif // PLATFORM_H
|
||||
|
|
|
@ -102,7 +102,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
|
|||
// try to find by linker(LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, LD_32_LIBRARY_PATH and so on...)
|
||||
if( !pHandle )
|
||||
{
|
||||
pHandle = dlopen( dllname, RTLD_LAZY );
|
||||
pHandle = dlopen( dllname, RTLD_NOW );
|
||||
if( pHandle )
|
||||
return pHandle;
|
||||
|
||||
|
@ -139,7 +139,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
|
|||
else
|
||||
#endif
|
||||
{
|
||||
if( !( hInst->hInstance = dlopen( hInst->fullPath, RTLD_LAZY ) ) )
|
||||
if( !( hInst->hInstance = dlopen( hInst->fullPath, RTLD_NOW ) ) )
|
||||
{
|
||||
COM_PushLibraryError( dlerror() );
|
||||
Mem_Free( hInst );
|
||||
|
@ -188,12 +188,7 @@ void *COM_GetProcAddress( void *hInstance, const char *name )
|
|||
|
||||
void *COM_FunctionFromName( void *hInstance, const char *pName )
|
||||
{
|
||||
void *function;
|
||||
if( !( function = COM_GetProcAddress( hInstance, pName ) ) )
|
||||
{
|
||||
Con_Reportf( S_ERROR "FunctionFromName: Can't get symbol %s: %s\n", pName, dlerror());
|
||||
}
|
||||
return function;
|
||||
return COM_GetProcAddress( hInstance, pName );
|
||||
}
|
||||
|
||||
#ifdef XASH_DYNAMIC_DLADDR
|
||||
|
|
|
@ -250,18 +250,6 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
|
|||
Key_Event( keynum, down );
|
||||
}
|
||||
|
||||
static void SDLash_MouseKey( int key, int down, int istouch )
|
||||
{
|
||||
if( CVAR_TO_BOOL( touch_emulate ) )
|
||||
{
|
||||
Touch_KeyEvent( key, down );
|
||||
}
|
||||
else if( in_mouseinitialized && !m_ignore->value && !istouch )
|
||||
{
|
||||
Key_Event( key, down );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SDLash_MouseEvent
|
||||
|
@ -270,14 +258,20 @@ SDLash_MouseEvent
|
|||
*/
|
||||
static void SDLash_MouseEvent( SDL_MouseButtonEvent button )
|
||||
{
|
||||
int down = button.state != SDL_RELEASED;
|
||||
uint mstate = 0;
|
||||
int down;
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( button.which == SDL_TOUCH_MOUSEID )
|
||||
return;
|
||||
#endif
|
||||
|
||||
if( button.state == SDL_RELEASED )
|
||||
down = 0;
|
||||
else if( button.clicks >= 2 )
|
||||
down = 2; // special state for double-click in UI
|
||||
else
|
||||
down = 1;
|
||||
|
||||
switch( button.button )
|
||||
{
|
||||
case SDL_BUTTON_LEFT:
|
||||
|
@ -297,10 +291,10 @@ static void SDLash_MouseEvent( SDL_MouseButtonEvent button )
|
|||
break;
|
||||
#if ! SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
case SDL_BUTTON_WHEELUP:
|
||||
Key_Event( K_MWHEELUP, down );
|
||||
IN_MWheelEvent( -1 );
|
||||
break;
|
||||
case SDL_BUTTON_WHEELDOWN:
|
||||
Key_Event( K_MWHEELDOWN, down );
|
||||
IN_MWheelEvent( 1 );
|
||||
break;
|
||||
#endif // ! SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
default:
|
||||
|
@ -435,9 +429,7 @@ static void SDLash_EventFilter( SDL_Event *event )
|
|||
/* Mouse events */
|
||||
case SDL_MOUSEMOTION:
|
||||
if( host.mouse_visible )
|
||||
{
|
||||
SDL_GetRelativeMouseState( NULL, NULL );
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
|
@ -478,12 +470,8 @@ static void SDLash_EventFilter( SDL_Event *event )
|
|||
break;
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
int wheelbutton = event->wheel.y < 0 ? K_MWHEELDOWN : K_MWHEELUP;
|
||||
Key_Event( wheelbutton, true );
|
||||
Key_Event( wheelbutton, false );
|
||||
IN_MWheelEvent( event->wheel.y );
|
||||
break;
|
||||
}
|
||||
|
||||
/* Touch events */
|
||||
case SDL_FINGERDOWN:
|
||||
|
@ -556,7 +544,8 @@ static void SDLash_EventFilter( SDL_Event *event )
|
|||
{
|
||||
// Swap axis to follow default axis binding:
|
||||
// LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft
|
||||
static int sdlControllerAxisToEngine[] = {
|
||||
static int sdlControllerAxisToEngine[] =
|
||||
{
|
||||
JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX,
|
||||
JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY,
|
||||
JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
|
@ -679,7 +668,7 @@ TODO: kill mouse in win32 clients too
|
|||
*/
|
||||
void Platform_PreCreateMove( void )
|
||||
{
|
||||
if( CVAR_TO_BOOL( m_ignore ) )
|
||||
if( CVAR_TO_BOOL( m_ignore ))
|
||||
{
|
||||
SDL_GetRelativeMouseState( NULL, NULL );
|
||||
SDL_ShowCursor( SDL_TRUE );
|
||||
|
|
|
@ -260,11 +260,11 @@ SDLash_InitCursors
|
|||
*/
|
||||
void SDLash_InitCursors( void )
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( cursors.initialized )
|
||||
SDLash_FreeCursors();
|
||||
|
||||
// load up all default cursors
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
cursors.cursors[dc_none] = NULL;
|
||||
cursors.cursors[dc_arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
cursors.cursors[dc_ibeam] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
|
@ -278,8 +278,8 @@ void SDLash_InitCursors( void )
|
|||
cursors.cursors[dc_sizeall] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
cursors.cursors[dc_no] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||
cursors.cursors[dc_hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
#endif
|
||||
cursors.initialized = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -314,11 +314,10 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
|
|||
{
|
||||
qboolean visible;
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( !cursors.initialized )
|
||||
return;
|
||||
|
||||
if( cls.key_dest != key_game || cl.paused )
|
||||
return;
|
||||
#endif
|
||||
|
||||
switch( type )
|
||||
{
|
||||
|
@ -331,22 +330,34 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
|
|||
break;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( CVAR_TO_BOOL( touch_emulate ))
|
||||
// never disable cursor in touch emulation mode
|
||||
if( !visible && touch_emulate.value )
|
||||
return;
|
||||
|
||||
if( visible && !host.mouse_visible )
|
||||
host.mouse_visible = visible;
|
||||
VGui_UpdateInternalCursorState( type );
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( host.mouse_visible )
|
||||
{
|
||||
SDL_SetCursor( cursors.cursors[type] );
|
||||
SDL_ShowCursor( true );
|
||||
Key_EnableTextInput( true, false );
|
||||
}
|
||||
else if( !visible && host.mouse_visible )
|
||||
else
|
||||
{
|
||||
SDL_ShowCursor( false );
|
||||
Key_EnableTextInput( false, false );
|
||||
}
|
||||
host.mouse_visible = visible;
|
||||
#else
|
||||
if( host.mouse_visible )
|
||||
{
|
||||
SDL_ShowCursor( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_ShowCursor( false );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ GNU General Public License for more details.
|
|||
#if XASH_SOUND == SOUND_SDL
|
||||
|
||||
#include "sound.h"
|
||||
#include "voice.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
|
@ -31,9 +32,11 @@ GNU General Public License for more details.
|
|||
#define SDL_OpenAudioDevice( a, b, c, d, e ) SDL_OpenAudio( ( c ), ( d ) )
|
||||
#define SDL_CloseAudioDevice( a ) SDL_CloseAudio()
|
||||
#define SDL_PauseAudioDevice( a, b ) SDL_PauseAudio( ( b ) )
|
||||
#define SDLash_IsAudioError( x ) ( x ) != 0
|
||||
#define SDL_LockAudioDevice( x ) SDL_LockAudio()
|
||||
#define SDL_UnlockAudioDevice( x ) SDL_UnlockAudio()
|
||||
#define SDLash_IsAudioError( x ) (( x ) != 0)
|
||||
#else
|
||||
#define SDLash_IsAudioError( x ) ( x ) == 0
|
||||
#define SDLash_IsAudioError( x ) (( x ) == 0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -43,6 +46,9 @@ so it can unlock and free the data block after it has been played.
|
|||
=======================================================================
|
||||
*/
|
||||
static int sdl_dev;
|
||||
static SDL_AudioDeviceID in_dev = 0;
|
||||
static SDL_AudioFormat sdl_format;
|
||||
static char sdl_backend_name[32];
|
||||
|
||||
//static qboolean snd_firsttime = true;
|
||||
//static qboolean primary_format_set;
|
||||
|
@ -133,9 +139,12 @@ qboolean SNDDMA_Init( void )
|
|||
dma.buffer = Z_Calloc( dma.samples * 2 );
|
||||
dma.samplepos = 0;
|
||||
|
||||
Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq );
|
||||
sdl_format = obtained.format;
|
||||
|
||||
Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq );
|
||||
Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL (%s)", SDL_GetCurrentAudioDriver( ));
|
||||
dma.initialized = true;
|
||||
dma.backendName = sdl_backend_name;
|
||||
|
||||
SNDDMA_Activate( true );
|
||||
|
||||
|
@ -156,7 +165,7 @@ Makes sure dma.buffer is valid
|
|||
*/
|
||||
void SNDDMA_BeginPainting( void )
|
||||
{
|
||||
SDL_LockAudio( );
|
||||
// SDL_LockAudioDevice( sdl_dev );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -169,7 +178,7 @@ Also unlocks the dsound buffer
|
|||
*/
|
||||
void SNDDMA_Submit( void )
|
||||
{
|
||||
SDL_UnlockAudio( );
|
||||
// SDL_UnlockAudioDevice( sdl_dev );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -220,4 +229,98 @@ void SNDDMA_Activate( qboolean active )
|
|||
|
||||
SDL_PauseAudioDevice( sdl_dev, !active );
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
SDL_SoundInputCallback
|
||||
===========
|
||||
*/
|
||||
void SDL_SoundInputCallback( void *userdata, Uint8 *stream, int len )
|
||||
{
|
||||
int size = Q_min( len, sizeof( voice.input_buffer ) - voice.input_buffer_pos );
|
||||
|
||||
// engine can't keep up, skip audio
|
||||
if( !size )
|
||||
return;
|
||||
|
||||
memcpy( voice.input_buffer + voice.input_buffer_pos, stream, size );
|
||||
voice.input_buffer_pos += size;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
VoiceCapture_Init
|
||||
===========
|
||||
*/
|
||||
qboolean VoiceCapture_Init( void )
|
||||
{
|
||||
SDL_AudioSpec wanted, spec;
|
||||
|
||||
if( !SDLash_IsAudioError( in_dev ))
|
||||
{
|
||||
VoiceCapture_Shutdown();
|
||||
}
|
||||
|
||||
SDL_zero( wanted );
|
||||
wanted.freq = voice.samplerate;
|
||||
wanted.format = AUDIO_S16LSB;
|
||||
wanted.channels = VOICE_PCM_CHANNELS;
|
||||
wanted.samples = voice.frame_size;
|
||||
wanted.callback = SDL_SoundInputCallback;
|
||||
|
||||
in_dev = SDL_OpenAudioDevice( NULL, SDL_TRUE, &wanted, &spec, 0 );
|
||||
|
||||
if( SDLash_IsAudioError( in_dev ))
|
||||
{
|
||||
Con_Printf( "VoiceCapture_Init: error creating capture device (%s)\n", SDL_GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
Con_Printf( S_NOTE "VoiceCapture_Init: capture device creation success (%i: %s)\n", in_dev, SDL_GetAudioDeviceName( in_dev, SDL_TRUE ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
VoiceCapture_Activate
|
||||
===========
|
||||
*/
|
||||
qboolean VoiceCapture_Activate( qboolean activate )
|
||||
{
|
||||
if( SDLash_IsAudioError( in_dev ))
|
||||
return false;
|
||||
|
||||
SDL_PauseAudioDevice( in_dev, activate ? SDL_FALSE : SDL_TRUE );
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
VoiceCapture_Lock
|
||||
===========
|
||||
*/
|
||||
qboolean VoiceCapture_Lock( qboolean lock )
|
||||
{
|
||||
if( SDLash_IsAudioError( in_dev ))
|
||||
return false;
|
||||
|
||||
if( lock ) SDL_LockAudioDevice( in_dev );
|
||||
else SDL_UnlockAudioDevice( in_dev );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==========
|
||||
VoiceCapture_Shutdown
|
||||
==========
|
||||
*/
|
||||
void VoiceCapture_Shutdown( void )
|
||||
{
|
||||
if( SDLash_IsAudioError( in_dev ))
|
||||
return;
|
||||
|
||||
SDL_CloseAudioDevice( in_dev );
|
||||
}
|
||||
|
||||
#endif // XASH_SOUND == SOUND_SDL
|
||||
|
|
|
@ -553,7 +553,9 @@ static qboolean VID_SetScreenResolution( int width, int height )
|
|||
Uint32 wndFlags = 0;
|
||||
static string wndname;
|
||||
|
||||
#if !XASH_APPLE
|
||||
if( vid_highdpi->value ) wndFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
#endif
|
||||
Q_strncpy( wndname, GI->title, sizeof( wndname ));
|
||||
|
||||
want.w = width;
|
||||
|
@ -598,7 +600,7 @@ void VID_RestoreScreenResolution( void )
|
|||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
}
|
||||
|
||||
#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32
|
||||
#if XASH_WIN32 // ICO support only for Win32
|
||||
#include "SDL_syswm.h"
|
||||
static void WIN_SetWindowIcon( HICON ico )
|
||||
{
|
||||
|
@ -609,7 +611,7 @@ static void WIN_SetWindowIcon( HICON ico )
|
|||
|
||||
if( SDL_GetWindowWMInfo( host.hWnd, &wminfo ) )
|
||||
{
|
||||
SetClassLong( wminfo.info.win.window, GCL_HICON, (LONG)ico );
|
||||
SetClassLongPtr( wminfo.info.win.window, GCLP_HICON, (LONG)ico );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -628,6 +630,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
|
|||
qboolean iconLoaded = false;
|
||||
char iconpath[MAX_STRING];
|
||||
int xpos, ypos;
|
||||
const char *localIcoPath;
|
||||
|
||||
if( vid_highdpi->value ) wndFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
Q_strncpy( wndname, GI->title, sizeof( wndname ));
|
||||
|
@ -686,14 +689,12 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
|
|||
VID_RestoreScreenResolution();
|
||||
}
|
||||
|
||||
#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32
|
||||
if( FS_FileExists( GI->iconpath, true ) )
|
||||
#if XASH_WIN32 // ICO support only for Win32
|
||||
if(( localIcoPath = FS_GetDiskPath( GI->iconpath, true )))
|
||||
{
|
||||
HICON ico;
|
||||
char localPath[MAX_PATH];
|
||||
|
||||
Q_snprintf( localPath, sizeof( localPath ), "%s/%s", GI->gamefolder, GI->iconpath );
|
||||
ico = (HICON)LoadImage( NULL, localPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE );
|
||||
ico = (HICON)LoadImage( NULL, localIcoPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE );
|
||||
|
||||
if( ico )
|
||||
{
|
||||
|
@ -728,7 +729,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
|
|||
}
|
||||
}
|
||||
|
||||
#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32
|
||||
#if XASH_WIN32 // ICO support only for Win32
|
||||
if( !iconLoaded )
|
||||
{
|
||||
WIN_SetWindowIcon( LoadIcon( host.hInst, MAKEINTRESOURCE( 101 ) ) );
|
||||
|
|
|
@ -94,5 +94,25 @@ void SNDDMA_Shutdown( void )
|
|||
}
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Init( void )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Activate( qboolean activate )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Lock( qboolean lock )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoiceCapture_Shutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
478
engine/platform/win32/lib_custom_win.c
Normal file
478
engine/platform/win32/lib_custom_win.c
Normal file
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
lib_custom_win.c - win32 custom dlls loader
|
||||
Copyright (C) 2008 Uncle Mike
|
||||
|
||||
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"
|
||||
|
||||
#if XASH_LIB == LIB_WIN32 && XASH_X86
|
||||
#include "lib_win.h"
|
||||
|
||||
#define NUMBER_OF_DIRECTORY_ENTRIES 16
|
||||
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
|
||||
#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION ))
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PIMAGE_NT_HEADERS headers;
|
||||
byte *codeBase;
|
||||
void **modules;
|
||||
int numModules;
|
||||
int initialized;
|
||||
} MEMORYMODULE, *PMEMORYMODULE;
|
||||
|
||||
// Protection flags for memory pages (Executable, Readable, Writeable)
|
||||
static int ProtectionFlags[2][2][2] =
|
||||
{
|
||||
{
|
||||
{ PAGE_NOACCESS, PAGE_WRITECOPY }, // not executable
|
||||
{ PAGE_READONLY, PAGE_READWRITE },
|
||||
},
|
||||
{
|
||||
{ PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY }, // executable
|
||||
{ PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE },
|
||||
},
|
||||
};
|
||||
|
||||
typedef BOOL (WINAPI *DllEntryProc)( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved );
|
||||
|
||||
#define GET_HEADER_DICTIONARY( module, idx ) &(module)->headers->OptionalHeader.DataDirectory[idx]
|
||||
|
||||
static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
|
||||
byte *codeBase = module->codeBase;
|
||||
int i, size;
|
||||
byte *dest;
|
||||
|
||||
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
||||
{
|
||||
if( section->SizeOfRawData == 0 )
|
||||
{
|
||||
// section doesn't contain data in the dll itself, but may define
|
||||
// uninitialized data
|
||||
size = old_headers->OptionalHeader.SectionAlignment;
|
||||
|
||||
if( size > 0 )
|
||||
{
|
||||
dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), size, MEM_COMMIT, PAGE_READWRITE );
|
||||
section->Misc.PhysicalAddress = (DWORD)dest;
|
||||
memset( dest, 0, size );
|
||||
}
|
||||
// section is empty
|
||||
continue;
|
||||
}
|
||||
|
||||
// commit memory block and copy data from dll
|
||||
dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE );
|
||||
memcpy( dest, (byte *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData );
|
||||
section->Misc.PhysicalAddress = (DWORD)dest;
|
||||
}
|
||||
}
|
||||
|
||||
static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
|
||||
byte *codeBase = module->codeBase;
|
||||
int i, size;
|
||||
|
||||
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
||||
{
|
||||
if( section->SizeOfRawData == 0 )
|
||||
{
|
||||
size = old_headers->OptionalHeader.SectionAlignment;
|
||||
if( size > 0 )
|
||||
{
|
||||
VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), size, MEM_DECOMMIT );
|
||||
section->Misc.PhysicalAddress = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), section->SizeOfRawData, MEM_DECOMMIT );
|
||||
section->Misc.PhysicalAddress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void FinalizeSections( MEMORYMODULE *module )
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
|
||||
int i;
|
||||
|
||||
// loop through all sections and change access flags
|
||||
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
||||
{
|
||||
DWORD protect, oldProtect, size;
|
||||
int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
||||
int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
|
||||
int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
||||
|
||||
if( section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE )
|
||||
{
|
||||
// section is not needed any more and can safely be freed
|
||||
VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT);
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine protection flags based on characteristics
|
||||
protect = ProtectionFlags[executable][readable][writeable];
|
||||
if( section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED )
|
||||
protect |= PAGE_NOCACHE;
|
||||
|
||||
// determine size of region
|
||||
size = section->SizeOfRawData;
|
||||
|
||||
if( size == 0 )
|
||||
{
|
||||
if( section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA )
|
||||
size = module->headers->OptionalHeader.SizeOfInitializedData;
|
||||
else if( section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA )
|
||||
size = module->headers->OptionalHeader.SizeOfUninitializedData;
|
||||
}
|
||||
|
||||
if( size > 0 )
|
||||
{
|
||||
// change memory access flags
|
||||
if( !VirtualProtect((LPVOID)section->Misc.PhysicalAddress, size, protect, &oldProtect ))
|
||||
Sys_Error( "error protecting memory page\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta )
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC );
|
||||
byte *codeBase = module->codeBase;
|
||||
DWORD i;
|
||||
|
||||
if( directory->Size > 0 )
|
||||
{
|
||||
PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
||||
for( ; relocation->VirtualAddress > 0; )
|
||||
{
|
||||
byte *dest = (byte *)CALCULATE_ADDRESS( codeBase, relocation->VirtualAddress );
|
||||
word *relInfo = (word *)((byte *)relocation + IMAGE_SIZEOF_BASE_RELOCATION );
|
||||
|
||||
for( i = 0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++ )
|
||||
{
|
||||
DWORD *patchAddrHL;
|
||||
int type, offset;
|
||||
|
||||
// the upper 4 bits define the type of relocation
|
||||
type = *relInfo >> 12;
|
||||
// the lower 12 bits define the offset
|
||||
offset = *relInfo & 0xfff;
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
// skip relocation
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
// change complete 32 bit address
|
||||
patchAddrHL = (DWORD *)CALCULATE_ADDRESS( dest, offset );
|
||||
*patchAddrHL += delta;
|
||||
break;
|
||||
default:
|
||||
Con_Reportf( S_ERROR "PerformBaseRelocation: unknown relocation: %d\n", type );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// advance to next relocation block
|
||||
relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( relocation, relocation->SizeOfBlock );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FARPROC MemoryGetProcAddress( void *module, const char *name )
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT );
|
||||
byte *codeBase = ((PMEMORYMODULE)module)->codeBase;
|
||||
PIMAGE_EXPORT_DIRECTORY exports;
|
||||
int idx = -1;
|
||||
DWORD i, *nameRef;
|
||||
WORD *ordinal;
|
||||
|
||||
if( directory->Size == 0 )
|
||||
{
|
||||
// no export table found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
||||
|
||||
if( exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0 )
|
||||
{
|
||||
// DLL doesn't export anything
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// search function name in list of exported names
|
||||
nameRef = (DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNames );
|
||||
ordinal = (WORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNameOrdinals );
|
||||
|
||||
for( i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++ )
|
||||
{
|
||||
// GetProcAddress case insensative ?????
|
||||
if( !Q_stricmp( name, (const char *)CALCULATE_ADDRESS( codeBase, *nameRef )))
|
||||
{
|
||||
idx = *ordinal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( idx == -1 )
|
||||
{
|
||||
// exported symbol not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if((DWORD)idx > exports->NumberOfFunctions )
|
||||
{
|
||||
// name <-> ordinal number don't match
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// addressOfFunctions contains the RVAs to the "real" functions
|
||||
return (FARPROC)CALCULATE_ADDRESS( codeBase, *(DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfFunctions + (idx * 4)));
|
||||
}
|
||||
|
||||
static int BuildImportTable( MEMORYMODULE *module )
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT );
|
||||
byte *codeBase = module->codeBase;
|
||||
int result = 1;
|
||||
|
||||
if( directory->Size > 0 )
|
||||
{
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
||||
|
||||
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR )) && importDesc->Name; importDesc++ )
|
||||
{
|
||||
DWORD *thunkRef, *funcRef;
|
||||
LPCSTR libname;
|
||||
void *handle;
|
||||
|
||||
libname = (LPCSTR)CALCULATE_ADDRESS( codeBase, importDesc->Name );
|
||||
handle = COM_LoadLibrary( libname, false, true );
|
||||
|
||||
if( handle == NULL )
|
||||
{
|
||||
Con_Printf( S_ERROR "couldn't load library %s\n", libname );
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
module->modules = (void *)Mem_Realloc( host.mempool, module->modules, (module->numModules + 1) * (sizeof( void* )));
|
||||
module->modules[module->numModules++] = handle;
|
||||
|
||||
if( importDesc->OriginalFirstThunk )
|
||||
{
|
||||
thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->OriginalFirstThunk );
|
||||
funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no hint table
|
||||
thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
||||
funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
||||
}
|
||||
|
||||
for( ; *thunkRef; thunkRef++, funcRef++ )
|
||||
{
|
||||
LPCSTR funcName;
|
||||
|
||||
if( IMAGE_SNAP_BY_ORDINAL( *thunkRef ))
|
||||
{
|
||||
funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef );
|
||||
*funcRef = (DWORD)COM_GetProcAddress( handle, funcName );
|
||||
}
|
||||
else
|
||||
{
|
||||
PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS( codeBase, *thunkRef );
|
||||
funcName = (LPCSTR)&thunkData->Name;
|
||||
*funcRef = (DWORD)COM_GetProcAddress( handle, funcName );
|
||||
}
|
||||
|
||||
if( *funcRef == 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s unable to find address: %s\n", libname, funcName );
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !result ) break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MemoryFreeLibrary( void *hInstance )
|
||||
{
|
||||
MEMORYMODULE *module = (MEMORYMODULE *)hInstance;
|
||||
|
||||
if( module != NULL )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( module->initialized != 0 )
|
||||
{
|
||||
// notify library about detaching from process
|
||||
DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS( module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint );
|
||||
(*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0 );
|
||||
module->initialized = 0;
|
||||
}
|
||||
|
||||
if( module->modules != NULL )
|
||||
{
|
||||
// free previously opened libraries
|
||||
for( i = 0; i < module->numModules; i++ )
|
||||
{
|
||||
if( module->modules[i] != NULL )
|
||||
COM_FreeLibrary( module->modules[i] );
|
||||
}
|
||||
Mem_Free( module->modules ); // Mem_Realloc end
|
||||
}
|
||||
|
||||
FreeSections( module->headers, module );
|
||||
|
||||
if( module->codeBase != NULL )
|
||||
{
|
||||
// release memory of library
|
||||
VirtualFree( module->codeBase, 0, MEM_RELEASE );
|
||||
}
|
||||
|
||||
HeapFree( GetProcessHeap(), 0, module );
|
||||
}
|
||||
}
|
||||
|
||||
void *MemoryLoadLibrary( const char *name )
|
||||
{
|
||||
MEMORYMODULE *result = NULL;
|
||||
PIMAGE_DOS_HEADER dos_header;
|
||||
PIMAGE_NT_HEADERS old_header;
|
||||
byte *code, *headers;
|
||||
DWORD locationDelta;
|
||||
DllEntryProc DllEntry;
|
||||
string errorstring;
|
||||
qboolean successfull;
|
||||
void *data = NULL;
|
||||
|
||||
data = FS_LoadFile( name, NULL, false );
|
||||
|
||||
if( !data )
|
||||
{
|
||||
Q_sprintf( errorstring, "couldn't load %s", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
dos_header = (PIMAGE_DOS_HEADER)data;
|
||||
if( dos_header->e_magic != IMAGE_DOS_SIGNATURE )
|
||||
{
|
||||
Q_sprintf( errorstring, "%s it's not a valid executable file", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
old_header = (PIMAGE_NT_HEADERS)&((const byte *)(data))[dos_header->e_lfanew];
|
||||
if( old_header->Signature != IMAGE_NT_SIGNATURE )
|
||||
{
|
||||
Q_sprintf( errorstring, "%s missing PE header", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
// reserve memory for image of library
|
||||
code = (byte *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE );
|
||||
|
||||
if( code == NULL )
|
||||
{
|
||||
// try to allocate memory at arbitrary position
|
||||
code = (byte *)VirtualAlloc( NULL, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE );
|
||||
}
|
||||
|
||||
if( code == NULL )
|
||||
{
|
||||
Q_sprintf( errorstring, "%s can't reserve memory", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
result = (MEMORYMODULE *)HeapAlloc( GetProcessHeap(), 0, sizeof( MEMORYMODULE ));
|
||||
result->codeBase = code;
|
||||
result->numModules = 0;
|
||||
result->modules = NULL;
|
||||
result->initialized = 0;
|
||||
|
||||
// XXX: is it correct to commit the complete memory region at once?
|
||||
// calling DllEntry raises an exception if we don't...
|
||||
VirtualAlloc( code, old_header->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE );
|
||||
|
||||
// commit memory for headers
|
||||
headers = (byte *)VirtualAlloc( code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE );
|
||||
|
||||
// copy PE header to code
|
||||
memcpy( headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders );
|
||||
result->headers = (PIMAGE_NT_HEADERS)&((const byte *)(headers))[dos_header->e_lfanew];
|
||||
|
||||
// update position
|
||||
result->headers->OptionalHeader.ImageBase = (DWORD)code;
|
||||
|
||||
// copy sections from DLL file block to new memory location
|
||||
CopySections( data, old_header, result );
|
||||
|
||||
// adjust base address of imported data
|
||||
locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase);
|
||||
if( locationDelta != 0 ) PerformBaseRelocation( result, locationDelta );
|
||||
|
||||
// load required dlls and adjust function table of imports
|
||||
if( !BuildImportTable( result ))
|
||||
{
|
||||
Q_sprintf( errorstring, "%s failed to build import table", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
// mark memory pages depending on section headers and release
|
||||
// sections that are marked as "discardable"
|
||||
FinalizeSections( result );
|
||||
|
||||
// get entry point of loaded library
|
||||
if( result->headers->OptionalHeader.AddressOfEntryPoint != 0 )
|
||||
{
|
||||
DllEntry = (DllEntryProc)CALCULATE_ADDRESS( code, result->headers->OptionalHeader.AddressOfEntryPoint );
|
||||
if( DllEntry == 0 )
|
||||
{
|
||||
Q_sprintf( errorstring, "%s has no entry point", name );
|
||||
goto library_error;
|
||||
}
|
||||
|
||||
// notify library about attaching to process
|
||||
successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0 );
|
||||
if( !successfull )
|
||||
{
|
||||
Q_sprintf( errorstring, "can't attach library %s", name );
|
||||
goto library_error;
|
||||
}
|
||||
result->initialized = 1;
|
||||
}
|
||||
|
||||
Mem_Free( data ); // release memory
|
||||
return (void *)result;
|
||||
library_error:
|
||||
// cleanup
|
||||
if( data ) Mem_Free( data );
|
||||
MemoryFreeLibrary( result );
|
||||
Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // XASH_LIB == LIB_WIN32 && XASH_X86
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue