mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 11:35:05 +08:00
Compare commits
16 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
004af02e99 | ||
|
|
8ad56eb085 | ||
|
|
32def35408 | ||
|
|
952ccb1247 | ||
|
|
6f0fb594e7 | ||
|
|
8b4b0479c8 | ||
|
|
c8bf7e5630 | ||
|
|
4987080e1e | ||
|
|
1d78e400ff | ||
|
|
2d1a885954 | ||
|
|
43ba2e747b | ||
|
|
eefb823ee2 | ||
|
|
a3c33e0fa9 | ||
|
|
16fe6b1b1a | ||
|
|
4275829bd5 | ||
|
|
1884f0a3ce |
@@ -4,7 +4,7 @@ task:
|
||||
image_family: freebsd-14-2
|
||||
setup_script:
|
||||
- pkg update
|
||||
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2 libbacktrace
|
||||
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2
|
||||
- git submodule update --init --recursive
|
||||
test_script:
|
||||
- ./scripts/cirrus/build_freebsd.sh dedicated
|
||||
@@ -16,7 +16,7 @@ task:
|
||||
image_family: freebsd-15-0-snap
|
||||
setup_script:
|
||||
- pkg update
|
||||
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2 libbacktrace
|
||||
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2
|
||||
- git submodule update --init --recursive
|
||||
test_script:
|
||||
- ./scripts/cirrus/build_freebsd.sh dedicated
|
||||
|
||||
8
.github/workflows/c-cpp.yml
vendored
8
.github/workflows/c-cpp.yml
vendored
@@ -66,8 +66,10 @@ jobs:
|
||||
targetos: apple
|
||||
targetarch: amd64
|
||||
env:
|
||||
SDL_VERSION: 2.32.0
|
||||
SDL_VERSION: 2.30.11
|
||||
FFMPEG_VERSION: 7.1
|
||||
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
||||
GH_CPU_OS: ${{ matrix.targetos }}
|
||||
GH_CROSSCOMPILING: ${{ matrix.cross }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -87,7 +89,6 @@ jobs:
|
||||
path: artifacts/*
|
||||
flatpak:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -101,7 +102,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build flatpak (Compat.i386)
|
||||
uses: FWGS/flatpak-github-actions/flatpak-builder@v6.3-fix
|
||||
uses: FWGS/flatpak-github-actions/flatpak-builder@v6.3
|
||||
with:
|
||||
bundle: ${{ matrix.app }}.flatpak
|
||||
manifest-path: scripts/flatpak/${{ matrix.app }}.yml
|
||||
@@ -120,6 +121,7 @@ jobs:
|
||||
--yes \
|
||||
--cleanup-tag \
|
||||
--repo "$GITHUB_REPOSITORY" || true
|
||||
sleep 20s
|
||||
gh run download "$GITHUB_RUN_ID" \
|
||||
--dir artifacts/ \
|
||||
--repo "$GITHUB_REPOSITORY"
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -27,13 +27,10 @@
|
||||
url = https://github.com/FWGS/MultiEmulator
|
||||
[submodule "3rdparty/libogg/libogg"]
|
||||
path = 3rdparty/libogg/libogg
|
||||
url = https://github.com/xiph/ogg.git
|
||||
url = https://gitlab.xiph.org/xiph/ogg.git
|
||||
[submodule "3rdparty/vorbis/vorbis-src"]
|
||||
path = 3rdparty/vorbis/vorbis-src
|
||||
url = https://github.com/xiph/vorbis.git
|
||||
url = https://gitlab.xiph.org/xiph/vorbis.git
|
||||
[submodule "3rdparty/opusfile/opusfile"]
|
||||
path = 3rdparty/opusfile/opusfile
|
||||
url = https://github.com/xiph/opusfile.git
|
||||
[submodule "3rdparty/libbacktrace/libbacktrace"]
|
||||
path = 3rdparty/libbacktrace/libbacktrace
|
||||
url = https://github.com/ianlancetaylor/libbacktrace
|
||||
url = https://gitlab.xiph.org/xiph/opusfile.git
|
||||
|
||||
2
3rdparty/MultiEmulator
vendored
2
3rdparty/MultiEmulator
vendored
Submodule 3rdparty/MultiEmulator updated: 11ff077ca5...7e989e670e
2
3rdparty/extras/xash-extras
vendored
2
3rdparty/extras/xash-extras
vendored
Submodule 3rdparty/extras/xash-extras updated: 0c83a63d00...1bdeab2d8f
2
3rdparty/gl4es/gl4es
vendored
2
3rdparty/gl4es/gl4es
vendored
Submodule 3rdparty/gl4es/gl4es updated: a744af14d4...e39434a2b1
1
3rdparty/libbacktrace/libbacktrace
vendored
1
3rdparty/libbacktrace/libbacktrace
vendored
Submodule 3rdparty/libbacktrace/libbacktrace deleted from 78af4ffa26
185
3rdparty/libbacktrace/wscript
vendored
185
3rdparty/libbacktrace/wscript
vendored
@@ -1,185 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
from waflib import TaskGen
|
||||
|
||||
FRAGMENT_ATOMIC='''int i;
|
||||
int main(void) {
|
||||
__atomic_load_n(&i, __ATOMIC_ACQUIRE);
|
||||
__atomic_store_n(&i, 1, __ATOMIC_RELEASE);
|
||||
return 0;
|
||||
}'''
|
||||
|
||||
FRAGMENT_SYNC='''int i;
|
||||
int main (void) {
|
||||
__sync_bool_compare_and_swap (&i, i, i);
|
||||
__sync_lock_test_and_set (&i, 1);
|
||||
__sync_lock_release (&i);
|
||||
return 0;
|
||||
}'''
|
||||
|
||||
FRAGMENT_GETPAGESIZE='''#include <unistd.h>
|
||||
int main(void) { return getpagesize() }'''
|
||||
|
||||
FRAGMENT_GETEXECNAME='''#include <stdlib.h>
|
||||
int main(void) { return getexecname() != 0 }'''
|
||||
|
||||
FRAGMENT_STRNLEN='''#include <string.h>
|
||||
int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }'''
|
||||
|
||||
FRAGMENT_DL_ITERATE_PHDR='''#include <%s>
|
||||
int main(void) { return dl_iterate_phdr(0, 0); }'''
|
||||
|
||||
FRAGMENT_FCNTL='''#include <fcntl.h>
|
||||
int main(void) { return fcntl(0, 0, 0); }'''
|
||||
|
||||
FRAGMENT_GETIPINFO='''#include "unwind.h"
|
||||
struct _Unwind_Context *context;
|
||||
int ip_before_insn = 0;
|
||||
int main(void) { return _Unwind_GetIPInfo(context, &ip_before_insn); }'''
|
||||
|
||||
FRAGMENT_LOADQUERY='''#include <sys/ldr.h>
|
||||
#include <sys/debug.h>
|
||||
int main(void) { return loadquery(0, 0, 0); }'''
|
||||
|
||||
FRAGMENT_KERN_PROC='''#include <sys/sysctl.h>
|
||||
#if !defined(%s) || !defined(KERN_PROC_PATHNAME)
|
||||
#error
|
||||
#endif
|
||||
int main(void) { return 0; }'''
|
||||
|
||||
FRAGMENT_LSTAT='''#include <sys/stat.h>
|
||||
struct stat st;
|
||||
int main(int argc, char **argv) { return lstat(argv[0], &st); }'''
|
||||
|
||||
FRAGMENT_READLINK='''#include <unistd.h>
|
||||
char buf[100];
|
||||
int main(int argc, char **argv) { return readlink(argv[0], buf, sizeof(buf)); }'''
|
||||
|
||||
def options(opt):
|
||||
pass
|
||||
|
||||
def configure(conf):
|
||||
# add unsupported platforms here
|
||||
if conf.env.DEST_OS in ['nswitch', 'psvita', 'dos']:
|
||||
conf.env.DISABLE_LIBBACKTRACE = True
|
||||
return
|
||||
|
||||
# win32 has it's own dbghelp-based backtrace, that's why we ship PDBs
|
||||
if conf.env.COMPILER_CC == 'msvc':
|
||||
conf.env.DISABLE_LIBBACKTRACE = True
|
||||
return
|
||||
|
||||
if not conf.path.find_dir('libbacktrace') or not conf.path.find_dir('libbacktrace/config'):
|
||||
conf.fatal('Can\'t find libbacktrace submodule. Run `git submodule update --init --recursive`.')
|
||||
return
|
||||
|
||||
if conf.env.DEST_SIZEOF_VOID_P == 8:
|
||||
conf.define('BACKTRACE_ELF_SIZE', 64)
|
||||
conf.define('BACKTRACE_XCOFF_SIZE', 64)
|
||||
else:
|
||||
conf.define('BACKTRACE_ELF_SIZE', 32)
|
||||
conf.define('BACKTRACE_XCOFF_SIZE', 32)
|
||||
|
||||
conf.define('_ALL_SOURCE', 1)
|
||||
conf.define('_GNU_SOURCE', 1)
|
||||
conf.define('_POSIX_PTHREAD_SEMANTICS', 1)
|
||||
conf.define('_TANDEM_SOURCE', 1)
|
||||
conf.define('__EXTENSIONS__', 1)
|
||||
conf.define('_DARWIN_USE_64_BIT_INODE', 1)
|
||||
conf.define('_LARGE_FILES', 1)
|
||||
conf.check_large_file(compiler='c', execute=False, mandatory=False) # sets _FILE_OFFSET_BITS
|
||||
|
||||
conf.env.CFLAGS_EXTRAFLAGS = conf.filter_cflags(['-funwind-tables', '-g'], [])
|
||||
|
||||
if conf.filter_cflags(['-frandom-seed=test'], []):
|
||||
conf.env.HAVE_FRANDOM_SEED = True
|
||||
|
||||
def check_header(hdr):
|
||||
return {'header_name':hdr, 'msg':'... %s header' % hdr, 'mandatory':False, 'id':hdr}
|
||||
|
||||
conf.multicheck(
|
||||
check_header('dlfcn.h'),
|
||||
check_header('inttypes.h'),
|
||||
check_header('link.h'),
|
||||
check_header('sys/link.h'),
|
||||
check_header('mach-o/dyld.h'),
|
||||
check_header('memory.h'),
|
||||
check_header('stdint.h'),
|
||||
check_header('stdlib.h'),
|
||||
check_header('strings.h'),
|
||||
check_header('string.h'),
|
||||
check_header('sys/ldr.h'),
|
||||
check_header('sys/mman.h'),
|
||||
check_header('sys/stat.h'),
|
||||
check_header('sys/types.h'),
|
||||
check_header('tlhelp32.h'),
|
||||
check_header('unistd.h'),
|
||||
check_header('windows.h'),
|
||||
|
||||
{'fragment':FRAGMENT_ATOMIC, 'msg':'... __atomic extensions', 'define_name':'HAVE_ATOMIC_FUNCTIONS', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_SYNC, 'msg':'... __sync extensions', 'define_name':'HAVE_SYNC_FUNCTIONS', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_GETPAGESIZE, 'msg':'... getpagesize function', 'define_name':'HAVE_DECL_GETPAGESIZE', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_STRNLEN, 'msg':'... strnlen function', 'define_name':'HAVE_DECL_STRNLEN', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_DL_ITERATE_PHDR % 'link.h', 'msg':'... dl_iterate_phdr function in link.h', 'define_name':'HAVE_DL_ITERATE_PHDR', 'after_tests': ['link.h'], 'mandatory':False},
|
||||
{'fragment':FRAGMENT_DL_ITERATE_PHDR % 'sys/link.h', 'msg':'... dl_iterate_phdr function in sys/link.h', 'define_name':'HAVE_DL_ITERATE_PHDR', 'after_tests': ['sys/link.h'], 'mandatory':False},
|
||||
{'fragment':FRAGMENT_FCNTL,'msg':'... fnctl function', 'define_name':'HAVE_FCNTL', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_GETEXECNAME,'msg':'... getexecname function','define_name':'HAVE_GETEXECNAME', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_GETIPINFO, 'msg':'... _Unwind_GetIPInfo function', 'define_name':'HAVE_GETIPINFO', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_KERN_PROC % 'KERN_PROC', 'msg': '... KERN_PROC and KERN_PROC_PATHNAME defines', 'define_name':'HAVE_KERN_PROC', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_KERN_PROC % 'KERN_PROC_ARGS', 'msg': '... KERN_PROC_ARGS and KERN_PROC_PATHNAME defines', 'define_name':'HAVE_KERN_PROC_ARGS', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_LOADQUERY, 'msg': '... loadquery function', 'define_name':'HAVE_LOADQUERY', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_LSTAT, 'msg': '... lstat function', 'define_name':'HAVE_LSTAT', 'mandatory':False},
|
||||
{'fragment':FRAGMENT_READLINK, 'msg': '... readlink function', 'define_name':'HAVE_READLINK', 'mandatory':False},
|
||||
|
||||
# {'lib':'lzma', 'define_name':'HAVE_LIBLZMA', 'uselib_store':'lzma', 'msg':'... lzma library', 'mandatory':False},
|
||||
# {'lib':'z', 'define_name':'HAVE_ZLIB', 'uselib_store':'z', 'msg':'... zlib library', 'mandatory':False},
|
||||
# {'lib':'zstd', 'define_name':'HAVE_ZSTD', 'uselib_store':'zstd', 'msg':'... zstd library', 'mandatory':False},
|
||||
msg='Checking for in parallel'
|
||||
)
|
||||
|
||||
conf.write_config_header()
|
||||
|
||||
conf.define('BACKTRACE_SUPPORTED', 1)
|
||||
conf.define('BACKTRACE_USES_MALLOC', 0)
|
||||
conf.define('BACKTRACE_SUPPORTS_THREADS', 1)
|
||||
conf.define('BACKTRACE_SUPPORTS_DATA', conf.env.DEST_BINFMT in ['elf', 'mac-o'])
|
||||
|
||||
conf.write_config_header('backtrace-supported.h')
|
||||
|
||||
@TaskGen.feature('frandomseed')
|
||||
@TaskGen.after_method('propagate_uselib_vars')
|
||||
def process_frandom_seed(ctx):
|
||||
tasks = getattr(ctx, 'compiled_tasks', [])
|
||||
|
||||
for task in tasks:
|
||||
out = task.outputs[0]
|
||||
task.env.CFLAGS = list(task.env.CFLAGS) # need a copy
|
||||
task.env.CFLAGS += ['-frandom-seed=%s' % out.path_from(out.ctx.bldnode)]
|
||||
|
||||
def build(bld):
|
||||
if bld.env.DISABLE_LIBBACKTRACE:
|
||||
return
|
||||
|
||||
# we specifically only want mmap-based allocators because calling malloc is not safe from signal handlers
|
||||
sources = ['atomic.c', 'dwarf.c', 'fileline.c', 'posix.c', 'print.c', 'sort.c', 'state.c', 'backtrace.c', 'simple.c', 'mmap.c', 'mmapio.c']
|
||||
|
||||
if bld.env.DEST_BINFMT == 'pe':
|
||||
sources += ['pecoff.c']
|
||||
elif bld.env.DEST_BINFMT == 'mac-o':
|
||||
sources += ['macho.c']
|
||||
elif bld.env.DEST_BINFMT == 'elf':
|
||||
sources += ['elf.c']
|
||||
else:
|
||||
sources += ['unknown.c']
|
||||
|
||||
task = bld.stlib(
|
||||
source = ['libbacktrace/' + i for i in sources],
|
||||
target = 'backtrace',
|
||||
features = 'frandomseed' if bld.env.HAVE_FRANDOM_SEED else '',
|
||||
use = 'EXTRAFLAGS lzma z zstd',
|
||||
includes = '. libbacktrace/',
|
||||
export_defines = 'HAVE_LIBBACKTRACE=1',
|
||||
export_includes = 'libbacktrace/'
|
||||
)
|
||||
|
||||
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: cbbfe2e23c...695ea1be2e
@@ -6,7 +6,7 @@ For connecting to GoldSrc-based servers, use this command:
|
||||
connect ip:port gs
|
||||
```
|
||||
|
||||
But keep in mind, there are requirement for server to be able to accept connections from Xash3D-based clients: it should use Reunion.
|
||||
But keep in mind, there are requirement for server to be able to accept connections from Xash3D-based clients: it should use Reunion or Dproto.
|
||||
Without this requirement, you will just get "Steam validation rejected" error on connecting.
|
||||
|
||||
That is because proper authorization with Steam API is not implemented in engine yet (but we have plans on it).
|
||||
|
||||
@@ -38,9 +38,6 @@ Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Ct
|
||||
## Deathmatch Classic
|
||||
Available in Valve's Half-Life repository - https://github.com/ValveSoftware/halflife/tree/master/dmc
|
||||
|
||||
## Delta Particles
|
||||
Available on ModDB - https://www.moddb.com/mods/half-life-delta/downloads/delta-particles-full-sources-maps-and-c-code
|
||||
|
||||
## Earth Special Forces
|
||||
Alpha 2.0 - https://www.gamers-desire.de/details/2830
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
13
README.md
13
README.md
@@ -1,7 +1,6 @@
|
||||
# Xash3D FWGS Engine <img align="right" width="128" height="128" src="https://github.com/FWGS/xash3d-fwgs/raw/master/game_launch/icon-xash-material.png" alt="Xash3D FWGS icon" />
|
||||
[](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
|
||||
[](http://fwgsdiscord.mentality.rip/) [](https://t.me/flyingwithgauss) \
|
||||
[](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
|
||||
[](https://builds.sr.ht/~a1batross/xash3d-fwgs?) [](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) [](http://fwgsdiscord.mentality.rip/) \
|
||||
[](https://github.com/FWGS/xash3d-fwgs/releases/latest) [](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
|
||||
|
||||
Xash3D ([pronounced](https://ipa-reader.com/?text=ks%C9%91%CA%82) `[ksɑʂ]`) FWGS is a game engine, aimed to provide compatibility with Half-Life Engine and extend it, as well as to give game developers well known workflow.
|
||||
|
||||
@@ -66,21 +65,21 @@ This repository contains our fork of HLSDK and restored source code for Half-Lif
|
||||
* Only for 32-bit engine on 64-bit x86 operating system:
|
||||
* Enable i386 on your system: `$ sudo dpkg --add-architecture i386`.
|
||||
* Install `aptitude` ([why?](https://github.com/FWGS/xash3d-fwgs/issues/1828#issuecomment-2415131759)): `$ sudo apt update && sudo apt upgrade && sudo apt install aptitude`
|
||||
* Install development tools: `$ sudo aptitude --without-recommends install git build-essential gcc-multilib g++-multilib libsdl2-dev:i386 libfreetype-dev:i386 libopus-dev:i386 libbz2-dev:i386 libvorbis-dev:i386 libopusfile-dev:i386 libogg-dev:i386`.
|
||||
* Install development tools: `$ sudo aptitude --without-recommends install git build-essential gcc-multilib g++-multilib libsdl2-dev:i386 libfontconfig-dev:i386 libfreetype-dev:i386 libopus-dev:i386 libbz2-dev:i386`.
|
||||
* Set PKG_CONFIG_PATH environment variable to point at 32-bit libraries: `$ export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig`.
|
||||
|
||||
* For 64-bit engine on 64-bit x86 and other non-x86 systems:
|
||||
* Install development tools: `$ sudo apt install git build-essential python libsdl2-dev libfreetype6-dev libopus-dev libbz2-dev libvorbis-dev libopusfile-dev libogg-dev`.
|
||||
* Install development tools: `$ sudo apt install git build-essential python libsdl2-dev libfontconfig-dev libfreetype6-dev libopus-dev libbz2-dev`.
|
||||
|
||||
* Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`.
|
||||
|
||||
##### RedHat/Fedora
|
||||
* Only for 32-bit engine on 64-bit x86 operating system:
|
||||
* Install development tools: `$ sudo dnf install git gcc gcc-c++ glibc-devel.i686 SDL2-devel.i686 opus-devel.i686 freetype-devel.i686 bzip2-devel.i686 libvorbis-devel.i686 opusfile-devel.i686 libogg-devel.i686`.
|
||||
* Install development tools: `$ sudo dnf install git gcc gcc-c++ glibc-devel.i686 SDL2-devel.i686 opus-devel.i686 fontconfig-devel.i686 freetype-devel.i686 bzip2-devel.i686`.
|
||||
* Set PKG_CONFIG_PATH environment variable to point at 32-bit libraries: `$ export PKG_CONFIG_PATH=/usr/lib/pkgconfig`.
|
||||
|
||||
* For 64-bit engine on 64-bit x86 and other non-x86 systems:
|
||||
* Install development tools: `$ sudo dnf install git gcc gcc-c++ SDL2-devel opus-devel freetype-devel bzip2-devel libvorbis-devel opusfile-devel libogg-devel`.
|
||||
* Install development tools: `$ sudo dnf install git gcc gcc-c++ SDL2-devel opus-devel fontconfig-devel freetype-devel bzip2-devel`.
|
||||
|
||||
* Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`.
|
||||
|
||||
|
||||
@@ -1,39 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
# Only used to build Android project
|
||||
|
||||
project(XASH_ANDROID)
|
||||
|
||||
# armeabi-v7a requires cpufeatures library
|
||||
if(ANDROID)
|
||||
include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
|
||||
add_library(cpufeatures ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
|
||||
target_link_libraries(cpufeatures dl)
|
||||
endif()
|
||||
include(AndroidNdkModules)
|
||||
android_ndk_import_module_cpufeatures()
|
||||
|
||||
include(FindPython)
|
||||
find_package(PythonInterp 2.7 REQUIRED)
|
||||
|
||||
get_filename_component(C_COMPILER_ID ${CMAKE_C_COMPILER} NAME_WE)
|
||||
get_filename_component(CXX_COMPILER_ID ${CMAKE_CXX_COMPILER} NAME_WE)
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
set(BUILD_TYPE "debug")
|
||||
else()
|
||||
set(BUILD_TYPE "release")
|
||||
list(APPEND WAF_EXTRA_ARGS --enable-poly-opt --enable-lto --enable-limited-debuginfo)
|
||||
list(APPEND WAF_EXTRA_ARGS --enable-poly-opt --enable-lto)
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL "x86")
|
||||
# HACKHACK: I don't know why but engine gets built as 64-bit binary here
|
||||
list(APPEND WAF_EXTRA_ARGS -4)
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
set(64BIT ON CACHE BOOL "" FORCE)
|
||||
list(APPEND WAF_EXTRA_ARGS -8) # only required for x86 when testing this cmakelist under linux
|
||||
endif()
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
set(WAF_CC "${CMAKE_C_COMPILER} --target=${CMAKE_C_COMPILER_TARGET}")
|
||||
set(WAF_CXX "${CMAKE_CXX_COMPILER} --target=${CMAKE_CXX_COMPILER_TARGET}")
|
||||
|
||||
# not cleanest way to get upper directory
|
||||
set(ENGINE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../")
|
||||
|
||||
set(WAF_CC "${CMAKE_C_COMPILER} --target=${CMAKE_C_COMPILER_TARGET}")
|
||||
set(WAF_CXX "${CMAKE_CXX_COMPILER} --target=${CMAKE_CXX_COMPILER_TARGET}")
|
||||
set(WAF ${Python_EXECUTABLE} ${ENGINE_SOURCE_DIR}waf -t ${ENGINE_SOURCE_DIR} -o ${CMAKE_CURRENT_BINARY_DIR}/xash3d-fwgs)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
CC=${WAF_CC} CXX=${WAF_CXX}
|
||||
AR=${CMAKE_AR} STRIP=${CMAKE_STRIP}
|
||||
${PYTHON_EXECUTABLE} waf configure -T ${BUILD_TYPE} ${WAF_EXTRA_ARGS} cmake
|
||||
--check-c-compiler=${C_COMPILER_ID} --check-cxx-compiler=${CXX_COMPILER_ID}
|
||||
-s "${ENGINE_SOURCE_DIR}/SDL" --skip-sdl2-sanity-check --enable-bundled-deps
|
||||
WORKING_DIRECTORY "${ENGINE_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/hlsdk-portable" hlsdk-portable)
|
||||
|
||||
# try to build minimal SDL. Enable features as we're gonna use them
|
||||
set(SDL_RENDER OFF)
|
||||
@@ -46,50 +56,5 @@ set(SDL_VULKAN OFF)
|
||||
set(SDL_OFFSCREEN OFF)
|
||||
set(SDL_STATIC OFF)
|
||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/SDL" SDL)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
# gradle passes backslashes to cmake, how does this even work for everybody else?
|
||||
string(REPLACE "\\" "/" CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
|
||||
|
||||
ExternalProject_Add(
|
||||
Xash3DFWGS
|
||||
SOURCE_DIR ${ENGINE_SOURCE_DIR}
|
||||
INSTALL_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
BUILD_IN_SOURCE TRUE
|
||||
DEPENDS SDL2
|
||||
BUILD_ALWAYS TRUE
|
||||
LOG_CONFIGURE TRUE
|
||||
LOG_BUILD TRUE
|
||||
LOG_INSTALL TRUE
|
||||
LOG_OUTPUT_ON_FAILURE TRUE
|
||||
LOG_MERGED_STDOUTERR TRUE
|
||||
|
||||
# USES_TERMINAL_CONFIGURE TRUE
|
||||
# USES_TERMINAL_BUILD TRUE
|
||||
# USES_TERMINAL_INSTALL TRUE
|
||||
|
||||
# NOTE: setting up WAFLOCK is important to avoid possible race conditions
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
|
||||
ANDROID_NDK=${ANDROID_NDK}
|
||||
BUILD_CMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
WAFLOCK=.lock-waf_android_${ANDROID_ABI}_build
|
||||
${WAF} configure -T ${BUILD_TYPE} --android=${ANDROID_ABI},,${CMAKE_SYSTEM_VERSION}
|
||||
-s "${ENGINE_SOURCE_DIR}/3rdparty/SDL" --enable-bundled-deps ${WAF_EXTRA_ARGS}
|
||||
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E env
|
||||
WAFLOCK=.lock-waf_android_${ANDROID_ABI}_build
|
||||
${WAF} build -v
|
||||
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E env
|
||||
WAFLOCK=.lock-waf_android_${ANDROID_ABI}_build
|
||||
${WAF} install --destdir=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
)
|
||||
|
||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/hlsdk-portable" hlsdk-portable)
|
||||
|
||||
# a1ba: without this, xash3d target will be ignored as nothing depends on it
|
||||
add_dependencies(client Xash3DFWGS)
|
||||
add_dependencies(server Xash3DFWGS)
|
||||
|
||||
|
||||
add_subdirectory("${ENGINE_SOURCE_DIR}/" xash3d-fwgs)
|
||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/mainui" mainui)
|
||||
|
||||
@@ -9,7 +9,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "su.xash.engine"
|
||||
ndkVersion = "28.0.13004108"
|
||||
ndkVersion = "27.2.12479018"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "su.xash"
|
||||
@@ -96,7 +96,6 @@ android {
|
||||
packaging {
|
||||
jniLibs {
|
||||
useLegacyPackaging = true
|
||||
keepDebugSymbols.add("**/*.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +112,7 @@ dependencies {
|
||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.work:work-runtime-ktx:2.9.0")
|
||||
// implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
// implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
|
||||
implementation("com.madgag.spongycastle:prov:1.58.0.0")
|
||||
implementation("in.dragonbra:javasteam:1.2.0")
|
||||
|
||||
@@ -20,7 +20,4 @@ kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
|
||||
# Enable verbose output for CMake
|
||||
android.native.buildOutput=verbose
|
||||
android.nonTransitiveRClass=true
|
||||
@@ -50,4 +50,8 @@ GNU General Public License for more details.
|
||||
#define LIB_WIN32 2
|
||||
#define LIB_STATIC 3
|
||||
|
||||
// movies (XASH_AVI)
|
||||
#define AVI_NULL 0
|
||||
#define AVI_FFMPEG 1
|
||||
|
||||
#endif /* BACKENDS_H */
|
||||
|
||||
@@ -78,7 +78,6 @@ SETUP BACKENDS DEFINITIONS
|
||||
// usually only 10-20 fds availiable
|
||||
#define XASH_REDUCE_FD
|
||||
#endif
|
||||
|
||||
#endif // XASH_DEDICATED
|
||||
|
||||
//
|
||||
@@ -105,6 +104,17 @@ SETUP BACKENDS DEFINITIONS
|
||||
#endif // !XASH_WIN32
|
||||
#endif
|
||||
|
||||
//
|
||||
// determine movie playback backend
|
||||
//
|
||||
#ifndef XASH_AVI
|
||||
#if HAVE_FFMPEG
|
||||
#define XASH_AVI AVI_FFMPEG
|
||||
#else
|
||||
#define XASH_AVI AVI_NULL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef XASH_STATIC_LIBS
|
||||
#define XASH_LIB LIB_STATIC
|
||||
#define XASH_INTERNAL_GAMELIBS
|
||||
|
||||
@@ -28,17 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#define PORT_ANY -1
|
||||
|
||||
typedef enum netadrtype_e
|
||||
{
|
||||
NA_UNDEFINED = 0,
|
||||
NA_LOOPBACK,
|
||||
NA_BROADCAST,
|
||||
NA_IP,
|
||||
NA_IPX,
|
||||
NA_BROADCAST_IPX,
|
||||
NA_IP6,
|
||||
NA_MULTICAST_IP6
|
||||
} netadrtype_t;
|
||||
typedef enum {NA_LOOPBACK = 1, NA_BROADCAST, NA_IP, NA_IPX, NA_BROADCAST_IPX, NA_IP6, NA_MULTICAST_IP6} netadrtype_t;
|
||||
|
||||
/*
|
||||
Original Quake-2 structure:
|
||||
@@ -56,60 +46,29 @@ typedef struct
|
||||
#pragma pack( push, 1 )
|
||||
typedef struct netadr_s
|
||||
{
|
||||
// the reason we do this evil thing, is that when this struct contains IPv6
|
||||
// address the `type` is 2-byte wide, but when it doesn't `type` must 4-byte
|
||||
// wide _and_ ip6_0 must be zeroed, to keep it binary compatible.
|
||||
#if XASH_LITTLE_ENDIAN
|
||||
uint16_t type;
|
||||
uint8_t ip6_0[2];
|
||||
#elif XASH_BIG_ENDIAN
|
||||
uint8_t ip6_0[2];
|
||||
uint16_t type;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
union
|
||||
{
|
||||
// IPv6 struct
|
||||
uint8_t ip6_1[14];
|
||||
struct
|
||||
{
|
||||
uint16_t type6;
|
||||
uint8_t ip6[16];
|
||||
};
|
||||
struct
|
||||
{
|
||||
uint32_t type; // must be netadrtype_t but will break with short enums
|
||||
union
|
||||
{
|
||||
uint8_t ip[4];
|
||||
uint32_t ip4; // for easier conversions
|
||||
uint8_t ip[4];
|
||||
uint32_t ip4; // for easier conversions
|
||||
};
|
||||
uint8_t ipx[10];
|
||||
};
|
||||
};
|
||||
uint16_t port;
|
||||
uint16_t port;
|
||||
} netadr_t;
|
||||
#pragma pack( pop )
|
||||
|
||||
static inline netadrtype_t NET_NetadrType( const netadr_t *a )
|
||||
{
|
||||
if( a->type == NA_IP6 || a->type == NA_MULTICAST_IP6 )
|
||||
return (netadrtype_t)a->type;
|
||||
|
||||
if( a->ip6_0[0] || a->ip6_0[1] )
|
||||
return NA_UNDEFINED;
|
||||
|
||||
return (netadrtype_t)a->type;
|
||||
}
|
||||
|
||||
static inline void NET_NetadrSetType( netadr_t *a, netadrtype_t type )
|
||||
{
|
||||
if( type == NA_IP6 || type == NA_MULTICAST_IP6 )
|
||||
{
|
||||
a->type = type;
|
||||
return;
|
||||
}
|
||||
|
||||
a->ip6_0[0] = a->ip6_0[1] = 0;
|
||||
a->type = type;
|
||||
}
|
||||
|
||||
STATIC_CHECK_SIZEOF( netadr_t, 20, 20 );
|
||||
|
||||
#endif // NET_ADR_H
|
||||
|
||||
@@ -86,4 +86,8 @@ GNU General Public License for more details.
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if defined XASH_SDL && !defined REF_DLL
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
#endif // PORT_H
|
||||
|
||||
@@ -157,6 +157,23 @@ typedef struct decallist_s
|
||||
modelstate_t studio_state; // studio decals only
|
||||
} decallist_t;
|
||||
|
||||
enum movie_parms_e
|
||||
{
|
||||
AVI_PARM_LAST = 0, // marker for SetParm to end parse parsing arguments
|
||||
AVI_RENDER_TEXNUM, // (int) sets texture to draw into, if 0 will draw to screen
|
||||
AVI_RENDER_X, // (int) when set to screen, sets position where to draw
|
||||
AVI_RENDER_Y,
|
||||
AVI_RENDER_W, // (int) sets texture or screen width
|
||||
AVI_RENDER_H, // set to -1 to draw full screen
|
||||
AVI_REWIND, // no argument, rewind playback to the beginning
|
||||
AVI_ENTNUM, // (int) entity number, -1 for no spatialization
|
||||
AVI_VOLUME, // (int) volume from 0 to 255
|
||||
AVI_ATTN, // (float) attenuation value
|
||||
AVI_PAUSE, // no argument, pauses playback
|
||||
AVI_RESUME, // no argument, resumes playback
|
||||
};
|
||||
|
||||
struct movie_state_s;
|
||||
struct ref_viewpass_s;
|
||||
|
||||
typedef struct render_api_s
|
||||
@@ -193,16 +210,16 @@ typedef struct render_api_s
|
||||
void (*R_EntityRemoveDecals)( struct model_s *mod ); // remove all the decals from specified entity (BSP only)
|
||||
|
||||
// AVIkit support
|
||||
void *(*AVI_LoadVideo)( const char *filename, qboolean load_audio );
|
||||
int (*AVI_GetVideoInfo)( void *Avi, int *xres, int *yres, float *duration ); // a1ba: changed longs to int
|
||||
int (*AVI_GetVideoFrameNumber)( void *Avi, float time );
|
||||
byte *(*AVI_GetVideoFrame)( void *Avi, int frame );
|
||||
struct movie_state_s *(*AVI_LoadVideo)( const char *filename, qboolean load_audio );
|
||||
qboolean (*AVI_GetVideoInfo)( struct movie_state_s *Avi, int *xres, int *yres, float *duration ); // a1ba: changed longs to int
|
||||
int (*AVI_GetVideoFrameNumber)( struct movie_state_s *Avi, float time );
|
||||
byte *(*AVI_GetVideoFrame)( struct movie_state_s *Avi, int frame );
|
||||
void (*AVI_UploadRawFrame)( int texture, int cols, int rows, int width, int height, const byte *data );
|
||||
void (*AVI_FreeVideo)( void *Avi );
|
||||
int (*AVI_IsActive)( void *Avi );
|
||||
void (*AVI_StreamSound)( void *Avi, int entnum, float fvol, float attn, float synctime );
|
||||
void (*AVI_Reserved0)( void ); // for potential interface expansion without broken compatibility
|
||||
void (*AVI_Reserved1)( void );
|
||||
void (*AVI_FreeVideo)( struct movie_state_s *Avi );
|
||||
qboolean (*AVI_IsActive)( struct movie_state_s *Avi );
|
||||
void (*AVI_StreamSound)( struct movie_state_s *Avi, int entnum, float fvol, float attn, float synctime );
|
||||
qboolean (*AVI_Think)( struct movie_state_s *Avi );
|
||||
qboolean (*AVI_SetParm)( struct movie_state_s *Avi, enum movie_parms_e parm, ... );
|
||||
|
||||
// glState related calls (must use this instead of normal gl-calls to prevent de-synchornize local states between engine and the client)
|
||||
void (*GL_Bind)( int tmu, unsigned int texnum );
|
||||
|
||||
@@ -13,19 +13,13 @@
|
||||
#endif // _WIN32
|
||||
|
||||
#include <sys/types.h> // off_t
|
||||
#ifdef STDINT_H
|
||||
#include STDINT_H
|
||||
#else // !STDINT_H
|
||||
#include <stdint.h>
|
||||
#endif // !STDINT_H
|
||||
#include <assert.h>
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef float vec_t;
|
||||
typedef vec_t vec2_t[2];
|
||||
#ifndef vec3_t // SDK renames it to Vector
|
||||
typedef vec_t vec3_t[3];
|
||||
#endif
|
||||
typedef vec_t vec4_t[4];
|
||||
typedef vec_t quat_t[4];
|
||||
typedef byte rgba_t[4]; // unsigned byte colorpack
|
||||
@@ -37,11 +31,11 @@ typedef uint32_t poolhandle_t;
|
||||
#undef true
|
||||
#undef false
|
||||
|
||||
// true and false are keywords in C++ and C23
|
||||
#if !__cplusplus && __STDC_VERSION__ < 202311L
|
||||
enum { false, true };
|
||||
#endif
|
||||
#ifndef __cplusplus
|
||||
typedef enum { false, true } qboolean;
|
||||
#else
|
||||
typedef int qboolean;
|
||||
#endif
|
||||
|
||||
#define MAX_STRING 256 // generic string
|
||||
#define MAX_VA_STRING 1024 // compatibility macro
|
||||
@@ -50,7 +44,6 @@ typedef int qboolean;
|
||||
|
||||
#define BIT( n ) ( 1U << ( n ))
|
||||
#define BIT64( n ) ( 1ULL << ( n ))
|
||||
|
||||
#define SetBits( iBitVector, bits ) ((iBitVector) = (iBitVector) | (bits))
|
||||
#define ClearBits( iBitVector, bits ) ((iBitVector) = (iBitVector) & ~(bits))
|
||||
#define FBitSet( iBitVector, bit ) ((iBitVector) & (bit))
|
||||
@@ -67,8 +60,6 @@ typedef int qboolean;
|
||||
#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' )
|
||||
#define ColorIndex( c ) ((( c ) - '0' ) & 7 )
|
||||
|
||||
#undef EXPORT
|
||||
|
||||
#if defined( __GNUC__ )
|
||||
#if defined( __i386__ )
|
||||
#define EXPORT __attribute__(( visibility( "default" ), force_align_arg_pointer ))
|
||||
|
||||
@@ -18,12 +18,11 @@ GNU General Public License for more details.
|
||||
//
|
||||
// avikit.c
|
||||
//
|
||||
typedef struct movie_state_s movie_state_t;
|
||||
typedef struct movie_state_s movie_state_t;
|
||||
int AVI_GetVideoFrameNumber( movie_state_t *Avi, float time );
|
||||
byte *AVI_GetVideoFrame( movie_state_t *Avi, int frame );
|
||||
qboolean AVI_GetVideoInfo( movie_state_t *Avi, int *xres, int *yres, float *duration );
|
||||
qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info );
|
||||
int AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, int offset, int length );
|
||||
qboolean AVI_HaveAudioTrack( const movie_state_t *Avi );
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet );
|
||||
movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio );
|
||||
int AVI_TimeToSoundPosition( movie_state_t *Avi, int time );
|
||||
@@ -34,4 +33,7 @@ movie_state_t *AVI_GetState( int num );
|
||||
qboolean AVI_Initailize( void );
|
||||
void AVI_Shutdown( void );
|
||||
|
||||
qboolean AVI_SetParm( movie_state_t *Avi, enum movie_parms_e parm, ... );
|
||||
qboolean AVI_Think( movie_state_t *Avi );
|
||||
|
||||
#endif // AVI_H
|
||||
|
||||
761
engine/client/avi/avi_ffmpeg.c
Normal file
761
engine/client/avi/avi_ffmpeg.c
Normal file
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
avi_ffmpreg.c - playing AVI files (ffmpeg backend)
|
||||
Copyright (C) FTEQW developers (for plugins/avplug/avdecode.c)
|
||||
Copyright (C) Sam Lantinga (for tests/testffmpeg.c)
|
||||
Copyright (C) 2023-2024 Alibek Omarov
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "defaults.h"
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
|
||||
static qboolean avi_initialized;
|
||||
static poolhandle_t avi_mempool;
|
||||
|
||||
#if XASH_AVI == AVI_FFMPEG
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswresample/swresample.h>
|
||||
|
||||
struct movie_state_s
|
||||
{
|
||||
// ffmpeg contexts
|
||||
AVFormatContext *fmt_ctx;
|
||||
AVCodecContext *video_ctx;
|
||||
AVCodecContext *audio_ctx;
|
||||
struct SwsContext *sws_ctx;
|
||||
struct SwrContext *swr_ctx;
|
||||
|
||||
AVPacket *pkt;
|
||||
AVFrame *aframe;
|
||||
AVFrame *vframe;
|
||||
AVFrame *vframe_copy;
|
||||
|
||||
int64_t first_time;
|
||||
int64_t last_time;
|
||||
|
||||
// video stream
|
||||
byte *dst;
|
||||
double duration;
|
||||
int video_stream;
|
||||
int xres;
|
||||
int yres;
|
||||
int dst_linesize;
|
||||
enum AVPixelFormat pix_fmt;
|
||||
|
||||
// rendering video parameters
|
||||
int x, y, w, h; // passed to R_DrawStretchRaw
|
||||
int texture; // passed to R_UploadStretchRaw
|
||||
|
||||
// audio stream
|
||||
int audio_stream;
|
||||
int channels;
|
||||
int rate;
|
||||
enum AVSampleFormat s_fmt;
|
||||
|
||||
byte *cached_audio;
|
||||
size_t cached_audio_buf_len; // absolute size of cached_audio array
|
||||
size_t cached_audio_len; // how many data in bytes we have in cached_audio array
|
||||
size_t cached_audio_pos; // how far we've read into cached_audio array
|
||||
|
||||
// rendering audio parameters
|
||||
float attn;
|
||||
int16_t entnum; // MAX_ENTITY_BITS is 13
|
||||
byte volume;
|
||||
byte active : 1;
|
||||
byte quiet : 1;
|
||||
byte paused : 1;
|
||||
};
|
||||
|
||||
qboolean AVI_SetParm( movie_state_t *Avi, enum movie_parms_e parm, ... )
|
||||
{
|
||||
qboolean ret = true;
|
||||
va_list va;
|
||||
va_start( va, parm );
|
||||
|
||||
while( parm != AVI_PARM_LAST )
|
||||
{
|
||||
float fval;
|
||||
int val;
|
||||
|
||||
switch( parm )
|
||||
{
|
||||
case AVI_RENDER_TEXNUM:
|
||||
Avi->texture = va_arg( va, int );
|
||||
break;
|
||||
case AVI_RENDER_X:
|
||||
Avi->x = va_arg( va, int );
|
||||
break;
|
||||
case AVI_RENDER_Y:
|
||||
Avi->y = va_arg( va, int );
|
||||
break;
|
||||
case AVI_RENDER_W:
|
||||
Avi->w = va_arg( va, int );
|
||||
break;
|
||||
case AVI_RENDER_H:
|
||||
Avi->h = va_arg( va, int );
|
||||
break;
|
||||
case AVI_REWIND:
|
||||
if( Avi->audio_ctx )
|
||||
avcodec_flush_buffers( Avi->audio_ctx );
|
||||
avcodec_flush_buffers( Avi->video_ctx );
|
||||
Avi->cached_audio_len = Avi->cached_audio_pos = 0;
|
||||
Avi->last_time = -1;
|
||||
Avi->first_time = 0;
|
||||
av_seek_frame( Avi->fmt_ctx, -1, 0, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD );
|
||||
break;
|
||||
case AVI_ENTNUM:
|
||||
val = va_arg( va, int );
|
||||
Avi->entnum = bound( 0, val, MAX_EDICTS );
|
||||
break;
|
||||
case AVI_VOLUME:
|
||||
val = va_arg( va, int );
|
||||
Avi->volume = bound( 0, val, 255 );
|
||||
break;
|
||||
case AVI_ATTN:
|
||||
fval = va_arg( va, double );
|
||||
Avi->attn = Q_max( 0.0f, fval );
|
||||
break;
|
||||
case AVI_PAUSE:
|
||||
Avi->paused = true;
|
||||
break;
|
||||
case AVI_RESUME:
|
||||
Avi->paused = false;
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
|
||||
parm = va_arg( va, enum movie_parms_e );
|
||||
}
|
||||
|
||||
va_end( va );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void AVI_SpewError( qboolean quiet, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
||||
static void AVI_SpewError( qboolean quiet, const char *fmt, ... )
|
||||
{
|
||||
char buf[MAX_VA_STRING];
|
||||
va_list va;
|
||||
|
||||
if( quiet )
|
||||
return;
|
||||
|
||||
va_start( va, fmt );
|
||||
Q_vsnprintf( buf, sizeof( buf ), fmt, va );
|
||||
va_end( va );
|
||||
|
||||
Con_Printf( S_ERROR "%s", buf );
|
||||
}
|
||||
|
||||
static void AVI_SpewAvError( qboolean quiet, const char *func, int numerr )
|
||||
{
|
||||
if( !quiet )
|
||||
Con_Printf( S_ERROR "%s: %s (%d)\n", func, av_err2str( numerr ), numerr );
|
||||
}
|
||||
|
||||
static int AVI_OpenCodecContext( AVCodecContext **dst_dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type, qboolean quiet )
|
||||
{
|
||||
const AVCodec *dec;
|
||||
AVCodecContext *dec_ctx;
|
||||
AVStream *st;
|
||||
int idx, ret;
|
||||
|
||||
if(( ret = av_find_best_stream( fmt_ctx, type, -1, -1, NULL, 0 )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_find_best_stream", ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
idx = ret;
|
||||
st = fmt_ctx->streams[idx];
|
||||
|
||||
if( !( dec = avcodec_find_decoder( st->codecpar->codec_id )))
|
||||
{
|
||||
AVI_SpewError( quiet, S_ERROR "Failed to find %s codec\n", av_get_media_type_string( type ));
|
||||
return AVERROR( EINVAL );
|
||||
}
|
||||
|
||||
if( !( dec_ctx = avcodec_alloc_context3( dec )))
|
||||
{
|
||||
AVI_SpewError( quiet, S_ERROR "Failed to allocate %s codec context", dec->name );
|
||||
return AVERROR( ENOMEM );
|
||||
}
|
||||
|
||||
if(( ret = avcodec_parameters_to_context( dec_ctx, st->codecpar )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "avcodec_parameters_to_context", ret );
|
||||
avcodec_free_context( &dec_ctx );
|
||||
return ret;
|
||||
}
|
||||
|
||||
dec_ctx->pkt_timebase = st->time_base;
|
||||
|
||||
if(( ret = avcodec_open2( dec_ctx, dec, NULL )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "avcodec_open2", ret );
|
||||
|
||||
avcodec_free_context( &dec_ctx );
|
||||
return ret;
|
||||
}
|
||||
|
||||
*dst_dec_ctx = dec_ctx;
|
||||
return idx; // always positive
|
||||
}
|
||||
|
||||
int AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AVI_TimeToSoundPosition( movie_state_t *Avi, int time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
qboolean AVI_GetVideoInfo( movie_state_t *Avi, int *xres, int *yres, float *duration )
|
||||
{
|
||||
if( !Avi->active )
|
||||
return false;
|
||||
|
||||
if( xres )
|
||||
*xres = Avi->xres;
|
||||
|
||||
if( yres )
|
||||
*yres = Avi->yres;
|
||||
|
||||
if( duration )
|
||||
*duration = Avi->duration;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean AVI_HaveAudioTrack( const movie_state_t *Avi )
|
||||
{
|
||||
return Avi ? Avi->active && Avi->audio_ctx : false;
|
||||
}
|
||||
|
||||
// just let it compile, bruh!
|
||||
byte *AVI_GetVideoFrame( movie_state_t *Avi, int target )
|
||||
{
|
||||
return Avi->dst;
|
||||
}
|
||||
|
||||
static void AVI_StreamAudio( movie_state_t *Avi )
|
||||
{
|
||||
int buffer_samples, file_samples, file_bytes;
|
||||
rawchan_t *ch = NULL;
|
||||
|
||||
// keep the same semantics, when S_RAW_SOUND_SOUNDTRACK doesn't play if S_StartStreaming wasn't enabled
|
||||
qboolean disable_stream = Avi->entnum == S_RAW_SOUND_SOUNDTRACK ? !s_listener.streaming : false;
|
||||
|
||||
if( !dma.initialized || disable_stream || s_listener.paused || !Avi->cached_audio )
|
||||
return;
|
||||
|
||||
ch = S_FindRawChannel( Avi->entnum, true );
|
||||
|
||||
if( !ch )
|
||||
return;
|
||||
|
||||
ch->master_vol = Avi->volume;
|
||||
ch->dist_mult = (Avi->attn / SND_CLIP_DISTANCE);
|
||||
|
||||
if( ch->s_rawend < soundtime )
|
||||
ch->s_rawend = soundtime;
|
||||
|
||||
while( ch->s_rawend < soundtime + ch->max_samples )
|
||||
{
|
||||
size_t copy;
|
||||
|
||||
buffer_samples = ch->max_samples - (ch->s_rawend - soundtime);
|
||||
|
||||
file_samples = buffer_samples * ((float)Avi->rate / SOUND_DMA_SPEED);
|
||||
if( file_samples <= 1 ) return; // no more samples need
|
||||
|
||||
file_bytes = file_samples * av_get_bytes_per_sample( Avi->s_fmt ) * Avi->channels;
|
||||
|
||||
if( file_bytes > ch->max_samples )
|
||||
{
|
||||
file_bytes = ch->max_samples;
|
||||
file_samples = file_bytes / ( av_get_bytes_per_sample( Avi->s_fmt ) * Avi->channels );
|
||||
}
|
||||
|
||||
copy = Q_min( file_bytes, Q_max( Avi->cached_audio_len - Avi->cached_audio_pos, 0 ));
|
||||
|
||||
if( !copy )
|
||||
break;
|
||||
|
||||
if( file_bytes > copy )
|
||||
{
|
||||
file_bytes = copy;
|
||||
file_samples = file_bytes / ( av_get_bytes_per_sample( Avi->s_fmt ) * Avi->channels );
|
||||
}
|
||||
|
||||
ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, file_samples, Avi->rate, av_get_bytes_per_sample( Avi->s_fmt ), Avi->channels, Avi->cached_audio + Avi->cached_audio_pos );
|
||||
Avi->cached_audio_pos += copy;
|
||||
}
|
||||
}
|
||||
|
||||
static void AVI_HandleAudio( movie_state_t *Avi, const AVFrame *frame )
|
||||
{
|
||||
int samples = frame->nb_samples;
|
||||
size_t len = samples * av_get_bytes_per_sample( Avi->s_fmt ) * Avi->channels;
|
||||
int outsamples;
|
||||
uint8_t *ptr;
|
||||
|
||||
// allocate data
|
||||
if( !Avi->cached_audio )
|
||||
{
|
||||
Avi->cached_audio_buf_len = len;
|
||||
Avi->cached_audio_pos = 0;
|
||||
Avi->cached_audio_len = 0;
|
||||
Avi->cached_audio = Mem_Malloc( avi_mempool, len );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( Avi->cached_audio_pos )
|
||||
{
|
||||
// Con_Printf( "%s: erasing old data of size %d\n", __func__, Avi->cached_audio_pos );
|
||||
Avi->cached_audio_len -= Avi->cached_audio_pos;
|
||||
memmove( Avi->cached_audio, Avi->cached_audio + Avi->cached_audio_pos, Avi->cached_audio_len );
|
||||
Avi->cached_audio_pos = 0;
|
||||
}
|
||||
|
||||
if( len + Avi->cached_audio_len > Avi->cached_audio_buf_len )
|
||||
{
|
||||
// Con_Printf( "%s: resizing old buffer of size %d to size %d\n", __func__, Avi->cached_audio_buf_len, len + Avi->cached_audio_buf_len );
|
||||
Avi->cached_audio_buf_len = len + Avi->cached_audio_len;
|
||||
Avi->cached_audio = Mem_Realloc( avi_mempool, Avi->cached_audio, Avi->cached_audio_buf_len );
|
||||
}
|
||||
}
|
||||
|
||||
ptr = Avi->cached_audio + Avi->cached_audio_len;
|
||||
outsamples = swr_convert( Avi->swr_ctx, &ptr, samples, (void *)frame->data, samples );
|
||||
Avi->cached_audio_len += outsamples * av_get_bytes_per_sample( Avi->s_fmt ) * Avi->channels;
|
||||
|
||||
// Con_Printf( "%s: got audio chunk of size %d samples\n", __func__, outsamples );
|
||||
}
|
||||
|
||||
qboolean AVI_Think( movie_state_t *Avi )
|
||||
{
|
||||
qboolean decoded = false;
|
||||
qboolean flushing = false;
|
||||
qboolean redraw = false;
|
||||
const double timebase = (double)Avi->video_ctx->pkt_timebase.den / Avi->video_ctx->pkt_timebase.num;
|
||||
int64_t curtime = round( Platform_DoubleTime() * timebase );
|
||||
|
||||
if( !Avi->first_time ) // always remember at which timestamp we started playing
|
||||
Avi->first_time = curtime;
|
||||
|
||||
if( Avi->paused )
|
||||
{
|
||||
// FIXME: there might be a better way to do this
|
||||
Avi->last_time = curtime;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Con_NPrintf( 1, "cached_audio_buf_len = %zu", Avi->cached_audio_buf_len );
|
||||
|
||||
while( 1 ) // try to get multiple decoded frames to keep up when we're running at low fps
|
||||
{
|
||||
int res;
|
||||
|
||||
AVI_StreamAudio( Avi ); // always flush audio buffers
|
||||
|
||||
// recalc time so we always play last possible frame
|
||||
curtime = round( Platform_DoubleTime() * timebase );
|
||||
|
||||
if( Avi->last_time > curtime )
|
||||
break;
|
||||
|
||||
if(( res = av_read_frame( Avi->fmt_ctx, Avi->pkt )) >= 0 )
|
||||
{
|
||||
if( Avi->pkt->stream_index == Avi->audio_stream )
|
||||
{
|
||||
res = avcodec_send_packet( Avi->audio_ctx, Avi->pkt );
|
||||
if( res < 0 )
|
||||
AVI_SpewAvError( Avi->quiet, "avcodec_send_packet (audio)", res );
|
||||
}
|
||||
else if( Avi->pkt->stream_index == Avi->video_stream )
|
||||
{
|
||||
res = avcodec_send_packet( Avi->video_ctx, Avi->pkt );
|
||||
if( res < 0 )
|
||||
AVI_SpewAvError( Avi->quiet, "avcodec_send_packet (video)", res );
|
||||
}
|
||||
av_packet_unref( Avi->pkt );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( res != AVERROR_EOF )
|
||||
AVI_SpewAvError( Avi->quiet, "av_read_frame", res );
|
||||
|
||||
if( Avi->audio_ctx )
|
||||
avcodec_flush_buffers( Avi->audio_ctx );
|
||||
|
||||
avcodec_flush_buffers( Avi->video_ctx );
|
||||
flushing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if( Avi->audio_ctx )
|
||||
{
|
||||
while( avcodec_receive_frame( Avi->audio_ctx, Avi->aframe ) == 0 )
|
||||
{
|
||||
AVI_HandleAudio( Avi, Avi->aframe );
|
||||
decoded = true;
|
||||
}
|
||||
}
|
||||
|
||||
while( avcodec_receive_frame( Avi->video_ctx, Avi->vframe ) == 0 )
|
||||
{
|
||||
Avi->last_time = Avi->first_time + Avi->vframe->best_effort_timestamp;
|
||||
decoded = true;
|
||||
|
||||
if( FBitSet( Avi->vframe->flags, AV_FRAME_FLAG_CORRUPT|AV_FRAME_FLAG_DISCARD ))
|
||||
continue;
|
||||
|
||||
if( Avi->vframe->decode_error_flags != 0 )
|
||||
continue;
|
||||
|
||||
av_frame_unref( Avi->vframe_copy );
|
||||
if( av_frame_ref( Avi->vframe_copy, Avi->vframe ) == 0 )
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( redraw )
|
||||
{
|
||||
sws_scale( Avi->sws_ctx, (void*)Avi->vframe_copy->data, Avi->vframe_copy->linesize, 0, Avi->video_ctx->height,
|
||||
&Avi->dst, &Avi->dst_linesize );
|
||||
av_frame_unref( Avi->vframe_copy );
|
||||
}
|
||||
|
||||
if( Avi->texture == 0 )
|
||||
{
|
||||
int w = Avi->w >= 0 ? Avi->w : refState.width;
|
||||
int h = Avi->h >= 0 ? Avi->h : refState.height;
|
||||
|
||||
ref.dllFuncs.R_DrawStretchRaw( Avi->x, Avi->y, w, h, Avi->xres, Avi->yres, Avi->dst, redraw );
|
||||
}
|
||||
else if( redraw && Avi->texture > 0 )
|
||||
ref.dllFuncs.AVI_UploadRawFrame( Avi->texture, Avi->xres, Avi->yres, Avi->w, Avi->h, Avi->dst );
|
||||
|
||||
if( flushing && !decoded )
|
||||
return false; // probably hit an EOF
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet )
|
||||
{
|
||||
byte *dst[4];
|
||||
int dst_linesize[4];
|
||||
int ret;
|
||||
|
||||
if( Avi->active )
|
||||
AVI_CloseVideo( Avi );
|
||||
|
||||
if( !filename || !avi_initialized )
|
||||
return;
|
||||
|
||||
Avi->active = false;
|
||||
Avi->quiet = quiet;
|
||||
Avi->video_ctx = Avi->audio_ctx = NULL;
|
||||
Avi->fmt_ctx = NULL;
|
||||
|
||||
if(( ret = avformat_open_input( &Avi->fmt_ctx, filename, NULL, NULL )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "avformat_open_input", ret );
|
||||
return;
|
||||
}
|
||||
|
||||
if(( ret = avformat_find_stream_info( Avi->fmt_ctx, NULL )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "avformat_find_stream_info", ret );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !( Avi->pkt = av_packet_alloc( )))
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_packet_alloc", 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !( Avi->vframe = av_frame_alloc( )))
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_frame_alloc (video)", 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !( Avi->vframe_copy = av_frame_alloc( )))
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_frame_alloc (video)", 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Avi->video_stream = AVI_OpenCodecContext( &Avi->video_ctx, Avi->fmt_ctx, AVMEDIA_TYPE_VIDEO, quiet );
|
||||
|
||||
if( Avi->video_stream < 0 )
|
||||
return;
|
||||
|
||||
Avi->xres = Avi->video_ctx->width;
|
||||
Avi->yres = Avi->video_ctx->height;
|
||||
Avi->pix_fmt = Avi->video_ctx->pix_fmt;
|
||||
Avi->duration = Avi->fmt_ctx->duration / (double)AV_TIME_BASE;
|
||||
Avi->entnum = S_RAW_SOUND_SOUNDTRACK;
|
||||
Avi->attn = ATTN_NONE;
|
||||
Avi->volume = 255;
|
||||
|
||||
if( !( Avi->sws_ctx = sws_getContext( Avi->xres, Avi->yres, Avi->pix_fmt,
|
||||
Avi->xres, Avi->yres, AV_PIX_FMT_BGR0, SWS_POINT, NULL, NULL, NULL )))
|
||||
{
|
||||
AVI_SpewAvError( quiet, "sws_getContext", 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if(( ret = av_image_alloc( dst, dst_linesize, Avi->xres, Avi->yres, AV_PIX_FMT_BGR0, 1 )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_image_alloc", ret );
|
||||
return;
|
||||
}
|
||||
|
||||
Avi->dst = dst[0];
|
||||
Avi->dst_linesize = dst_linesize[0];
|
||||
|
||||
if( load_audio )
|
||||
{
|
||||
if( !( Avi->aframe = av_frame_alloc( )))
|
||||
{
|
||||
AVI_SpewAvError( quiet, "av_frame_alloc (audio)", 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
Avi->audio_stream = AVI_OpenCodecContext( &Avi->audio_ctx, Avi->fmt_ctx, AVMEDIA_TYPE_AUDIO, quiet );
|
||||
|
||||
// audio stream was requested but it wasn't found
|
||||
if( Avi->audio_stream < 0 )
|
||||
return;
|
||||
|
||||
Avi->channels = Q_min( Avi->audio_ctx->ch_layout.nb_channels, 2 );
|
||||
if( Avi->audio_ctx->sample_fmt == AV_SAMPLE_FMT_U8 || Avi->audio_ctx->sample_fmt == AV_SAMPLE_FMT_U8P )
|
||||
Avi->s_fmt = AV_SAMPLE_FMT_U8;
|
||||
else Avi->s_fmt = AV_SAMPLE_FMT_S16;
|
||||
Avi->rate = Avi->audio_ctx->sample_rate;
|
||||
|
||||
if(( ret = swr_alloc_set_opts2( &Avi->swr_ctx, &Avi->audio_ctx->ch_layout, Avi->s_fmt, Avi->rate,
|
||||
&Avi->audio_ctx->ch_layout, Avi->audio_ctx->sample_fmt, Avi->audio_ctx->sample_rate, 0, 0 )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "swr_alloc_set_opts2", ret );
|
||||
return;
|
||||
}
|
||||
|
||||
if(( ret = swr_init( Avi->swr_ctx )) < 0 )
|
||||
{
|
||||
AVI_SpewAvError( quiet, "swr_init", ret );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Avi->active = true;
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
if( Avi->active )
|
||||
{
|
||||
if( Avi->cached_audio )
|
||||
Mem_Free( Avi->cached_audio );
|
||||
|
||||
swr_free( &Avi->swr_ctx );
|
||||
avcodec_free_context( &Avi->audio_ctx );
|
||||
av_frame_free( &Avi->aframe );
|
||||
|
||||
av_free( Avi->dst );
|
||||
sws_freeContext( Avi->sws_ctx );
|
||||
avcodec_free_context( &Avi->video_ctx );
|
||||
av_frame_free( &Avi->vframe );
|
||||
av_frame_free( &Avi->vframe_copy );
|
||||
|
||||
av_packet_free( &Avi->pkt );
|
||||
|
||||
avformat_close_input( &Avi->fmt_ctx );
|
||||
}
|
||||
|
||||
memset( Avi, 0, sizeof( *Avi ));
|
||||
}
|
||||
|
||||
static void AVI_PrintFFmpegVersion( void )
|
||||
{
|
||||
uint ver;
|
||||
|
||||
// print version we're compiled with and which version we're running with
|
||||
ver = avutil_version();
|
||||
Con_Reportf( "AVI: %s (runtime %d.%d.%d)\n", LIBAVUTIL_IDENT, AV_VERSION_MAJOR( ver ), AV_VERSION_MINOR( ver ), AV_VERSION_MICRO( ver ));
|
||||
|
||||
ver = avformat_version();
|
||||
Con_Reportf( "AVI: %s (runtime %d.%d.%d)\n", LIBAVFORMAT_IDENT, AV_VERSION_MAJOR( ver ), AV_VERSION_MINOR( ver ), AV_VERSION_MICRO( ver ));
|
||||
|
||||
ver = avformat_version();
|
||||
Con_Reportf( "AVI: %s (runtime %d.%d.%d)\n", LIBAVCODEC_IDENT, AV_VERSION_MAJOR( ver ), AV_VERSION_MINOR( ver ), AV_VERSION_MICRO( ver ));
|
||||
|
||||
ver = swscale_version();
|
||||
Con_Reportf( "AVI: %s (runtime %d.%d.%d)\n", LIBSWSCALE_IDENT, AV_VERSION_MAJOR( ver ), AV_VERSION_MINOR( ver ), AV_VERSION_MICRO( ver ));
|
||||
|
||||
ver = swresample_version();
|
||||
Con_Reportf( "AVI: %s (runtime %d.%d.%d)\n", LIBSWRESAMPLE_IDENT, AV_VERSION_MAJOR( ver ), AV_VERSION_MINOR( ver ), AV_VERSION_MICRO( ver ));
|
||||
}
|
||||
#else
|
||||
struct movie_state_s
|
||||
{
|
||||
qboolean active;
|
||||
};
|
||||
|
||||
int AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte *AVI_GetVideoFrame( movie_state_t *Avi, int frame )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qboolean AVI_GetVideoInfo( movie_state_t *Avi, int *xres, int *yres, float *duration )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean AVI_HaveAudioTrack( const movie_state_t *Avi )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int AVI_TimeToSoundPosition( movie_state_t *Avi, int time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
qboolean AVI_Think( movie_state_t *Avi )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean AVI_SetParm( movie_state_t *Avi, enum movie_parms_e parm, ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void AVI_PrintFFmpegVersion( void )
|
||||
{
|
||||
|
||||
}
|
||||
#endif // XASH_AVI == AVI_NULL
|
||||
|
||||
static movie_state_t avi[2];
|
||||
movie_state_t *AVI_GetState( int num )
|
||||
{
|
||||
return &avi[num];
|
||||
}
|
||||
|
||||
qboolean AVI_IsActive( movie_state_t *Avi )
|
||||
{
|
||||
return Avi ? Avi->active : false;
|
||||
}
|
||||
|
||||
qboolean AVI_Initailize( void )
|
||||
{
|
||||
if( XASH_AVI == AVI_NULL )
|
||||
{
|
||||
Con_Printf( "AVI: Not supported\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Sys_CheckParm( "-noavi" ))
|
||||
{
|
||||
Con_Printf( "AVI: Disabled\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
AVI_PrintFFmpegVersion();
|
||||
|
||||
avi_initialized = true;
|
||||
avi_mempool = Mem_AllocPool( "AVI Zone" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AVI_Shutdown( void )
|
||||
{
|
||||
Mem_FreePool( &avi_mempool );
|
||||
avi_initialized = XASH_AVI != AVI_NULL;
|
||||
}
|
||||
|
||||
movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio )
|
||||
{
|
||||
movie_state_t *Avi;
|
||||
string path;
|
||||
const char *fullpath;
|
||||
|
||||
// fast reject
|
||||
if( !avi_initialized )
|
||||
return NULL;
|
||||
|
||||
// open cinematic
|
||||
Q_snprintf( path, sizeof( path ), "media/%s", filename );
|
||||
COM_DefaultExtension( path, ".avi", sizeof( path ));
|
||||
fullpath = FS_GetDiskPath( path, false );
|
||||
|
||||
if( FS_FileExists( path, false ) && !fullpath )
|
||||
{
|
||||
Con_Printf( "Couldn't load %s from packfile. Please extract it\n", path );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Avi = Mem_Calloc( avi_mempool, sizeof( movie_state_t ));
|
||||
AVI_OpenVideo( Avi, fullpath, load_audio, false );
|
||||
|
||||
if( !AVI_IsActive( Avi ))
|
||||
{
|
||||
AVI_FreeVideo( Avi ); // something bad happens
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// all done
|
||||
return Avi;
|
||||
}
|
||||
|
||||
void AVI_FreeVideo( movie_state_t *Avi )
|
||||
{
|
||||
if( !Avi )
|
||||
return;
|
||||
|
||||
AVI_CloseVideo( Avi );
|
||||
|
||||
if( Mem_IsAllocatedExt( avi_mempool, Avi ))
|
||||
Mem_Free( Avi );
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
avi_stub.c - playing AVI files (stub)
|
||||
Copyright (C) 2018 a1batross
|
||||
|
||||
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 "build.h"
|
||||
#if !XASH_WIN32
|
||||
#include "common.h"
|
||||
|
||||
int AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte *AVI_GetVideoFrame( movie_state_t *Avi, int frame )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qboolean AVI_GetVideoInfo( movie_state_t *Avi, int *xres, int *yres, float *duration )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, int offset, int length )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int AVI_TimeToSoundPosition( movie_state_t *Avi, int time )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
qboolean AVI_IsActive( movie_state_t *Avi )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AVI_FreeVideo( movie_state_t *Avi )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
movie_state_t *AVI_GetState( int num )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qboolean AVI_Initailize( void )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AVI_Shutdown( void )
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
@@ -1,732 +0,0 @@
|
||||
/*
|
||||
avi_win.c - playing AVI files (based on original AVIKit code, Win32 version)
|
||||
Copyright (c) 2003-2004, Ruari O'Sullivan
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "build.h"
|
||||
#if XASH_WIN32
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include <vfw.h> // video for windows
|
||||
|
||||
// msvfw32.dll exports
|
||||
static HDRAWDIB (_stdcall *pDrawDibOpen)( void );
|
||||
static BOOL (_stdcall *pDrawDibClose)( HDRAWDIB hdd );
|
||||
static BOOL (_stdcall *pDrawDibDraw)( HDRAWDIB, HDC, int, int, int, int, LPBITMAPINFOHEADER, void*, int, int, int, int, uint );
|
||||
|
||||
static dllfunc_t msvfw_funcs[] =
|
||||
{
|
||||
{ "DrawDibOpen", (void **) &pDrawDibOpen },
|
||||
{ "DrawDibDraw", (void **) &pDrawDibDraw },
|
||||
{ "DrawDibClose", (void **) &pDrawDibClose },
|
||||
};
|
||||
|
||||
dll_info_t msvfw_dll = { "msvfw32.dll", msvfw_funcs, ARRAYSIZE( msvfw_funcs ), false };
|
||||
|
||||
// msacm32.dll exports
|
||||
static MMRESULT (_stdcall *pacmStreamOpen)( LPHACMSTREAM, HACMDRIVER, LPWAVEFORMATEX, LPWAVEFORMATEX, LPWAVEFILTER, DWORD, DWORD, DWORD );
|
||||
static MMRESULT (_stdcall *pacmStreamPrepareHeader)( HACMSTREAM, LPACMSTREAMHEADER, DWORD );
|
||||
static MMRESULT (_stdcall *pacmStreamUnprepareHeader)( HACMSTREAM, LPACMSTREAMHEADER, DWORD );
|
||||
static MMRESULT (_stdcall *pacmStreamConvert)( HACMSTREAM, LPACMSTREAMHEADER, DWORD );
|
||||
static MMRESULT (_stdcall *pacmStreamSize)( HACMSTREAM, DWORD, LPDWORD, DWORD );
|
||||
static MMRESULT (_stdcall *pacmStreamClose)( HACMSTREAM, DWORD );
|
||||
|
||||
static dllfunc_t msacm_funcs[] =
|
||||
{
|
||||
{ "acmStreamOpen", (void **) &pacmStreamOpen },
|
||||
{ "acmStreamPrepareHeader", (void **) &pacmStreamPrepareHeader },
|
||||
{ "acmStreamUnprepareHeader", (void **) &pacmStreamUnprepareHeader },
|
||||
{ "acmStreamConvert", (void **) &pacmStreamConvert },
|
||||
{ "acmStreamSize", (void **) &pacmStreamSize },
|
||||
{ "acmStreamClose", (void **) &pacmStreamClose },
|
||||
};
|
||||
|
||||
dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, ARRAYSIZE( msacm_funcs ), false };
|
||||
|
||||
// avifil32.dll exports
|
||||
static int (_stdcall *pAVIStreamInfo)( PAVISTREAM pavi, AVISTREAMINFO *psi, LONG lSize );
|
||||
static int (_stdcall *pAVIStreamRead)( PAVISTREAM pavi, LONG lStart, LONG lSamples, void *lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples );
|
||||
static PGETFRAME (_stdcall *pAVIStreamGetFrameOpen)( PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted );
|
||||
static int (_stdcall *pAVIStreamTimeToSample)( PAVISTREAM pavi, LONG lTime );
|
||||
static void* (_stdcall *pAVIStreamGetFrame)( PGETFRAME pg, LONG lPos );
|
||||
static int (_stdcall *pAVIStreamGetFrameClose)( PGETFRAME pg );
|
||||
static dword (_stdcall *pAVIStreamRelease)( PAVISTREAM pavi );
|
||||
static int (_stdcall *pAVIFileOpenW)( PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode, LPCLSID lpHandler );
|
||||
static int (_stdcall *pAVIFileGetStream)( PAVIFILE pfile, PAVISTREAM *ppavi, DWORD fccType, LONG lParam );
|
||||
static int (_stdcall *pAVIStreamReadFormat)( PAVISTREAM pavi, LONG lPos,LPVOID lpFormat, LONG *lpcbFormat );
|
||||
static int (_stdcall *pAVIStreamStart)( PAVISTREAM pavi );
|
||||
static dword (_stdcall *pAVIFileRelease)( PAVIFILE pfile );
|
||||
static void (_stdcall *pAVIFileInit)( void );
|
||||
static void (_stdcall *pAVIFileExit)( void );
|
||||
|
||||
static dllfunc_t avifile_funcs[] =
|
||||
{
|
||||
{ "AVIFileExit", (void **) &pAVIFileExit },
|
||||
{ "AVIFileGetStream", (void **) &pAVIFileGetStream },
|
||||
{ "AVIFileInit", (void **) &pAVIFileInit },
|
||||
{ "AVIFileOpenW", (void **) &pAVIFileOpenW },
|
||||
{ "AVIFileRelease", (void **) &pAVIFileRelease },
|
||||
{ "AVIStreamGetFrame", (void **) &pAVIStreamGetFrame },
|
||||
{ "AVIStreamGetFrameClose", (void **) &pAVIStreamGetFrameClose },
|
||||
{ "AVIStreamGetFrameOpen", (void **) &pAVIStreamGetFrameOpen },
|
||||
{ "AVIStreamInfoA", (void **) &pAVIStreamInfo },
|
||||
{ "AVIStreamRead", (void **) &pAVIStreamRead },
|
||||
{ "AVIStreamReadFormat", (void **) &pAVIStreamReadFormat },
|
||||
{ "AVIStreamRelease", (void **) &pAVIStreamRelease },
|
||||
{ "AVIStreamStart", (void **) &pAVIStreamStart },
|
||||
{ "AVIStreamTimeToSample", (void **) &pAVIStreamTimeToSample },
|
||||
};
|
||||
|
||||
dll_info_t avifile_dll = { "avifil32.dll", avifile_funcs, ARRAYSIZE( avifile_funcs ), false };
|
||||
|
||||
typedef struct movie_state_s
|
||||
{
|
||||
qboolean active;
|
||||
qboolean quiet; // ignore error messages
|
||||
|
||||
PAVIFILE pfile; // avi file pointer
|
||||
PAVISTREAM video_stream; // video stream pointer
|
||||
PGETFRAME video_getframe; // pointer to getframe object for video stream
|
||||
int video_frames; // total frames
|
||||
int video_xres; // video stream resolution
|
||||
int video_yres;
|
||||
float video_fps; // video stream fps
|
||||
|
||||
PAVISTREAM audio_stream; // audio stream pointer
|
||||
WAVEFORMAT *audio_header; // audio stream header
|
||||
int audio_header_size; // WAVEFORMAT is returned for PCM data; WAVEFORMATEX for others
|
||||
int audio_codec; // WAVE_FORMAT_PCM is oldstyle: anything else needs conversion
|
||||
int audio_length; // in converted samples
|
||||
int audio_bytes_per_sample; // guess.
|
||||
|
||||
// compressed audio specific data
|
||||
dword cpa_blockalign; // block size to read
|
||||
HACMSTREAM cpa_conversion_stream;
|
||||
ACMSTREAMHEADER cpa_conversion_header;
|
||||
byte *cpa_srcbuffer; // maintained buffer for raw data
|
||||
byte *cpa_dstbuffer;
|
||||
|
||||
dword cpa_blocknum; // current block
|
||||
dword cpa_blockpos; // read position in current block
|
||||
dword cpa_blockoffset; // corresponding offset in bytes in the output stream
|
||||
|
||||
// for additional unpack Ms-RLE codecs etc
|
||||
HDC hDC; // compatible DC
|
||||
HDRAWDIB hDD; // DrawDib handler
|
||||
HBITMAP hBitmap; // for DIB conversions
|
||||
byte *pframe_data; // converted framedata
|
||||
} movie_state_t;
|
||||
|
||||
static qboolean avi_initialized = false;
|
||||
static movie_state_t avi[2];
|
||||
|
||||
// Converts a compressed audio stream into uncompressed PCM.
|
||||
qboolean AVI_ACMConvertAudio( movie_state_t *Avi )
|
||||
{
|
||||
WAVEFORMATEX dest_header, *sh, *dh;
|
||||
AVISTREAMINFO stream_info;
|
||||
dword dest_length;
|
||||
short bits;
|
||||
|
||||
// WMA codecs, both versions - they simply don't work.
|
||||
if( Avi->audio_header->wFormatTag == 0x160 || Avi->audio_header->wFormatTag == 0x161 )
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_Reportf( S_ERROR "ACM does not support this audio codec.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get audio stream info to work with
|
||||
pAVIStreamInfo( Avi->audio_stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( Avi->audio_header_size < sizeof( WAVEFORMATEX ))
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_Reportf( S_ERROR "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
sh = (WAVEFORMATEX *)Avi->audio_header;
|
||||
bits = 16; // predict state
|
||||
|
||||
// how much of this is actually required?
|
||||
dest_header.wFormatTag = WAVE_FORMAT_PCM; // yay
|
||||
dest_header.wBitsPerSample = bits; // 16bit
|
||||
dest_header.nChannels = sh->nChannels;
|
||||
dest_header.nSamplesPerSec = sh->nSamplesPerSec; // take straight from the source stream
|
||||
dest_header.nAvgBytesPerSec = (bits >> 3) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = (bits >> 3) * sh->nChannels;
|
||||
dest_header.cbSize = 0; // no more data.
|
||||
|
||||
dh = &dest_header;
|
||||
|
||||
// open the stream
|
||||
if( pacmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
// try with 8 bit destination instead
|
||||
bits = 8;
|
||||
|
||||
dest_header.wBitsPerSample = bits; // 8bit
|
||||
dest_header.nAvgBytesPerSec = ( bits >> 3 ) * sh->nChannels * sh->nSamplesPerSec;
|
||||
dest_header.nBlockAlign = ( bits >> 3 ) * sh->nChannels; // 1 sample at a time
|
||||
|
||||
if( pacmStreamOpen( &Avi->cpa_conversion_stream, NULL, sh, dh, NULL, 0, 0, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_Reportf( S_ERROR "ACM failed to open conversion stream.\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Avi->cpa_blockalign = sh->nBlockAlign;
|
||||
dest_length = 0;
|
||||
|
||||
// mp3 specific fix
|
||||
if( sh->wFormatTag == 0x55 )
|
||||
{
|
||||
LPMPEGLAYER3WAVEFORMAT k;
|
||||
|
||||
k = (LPMPEGLAYER3WAVEFORMAT)sh;
|
||||
Avi->cpa_blockalign = k->nBlockSize;
|
||||
}
|
||||
|
||||
// get the size of the output buffer for streaming the compressed audio
|
||||
if( pacmStreamSize( Avi->cpa_conversion_stream, Avi->cpa_blockalign, &dest_length, ACM_STREAMSIZEF_SOURCE ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_Reportf( S_ERROR "Couldn't get ACM conversion stream size.\n" );
|
||||
pacmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_srcbuffer = (byte *)Mem_Malloc( cls.mempool, Avi->cpa_blockalign );
|
||||
Avi->cpa_dstbuffer = (byte *)Mem_Malloc( cls.mempool, dest_length ); // maintained buffer for raw data
|
||||
|
||||
// prep the headers!
|
||||
Avi->cpa_conversion_header.cbStruct = sizeof( ACMSTREAMHEADER );
|
||||
Avi->cpa_conversion_header.fdwStatus = 0;
|
||||
Avi->cpa_conversion_header.dwUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbSrc = Avi->cpa_srcbuffer; // source buffer
|
||||
Avi->cpa_conversion_header.cbSrcLength = Avi->cpa_blockalign; // source buffer size
|
||||
Avi->cpa_conversion_header.cbSrcLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwSrcUser = 0; // no user data
|
||||
Avi->cpa_conversion_header.pbDst = Avi->cpa_dstbuffer; // dest buffer
|
||||
Avi->cpa_conversion_header.cbDstLength = dest_length; // dest buffer size
|
||||
Avi->cpa_conversion_header.cbDstLengthUsed = 0;
|
||||
Avi->cpa_conversion_header.dwDstUser = 0; // no user data
|
||||
|
||||
if( pacmStreamPrepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_Reportf( S_ERROR "couldn't prepare stream headers.\n" );
|
||||
pacmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
return false;
|
||||
}
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->audio_bytes_per_sample = (bits >> 3 ) * Avi->audio_header->nChannels;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean AVI_GetVideoInfo( movie_state_t *Avi, int *xres, int *yres, float *duration )
|
||||
{
|
||||
if( !Avi->active )
|
||||
return false;
|
||||
|
||||
if( xres != NULL )
|
||||
*xres = Avi->video_xres;
|
||||
|
||||
if( yres != NULL )
|
||||
*yres = Avi->video_yres;
|
||||
|
||||
if( duration != NULL )
|
||||
*duration = (float)Avi->video_frames / Avi->video_fps;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns a unique frame identifier
|
||||
int AVI_GetVideoFrameNumber( movie_state_t *Avi, float time )
|
||||
{
|
||||
if( !Avi->active )
|
||||
return 0;
|
||||
|
||||
return (time * Avi->video_fps);
|
||||
}
|
||||
|
||||
int AVI_TimeToSoundPosition( movie_state_t *Avi, int time )
|
||||
{
|
||||
if( !Avi->active || !Avi->audio_stream )
|
||||
return 0;
|
||||
|
||||
// UNDONE: what about compressed audio?
|
||||
return pAVIStreamTimeToSample( Avi->audio_stream, time ) * Avi->audio_bytes_per_sample;
|
||||
}
|
||||
|
||||
// gets the raw frame data
|
||||
byte *AVI_GetVideoFrame( movie_state_t *Avi, int frame )
|
||||
{
|
||||
LPBITMAPINFOHEADER frame_info;
|
||||
byte *frame_raw;
|
||||
|
||||
if( !Avi->active ) return NULL;
|
||||
|
||||
if( frame >= Avi->video_frames )
|
||||
frame = Avi->video_frames - 1;
|
||||
|
||||
frame_info = (LPBITMAPINFOHEADER)pAVIStreamGetFrame( Avi->video_getframe, frame );
|
||||
frame_raw = (byte *)frame_info + frame_info->biSize + frame_info->biClrUsed * sizeof( RGBQUAD );
|
||||
pDrawDibDraw( Avi->hDD, Avi->hDC, 0, 0, Avi->video_xres, Avi->video_yres, frame_info, frame_raw, 0, 0, Avi->video_xres, Avi->video_yres, 0 );
|
||||
|
||||
return Avi->pframe_data;
|
||||
}
|
||||
|
||||
qboolean AVI_GetAudioInfo( movie_state_t *Avi, wavdata_t *snd_info )
|
||||
{
|
||||
if( !Avi->active || Avi->audio_stream == NULL || snd_info == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_info->rate = Avi->audio_header->nSamplesPerSec;
|
||||
snd_info->channels = Avi->audio_header->nChannels;
|
||||
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM ) // uncompressed audio!
|
||||
snd_info->width = ( Avi->audio_bytes_per_sample > Avi->audio_header->nChannels ) ? 2 : 1;
|
||||
else snd_info->width = 2; // assume compressed audio is always 16 bit
|
||||
|
||||
snd_info->size = snd_info->rate * snd_info->width * snd_info->channels;
|
||||
snd_info->loopStart = 0; // using loopStart as streampos
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// sync the current audio read to a specific offset
|
||||
qboolean AVI_SeekPosition( movie_state_t *Avi, dword offset )
|
||||
{
|
||||
int breaker;
|
||||
|
||||
if( offset < Avi->cpa_blockoffset ) // well, shit. we can't seek backwards... restart
|
||||
{
|
||||
if( Avi->cpa_blockoffset - offset < 500000 )
|
||||
return false; // don't bother if it's gonna catch up soon
|
||||
|
||||
Avi->cpa_blocknum = 0; // start at 0, eh.
|
||||
Avi->cpa_blockpos = 0;
|
||||
Avi->cpa_blockoffset = 0;
|
||||
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START );
|
||||
|
||||
// convert first chunk twice. it often fails the first time. BLACK MAGIC.
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
}
|
||||
|
||||
// now then: seek forwards to the required block
|
||||
breaker = 30; // maximum zero blocks: anti-freeze protection
|
||||
|
||||
while( Avi->cpa_blockoffset + Avi->cpa_conversion_header.cbDstLengthUsed < offset )
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
if( Avi->cpa_conversion_header.cbDstLengthUsed == 0 )
|
||||
breaker--;
|
||||
else breaker = 30;
|
||||
|
||||
if( breaker <= 0 )
|
||||
return false;
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
}
|
||||
|
||||
// seek to the right position inside the block
|
||||
Avi->cpa_blockpos = offset - Avi->cpa_blockoffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get a chunk of audio from the stream (in bytes)
|
||||
int AVI_GetAudioChunk( movie_state_t *Avi, char *audiodata, int offset, int length )
|
||||
{
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
// zero data past the end of the file
|
||||
if( offset + length > Avi->audio_length )
|
||||
{
|
||||
if( offset <= Avi->audio_length )
|
||||
{
|
||||
int remaining_length = Avi->audio_length - offset;
|
||||
|
||||
AVI_GetAudioChunk( Avi, audiodata, offset, remaining_length );
|
||||
|
||||
for( i = remaining_length; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we out of soundtrack, just zeroing buffer
|
||||
for( i = 0; i < length; i++ )
|
||||
audiodata[i] = 0;
|
||||
|
||||
// return length;
|
||||
}
|
||||
}
|
||||
|
||||
// uncompressed audio!
|
||||
if( Avi->audio_codec == WAVE_FORMAT_PCM )
|
||||
{
|
||||
// very simple - read straight out
|
||||
pAVIStreamRead( Avi->audio_stream, offset / Avi->audio_bytes_per_sample, length / Avi->audio_bytes_per_sample, audiodata, length, &result, NULL );
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// compressed audio!
|
||||
result = 0;
|
||||
|
||||
// seek to correct chunk and all that stuff
|
||||
if( !AVI_SeekPosition( Avi, offset ))
|
||||
return 0; // don't continue if we're waiting for the play pointer to catch up
|
||||
|
||||
while( length > 0 )
|
||||
{
|
||||
int blockread = Avi->cpa_conversion_header.cbDstLengthUsed - Avi->cpa_blockpos;
|
||||
|
||||
if( blockread <= 0 ) // read next
|
||||
{
|
||||
Avi->cpa_blocknum++;
|
||||
Avi->cpa_blockoffset += Avi->cpa_conversion_header.cbDstLengthUsed;
|
||||
|
||||
pAVIStreamRead( Avi->audio_stream, Avi->cpa_blocknum * Avi->cpa_blockalign, Avi->cpa_blockalign, Avi->cpa_srcbuffer, Avi->cpa_blockalign, NULL, NULL );
|
||||
pacmStreamConvert( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, ACM_STREAMCONVERTF_BLOCKALIGN );
|
||||
|
||||
Avi->cpa_blockpos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( blockread > length )
|
||||
blockread = length;
|
||||
|
||||
// copy the data
|
||||
memcpy( audiodata + result, (void *)( Avi->cpa_dstbuffer + Avi->cpa_blockpos ), blockread );
|
||||
|
||||
Avi->cpa_blockpos += blockread;
|
||||
result += blockread;
|
||||
length -= blockread;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void AVI_CloseVideo( movie_state_t *Avi )
|
||||
{
|
||||
if( Avi->active )
|
||||
{
|
||||
pAVIStreamGetFrameClose( Avi->video_getframe );
|
||||
|
||||
if( Avi->audio_stream != NULL )
|
||||
{
|
||||
pAVIStreamRelease( Avi->audio_stream );
|
||||
Mem_Free( Avi->audio_header );
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
pacmStreamUnprepareHeader( Avi->cpa_conversion_stream, &Avi->cpa_conversion_header, 0 );
|
||||
pacmStreamClose( Avi->cpa_conversion_stream, 0 );
|
||||
Mem_Free( Avi->cpa_srcbuffer );
|
||||
Mem_Free( Avi->cpa_dstbuffer );
|
||||
}
|
||||
}
|
||||
|
||||
pAVIStreamRelease( Avi->video_stream );
|
||||
|
||||
DeleteObject( Avi->hBitmap );
|
||||
pDrawDibClose( Avi->hDD );
|
||||
DeleteDC( Avi->hDC );
|
||||
}
|
||||
|
||||
memset( Avi, 0, sizeof( movie_state_t ));
|
||||
}
|
||||
|
||||
void AVI_OpenVideo( movie_state_t *Avi, const char *filename, qboolean load_audio, int quiet )
|
||||
{
|
||||
BITMAPINFOHEADER bmih;
|
||||
AVISTREAMINFO stream_info;
|
||||
int opened_streams = 0;
|
||||
LONG hr;
|
||||
wchar_t pathBuffer[MAX_PATH];
|
||||
|
||||
// default state: non-working.
|
||||
Avi->active = false;
|
||||
Avi->quiet = quiet;
|
||||
|
||||
// can't load Video For Windows :-(
|
||||
if( !avi_initialized ) return;
|
||||
|
||||
// convert to wide char
|
||||
if( MultiByteToWideChar( CP_UTF8, 0, filename, -1, pathBuffer, ARRAYSIZE( pathBuffer )) <= 0 )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "filename buffer limit exceeded\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// load the AVI
|
||||
hr = pAVIFileOpenW( &Avi->pfile, pathBuffer, OF_SHARE_DENY_WRITE, 0L );
|
||||
|
||||
if( hr != 0 ) // error opening AVI:
|
||||
{
|
||||
switch( hr )
|
||||
{
|
||||
case AVIERR_BADFORMAT:
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "corrupt file or unknown format.\n" );
|
||||
break;
|
||||
case AVIERR_MEMORY:
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "insufficient memory to open file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEREAD:
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "disk error reading file.\n" );
|
||||
break;
|
||||
case AVIERR_FILEOPEN:
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "disk error opening file.\n" );
|
||||
break;
|
||||
case REGDB_E_CLASSNOTREG:
|
||||
default:
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "no handler found (or file not found).\n" );
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Avi->video_stream = Avi->audio_stream = NULL;
|
||||
|
||||
// open the streams until a stream is not available.
|
||||
while( 1 )
|
||||
{
|
||||
PAVISTREAM stream = NULL;
|
||||
|
||||
if( pAVIFileGetStream( Avi->pfile, &stream, 0L, opened_streams++ ) != AVIERR_OK )
|
||||
break;
|
||||
|
||||
if( stream == NULL )
|
||||
break;
|
||||
|
||||
pAVIStreamInfo( stream, &stream_info, sizeof( stream_info ));
|
||||
|
||||
if( stream_info.fccType == streamtypeVIDEO && Avi->video_stream == NULL )
|
||||
{
|
||||
Avi->video_stream = stream;
|
||||
Avi->video_frames = stream_info.dwLength;
|
||||
Avi->video_xres = stream_info.rcFrame.right - stream_info.rcFrame.left;
|
||||
Avi->video_yres = stream_info.rcFrame.bottom - stream_info.rcFrame.top;
|
||||
Avi->video_fps = (float)stream_info.dwRate / (float)stream_info.dwScale;
|
||||
}
|
||||
else if( stream_info.fccType == streamtypeAUDIO && Avi->audio_stream == NULL && load_audio )
|
||||
{
|
||||
int size;
|
||||
|
||||
Avi->audio_stream = stream;
|
||||
|
||||
// read the audio header
|
||||
pAVIStreamReadFormat( Avi->audio_stream, pAVIStreamStart( Avi->audio_stream ), 0, &size );
|
||||
|
||||
Avi->audio_header = (WAVEFORMAT *)Mem_Malloc( cls.mempool, size );
|
||||
pAVIStreamReadFormat( Avi->audio_stream, pAVIStreamStart( Avi->audio_stream ), Avi->audio_header, &size );
|
||||
Avi->audio_header_size = size;
|
||||
Avi->audio_codec = Avi->audio_header->wFormatTag;
|
||||
|
||||
// length of converted audio in samples
|
||||
Avi->audio_length = (int)((float)stream_info.dwLength / Avi->audio_header->nAvgBytesPerSec );
|
||||
Avi->audio_length *= Avi->audio_header->nSamplesPerSec;
|
||||
|
||||
if( Avi->audio_codec != WAVE_FORMAT_PCM )
|
||||
{
|
||||
if( !AVI_ACMConvertAudio( Avi ))
|
||||
{
|
||||
Mem_Free( Avi->audio_header );
|
||||
Avi->audio_stream = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else Avi->audio_bytes_per_sample = Avi->audio_header->nBlockAlign;
|
||||
Avi->audio_length *= Avi->audio_bytes_per_sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
pAVIStreamRelease( stream );
|
||||
}
|
||||
}
|
||||
|
||||
// display error message-stream not found.
|
||||
if( Avi->video_stream == NULL )
|
||||
{
|
||||
if( Avi->pfile ) // if file is open, close it
|
||||
pAVIFileRelease( Avi->pfile );
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "couldn't find a valid video stream.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
pAVIFileRelease( Avi->pfile ); // release the file
|
||||
Avi->video_getframe = pAVIStreamGetFrameOpen( Avi->video_stream, NULL ); // open the frame getter
|
||||
|
||||
if( Avi->video_getframe == NULL )
|
||||
{
|
||||
if( !Avi->quiet )
|
||||
Con_DPrintf( S_ERROR "error attempting to read video frames.\n" );
|
||||
return; // couldn't open frame getter.
|
||||
}
|
||||
|
||||
bmih.biSize = sizeof( BITMAPINFOHEADER );
|
||||
bmih.biPlanes = 1;
|
||||
bmih.biBitCount = 32;
|
||||
bmih.biCompression = BI_RGB;
|
||||
bmih.biWidth = Avi->video_xres;
|
||||
bmih.biHeight = -Avi->video_yres; // invert height to flip image upside down
|
||||
|
||||
Avi->hDC = CreateCompatibleDC( 0 );
|
||||
Avi->hDD = pDrawDibOpen();
|
||||
Avi->hBitmap = CreateDIBSection( Avi->hDC, (BITMAPINFO*)(&bmih), DIB_RGB_COLORS, (void**)(&Avi->pframe_data), NULL, 0 );
|
||||
SelectObject( Avi->hDC, Avi->hBitmap );
|
||||
|
||||
Avi->active = true; // done
|
||||
}
|
||||
|
||||
qboolean AVI_IsActive( movie_state_t *Avi )
|
||||
{
|
||||
if( Avi != NULL )
|
||||
return Avi->active;
|
||||
return false;
|
||||
}
|
||||
|
||||
movie_state_t *AVI_GetState( int num )
|
||||
{
|
||||
return &avi[num];
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
AVIKit user interface
|
||||
|
||||
=============
|
||||
*/
|
||||
movie_state_t *AVI_LoadVideo( const char *filename, qboolean load_audio )
|
||||
{
|
||||
movie_state_t *Avi;
|
||||
string path;
|
||||
const char *fullpath;
|
||||
|
||||
// fast reject
|
||||
if( !avi_initialized )
|
||||
return NULL;
|
||||
|
||||
// open cinematic
|
||||
Q_snprintf( path, sizeof( path ), "media/%s", filename );
|
||||
COM_DefaultExtension( path, ".avi", sizeof( path ));
|
||||
fullpath = FS_GetDiskPath( path, false );
|
||||
|
||||
if( FS_FileExists( path, false ) && !fullpath )
|
||||
{
|
||||
Con_Printf( "Couldn't load %s from packfile. Please extract it\n", path );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Avi = Mem_Malloc( cls.mempool, sizeof( movie_state_t ));
|
||||
AVI_OpenVideo( Avi, fullpath, load_audio, false );
|
||||
|
||||
if( !AVI_IsActive( Avi ))
|
||||
{
|
||||
AVI_FreeVideo( Avi ); // something bad happens
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// all done
|
||||
return Avi;
|
||||
}
|
||||
|
||||
void AVI_FreeVideo( movie_state_t *state )
|
||||
{
|
||||
if( !state ) return;
|
||||
|
||||
if( Mem_IsAllocatedExt( cls.mempool, state ))
|
||||
{
|
||||
AVI_CloseVideo( state );
|
||||
Mem_Free( state );
|
||||
}
|
||||
}
|
||||
|
||||
qboolean AVI_Initailize( void )
|
||||
{
|
||||
if( Sys_CheckParm( "-noavi" ))
|
||||
{
|
||||
Con_Printf( "AVI: Disabled\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !Sys_LoadLibrary( &avifile_dll ))
|
||||
return false;
|
||||
|
||||
if( !Sys_LoadLibrary( &msvfw_dll ))
|
||||
{
|
||||
Sys_FreeLibrary( &avifile_dll );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !Sys_LoadLibrary( &msacm_dll ))
|
||||
{
|
||||
Sys_FreeLibrary( &avifile_dll );
|
||||
Sys_FreeLibrary( &msvfw_dll );
|
||||
return false;
|
||||
}
|
||||
|
||||
avi_initialized = true;
|
||||
pAVIFileInit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AVI_Shutdown( void )
|
||||
{
|
||||
if( !avi_initialized ) return;
|
||||
|
||||
pAVIFileExit();
|
||||
|
||||
Sys_FreeLibrary( &avifile_dll );
|
||||
Sys_FreeLibrary( &msvfw_dll );
|
||||
Sys_FreeLibrary( &msacm_dll );
|
||||
avi_initialized = false;
|
||||
}
|
||||
#endif // _WIN32
|
||||
@@ -42,7 +42,8 @@ void CL_PlayVideo_f( void )
|
||||
switch( Cmd_Argc( ))
|
||||
{
|
||||
case 2: // simple user version
|
||||
Q_snprintf( path, sizeof( path ), "media/%s.avi", Cmd_Argv( 1 ));
|
||||
Q_snprintf( path, sizeof( path ), "media/%s", Cmd_Argv( 1 ));
|
||||
COM_DefaultExtension( path, ".avi", sizeof( path ));
|
||||
SCR_PlayCinematic( path );
|
||||
break;
|
||||
case 3: // sequenced cinematics used this
|
||||
|
||||
@@ -198,44 +198,3 @@ void CL_WriteMessageHistory( void )
|
||||
CL_WriteErrorMessage( old->starting_offset, msg );
|
||||
cls_message_debug.parsing = false;
|
||||
}
|
||||
|
||||
void CL_ReplayBufferDat_f( void )
|
||||
{
|
||||
file_t *f = FS_Open( Cmd_Argv( 1 ), "rb", true );
|
||||
sizebuf_t msg;
|
||||
char buffer[NET_MAX_MESSAGE];
|
||||
int starting_count, current_count, protocol;
|
||||
fs_offset_t len;
|
||||
|
||||
if( !f )
|
||||
return;
|
||||
|
||||
FS_Read( f, &starting_count, sizeof( starting_count ));
|
||||
FS_Read( f, ¤t_count, sizeof( current_count ));
|
||||
FS_Read( f, &protocol, sizeof( protocol ));
|
||||
|
||||
cls.legacymode = protocol;
|
||||
|
||||
len = FS_Read( f, buffer, sizeof( buffer ));
|
||||
FS_Close( f );
|
||||
|
||||
MSG_Init( &msg, __func__, buffer, len );
|
||||
|
||||
Delta_Shutdown();
|
||||
Delta_Init();
|
||||
|
||||
clgame.maxEntities = MAX_EDICTS;
|
||||
clgame.entities = Mem_Calloc( clgame.mempool, sizeof( *clgame.entities ) * clgame.maxEntities );
|
||||
|
||||
// ad-hoc implement
|
||||
#if 0
|
||||
{
|
||||
const int message_pos = 12; // put real number here
|
||||
MSG_SeekToBit( &msg, ( message_pos - 12 + 1 ) << 3, SEEK_SET );
|
||||
|
||||
CL_ParseYourMom( &msg, protocol );
|
||||
}
|
||||
#endif
|
||||
|
||||
Sys_Quit( __func__ );
|
||||
}
|
||||
|
||||
@@ -13,12 +13,10 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include "common.h"
|
||||
#include "filesystem.h"
|
||||
#include "client.h"
|
||||
#include "qfont.h"
|
||||
#include "utflib.h"
|
||||
|
||||
qboolean CL_FixedFont( cl_font_t *font )
|
||||
{
|
||||
@@ -175,27 +173,31 @@ static int CL_CalcTabStop( const cl_font_t *font, int x )
|
||||
return stop;
|
||||
}
|
||||
|
||||
static int CL_DrawBitmapCharacter( float x, float y, uint32_t uc, const rgba_t color, const cl_font_t *font, int flags )
|
||||
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags )
|
||||
{
|
||||
wrect_t *rc;
|
||||
float w, h;
|
||||
float s1, t1, s2, t2, half = 0.5f;
|
||||
int texw, texh;
|
||||
uint32_t number;
|
||||
|
||||
// bitmap fonts need conversion. Do this and validate the result
|
||||
if( g_codepage == 1251 )
|
||||
number = Q_UnicodeToCP1251( uc );
|
||||
else if( g_codepage == 1252 )
|
||||
number = Q_UnicodeToCP1252( uc );
|
||||
else
|
||||
number = uc;
|
||||
|
||||
if( !number || number >= ARRAYSIZE( font->charWidths ))
|
||||
if( !font || !font->valid || y < -font->charHeight )
|
||||
return 0;
|
||||
|
||||
// start rendering
|
||||
if( !font->charWidths[number] )
|
||||
// check if printable
|
||||
if( number <= 32 )
|
||||
{
|
||||
if( number == ' ' )
|
||||
return font->charWidths[' '];
|
||||
else if( number == '\t' )
|
||||
return CL_CalcTabStop( font, x );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
number = Con_UtfProcessChar( number & 255 );
|
||||
else number &= 255;
|
||||
|
||||
if( !number || !font->charWidths[number])
|
||||
return 0;
|
||||
|
||||
R_GetTextureParms( &texw, &texh, font->hFontTexture );
|
||||
@@ -229,60 +231,29 @@ static int CL_DrawBitmapCharacter( float x, float y, uint32_t uc, const rgba_t c
|
||||
return font->charWidths[number];
|
||||
}
|
||||
|
||||
static int CL_DrawTrueTypeCharacter( float x, float y, uint32_t uc, const rgba_t color, cl_font_t *font, int flags )
|
||||
{
|
||||
// stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CL_DrawCharacter( float x, float y, uint32_t uc, const rgba_t color, const cl_font_t *font, int flags )
|
||||
{
|
||||
if( !font || !font->valid || y < -font->charHeight )
|
||||
return 0;
|
||||
|
||||
// check if printable
|
||||
if( uc <= 32 )
|
||||
{
|
||||
if( uc == ' ' )
|
||||
return font->charWidths[' '];
|
||||
else if( uc == '\t' )
|
||||
return CL_CalcTabStop( font, x );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( font->type == FONT_TRUETYPE )
|
||||
return CL_DrawTrueTypeCharacter( x, y, uc, color, font, flags );
|
||||
|
||||
return CL_DrawBitmapCharacter( x, y, uc, color, font, flags );
|
||||
}
|
||||
|
||||
int CL_DrawString( float x, float y, const char *s, const rgba_t color, const cl_font_t *font, int flags )
|
||||
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags )
|
||||
{
|
||||
rgba_t current_color;
|
||||
int draw_len = 0;
|
||||
utfstate_t utfstate = { 0 };
|
||||
|
||||
if( !font || !font->valid )
|
||||
return 0;
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
Con_UtfProcessChar( 0 ); // clear utf state
|
||||
|
||||
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
|
||||
CL_SetFontRendermode( font );
|
||||
|
||||
Vector4Copy( color, current_color );
|
||||
|
||||
for( ; *s; s++ )
|
||||
while( *s )
|
||||
{
|
||||
uint32_t uc = (byte)*s;
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
uc = Q_DecodeUTF8( &utfstate, uc );
|
||||
|
||||
if( !uc )
|
||||
continue;
|
||||
|
||||
if( uc == '\n' )
|
||||
if( *s == '\n' )
|
||||
{
|
||||
if( !s[1] ) // ignore newline at end of string if next
|
||||
s++;
|
||||
|
||||
if( !*s )
|
||||
break;
|
||||
|
||||
// some client functions ignore newlines
|
||||
@@ -293,29 +264,30 @@ int CL_DrawString( float x, float y, const char *s, const rgba_t color, const cl
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_RESETCOLORONLF ))
|
||||
Vector4Copy( color, current_color );
|
||||
|
||||
Vector4Copy( color, current_color );
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for colorstrings
|
||||
if( uc == '^' && isdigit( s[1] ))
|
||||
if( IsColorString( s ))
|
||||
{
|
||||
// don't copy alpha
|
||||
if( !FBitSet( flags, FONT_DRAW_FORCECOL ))
|
||||
VectorCopy( g_color_table[ColorIndex( s[1] )], current_color );
|
||||
VectorCopy( g_color_table[ColorIndex(*( s + 1 ))], current_color );
|
||||
|
||||
s++;
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip setting rendermode, it was changed for this string already
|
||||
draw_len += CL_DrawCharacter( x + draw_len, y, uc, current_color, font, flags | FONT_DRAW_NORENDERMODE );
|
||||
draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, current_color, font, flags | FONT_DRAW_NORENDERMODE );
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
return draw_len;
|
||||
}
|
||||
|
||||
int CL_DrawStringf( const cl_font_t *font, float x, float y, const rgba_t color, int flags, const char *fmt, ... )
|
||||
int CL_DrawStringf( cl_font_t *font, float x, float y, rgba_t color, int flags, const char *fmt, ... )
|
||||
{
|
||||
va_list va;
|
||||
char buf[MAX_VA_STRING];
|
||||
@@ -327,63 +299,20 @@ int CL_DrawStringf( const cl_font_t *font, float x, float y, const rgba_t color,
|
||||
return CL_DrawString( x, y, buf, color, font, flags );
|
||||
}
|
||||
|
||||
static int CL_DrawBitmapCharacterLen( const cl_font_t *font, uint32_t uc )
|
||||
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height )
|
||||
{
|
||||
int number;
|
||||
|
||||
if( g_codepage == 1251 )
|
||||
number = Q_UnicodeToCP1251( uc );
|
||||
else if( g_codepage == 1252 )
|
||||
number = Q_UnicodeToCP1252( uc );
|
||||
else
|
||||
number = uc;
|
||||
|
||||
if( !number || number >= ARRAYSIZE( font->charWidths ))
|
||||
return 0;
|
||||
|
||||
return font->charWidths[number];
|
||||
}
|
||||
|
||||
static int CL_DrawTrueTypeCharacterLen( const cl_font_t *font, uint32_t uc )
|
||||
{
|
||||
// stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CL_DrawCharacterLen( const cl_font_t *font, uint32_t uc, int *width, int *height )
|
||||
{
|
||||
if( !font || !font->valid )
|
||||
return;
|
||||
|
||||
if( height )
|
||||
*height = font->charHeight;
|
||||
|
||||
if( !font || !font->valid ) return;
|
||||
if( width )
|
||||
{
|
||||
// basically matches logic of CL_DrawCharacter
|
||||
if( uc <= 32 )
|
||||
{
|
||||
if( uc == ' ' )
|
||||
*width = font->charWidths[' '];
|
||||
else if( uc == '\t' )
|
||||
*width = CL_CalcTabStop( font, 0 ); // at least return max tabstop
|
||||
else
|
||||
*width = 0;
|
||||
}
|
||||
else if( font->type == FONT_TRUETYPE )
|
||||
{
|
||||
*width = CL_DrawTrueTypeCharacterLen( font, uc );
|
||||
}
|
||||
else
|
||||
{
|
||||
*width = CL_DrawBitmapCharacterLen( font, uc );
|
||||
}
|
||||
if( number == '\t' )
|
||||
*width = CL_CalcTabStop( font, 0 ); // at least return max tabstop
|
||||
else *width = font->charWidths[number & 255];
|
||||
}
|
||||
if( height ) *height = font->charHeight;
|
||||
}
|
||||
|
||||
void CL_DrawStringLen( const cl_font_t *font, const char *s, int *width, int *height, int flags )
|
||||
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags )
|
||||
{
|
||||
utfstate_t utfstate = { 0 };
|
||||
int draw_len = 0;
|
||||
|
||||
if( !font || !font->valid )
|
||||
@@ -398,45 +327,54 @@ void CL_DrawStringLen( const cl_font_t *font, const char *s, int *width, int *he
|
||||
if( !COM_CheckString( s ))
|
||||
return;
|
||||
|
||||
for( ; *s; s++ )
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
Con_UtfProcessChar( 0 ); // reset utf state
|
||||
|
||||
while( *s )
|
||||
{
|
||||
uint32_t uc = (byte)*s;
|
||||
int char_width = 0;
|
||||
int number;
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
uc = Q_DecodeUTF8( &utfstate, uc );
|
||||
|
||||
if( !uc )
|
||||
continue;
|
||||
|
||||
if( uc == '\n' )
|
||||
if( *s == '\n' )
|
||||
{
|
||||
// BUG: no check for end string here
|
||||
// but high chances somebody's relying on this
|
||||
s++;
|
||||
draw_len = 0;
|
||||
|
||||
// some client functions ignore newlines
|
||||
if( !FBitSet( flags, FONT_DRAW_NOLF ))
|
||||
{
|
||||
if( height )
|
||||
*height += font->charHeight;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for colorstrings
|
||||
if( uc == '^' && isdigit( s[1] ))
|
||||
else if( *s == '\t' )
|
||||
{
|
||||
draw_len += CL_CalcTabStop( font, 0 ); // at least return max tabstop
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
CL_DrawCharacterLen( font, uc, &char_width, NULL );
|
||||
draw_len += char_width;
|
||||
|
||||
if( width )
|
||||
if( IsColorString( s ))
|
||||
{
|
||||
if( *width < draw_len )
|
||||
*width = draw_len;
|
||||
s += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FONT_DRAW_UTF8 ))
|
||||
number = Con_UtfProcessChar( (byte)*s );
|
||||
else number = (byte)*s;
|
||||
|
||||
if( number )
|
||||
{
|
||||
draw_len += font->charWidths[number];
|
||||
|
||||
if( width )
|
||||
{
|
||||
if( draw_len > *width )
|
||||
*width = draw_len;
|
||||
}
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#if XASH_SDL
|
||||
#include <SDL.h> // SDL_GetWindowPosition
|
||||
#endif // XASH_SDL
|
||||
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include "const.h"
|
||||
@@ -33,7 +29,6 @@ GNU General Public License for more details.
|
||||
#include "vgui_draw.h"
|
||||
#include "sound.h" // SND_STOP_LOOPING
|
||||
#include "platform/platform.h"
|
||||
#include "utflib.h"
|
||||
|
||||
#define MAX_LINELENGTH 80
|
||||
#define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels)
|
||||
@@ -311,22 +306,21 @@ void SPR_AdjustSize( float *x, float *y, float *w, float *h )
|
||||
|
||||
static void SPR_AdjustTexCoords( int texnum, float width, float height, float *s1, float *t1, float *s2, float *t2 )
|
||||
{
|
||||
const qboolean filtering = REF_GET_PARM( PARM_TEX_FILTERING, texnum );
|
||||
const int xremainder = refState.width % clgame.scrInfo.iWidth;
|
||||
const int yremainder = refState.height % clgame.scrInfo.iHeight;
|
||||
|
||||
if(( filtering || xremainder ) && refState.width != clgame.scrInfo.iWidth )
|
||||
if( REF_GET_PARM( PARM_TEX_FILTERING, texnum ))
|
||||
{
|
||||
// align to texel if scaling
|
||||
*s1 += 0.5f;
|
||||
*s2 -= 0.5f;
|
||||
}
|
||||
if( refState.width != clgame.scrInfo.iWidth )
|
||||
{
|
||||
// align to texel if scaling
|
||||
*s1 += 0.5f;
|
||||
*s2 -= 0.5f;
|
||||
}
|
||||
|
||||
if(( filtering || yremainder ) && refState.height != clgame.scrInfo.iHeight )
|
||||
{
|
||||
// align to texel if scaling
|
||||
*t1 += 0.5f;
|
||||
*t2 -= 0.5f;
|
||||
if( refState.height != clgame.scrInfo.iHeight )
|
||||
{
|
||||
// align to texel if scaling
|
||||
*t1 += 0.5f;
|
||||
*t2 -= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
*s1 /= width;
|
||||
@@ -409,10 +403,8 @@ void CL_DrawCenterPrint( void )
|
||||
char *pText;
|
||||
int i, j, x, y;
|
||||
int width, lineLength;
|
||||
byte *colorDefault;
|
||||
uint32_t line[MAX_LINELENGTH];
|
||||
byte *colorDefault, line[MAX_LINELENGTH];
|
||||
int charWidth, charHeight;
|
||||
utfstate_t utfstate = { 0 };
|
||||
|
||||
if( !clgame.centerPrint.time )
|
||||
return;
|
||||
@@ -435,19 +427,14 @@ void CL_DrawCenterPrint( void )
|
||||
lineLength = 0;
|
||||
width = 0;
|
||||
|
||||
for( ; *pText && *pText != '\n' && lineLength < MAX_LINELENGTH; pText++ )
|
||||
while( *pText && *pText != '\n' && lineLength < MAX_LINELENGTH )
|
||||
{
|
||||
byte c = *pText;
|
||||
uint32_t uc = Q_DecodeUTF8( &utfstate, c );
|
||||
|
||||
if( !uc )
|
||||
continue;
|
||||
|
||||
line[lineLength] = uc;
|
||||
CL_DrawCharacterLen( font, uc, &charWidth, NULL );
|
||||
|
||||
line[lineLength] = c;
|
||||
CL_DrawCharacterLen( font, c, &charWidth, NULL );
|
||||
width += charWidth;
|
||||
lineLength++;
|
||||
pText++;
|
||||
}
|
||||
|
||||
if( lineLength == MAX_LINELENGTH )
|
||||
@@ -461,7 +448,7 @@ void CL_DrawCenterPrint( void )
|
||||
for( j = 0; j < lineLength; j++ )
|
||||
{
|
||||
if( x >= 0 && y >= 0 && x <= refState.width )
|
||||
x += CL_DrawCharacter( x, y, line[j], colorDefault, font, FONT_DRAW_HUD | FONT_DRAW_NORENDERMODE );
|
||||
x += CL_DrawCharacter( x, y, line[j], colorDefault, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD | FONT_DRAW_NORENDERMODE );
|
||||
}
|
||||
y += charHeight;
|
||||
}
|
||||
@@ -1711,12 +1698,7 @@ int GAME_EXPORT CL_GetScreenInfo( SCREENINFO *pscrinfo )
|
||||
clgame.scrInfo.iSize = sizeof( clgame.scrInfo );
|
||||
clgame.scrInfo.iFlags = SCRINFO_SCREENFLASH;
|
||||
|
||||
if( hud_scale.value >= 320.0f && hud_scale.value >= hud_scale_minimal_width.value )
|
||||
{
|
||||
scale_factor = refState.width / hud_scale.value;
|
||||
apply_scale_factor = true;
|
||||
}
|
||||
else if( scale_factor && scale_factor != 1.0f )
|
||||
if( scale_factor && scale_factor != 1.0f )
|
||||
{
|
||||
float scaled_width = (float)refState.width / scale_factor;
|
||||
if( scaled_width >= hud_scale_minimal_width.value )
|
||||
@@ -1997,13 +1979,7 @@ static int GAME_EXPORT pfnDrawCharacter( int x, int y, int number, int r, int g,
|
||||
int flags = FONT_DRAW_HUD;
|
||||
|
||||
if( hud_utf8.value )
|
||||
{
|
||||
static utfstate_t utfstate;
|
||||
number = Q_DecodeUTF8( &utfstate, number );
|
||||
|
||||
if( !number )
|
||||
return 0;
|
||||
}
|
||||
flags |= FONT_DRAW_UTF8;
|
||||
|
||||
return CL_DrawCharacter( x, y, number, color, &cls.creditsFont, flags );
|
||||
}
|
||||
@@ -3066,8 +3042,12 @@ pfnDrawString
|
||||
*/
|
||||
static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b )
|
||||
{
|
||||
const rgba_t color = { r, g, b, 255 };
|
||||
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF | FONT_DRAW_UTF8;
|
||||
rgba_t color = { r, g, b, 255 };
|
||||
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF;
|
||||
|
||||
if( hud_utf8.value )
|
||||
SetBits( flags, FONT_DRAW_UTF8 );
|
||||
|
||||
return CL_DrawString( x, y, str, color, &cls.creditsFont, flags );
|
||||
}
|
||||
|
||||
@@ -3079,10 +3059,13 @@ pfnDrawStringReverse
|
||||
*/
|
||||
static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b )
|
||||
{
|
||||
const rgba_t color = { r, g, b, 255 };
|
||||
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF | FONT_DRAW_UTF8;
|
||||
rgba_t color = { r, g, b, 255 };
|
||||
int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF;
|
||||
int width;
|
||||
|
||||
if( hud_utf8.value )
|
||||
SetBits( flags, FONT_DRAW_UTF8 );
|
||||
|
||||
CL_DrawStringLen( &cls.creditsFont, str, &width, NULL, flags );
|
||||
|
||||
x -= width;
|
||||
@@ -3438,7 +3421,7 @@ static void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags,
|
||||
return;
|
||||
}
|
||||
|
||||
if( NET_NetadrType( remote_address ) == NA_IPX || NET_NetadrType( remote_address ) == NA_BROADCAST_IPX )
|
||||
if( remote_address->type == NA_IPX || remote_address->type == NA_BROADCAST_IPX )
|
||||
return; // IPX no longer support
|
||||
|
||||
if( request == NETAPI_REQUEST_SERVERLIST )
|
||||
|
||||
@@ -302,14 +302,11 @@ void UI_ConnectionProgress_ParseServerInfo( const char *server )
|
||||
|
||||
static void GAME_EXPORT UI_DrawLogo( const char *filename, float x, float y, float width, float height )
|
||||
{
|
||||
static float cin_time;
|
||||
static int last_frame = -1;
|
||||
byte *cin_data = NULL;
|
||||
movie_state_t *cin_state;
|
||||
int cin_frame;
|
||||
qboolean redraw = false;
|
||||
|
||||
if( !gameui.drawLogo ) return;
|
||||
if( !gameui.drawLogo )
|
||||
return;
|
||||
|
||||
cin_state = AVI_GetState( CIN_LOGO );
|
||||
|
||||
if( !AVI_IsActive( cin_state ))
|
||||
@@ -336,37 +333,25 @@ static void GAME_EXPORT UI_DrawLogo( const char *filename, float x, float y, flo
|
||||
gameui.drawLogo = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cin_time = 0.0f;
|
||||
last_frame = -1;
|
||||
}
|
||||
|
||||
if( width <= 0 || height <= 0 )
|
||||
{
|
||||
// precache call, don't draw
|
||||
cin_time = 0.0f;
|
||||
last_frame = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// advances cinematic time (ignores maxfps and host_framerate settings)
|
||||
cin_time += host.realframetime;
|
||||
|
||||
// restarts the cinematic
|
||||
if( cin_time > gameui.logo_length )
|
||||
cin_time = 0.0f;
|
||||
AVI_SetParm( cin_state,
|
||||
AVI_RENDER_TEXNUM, 0,
|
||||
AVI_RENDER_X, (int)x,
|
||||
AVI_RENDER_Y, (int)y,
|
||||
AVI_RENDER_W, (int)width,
|
||||
AVI_RENDER_H, (int)height,
|
||||
AVI_PARM_LAST );
|
||||
|
||||
// read the next frame
|
||||
cin_frame = AVI_GetVideoFrameNumber( cin_state, cin_time );
|
||||
|
||||
if( cin_frame != last_frame )
|
||||
{
|
||||
cin_data = AVI_GetVideoFrame( cin_state, cin_frame );
|
||||
last_frame = cin_frame;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
ref.dllFuncs.R_DrawStretchRaw( x, y, width, height, gameui.logo_xres, gameui.logo_yres, cin_data, redraw );
|
||||
if( !AVI_Think( cin_state ))
|
||||
AVI_SetParm( cin_state, AVI_REWIND, AVI_PARM_LAST );
|
||||
}
|
||||
|
||||
static int GAME_EXPORT UI_GetLogoWidth( void )
|
||||
@@ -1324,21 +1309,10 @@ static int pfnIsCvarReadOnly( const char *name )
|
||||
return FBitSet( cv->flags, FCVAR_READ_ONLY ) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int GAME_EXPORT pfnUtfProcessChar( int in )
|
||||
{
|
||||
// compatibility export, deprecated, do not use
|
||||
|
||||
if( !cls.accept_utf8 ) // incoming character is not a UTF-8 sequence
|
||||
return in;
|
||||
|
||||
// otherwise, decode it and convert to selected codepage
|
||||
return Con_UtfProcessCharForce( in );
|
||||
}
|
||||
|
||||
static ui_extendedfuncs_t gExtendedfuncs =
|
||||
{
|
||||
pfnEnableTextInput,
|
||||
pfnUtfProcessChar,
|
||||
Con_UtfProcessChar,
|
||||
Con_UtfMoveLeft,
|
||||
Con_UtfMoveRight,
|
||||
pfnGetRenderers,
|
||||
|
||||
@@ -68,7 +68,7 @@ CVAR_DEFINE_AUTO( hud_scale_minimal_width, "640", FCVAR_ARCHIVE|FCVAR_LATCH, "if
|
||||
CVAR_DEFINE_AUTO( cl_solid_players, "1", 0, "Make all players not solid (can't traceline them)" );
|
||||
CVAR_DEFINE_AUTO( cl_updaterate, "20", FCVAR_USERINFO|FCVAR_ARCHIVE, "refresh rate of server messages" );
|
||||
CVAR_DEFINE_AUTO( cl_showevents, "0", FCVAR_ARCHIVE, "show events playback" );
|
||||
CVAR_DEFINE_AUTO( cl_cmdrate, "30", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" );
|
||||
CVAR_DEFINE_AUTO( cl_cmdrate, "60", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" );
|
||||
CVAR_DEFINE( cl_interp, "ex_interp", "0.1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "Interpolate object positions starting this many seconds in past" );
|
||||
CVAR_DEFINE_AUTO( cl_nointerp, "0", 0, "disable interpolation of entities and players" );
|
||||
static CVAR_DEFINE_AUTO( cl_dlmax, "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "max allowed outcoming fragment size" );
|
||||
@@ -94,10 +94,11 @@ static CVAR_DEFINE_AUTO( name, username, FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_PRIN
|
||||
static CVAR_DEFINE_AUTO( model, "", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player model ('player' is a singleplayer model)" );
|
||||
static CVAR_DEFINE_AUTO( topcolor, "0", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player top color" );
|
||||
static CVAR_DEFINE_AUTO( bottomcolor, "0", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player bottom color" );
|
||||
CVAR_DEFINE_AUTO( rate, "25000", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player network rate" );
|
||||
CVAR_DEFINE_AUTO( rate, "3500", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player network rate" );
|
||||
|
||||
static CVAR_DEFINE_AUTO( cl_ticket_generator, "revemu2013", FCVAR_ARCHIVE, "you wouldn't steal a car" );
|
||||
|
||||
|
||||
client_t cl;
|
||||
client_static_t cls;
|
||||
clgame_static_t clgame;
|
||||
@@ -1008,7 +1009,7 @@ static void CL_WriteSteamTicket( sizebuf_t *send )
|
||||
const char *s;
|
||||
uint32_t crc;
|
||||
char buf[768] = { 0 }; // setti and steamemu return 768
|
||||
int i = sizeof( buf );
|
||||
size_t i = sizeof( buf );
|
||||
|
||||
if( !Q_strcmp( cl_ticket_generator.string, "null" ))
|
||||
{
|
||||
@@ -1027,7 +1028,24 @@ static void CL_WriteSteamTicket( sizebuf_t *send )
|
||||
CRC32_Init( &crc );
|
||||
CRC32_ProcessBuffer( &crc, s, Q_strlen( s ));
|
||||
crc = CRC32_Final( crc );
|
||||
i = GenerateRevEmu2013( buf, s, crc );
|
||||
|
||||
if( !Q_stricmp( cl_ticket_generator.string, "revemu2013" ))
|
||||
i = GenerateRevEmu2013( buf, crc );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "sc2009" ))
|
||||
i = GenerateSC2009( buf, crc );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "oldrevemu" ))
|
||||
i = GenerateOldRevEmu( buf, crc );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "steamemu" ))
|
||||
i = GenerateSteamEmu( buf, crc );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "revemu" ))
|
||||
i = GenerateRevEmu( buf, crc );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "setti" ))
|
||||
i = GenerateSetti( buf );
|
||||
else if( !Q_stricmp( cl_ticket_generator.string, "avsmp" ))
|
||||
i = GenerateAVSMP( buf, crc, true );
|
||||
else
|
||||
Con_Printf( "%s: unknown generator %s, supported are: null, revemu2003, sc2009, oldrevemu, steamemu, revemu, setti, avsmp\n", __func__, cl_ticket_generator.string );
|
||||
|
||||
MSG_WriteBytes( send, buf, i );
|
||||
}
|
||||
|
||||
@@ -1045,7 +1063,6 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
|
||||
const char *key = ID_GetMD5();
|
||||
netadr_t adr = { 0 };
|
||||
int input_devices;
|
||||
netadrtype_t adrtype;
|
||||
|
||||
protinfo[0] = 0;
|
||||
|
||||
@@ -1056,16 +1073,14 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
|
||||
return;
|
||||
}
|
||||
|
||||
adrtype = NET_NetadrType( &adr );
|
||||
|
||||
if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER );
|
||||
|
||||
input_devices = IN_CollectInputDevices();
|
||||
IN_LockInputDevices( adrtype != NA_LOOPBACK ? true : false );
|
||||
IN_LockInputDevices( adr.type != NA_LOOPBACK ? true : false );
|
||||
|
||||
// GoldSrc doesn't need sv_cheats set to 0, it's handled by svc_goldsrc_sendextrainfo
|
||||
// it also doesn't need useragent string
|
||||
if( adrtype != NA_LOOPBACK && proto != PROTO_GOLDSRC )
|
||||
if( adr.type != NA_LOOPBACK && proto != PROTO_GOLDSRC )
|
||||
{
|
||||
Cvar_SetCheatState();
|
||||
Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER );
|
||||
@@ -1165,11 +1180,7 @@ Returns bandwidth test fragment size
|
||||
*/
|
||||
static int CL_GetTestFragmentSize( void )
|
||||
{
|
||||
// const int fragmentSizes[CL_TEST_RETRIES] = { 64000, 32000, 10666, 5200, 1400 };
|
||||
|
||||
// it turns out, even if we pass the bandwidth test, it doesn't mean we can use such large fragments
|
||||
// as a temporary solution, use smaller fragment sizes
|
||||
const int fragmentSizes[CL_TEST_RETRIES] = { 1400, 1200, 1000, 800, 508 };
|
||||
const int fragmentSizes[CL_TEST_RETRIES] = { 64000, 32000, 10666, 5200, 1400 };
|
||||
if( cls.connect_retry >= 0 && cls.connect_retry < CL_TEST_RETRIES )
|
||||
return bound( FRAGMENT_MIN_SIZE, fragmentSizes[cls.connect_retry], FRAGMENT_MAX_SIZE );
|
||||
else
|
||||
@@ -1207,7 +1218,7 @@ static void CL_CheckForResend( void )
|
||||
cls.signon = 0;
|
||||
cls.state = ca_connecting;
|
||||
Q_strncpy( cls.servername, "localhost", sizeof( cls.servername ));
|
||||
NET_NetadrSetType( &cls.serveradr, NA_LOOPBACK );
|
||||
cls.serveradr.type = NA_LOOPBACK;
|
||||
cls.legacymode = PROTO_CURRENT;
|
||||
|
||||
// we don't need a challenge on the localhost
|
||||
@@ -1543,8 +1554,8 @@ static void CL_SendDisconnectMessage( connprotocol_t proto )
|
||||
MSG_WriteString( &buf, "dropclient\n" );
|
||||
else MSG_WriteString( &buf, "disconnect" );
|
||||
|
||||
if( NET_NetadrType( &cls.netchan.remote_address ) == NA_UNDEFINED )
|
||||
NET_NetadrSetType( &cls.netchan.remote_address, NA_LOOPBACK );
|
||||
if( !cls.netchan.remote_address.type )
|
||||
cls.netchan.remote_address.type = NA_LOOPBACK;
|
||||
|
||||
// make sure message will be delivered
|
||||
Netchan_TransmitBits( &cls.netchan, MSG_GetNumBitsWritten( &buf ), MSG_GetData( &buf ));
|
||||
@@ -1713,17 +1724,19 @@ CL_LocalServers_f
|
||||
*/
|
||||
static void CL_LocalServers_f( void )
|
||||
{
|
||||
netadr_t adr = { 0 };
|
||||
netadr_t adr;
|
||||
|
||||
memset( &adr, 0, sizeof( adr ));
|
||||
|
||||
Con_Printf( "Scanning for servers on the local network area...\n" );
|
||||
NET_Config( true, true ); // allow remote
|
||||
|
||||
// send a broadcast packet
|
||||
NET_NetadrSetType( &adr, NA_BROADCAST );
|
||||
adr.type = NA_BROADCAST;
|
||||
adr.port = MSG_BigShort( PORT_SERVER );
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION );
|
||||
|
||||
NET_NetadrSetType( &adr, NA_MULTICAST_IP6 );
|
||||
adr.type = NA_MULTICAST_IP6;
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION );
|
||||
}
|
||||
|
||||
@@ -2467,18 +2480,18 @@ static void CL_ServerList( netadr_t from, sizebuf_t *msg )
|
||||
while( MSG_GetNumBitsLeft( msg ) > 8 )
|
||||
{
|
||||
uint8_t addr[16];
|
||||
netadr_t servadr = { 0 };
|
||||
netadr_t servadr;
|
||||
|
||||
if( NET_NetadrType( &from ) == NA_IP6 ) // IPv6 master server only sends IPv6 addresses
|
||||
if( from.type6 == NA_IP6 ) // IPv6 master server only sends IPv6 addresses
|
||||
{
|
||||
MSG_ReadBytes( msg, addr, sizeof( addr ));
|
||||
NET_IP6BytesToNetadr( &servadr, addr );
|
||||
NET_NetadrSetType( &servadr, NA_IP6 );
|
||||
servadr.type6 = NA_IP6;
|
||||
}
|
||||
else
|
||||
{
|
||||
MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP
|
||||
NET_NetadrSetType( &servadr, NA_IP );
|
||||
servadr.type = NA_IP;
|
||||
}
|
||||
servadr.port = MSG_ReadShort( msg ); // 2 bytes for Port
|
||||
|
||||
@@ -3126,9 +3139,6 @@ static qboolean CL_ShouldRescanFilesystem( void )
|
||||
}
|
||||
}
|
||||
|
||||
if( FBitSet( fs_mount_lv.flags|fs_mount_hd.flags|fs_mount_addon.flags|fs_mount_l10n.flags|ui_language.flags, FCVAR_CHANGED ))
|
||||
retval = true;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -3138,7 +3148,7 @@ qboolean CL_PrecacheResources( void )
|
||||
|
||||
// if we downloaded new WAD files or any other archives they must be added to searchpath
|
||||
if( CL_ShouldRescanFilesystem( ))
|
||||
FS_Rescan_f();
|
||||
g_fsapi.Rescan();
|
||||
|
||||
// NOTE: world need to be loaded as first model
|
||||
for( pRes = cl.resourcesonhand.pNext; pRes && pRes != &cl.resourcesonhand; pRes = pRes->pNext )
|
||||
@@ -3479,8 +3489,6 @@ static void CL_InitLocal( void )
|
||||
Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f, "sent by server when serverinfo changes" );
|
||||
Cmd_AddCommand ("upload", CL_BeginUpload_f, "uploading file to the server" );
|
||||
|
||||
Cmd_AddRestrictedCommand( "replaybufferdat", CL_ReplayBufferDat_f, "development and debugging tool" );
|
||||
|
||||
Cmd_AddRestrictedCommand ("quit", CL_Quit_f, "quit from game" );
|
||||
Cmd_AddRestrictedCommand ("exit", CL_Quit_f, "quit from game" );
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ GNU General Public License for more details.
|
||||
#include "library.h"
|
||||
#include "input.h"
|
||||
#include "platform/platform.h"
|
||||
#include "utflib.h"
|
||||
|
||||
static CVAR_DEFINE_AUTO( vibration_length, "1.0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "vibration length" );
|
||||
static CVAR_DEFINE_AUTO( vibration_enable, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "enable vibration" );
|
||||
@@ -59,13 +58,7 @@ static int pfnDrawScaledCharacter( int x, int y, int number, int r, int g, int b
|
||||
int flags = FONT_DRAW_HUD;
|
||||
|
||||
if( hud_utf8.value )
|
||||
{
|
||||
static utfstate_t utfstate;
|
||||
number = Q_DecodeUTF8( &utfstate, number );
|
||||
|
||||
if( !number )
|
||||
return 0;
|
||||
}
|
||||
SetBits( flags, FONT_DRAW_UTF8 );
|
||||
|
||||
if( fabs( g_font_scale - scale ) > 0.1f ||
|
||||
g_scaled_font.hFontTexture != cls.creditsFont.hFontTexture )
|
||||
@@ -99,11 +92,6 @@ static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned i
|
||||
return COM_ParseFileSafe( data, buf, size, flags, len, NULL );
|
||||
}
|
||||
|
||||
static void GAME_EXPORT pfnSetCustomClientID( const char *id )
|
||||
{
|
||||
// deprecated
|
||||
}
|
||||
|
||||
static const mobile_engfuncs_t gMobileEngfuncs =
|
||||
{
|
||||
MOBILITY_API_VERSION,
|
||||
@@ -118,7 +106,7 @@ static const mobile_engfuncs_t gMobileEngfuncs =
|
||||
pfnDrawScaledCharacter,
|
||||
Sys_Warn,
|
||||
Sys_GetNativeObject,
|
||||
pfnSetCustomClientID,
|
||||
ID_SetCustomClientID,
|
||||
pfnParseFileSafe
|
||||
};
|
||||
|
||||
|
||||
@@ -83,10 +83,7 @@ static void CL_ParseNewMovevars( sizebuf_t *msg )
|
||||
R_SetupSky( clgame.movevars.skyName );
|
||||
|
||||
clgame.oldmovevars = clgame.movevars;
|
||||
|
||||
// FIXME: set world wave height when entities will be allocated
|
||||
if( clgame.entities )
|
||||
clgame.entities->curstate.scale = clgame.movevars.waveHeight;
|
||||
clgame.entities->curstate.scale = clgame.movevars.waveHeight;
|
||||
|
||||
// keep features an actual!
|
||||
clgame.oldmovevars.features = clgame.movevars.features = host.features;
|
||||
|
||||
@@ -233,7 +233,7 @@ static intptr_t pfnRenderGetParm( int parm, int arg )
|
||||
return CL_RenderGetParm( parm, arg, true );
|
||||
}
|
||||
|
||||
static void pfnAVI_StreamSound( void *avi, int entnum, float fvol, float attn, float synctime )
|
||||
static void pfnAVI_StreamSound( movie_state_t *avi, int entnum, float fvol, float attn, float synctime )
|
||||
{
|
||||
return; // stub, use AVI_SetParm and AVI_Think to stream AVI sound
|
||||
}
|
||||
@@ -263,16 +263,16 @@ static render_api_t gRenderAPI =
|
||||
NULL, // DrawSingleDecal,
|
||||
NULL, // R_DecalSetupVerts,
|
||||
NULL, // R_EntityRemoveDecals,
|
||||
(void*)AVI_LoadVideo,
|
||||
(void*)AVI_GetVideoInfo,
|
||||
(void*)AVI_GetVideoFrameNumber,
|
||||
(void*)AVI_GetVideoFrame,
|
||||
AVI_LoadVideo,
|
||||
AVI_GetVideoInfo,
|
||||
AVI_GetVideoFrameNumber,
|
||||
AVI_GetVideoFrame,
|
||||
NULL, // R_UploadStretchRaw,
|
||||
(void*)AVI_FreeVideo,
|
||||
(void*)AVI_IsActive,
|
||||
(void*)pfnAVI_StreamSound,
|
||||
NULL,
|
||||
NULL,
|
||||
AVI_FreeVideo,
|
||||
AVI_IsActive,
|
||||
pfnAVI_StreamSound,
|
||||
AVI_Think,
|
||||
AVI_SetParm,
|
||||
NULL, // GL_Bind,
|
||||
NULL, // GL_SelectTexture,
|
||||
NULL, // GL_LoadTexMatrixExt,
|
||||
|
||||
@@ -2429,7 +2429,7 @@ void CL_SetLightstyle( int style, const char *s, float f )
|
||||
|
||||
if( unlikely( style < 0 || style >= MAX_LIGHTSTYLES ))
|
||||
{
|
||||
Con_Printf( S_WARN "%s: ignored invalid lightstyle id %d\n", __func__, style );
|
||||
Con_Printf( S_WARN "%s: ignored invalid lightstyle id %d\n", style );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,6 @@ AVI PLAYING
|
||||
=================================================================
|
||||
*/
|
||||
|
||||
static int xres, yres;
|
||||
static float video_duration;
|
||||
static float cin_time;
|
||||
static int cin_frame;
|
||||
static wavdata_t cin_audio;
|
||||
static movie_state_t *cin_state;
|
||||
|
||||
/*
|
||||
@@ -88,7 +83,17 @@ void SCR_CheckStartupVids( void )
|
||||
char *pfile;
|
||||
string token;
|
||||
|
||||
if( Sys_CheckParm( "-nointro" ) || host_developer.value || cls.demonum != -1 || GameState->nextstate != STATE_RUNFRAME )
|
||||
#if 0
|
||||
if( host_developer.value )
|
||||
{
|
||||
// don't run movies where we in developer-mode
|
||||
cls.movienum = -1;
|
||||
CL_CheckStartupDemos();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( Sys_CheckParm( "-nointro" ) || cls.demonum != -1 || GameState->nextstate != STATE_RUNFRAME )
|
||||
{
|
||||
// don't run movies where we in developer-mode
|
||||
cls.movienum = -1;
|
||||
@@ -147,23 +152,9 @@ void SCR_RunCinematic( void )
|
||||
Key_SetKeyDest( key_menu );
|
||||
S_StopStreaming();
|
||||
cls.movienum = -1;
|
||||
cin_time = 0.0f;
|
||||
cls.signon = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// advances cinematic time (ignores maxfps and host_framerate settings)
|
||||
cin_time += host.realframetime;
|
||||
|
||||
// stop the video after it finishes
|
||||
if( cin_time > video_duration + 0.1f )
|
||||
{
|
||||
SCR_NextMovie( );
|
||||
return;
|
||||
}
|
||||
|
||||
// read the next frame
|
||||
cin_frame = AVI_GetVideoFrameNumber( cin_state, cin_time );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -176,21 +167,11 @@ should be skipped
|
||||
*/
|
||||
qboolean SCR_DrawCinematic( void )
|
||||
{
|
||||
static int last_frame = -1;
|
||||
qboolean redraw = false;
|
||||
byte *frame = NULL;
|
||||
|
||||
if( !ref.initialized || cin_time <= 0.0f )
|
||||
if( !ref.initialized )
|
||||
return false;
|
||||
|
||||
if( cin_frame != last_frame )
|
||||
{
|
||||
frame = AVI_GetVideoFrame( cin_state, cin_frame );
|
||||
last_frame = cin_frame;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
ref.dllFuncs.R_DrawStretchRaw( 0, 0, refState.width, refState.height, xres, yres, frame, redraw );
|
||||
if( !AVI_Think( cin_state ))
|
||||
return SCR_NextMovie();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -219,45 +200,28 @@ qboolean SCR_PlayCinematic( const char *arg )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( AVI_GetVideoInfo( cin_state, &xres, &yres, &video_duration ))) // couldn't open this at all.
|
||||
{
|
||||
AVI_CloseVideo( cin_state );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( AVI_GetAudioInfo( cin_state, &cin_audio ))
|
||||
if( AVI_HaveAudioTrack( cin_state ))
|
||||
{
|
||||
// begin streaming
|
||||
S_StopAllSounds( true );
|
||||
S_StartStreaming();
|
||||
}
|
||||
|
||||
AVI_SetParm( cin_state,
|
||||
AVI_RENDER_X, 0,
|
||||
AVI_RENDER_Y, 0,
|
||||
AVI_RENDER_W, -1,
|
||||
AVI_RENDER_H, -1,
|
||||
AVI_PARM_LAST );
|
||||
|
||||
UI_SetActiveMenu( false );
|
||||
cls.state = ca_cinematic;
|
||||
Con_FastClose();
|
||||
cin_time = 0.0f;
|
||||
cls.signon = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SCR_GetAudioChunk( char *rawdata, int length )
|
||||
{
|
||||
int r;
|
||||
|
||||
r = AVI_GetAudioChunk( cin_state, rawdata, cin_audio.loopStart, length );
|
||||
cin_audio.loopStart += r; // advance play position
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
wavdata_t *SCR_GetMovieInfo( void )
|
||||
{
|
||||
if( AVI_IsActive( cin_state ))
|
||||
return &cin_audio;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
@@ -270,7 +234,6 @@ void SCR_StopCinematic( void )
|
||||
|
||||
AVI_CloseVideo( cin_state );
|
||||
S_StopStreaming();
|
||||
cin_time = 0.0f;
|
||||
|
||||
cls.state = ca_disconnected;
|
||||
cls.signon = 0;
|
||||
|
||||
@@ -326,12 +326,8 @@ typedef struct
|
||||
pfnEventHook func; // user-defined function
|
||||
} cl_user_event_t;
|
||||
|
||||
enum
|
||||
{
|
||||
FONT_FIXED,
|
||||
FONT_VARIABLE,
|
||||
FONT_TRUETYPE
|
||||
};
|
||||
#define FONT_FIXED 0
|
||||
#define FONT_VARIABLE 1
|
||||
|
||||
#define FONT_DRAW_HUD BIT( 0 ) // pass to drawing function to apply hud_scale
|
||||
#define FONT_DRAW_UTF8 BIT( 1 ) // call UtfProcessChar
|
||||
@@ -741,7 +737,6 @@ void CL_ClearResourceLists( void );
|
||||
//
|
||||
// cl_debug.c
|
||||
//
|
||||
void CL_ReplayBufferDat_f( void );
|
||||
void CL_Parse_Debug( qboolean enable );
|
||||
void CL_Parse_RecordCommand( int cmd, int startoffset );
|
||||
void CL_ResetFrame( frame_t *frame );
|
||||
@@ -811,12 +806,11 @@ qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float sc
|
||||
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags );
|
||||
void CL_FreeFont( cl_font_t *font );
|
||||
void CL_SetFontRendermode( cl_font_t *font );
|
||||
int CL_DrawCharacter( float x, float y, uint32_t uc, const rgba_t color, const cl_font_t *font, int flags );
|
||||
void CL_DrawCharacterLen( const cl_font_t *font, uint32_t uc, int *width, int *height );
|
||||
|
||||
int CL_DrawString( float x, float y, const char *s, const rgba_t color, const cl_font_t *font, int flags );
|
||||
int CL_DrawStringf( const cl_font_t *font, float x, float y, const rgba_t color, int flags, const char *fmt, ... ) FORMAT_CHECK( 6 );
|
||||
void CL_DrawStringLen( const cl_font_t *font, const char *s, int *width, int *height, int flags );
|
||||
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags );
|
||||
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags );
|
||||
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height );
|
||||
void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags );
|
||||
int CL_DrawStringf( cl_font_t *font, float x, float y, rgba_t color, int flags, const char *fmt, ... ) FORMAT_CHECK( 6 );
|
||||
|
||||
|
||||
//
|
||||
@@ -849,21 +843,19 @@ void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int he
|
||||
void CL_DisableScissor( scissor_state_t *scissor );
|
||||
qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 );
|
||||
|
||||
static inline cl_entity_t *CL_EDICT_NUM( int index )
|
||||
static inline cl_entity_t *CL_EDICT_NUM( int n )
|
||||
{
|
||||
if( !clgame.entities ) // not in game yet
|
||||
if( !clgame.entities )
|
||||
{
|
||||
Host_Error( "%s: clgame.entities is NULL\n", __func__ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( index < 0 || index >= clgame.maxEntities )
|
||||
{
|
||||
Host_Error( "%s: bad number %i\n", __func__, index );
|
||||
return NULL;
|
||||
}
|
||||
if(( n >= 0 ) && ( n < clgame.maxEntities ))
|
||||
return clgame.entities + n;
|
||||
|
||||
return clgame.entities + index;
|
||||
Host_Error( "%s: bad number %i\n", __func__, n );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline cl_entity_t *CL_GetEntityByIndex( int index )
|
||||
@@ -874,7 +866,10 @@ static inline cl_entity_t *CL_GetEntityByIndex( int index )
|
||||
if( index < 0 || index >= clgame.maxEntities )
|
||||
return NULL;
|
||||
|
||||
return clgame.entities + index;
|
||||
if( index == 0 )
|
||||
return clgame.entities;
|
||||
|
||||
return CL_EDICT_NUM( index );
|
||||
}
|
||||
|
||||
static inline model_t *CL_ModelHandle( int modelindex )
|
||||
@@ -884,7 +879,7 @@ static inline model_t *CL_ModelHandle( int modelindex )
|
||||
|
||||
static inline qboolean CL_IsThirdPerson( void )
|
||||
{
|
||||
return clgame.dllFuncs.CL_IsThirdPerson();
|
||||
return clgame.dllFuncs.CL_IsThirdPerson() ? true : false;
|
||||
}
|
||||
|
||||
static inline cl_entity_t *CL_GetLocalPlayer( void )
|
||||
@@ -1095,7 +1090,6 @@ void CL_ReadLineFile_f( void );
|
||||
// console.c
|
||||
//
|
||||
extern convar_t con_fontsize;
|
||||
extern int g_codepage;
|
||||
int Con_Visible( void );
|
||||
qboolean Con_FixedFont( void );
|
||||
void Con_VidInit( void );
|
||||
@@ -1106,13 +1100,14 @@ void Con_DrawDebug( void );
|
||||
void Con_RunConsole( void );
|
||||
void Con_DrawConsole( void );
|
||||
void Con_DrawVersion( void );
|
||||
int Con_UtfProcessChar( int in );
|
||||
int Con_UtfProcessCharForce( int in );
|
||||
int Con_UtfMoveLeft( char *str, int pos );
|
||||
int Con_UtfMoveRight( char *str, int pos, int length );
|
||||
void Con_DefaultColor( int r, int g, int b, qboolean gameui );
|
||||
cl_font_t *Con_GetCurFont( void );
|
||||
cl_font_t *Con_GetFont( int num );
|
||||
int Con_DrawString( int x, int y, const char *string, const rgba_t setColor ); // legacy, use cl_font.c
|
||||
int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); // legacy, use cl_font.c
|
||||
void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height ); // legacy, use cl_font.c
|
||||
void Con_CharEvent( int key );
|
||||
void Key_Console( int key );
|
||||
@@ -1219,6 +1214,7 @@ void OSK_Draw( void );
|
||||
//
|
||||
void ID_Init( void );
|
||||
const char *ID_GetMD5( void );
|
||||
void GAME_EXPORT ID_SetCustomClientID( const char *id );
|
||||
|
||||
extern rgba_t g_color_table[8];
|
||||
extern triangleapi_t gTriApi;
|
||||
|
||||
@@ -34,7 +34,7 @@ static CVAR_DEFINE_AUTO( con_color, "240 180 24", FCVAR_ARCHIVE, "set a custom c
|
||||
static CVAR_DEFINE_AUTO( scr_drawversion, "1", FCVAR_ARCHIVE, "draw version in menu or screenshots, doesn't affect console" );
|
||||
static CVAR_DEFINE_AUTO( con_oldfont, "0", 0, "use legacy font from gfx.wad, might be missing or broken" );
|
||||
|
||||
int g_codepage = 0;
|
||||
static int g_codepage = 0;
|
||||
|
||||
static qboolean g_messagemode_privileged = true;
|
||||
|
||||
@@ -622,7 +622,7 @@ int Con_UtfProcessCharForce( int in )
|
||||
// TODO: get rid of global state where possible
|
||||
static utfstate_t state = { 0 };
|
||||
|
||||
uint32_t ch = Q_DecodeUTF8( &state, in );
|
||||
int ch = Q_DecodeUTF8( &state, in );
|
||||
|
||||
if( g_codepage == 1251 )
|
||||
return Q_UnicodeToCP1251( ch );
|
||||
@@ -632,6 +632,15 @@ int Con_UtfProcessCharForce( int in )
|
||||
return '?'; // not implemented yet
|
||||
}
|
||||
|
||||
int GAME_EXPORT Con_UtfProcessChar( int in )
|
||||
{
|
||||
if( !cls.accept_utf8 ) // incoming character is not a UTF-8 sequence
|
||||
return in;
|
||||
|
||||
// otherwise, decode it and convert to selected codepage
|
||||
return Con_UtfProcessCharForce( in );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Con_UtfMoveLeft
|
||||
@@ -750,7 +759,7 @@ Con_DrawString
|
||||
client version of routine
|
||||
====================
|
||||
*/
|
||||
int Con_DrawString( int x, int y, const char *string, const rgba_t setColor )
|
||||
int Con_DrawString( int x, int y, const char *string, rgba_t setColor )
|
||||
{
|
||||
return CL_DrawString( x, y, string, setColor, con.curFont, FONT_DRAW_UTF8 );
|
||||
}
|
||||
@@ -834,14 +843,11 @@ If no console is visible, the notify window will pop up.
|
||||
*/
|
||||
void Con_Print( const char *txt )
|
||||
{
|
||||
static qboolean cr_pending = false;
|
||||
static qboolean colorstring = false;
|
||||
static char buf[MAX_PRINT_MSG];
|
||||
static int lastlength = 0;
|
||||
static int bufpos = 0;
|
||||
static int charpos = 0;
|
||||
|
||||
qboolean norefresh = false;
|
||||
static int cr_pending = 0;
|
||||
static char buf[MAX_PRINT_MSG];
|
||||
qboolean norefresh = false;
|
||||
static int lastlength = 0;
|
||||
static int bufpos = 0;
|
||||
int c, mask = 0;
|
||||
|
||||
// client not running
|
||||
@@ -867,7 +873,7 @@ void Con_Print( const char *txt )
|
||||
if( cr_pending )
|
||||
{
|
||||
Con_DeleteLastLine();
|
||||
cr_pending = false;
|
||||
cr_pending = 0;
|
||||
}
|
||||
c = *txt;
|
||||
|
||||
@@ -880,42 +886,23 @@ void Con_Print( const char *txt )
|
||||
{
|
||||
Con_AddLine( buf, bufpos, true );
|
||||
lastlength = CON_LINES_LAST().length;
|
||||
cr_pending = true;
|
||||
cr_pending = 1;
|
||||
bufpos = 0;
|
||||
charpos = 0;
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
Con_AddLine( buf, bufpos, true );
|
||||
lastlength = CON_LINES_LAST().length;
|
||||
bufpos = 0;
|
||||
charpos = 0;
|
||||
break;
|
||||
default:
|
||||
|
||||
buf[bufpos++] = c | mask;
|
||||
|
||||
if( IsColorString( txt ))
|
||||
{
|
||||
// first color string character
|
||||
colorstring = true;
|
||||
}
|
||||
else if( colorstring )
|
||||
{
|
||||
// second color string character
|
||||
colorstring = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a color string, move char counter
|
||||
charpos++;
|
||||
}
|
||||
|
||||
if(( bufpos >= sizeof( buf ) - 1 ) || charpos >= ( con.linewidth - 1 ))
|
||||
if(( bufpos >= sizeof( buf ) - 1 ) || bufpos >= ( con.linewidth - 1 ))
|
||||
{
|
||||
Con_AddLine( buf, bufpos, true );
|
||||
lastlength = CON_LINES_LAST().length;
|
||||
bufpos = 0;
|
||||
charpos = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -931,7 +918,6 @@ void Con_Print( const char *txt )
|
||||
Con_AddLine( buf, bufpos, lastlength != 0 );
|
||||
lastlength = 0;
|
||||
bufpos = 0;
|
||||
charpos = 0;
|
||||
}
|
||||
|
||||
// pump messages to avoid window hanging
|
||||
@@ -1258,16 +1244,45 @@ static void Field_CharEvent( field_t *edit, int ch )
|
||||
Field_DrawInputLine
|
||||
==================
|
||||
*/
|
||||
static void Field_DrawInputLine( int x, int y, const field_t *edit )
|
||||
static void Field_DrawInputLine( int x, int y, field_t *edit )
|
||||
{
|
||||
int curPos;
|
||||
char str[MAX_SYSPATH];
|
||||
const byte *colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
|
||||
const int prestep = bound( 0, edit->scroll, sizeof( edit->buffer ) - 1 );
|
||||
const int drawLen = bound( 0, edit->widthInChars, sizeof( str ));
|
||||
const int cursorCharPos = bound( 0, edit->cursor - prestep, sizeof( str ));
|
||||
int len, cursorChar;
|
||||
int drawLen;
|
||||
int prestep, curPos;
|
||||
char str[MAX_SYSPATH];
|
||||
byte *colorDefault;
|
||||
|
||||
Q_strncpy( str, edit->buffer + prestep, drawLen );
|
||||
drawLen = edit->widthInChars;
|
||||
len = Q_strlen( edit->buffer ) + 1;
|
||||
colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
|
||||
|
||||
// guarantee that cursor will be visible
|
||||
if( len <= drawLen )
|
||||
{
|
||||
prestep = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( edit->scroll + drawLen > len )
|
||||
{
|
||||
edit->scroll = len - drawLen;
|
||||
if( edit->scroll < 0 ) edit->scroll = 0;
|
||||
}
|
||||
|
||||
prestep = edit->scroll;
|
||||
}
|
||||
|
||||
if( prestep + drawLen > len )
|
||||
drawLen = len - prestep;
|
||||
|
||||
// extract <drawLen> characters from the field at <prestep>
|
||||
drawLen = Q_min( drawLen, MAX_SYSPATH - 1 );
|
||||
|
||||
memcpy( str, edit->buffer + prestep, drawLen );
|
||||
str[drawLen] = 0;
|
||||
|
||||
// save char for overstrike
|
||||
cursorChar = str[edit->cursor - prestep];
|
||||
|
||||
// draw it
|
||||
CL_DrawString( x, y, str, colorDefault, con.curFont, FONT_DRAW_UTF8 );
|
||||
@@ -1276,7 +1291,7 @@ static void Field_DrawInputLine( int x, int y, const field_t *edit )
|
||||
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
|
||||
|
||||
// calc cursor position
|
||||
str[cursorCharPos] = 0;
|
||||
str[edit->cursor - prestep] = 0;
|
||||
CL_DrawStringLen( con.curFont, str, &curPos, NULL, FONT_DRAW_UTF8 );
|
||||
|
||||
if( host.key_overstrike )
|
||||
@@ -1452,16 +1467,6 @@ Handles history and console scrollback
|
||||
*/
|
||||
void Key_Console( int key )
|
||||
{
|
||||
// exit the console by pressing MINUS on NSwitch
|
||||
// or both Back(Select)/Start buttons for everyone else
|
||||
if( key == K_BACK_BUTTON || key == K_START_BUTTON || key == K_ESCAPE )
|
||||
{
|
||||
if( cls.state == ca_active && !cl.background )
|
||||
Key_SetKeyDest( key_game );
|
||||
else UI_SetActiveMenu( true );
|
||||
return;
|
||||
}
|
||||
|
||||
// ctrl-L clears screen
|
||||
if( key == 'l' && Key_IsDown( K_CTRL ))
|
||||
{
|
||||
@@ -1570,6 +1575,16 @@ void Key_Console( int key )
|
||||
return;
|
||||
}
|
||||
|
||||
// exit the console by pressing MINUS on NSwitch
|
||||
// or both Back(Select)/Start buttons for everyone else
|
||||
if( key == K_BACK_BUTTON || key == K_START_BUTTON )
|
||||
{
|
||||
if( cls.state == ca_active && !cl.background )
|
||||
Key_SetKeyDest( key_game );
|
||||
else UI_SetActiveMenu( true );
|
||||
return;
|
||||
}
|
||||
|
||||
// pass to the normal editline routine
|
||||
Field_KeyDownEvent( &con.input, key );
|
||||
}
|
||||
@@ -1993,7 +2008,7 @@ void Con_DrawVersion( void )
|
||||
int start, height = refState.height;
|
||||
string curbuild;
|
||||
|
||||
if( !scr_drawversion.value )
|
||||
if( !scr_drawversion.value || CL_IsDevOverviewMode() == 2 || net_graph.value )
|
||||
return;
|
||||
|
||||
if( cls.key_dest == key_menu )
|
||||
@@ -2005,9 +2020,6 @@ void Con_DrawVersion( void )
|
||||
{
|
||||
qboolean draw_version;
|
||||
|
||||
if( CL_IsDevOverviewMode() == 2 || net_graph.value )
|
||||
return;
|
||||
|
||||
draw_version = cls.scrshot_action == scrshot_normal
|
||||
|| cls.scrshot_action == scrshot_snapshot
|
||||
|| host.force_draw_version_time > host.realtime;
|
||||
@@ -2080,7 +2092,10 @@ void Con_RunConsole( void )
|
||||
}
|
||||
else
|
||||
{
|
||||
g_codepage = 0;
|
||||
Con_Printf( S_WARN "Unknown charset %s, defaulting to cp1252", con_charset.string );
|
||||
|
||||
Cvar_DirectSet( &con_charset, "cp1252" );
|
||||
g_codepage = 1252;
|
||||
}
|
||||
|
||||
cls.accept_utf8 = !Q_stricmp( cl_charset.string, "utf-8" );
|
||||
|
||||
@@ -19,8 +19,8 @@ GNU General Public License for more details.
|
||||
#if !XASH_WIN32
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
static char id_md5[33];
|
||||
static char id_customid[MAX_STRING];
|
||||
|
||||
/*
|
||||
==========================================================
|
||||
@@ -590,9 +590,25 @@ static void ID_Check( void )
|
||||
|
||||
const char *ID_GetMD5( void )
|
||||
{
|
||||
if( id_customid[0] )
|
||||
return id_customid;
|
||||
return id_md5;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ID_SetCustomClientID
|
||||
|
||||
===============
|
||||
*/
|
||||
void GAME_EXPORT ID_SetCustomClientID( const char *id )
|
||||
{
|
||||
if( !id )
|
||||
return;
|
||||
|
||||
Q_strncpy( id_customid, id, sizeof( id_customid ) );
|
||||
}
|
||||
|
||||
void ID_Init( void )
|
||||
{
|
||||
MD5Context_t hash = { 0 };
|
||||
|
||||
@@ -43,6 +43,7 @@ static struct joy_axis_s
|
||||
short val;
|
||||
short prevval;
|
||||
} joyaxis[MAX_AXES] = { 0 };
|
||||
static byte currentbinding; // add posibility to remap keys, to place it in joykeys[]
|
||||
static qboolean joy_initialized;
|
||||
|
||||
static CVAR_DEFINE_AUTO( joy_pitch, "100.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick pitch sensitivity" );
|
||||
@@ -59,9 +60,9 @@ static CVAR_DEFINE_AUTO( joy_pitch_deadzone, DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE
|
||||
static CVAR_DEFINE_AUTO( joy_yaw_deadzone, DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "yaw axis deadzone. Value from 0 to 32767" );
|
||||
static CVAR_DEFINE_AUTO( joy_axis_binding, "sfpyrl", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "axis hardware id to engine inner axis binding, "
|
||||
"s - side, f - forward, y - yaw, p - pitch, r - left trigger, l - right trigger" );
|
||||
static CVAR_DEFINE_AUTO( joy_found, "0", FCVAR_READ_ONLY, "is joystick is connected" );
|
||||
static CVAR_DEFINE_AUTO( joy_index, "0", FCVAR_READ_ONLY, "current active joystick" );
|
||||
CVAR_DEFINE_AUTO( joy_enable, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable joystick" );
|
||||
static CVAR_DEFINE_AUTO( joy_have_gyro, "0", FCVAR_READ_ONLY, "tells whether current active gamepad has gyroscope or not" );
|
||||
static CVAR_DEFINE_AUTO( joy_calibrated, "0", FCVAR_READ_ONLY, "tells whether current active gamepad gyroscope has been calibrated or not" );
|
||||
|
||||
/*
|
||||
============
|
||||
@@ -70,30 +71,7 @@ Joy_IsActive
|
||||
*/
|
||||
qboolean Joy_IsActive( void )
|
||||
{
|
||||
return joy_enable.value;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
Joy_SetCapabilities
|
||||
===========
|
||||
*/
|
||||
void Joy_SetCapabilities( qboolean have_gyro )
|
||||
{
|
||||
Cvar_FullSet( joy_have_gyro.name, have_gyro ? "1" : "0", joy_have_gyro.flags );
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
Joy_SetCalibrationState
|
||||
===========
|
||||
*/
|
||||
void Joy_SetCalibrationState( joy_calibration_state_t state )
|
||||
{
|
||||
if( (int)joy_calibrated.value == state )
|
||||
return;
|
||||
|
||||
Cvar_FullSet( joy_calibrated.name, va( "%d", state ), joy_calibrated.flags );
|
||||
return joy_found.value && joy_enable.value;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -103,7 +81,7 @@ Joy_HatMotionEvent
|
||||
DPad events
|
||||
============
|
||||
*/
|
||||
static void Joy_HatMotionEvent( int value )
|
||||
void Joy_HatMotionEvent( byte hat, byte value )
|
||||
{
|
||||
struct
|
||||
{
|
||||
@@ -118,6 +96,9 @@ static void Joy_HatMotionEvent( int value )
|
||||
};
|
||||
int i;
|
||||
|
||||
if( !joy_found.value )
|
||||
return;
|
||||
|
||||
for( i = 0; i < ARRAYSIZE( keys ); i++ )
|
||||
{
|
||||
if( value & keys[i].mask )
|
||||
@@ -244,7 +225,7 @@ static void Joy_ProcessStick( const engineAxis_t engineAxis, short value )
|
||||
val |= Joy_GetHatValueForAxis( JOY_AXIS_SIDE );
|
||||
val |= Joy_GetHatValueForAxis( JOY_AXIS_FWD );
|
||||
|
||||
Joy_HatMotionEvent( val );
|
||||
Joy_HatMotionEvent( 0, val );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,9 +236,23 @@ Joy_AxisMotionEvent
|
||||
Axis events
|
||||
=============
|
||||
*/
|
||||
void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value )
|
||||
void Joy_AxisMotionEvent( byte axis, short value )
|
||||
{
|
||||
if( engineAxis >= JOY_AXIS_NULL )
|
||||
if( !joy_found.value )
|
||||
return;
|
||||
|
||||
if( axis >= MAX_AXES )
|
||||
{
|
||||
Con_Reportf( "Only 6 axes is supported\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Joy_KnownAxisMotionEvent( joyaxesmap[axis], value );
|
||||
}
|
||||
|
||||
void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value )
|
||||
{
|
||||
if( engineAxis == JOY_AXIS_NULL )
|
||||
return;
|
||||
|
||||
if( value == joyaxis[engineAxis].val )
|
||||
@@ -271,14 +266,66 @@ void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value )
|
||||
|
||||
/*
|
||||
=============
|
||||
Joy_GyroEvent
|
||||
Joy_BallMotionEvent
|
||||
|
||||
Gyroscope events
|
||||
Trackball events. UNDONE
|
||||
=============
|
||||
*/
|
||||
void Joy_GyroEvent( vec3_t data )
|
||||
void Joy_BallMotionEvent( byte ball, short xrel, short yrel )
|
||||
{
|
||||
//if( !joy_found.value )
|
||||
// return;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Joy_ButtonEvent
|
||||
|
||||
Button events
|
||||
=============
|
||||
*/
|
||||
void Joy_ButtonEvent( byte button, byte down )
|
||||
{
|
||||
if( !joy_found.value )
|
||||
return;
|
||||
|
||||
// generic game button code.
|
||||
if( button > 32 )
|
||||
{
|
||||
int origbutton = button;
|
||||
button = ( button & 31 ) + K_AUX1;
|
||||
|
||||
Con_Reportf( "Only 32 joybuttons is supported, converting %i button ID to %s\n", origbutton, Key_KeynumToString( button ) );
|
||||
}
|
||||
else button += K_AUX1;
|
||||
|
||||
Key_Event( button, down );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Joy_RemoveEvent
|
||||
|
||||
Called when joystick is removed. For future expansion
|
||||
=============
|
||||
*/
|
||||
void Joy_RemoveEvent( void )
|
||||
{
|
||||
if( joy_found.value )
|
||||
Cvar_FullSet( "joy_found", "0", FCVAR_READ_ONLY );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Joy_RemoveEvent
|
||||
|
||||
Called when joystick is removed. For future expansion
|
||||
=============
|
||||
*/
|
||||
void Joy_AddEvent( void )
|
||||
{
|
||||
if( joy_enable.value && !joy_found.value )
|
||||
Cvar_FullSet( "joy_found", "1", FCVAR_READ_ONLY );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -317,19 +364,14 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
|
||||
|
||||
*fw -= joy_forward.value * (float)joyaxis[JOY_AXIS_FWD ].val/(float)SHRT_MAX; // must be form -1.0 to 1.0
|
||||
*side += joy_side.value * (float)joyaxis[JOY_AXIS_SIDE].val/(float)SHRT_MAX;
|
||||
#if !defined(XASH_SDL)
|
||||
*dpitch += joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime; // abs axis rotate is frametime related
|
||||
*dyaw -= joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
|
||||
#else
|
||||
// HACKHACK: SDL have inverted look axis.
|
||||
*dpitch -= joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime;
|
||||
*dyaw += joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
|
||||
}
|
||||
|
||||
static void Joy_CalibrateGyro_f( void )
|
||||
{
|
||||
if( !joy_have_gyro.value )
|
||||
{
|
||||
Con_Printf( "Current active gamepad doesn't have gyroscope\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Platform_CalibrateGamepadGyro();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -341,8 +383,6 @@ Main init procedure
|
||||
*/
|
||||
void Joy_Init( void )
|
||||
{
|
||||
Cmd_AddRestrictedCommand( "joy_calibrate_gyro", Joy_CalibrateGyro_f, "calibrate gamepad gyroscope. You must to put gamepad on stationary surface" );
|
||||
|
||||
Cvar_RegisterVariable( &joy_pitch );
|
||||
Cvar_RegisterVariable( &joy_yaw );
|
||||
Cvar_RegisterVariable( &joy_side );
|
||||
@@ -362,9 +402,11 @@ void Joy_Init( void )
|
||||
Cvar_RegisterVariable( &joy_yaw_deadzone );
|
||||
|
||||
Cvar_RegisterVariable( &joy_axis_binding );
|
||||
Cvar_RegisterVariable( &joy_found );
|
||||
// we doesn't loaded config.cfg yet, so this cvar is not archive.
|
||||
// change by +set joy_index in cmdline
|
||||
Cvar_RegisterVariable( &joy_index );
|
||||
|
||||
Cvar_RegisterVariable( &joy_have_gyro );
|
||||
Cvar_RegisterVariable( &joy_calibrated );
|
||||
Cvar_RegisterVariable( &joy_enable );
|
||||
|
||||
// renamed from -nojoy to -noenginejoy to not conflict with
|
||||
@@ -375,7 +417,7 @@ void Joy_Init( void )
|
||||
return;
|
||||
}
|
||||
|
||||
Platform_JoyInit();
|
||||
Cvar_FullSet( "joy_found", va( "%d", Platform_JoyInit( joy_index.value )), FCVAR_READ_ONLY );
|
||||
|
||||
joy_initialized = true;
|
||||
}
|
||||
@@ -389,5 +431,8 @@ Shutdown joystick code
|
||||
*/
|
||||
void Joy_Shutdown( void )
|
||||
{
|
||||
Platform_JoyShutdown();
|
||||
if( joy_initialized )
|
||||
{
|
||||
Cvar_FullSet( "joy_found", 0, FCVAR_READ_ONLY );
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -205,10 +205,9 @@ void IN_ToggleClientMouse( int newstate, int oldstate )
|
||||
}
|
||||
}
|
||||
|
||||
void IN_SetRelativeMouseMode( qboolean set )
|
||||
static void IN_SetRelativeMouseMode( qboolean set, qboolean verbose )
|
||||
{
|
||||
static qboolean s_bRawInput;
|
||||
qboolean verbose = m_grab_debug.value ? true : false;
|
||||
|
||||
if( set && !s_bRawInput )
|
||||
{
|
||||
@@ -232,10 +231,9 @@ void IN_SetRelativeMouseMode( qboolean set )
|
||||
}
|
||||
}
|
||||
|
||||
void IN_SetMouseGrab( qboolean set )
|
||||
static void IN_SetMouseGrab( qboolean set, qboolean verbose )
|
||||
{
|
||||
static qboolean s_bMouseGrab;
|
||||
qboolean verbose = m_grab_debug.value ? true : false;
|
||||
|
||||
if( set && !s_bMouseGrab )
|
||||
{
|
||||
@@ -260,7 +258,7 @@ void IN_SetMouseGrab( qboolean set )
|
||||
|
||||
static void IN_CheckMouseState( qboolean active )
|
||||
{
|
||||
qboolean use_raw_input;
|
||||
qboolean use_raw_input, verbose;
|
||||
|
||||
#if XASH_WIN32
|
||||
use_raw_input = ( m_rawinput.value && clgame.client_dll_uses_sdl ) || clgame.dllFuncs.pfnLookEvent != NULL;
|
||||
@@ -268,18 +266,20 @@ static void IN_CheckMouseState( qboolean active )
|
||||
use_raw_input = true; // always use SDL code
|
||||
#endif
|
||||
|
||||
verbose = m_grab_debug.value ? true : false;
|
||||
|
||||
if( m_ignore.value )
|
||||
active = false;
|
||||
|
||||
if( active && use_raw_input && !host.mouse_visible && cls.state == ca_active )
|
||||
IN_SetRelativeMouseMode( true );
|
||||
IN_SetRelativeMouseMode( true, verbose );
|
||||
else
|
||||
IN_SetRelativeMouseMode( false );
|
||||
IN_SetRelativeMouseMode( false, verbose );
|
||||
|
||||
if( active && !host.mouse_visible && cls.state == ca_active )
|
||||
IN_SetMouseGrab( true );
|
||||
IN_SetMouseGrab( true, verbose );
|
||||
else
|
||||
IN_SetMouseGrab( false );
|
||||
IN_SetMouseGrab( false, verbose );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -332,7 +332,7 @@ static void IN_MouseMove( void )
|
||||
if( !in_mouseinitialized )
|
||||
return;
|
||||
|
||||
if( Touch_WantVisibleCursor( ))
|
||||
if( Touch_Emulated( ))
|
||||
{
|
||||
// touch emulation overrides all input
|
||||
Touch_KeyEvent( 0, 0 );
|
||||
@@ -363,7 +363,7 @@ void IN_MouseEvent( int key, int down )
|
||||
else ClearBits( in_mstate, BIT( key ));
|
||||
|
||||
// touch emulation overrides all input
|
||||
if( Touch_WantVisibleCursor( ))
|
||||
if( Touch_Emulated( ))
|
||||
{
|
||||
Touch_KeyEvent( K_MOUSE1 + key, down );
|
||||
}
|
||||
|
||||
@@ -46,9 +46,6 @@ uint IN_CollectInputDevices( void );
|
||||
void IN_LockInputDevices( qboolean lock );
|
||||
void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active );
|
||||
|
||||
void IN_SetRelativeMouseMode( qboolean set );
|
||||
void IN_SetMouseGrab( qboolean set );
|
||||
|
||||
extern convar_t m_yaw;
|
||||
extern convar_t m_pitch;
|
||||
//
|
||||
@@ -77,6 +74,7 @@ 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 );
|
||||
qboolean Touch_Emulated( void );
|
||||
void Touch_NotifyResize( void );
|
||||
|
||||
//
|
||||
@@ -106,21 +104,18 @@ typedef enum engineAxis_e
|
||||
JOY_AXIS_NULL
|
||||
} engineAxis_t;
|
||||
|
||||
typedef enum joy_calibration_state_s
|
||||
{
|
||||
JOY_NOT_CALIBRATED = 0,
|
||||
JOY_CALIBRATING,
|
||||
JOY_FAILED_TO_CALIBRATE,
|
||||
JOY_CALIBRATED
|
||||
} joy_calibration_state_t;
|
||||
|
||||
qboolean Joy_IsActive( void );
|
||||
void Joy_SetCapabilities( qboolean have_gyro );
|
||||
void Joy_SetCalibrationState( joy_calibration_state_t state );
|
||||
void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value );
|
||||
void Joy_GyroEvent( vec3_t data );
|
||||
void Joy_HatMotionEvent( byte hat, byte value );
|
||||
void Joy_AxisMotionEvent( byte axis, short value );
|
||||
void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value );
|
||||
void Joy_BallMotionEvent( byte ball, short xrel, short yrel );
|
||||
void Joy_ButtonEvent( byte button, byte down );
|
||||
void Joy_AddEvent( void );
|
||||
void Joy_RemoveEvent( void );
|
||||
void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw );
|
||||
void Joy_Init( void );
|
||||
void Joy_Shutdown( void );
|
||||
void Joy_EnableTextInput(qboolean enable, qboolean force);
|
||||
|
||||
|
||||
#endif//INPUT_H
|
||||
|
||||
@@ -726,8 +726,18 @@ void GAME_EXPORT Key_Event( int key, int down )
|
||||
return; // handled in client.dll
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case key_message:
|
||||
Key_Message( key );
|
||||
return;
|
||||
case key_console:
|
||||
if( cls.state == ca_active && !cl.background )
|
||||
Key_SetKeyDest( key_game );
|
||||
else UI_SetActiveMenu( true );
|
||||
return;
|
||||
case key_menu:
|
||||
UI_KeyEvent( key, true );
|
||||
return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,7 +967,6 @@ enum
|
||||
OSK_ENTER,
|
||||
OSK_SPECKEY_LAST
|
||||
};
|
||||
|
||||
static const char *osk_keylayout[][4] =
|
||||
{
|
||||
{
|
||||
@@ -974,7 +983,7 @@ static const char *osk_keylayout[][4] =
|
||||
}
|
||||
};
|
||||
|
||||
static struct osk_s
|
||||
struct osk_s
|
||||
{
|
||||
qboolean enable;
|
||||
int curlayout;
|
||||
@@ -1008,6 +1017,7 @@ static qboolean OSK_KeyEvent( int key, int down )
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
switch ( key )
|
||||
{
|
||||
case K_ENTER:
|
||||
@@ -1050,6 +1060,10 @@ static qboolean OSK_KeyEvent( int key, int down )
|
||||
|
||||
ch = (byte)osk.curbutton.val;
|
||||
|
||||
// do not pass UTF-8 sequence into the engine, convert it here
|
||||
if( !cls.accept_utf8 )
|
||||
ch = Con_UtfProcessCharForce( ch );
|
||||
|
||||
if( !ch )
|
||||
break;
|
||||
|
||||
@@ -1093,7 +1107,7 @@ static qboolean OSK_KeyEvent( int key, int down )
|
||||
|
||||
/*
|
||||
=============
|
||||
OSK_EnableTextInput
|
||||
Joy_EnableTextInput
|
||||
|
||||
Enables built-in IME
|
||||
=============
|
||||
@@ -1184,15 +1198,13 @@ void OSK_Draw( void )
|
||||
X_STEP * MAX_OSK_ROWS * refState.width,
|
||||
Y_STEP * MAX_OSK_LINES * refState.height, 100, 100, 100, 100 );
|
||||
|
||||
OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
|
||||
OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
|
||||
OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
|
||||
OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
|
||||
|
||||
OSK_DrawSpecialButton( "sh", X_START, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
|
||||
OSK_DrawSpecialButton( "en", X_START + X_STEP * 12, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
|
||||
|
||||
for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP )
|
||||
{
|
||||
for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP )
|
||||
for( x = X_START, i = 0; i < MAX_OSK_ROWS; i++, x += X_STEP )
|
||||
OSK_DrawSymbolButton( curlayout[j][i], x, y, X_STEP, Y_STEP );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ GNU General Public License for more details.
|
||||
#include "pm_local.h"
|
||||
#include "platform/platform.h"
|
||||
|
||||
#define SND_CLIP_DISTANCE 1000.0f
|
||||
|
||||
dma_t dma;
|
||||
poolhandle_t sndpool;
|
||||
static soundfade_t soundfade;
|
||||
@@ -1111,7 +1109,7 @@ rawchan_t *S_FindRawChannel( int entnum, qboolean create )
|
||||
S_RawSamplesStereo
|
||||
===================
|
||||
*/
|
||||
static uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint max_samples, uint samples, uint rate, word width, word channels, const byte *data )
|
||||
uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint max_samples, uint samples, uint rate, word width, word channels, const byte *data )
|
||||
{
|
||||
uint fracstep, samplefrac;
|
||||
uint src, dst;
|
||||
@@ -1193,20 +1191,6 @@ void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word chan
|
||||
ch->leftvol = ch->rightvol = snd_vol;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
S_RawSamples
|
||||
===================
|
||||
*/
|
||||
void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum )
|
||||
{
|
||||
int snd_vol = 128;
|
||||
|
||||
if( entnum < 0 ) snd_vol = 256; // bg track or movie track
|
||||
|
||||
S_RawEntSamples( entnum, samples, rate, width, channels, data, snd_vol );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
S_FreeIdleRawChannels
|
||||
@@ -1610,7 +1594,6 @@ void SND_UpdateSound( void )
|
||||
}
|
||||
|
||||
S_StreamBackgroundTrack ();
|
||||
S_StreamSoundTrack ();
|
||||
|
||||
// mix some sound
|
||||
S_UpdateChannels ();
|
||||
|
||||
@@ -242,7 +242,7 @@ void S_StreamBackgroundTrack( void )
|
||||
if( r > 0 )
|
||||
{
|
||||
// add to raw buffer
|
||||
S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_BACKGROUNDTRACK );
|
||||
S_RawEntSamples( S_RAW_SOUND_BACKGROUNDTRACK, fileSamples, info->rate, info->width, info->channels, raw, 255 );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -287,66 +287,3 @@ void S_StopStreaming( void )
|
||||
if( !dma.initialized ) return;
|
||||
s_listener.streaming = false;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_StreamSoundTrack
|
||||
=================
|
||||
*/
|
||||
void S_StreamSoundTrack( void )
|
||||
{
|
||||
int bufferSamples;
|
||||
int fileSamples;
|
||||
byte raw[MAX_RAW_SAMPLES];
|
||||
int r, fileBytes;
|
||||
rawchan_t *ch = NULL;
|
||||
|
||||
if( !dma.initialized || !s_listener.streaming || s_listener.paused )
|
||||
return;
|
||||
|
||||
ch = S_FindRawChannel( S_RAW_SOUND_SOUNDTRACK, true );
|
||||
|
||||
Assert( ch != NULL );
|
||||
|
||||
// see how many samples should be copied into the raw buffer
|
||||
if( ch->s_rawend < soundtime )
|
||||
ch->s_rawend = soundtime;
|
||||
|
||||
while( ch->s_rawend < soundtime + ch->max_samples )
|
||||
{
|
||||
wavdata_t *info = SCR_GetMovieInfo();
|
||||
|
||||
if( !info ) break; // bad soundtrack?
|
||||
|
||||
bufferSamples = ch->max_samples - (ch->s_rawend - soundtime);
|
||||
|
||||
// decide how much data needs to be read from the file
|
||||
fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED );
|
||||
if( fileSamples <= 1 ) return; // no more samples need
|
||||
|
||||
// our max buffer size
|
||||
fileBytes = fileSamples * ( info->width * info->channels );
|
||||
|
||||
if( fileBytes > sizeof( raw ))
|
||||
{
|
||||
fileBytes = sizeof( raw );
|
||||
fileSamples = fileBytes / ( info->width * info->channels );
|
||||
}
|
||||
|
||||
// read audio stream
|
||||
r = SCR_GetAudioChunk( raw, fileBytes );
|
||||
|
||||
if( r < fileBytes )
|
||||
{
|
||||
fileBytes = r;
|
||||
fileSamples = r / ( info->width * info->channels );
|
||||
}
|
||||
|
||||
if( r > 0 )
|
||||
{
|
||||
// add to raw buffer
|
||||
S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_SOUNDTRACK );
|
||||
}
|
||||
else break; // no more samples for this frame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,8 @@ typedef int sound_t;
|
||||
#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 48
|
||||
#define MAX_RAW_SAMPLES 8192
|
||||
#define MAX_RAW_SAMPLES 16384
|
||||
#define SND_CLIP_DISTANCE 1000.0f
|
||||
|
||||
extern sound_t ambient_sfx[NUM_AMBIENTS];
|
||||
extern qboolean snd_ambient;
|
||||
@@ -243,8 +244,8 @@ 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 );
|
||||
uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint max_samples, uint samples, uint rate, word width, word channels, const byte *data );
|
||||
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 );
|
||||
void S_StopAllSounds( qboolean ambient );
|
||||
@@ -264,7 +265,6 @@ void SND_ForceCloseMouth( int entnum );
|
||||
//
|
||||
// s_stream.c
|
||||
//
|
||||
void S_StreamSoundTrack( void );
|
||||
void S_StreamBackgroundTrack( void );
|
||||
void S_PrintBackgroundTrackState( void );
|
||||
void S_FadeMusicVolume( float fadePercent );
|
||||
|
||||
@@ -76,14 +76,23 @@ 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
|
||||
|
||||
enum dev_level_e
|
||||
enum
|
||||
{
|
||||
DEV_NONE = 0,
|
||||
DEV_NORMAL,
|
||||
DEV_EXTENDED
|
||||
};
|
||||
|
||||
typedef enum instance_e
|
||||
enum
|
||||
{
|
||||
D_INFO = 1, // "-dev 1", shows various system messages
|
||||
D_WARN, // "-dev 2", shows not critical system warnings
|
||||
D_ERROR, // "-dev 3", shows critical warnings
|
||||
D_REPORT, // "-dev 4", special case for game reports
|
||||
D_NOTE // "-dev 5", show system notifications for engine developers
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HOST_NORMAL, // listen server, singleplayer
|
||||
HOST_DEDICATED,
|
||||
@@ -167,11 +176,6 @@ extern convar_t cl_filterstuffcmd;
|
||||
extern convar_t rcon_password;
|
||||
extern convar_t hpk_custom_file;
|
||||
extern convar_t con_gamemaps;
|
||||
extern convar_t fs_mount_lv;
|
||||
extern convar_t fs_mount_hd;
|
||||
extern convar_t fs_mount_addon;
|
||||
extern convar_t fs_mount_l10n;
|
||||
extern convar_t ui_language; // historically used for UI, but now controls mounted localization directory
|
||||
|
||||
#define Mod_AllowMaterials() ( host_allow_materials.value != 0.0f && !FBitSet( host.features, ENGINE_DISABLE_HDTEXTURES ))
|
||||
|
||||
@@ -190,7 +194,7 @@ GAMEINFO stuff
|
||||
internal shared gameinfo structure (readonly for engine parts)
|
||||
========================================================================
|
||||
*/
|
||||
typedef enum host_status_e
|
||||
typedef enum
|
||||
{
|
||||
HOST_INIT = 0, // initalize operations
|
||||
HOST_FRAME, // host running
|
||||
@@ -201,7 +205,7 @@ typedef enum host_status_e
|
||||
HOST_CRASHED // an exception handler called
|
||||
} host_status_t;
|
||||
|
||||
typedef enum host_state_e
|
||||
typedef enum
|
||||
{
|
||||
STATE_RUNFRAME = 0,
|
||||
STATE_LOAD_LEVEL,
|
||||
@@ -210,7 +214,7 @@ typedef enum host_state_e
|
||||
STATE_GAME_SHUTDOWN,
|
||||
} host_state_t;
|
||||
|
||||
typedef struct game_status_e
|
||||
typedef struct
|
||||
{
|
||||
host_state_t curstate;
|
||||
host_state_t nextstate;
|
||||
@@ -221,7 +225,7 @@ typedef struct game_status_e
|
||||
qboolean newGame; // unload the server.dll before start a new map
|
||||
} game_status_t;
|
||||
|
||||
typedef enum keydest_e
|
||||
typedef enum
|
||||
{
|
||||
key_console = 0,
|
||||
key_game,
|
||||
@@ -229,7 +233,7 @@ typedef enum keydest_e
|
||||
key_message
|
||||
} keydest_t;
|
||||
|
||||
typedef enum rdtype_e
|
||||
typedef enum
|
||||
{
|
||||
RD_NONE = 0,
|
||||
RD_CLIENT,
|
||||
@@ -239,7 +243,7 @@ typedef enum rdtype_e
|
||||
#include "net_ws.h"
|
||||
|
||||
// console field
|
||||
typedef struct field_e
|
||||
typedef struct
|
||||
{
|
||||
string buffer;
|
||||
int cursor;
|
||||
@@ -257,7 +261,7 @@ typedef struct host_redirect_s
|
||||
int lines;
|
||||
} host_redirect_t;
|
||||
|
||||
typedef struct soundlist_e
|
||||
typedef struct
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
short entnum;
|
||||
@@ -298,7 +302,7 @@ typedef struct host_parm_s
|
||||
|
||||
host_status_t status; // global host state
|
||||
game_status_t game; // game manager
|
||||
instance_t type; // running at
|
||||
uint type; // running at
|
||||
poolhandle_t mempool; // static mempool for misc allocations
|
||||
poolhandle_t imagepool; // imagelib mempool
|
||||
poolhandle_t soundpool; // soundlib mempool
|
||||
@@ -415,8 +419,6 @@ byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamediro
|
||||
MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr )
|
||||
MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
void FS_Rescan_f( void );
|
||||
void FS_CheckConfig( void );
|
||||
|
||||
//
|
||||
// cmd.c
|
||||
@@ -494,7 +496,7 @@ internal sound format
|
||||
typically expanded to wav buffer
|
||||
========================================================================
|
||||
*/
|
||||
typedef enum sndformat_e
|
||||
typedef enum
|
||||
{
|
||||
WF_UNKNOWN = 0,
|
||||
WF_PCMDATA,
|
||||
@@ -505,7 +507,7 @@ typedef enum sndformat_e
|
||||
} sndformat_t;
|
||||
|
||||
// wavdata output flags
|
||||
typedef enum sndFlags_e
|
||||
typedef enum
|
||||
{
|
||||
// wavdata->flags
|
||||
SOUND_LOOPED = BIT( 0 ), // this is looped sound (contain cue markers)
|
||||
@@ -515,7 +517,7 @@ typedef enum sndFlags_e
|
||||
SOUND_RESAMPLE = BIT( 12 ), // resample sound to specified rate
|
||||
} sndFlags_t;
|
||||
|
||||
typedef struct wavdata_s
|
||||
typedef struct
|
||||
{
|
||||
size_t size; // for bounds checking
|
||||
uint loopStart; // offset at this point sound will be looping while playing more than only once
|
||||
@@ -731,7 +733,7 @@ qboolean UI_CreditsActive( void );
|
||||
int CL_GetMaxClients( void );
|
||||
#else
|
||||
static inline qboolean CL_Initialized( void ) { return false; }
|
||||
static inline qboolean CL_IsInGame( void ) { return true; } // always true for dedicated
|
||||
static inline qboolean CL_IsInGame( void ) { return false; }
|
||||
static inline qboolean CL_IsInConsole( void ) { return false; }
|
||||
static inline qboolean CL_IsIntermission( void ) { return false; }
|
||||
static inline qboolean CL_DisableVisibility( void ) { return false; }
|
||||
@@ -782,8 +784,6 @@ void SCR_Init( void );
|
||||
void SCR_UpdateScreen( void );
|
||||
void SCR_BeginLoadingPlaque( qboolean is_background );
|
||||
void SCR_CheckStartupVids( void );
|
||||
int SCR_GetAudioChunk( char *rawdata, int length );
|
||||
wavdata_t *SCR_GetMovieInfo( void );
|
||||
void SCR_Shutdown( void );
|
||||
void Con_Print( const char *txt );
|
||||
void Con_NPrintf( int idx, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
||||
|
||||
@@ -498,6 +498,7 @@ static qboolean Cmd_GetSoundList( const char *s, char *completedname, int length
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
/*
|
||||
=====================================
|
||||
Cmd_GetItemsList
|
||||
@@ -507,7 +508,6 @@ Prints or complete item classname (weapons only)
|
||||
*/
|
||||
static qboolean Cmd_GetItemsList( const char *s, char *completedname, int length )
|
||||
{
|
||||
#if !XASH_DEDICATED
|
||||
search_t *t;
|
||||
string matchbuf;
|
||||
int i, numitems;
|
||||
@@ -521,7 +521,7 @@ static qboolean Cmd_GetItemsList( const char *s, char *completedname, int length
|
||||
Q_strncpy( completedname, matchbuf, length );
|
||||
if( t->numfilenames == 1 ) return true;
|
||||
|
||||
for( i = 0, numitems = 0; i < t->numfilenames; i++ )
|
||||
for(i = 0, numitems = 0; i < t->numfilenames; i++)
|
||||
{
|
||||
if( Q_stricmp( COM_FileExtension( t->filenames[i] ), "txt" ))
|
||||
continue;
|
||||
@@ -544,8 +544,6 @@ static qboolean Cmd_GetItemsList( const char *s, char *completedname, int length
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#endif // !XASH_DEDICATED
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -557,7 +555,6 @@ Autocomplete for bind command
|
||||
*/
|
||||
static qboolean Cmd_GetKeysList( const char *s, char *completedname, int length )
|
||||
{
|
||||
#if !XASH_DEDICATED
|
||||
size_t i, numkeys;
|
||||
string keys[256];
|
||||
string matchbuf;
|
||||
@@ -598,9 +595,8 @@ static qboolean Cmd_GetKeysList( const char *s, char *completedname, int length
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif // !XASH_DEDICATED
|
||||
return false;
|
||||
}
|
||||
#endif // XASH_DEDICATED
|
||||
|
||||
/*
|
||||
===============
|
||||
@@ -608,20 +604,13 @@ Con_AddCommandToList
|
||||
|
||||
===============
|
||||
*/
|
||||
static void Con_AddCommandToList( const char *s, const char *value, const void *ptoggle, void *_autocompleteList )
|
||||
static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *_autocompleteList )
|
||||
{
|
||||
con_autocomplete_t *list = (con_autocomplete_t*)_autocompleteList;
|
||||
qboolean toggle = ptoggle != NULL && *(qboolean *)ptoggle;
|
||||
|
||||
if( *s == '@' ) return; // never show system cvars or cmds
|
||||
if( list->matchCount >= CON_MAXCMDS ) return; // list is full
|
||||
|
||||
if( toggle )
|
||||
{
|
||||
if( Q_strcmp( value, "0" ) && Q_strcmp( value, "1" ))
|
||||
return; // exclude non-toggable cvars
|
||||
}
|
||||
|
||||
if( Q_strnicmp( s, list->completionString, Q_strlen( list->completionString ) ) )
|
||||
return; // no match
|
||||
|
||||
@@ -645,11 +634,13 @@ Cmd_GetCommandsList
|
||||
Autocomplete for bind command
|
||||
=====================================
|
||||
*/
|
||||
static qboolean Cmd_GetCommandsAndCvarsList( const char *s, char *completedname, int length, qboolean cmds, qboolean cvars, qboolean toggle )
|
||||
static qboolean Cmd_GetCommandsList( const char *s, char *completedname, int length )
|
||||
{
|
||||
size_t i;
|
||||
string matchbuf;
|
||||
con_autocomplete_t list = { 0 }; // local autocomplete list
|
||||
con_autocomplete_t list; // local autocomplete list
|
||||
|
||||
memset( &list, 0, sizeof( list ));
|
||||
|
||||
list.completionString = s;
|
||||
|
||||
@@ -661,16 +652,8 @@ static qboolean Cmd_GetCommandsAndCvarsList( const char *s, char *completedname,
|
||||
return false;
|
||||
|
||||
// find matching commands and variables
|
||||
if( cvars )
|
||||
{
|
||||
Cvar_LookupVars( 0, &toggle, &list, (setpair_t)Con_AddCommandToList );
|
||||
}
|
||||
|
||||
if( cmds )
|
||||
{
|
||||
toggle = false;
|
||||
Cmd_LookupCmds( &toggle, &list, (setpair_t)Con_AddCommandToList );
|
||||
}
|
||||
Cmd_LookupCmds( NULL, &list, (setpair_t)Con_AddCommandToList );
|
||||
Cvar_LookupVars( 0, NULL, &list, (setpair_t)Con_AddCommandToList );
|
||||
|
||||
if( !list.matchCount ) return false;
|
||||
Q_strncpy( matchbuf, list.cmds[0], sizeof( matchbuf ));
|
||||
@@ -686,7 +669,7 @@ static qboolean Cmd_GetCommandsAndCvarsList( const char *s, char *completedname,
|
||||
Con_Printf( "%16s\n", matchbuf );
|
||||
}
|
||||
|
||||
Con_Printf( "\n^3 %i %s found.\n", list.matchCount, cmds ? "commands" : "variables" );
|
||||
Con_Printf( "\n^3 %i commands found.\n", list.matchCount );
|
||||
|
||||
if( completedname && length )
|
||||
{
|
||||
@@ -708,30 +691,6 @@ static qboolean Cmd_GetCommandsAndCvarsList( const char *s, char *completedname,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================
|
||||
Cmd_GetCommandsList
|
||||
|
||||
Autocomplete for bind command
|
||||
=====================================
|
||||
*/
|
||||
static qboolean Cmd_GetCommandsList( const char *s, char *completedname, int length )
|
||||
{
|
||||
return Cmd_GetCommandsAndCvarsList( s, completedname, length, true, true, false );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================
|
||||
Cmd_GetCvarList
|
||||
|
||||
Autocomplete for bind command
|
||||
=====================================
|
||||
*/
|
||||
static qboolean Cmd_GetCvarsList( const char *s, char *completedname, int length )
|
||||
{
|
||||
qboolean toggle = !Q_stricmp( Cmd_Argv( 0 ), "toggle" );
|
||||
return Cmd_GetCommandsAndCvarsList( s, completedname, length, false, true, toggle );
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================
|
||||
@@ -1051,38 +1010,36 @@ int GAME_EXPORT Cmd_CheckMapsList( int fRefresh )
|
||||
return Cmd_CheckMapsList_R( fRefresh, true );
|
||||
}
|
||||
|
||||
// keep this sorted
|
||||
static const autocomplete_list_t cmd_list[] =
|
||||
{
|
||||
{ "bind", 1, Cmd_GetKeysList },
|
||||
{ "bind", 2, Cmd_GetCommandsList },
|
||||
{ "cd", 1, Cmd_GetCDList },
|
||||
{ "map_background", 1, Cmd_GetMapList },
|
||||
{ "changelevel2", 1, Cmd_GetMapList },
|
||||
{ "changelevel", 1, Cmd_GetMapList },
|
||||
{ "drop", 1, Cmd_GetItemsList },
|
||||
{ "entpatch", 1, Cmd_GetMapList },
|
||||
{ "exec", 1, Cmd_GetConfigList },
|
||||
{ "game", 1, Cmd_GetGamesList },
|
||||
{ "give", 1, Cmd_GetItemsList },
|
||||
{ "hpkextract", 1, Cmd_GetCustomList },
|
||||
{ "hpklist", 1, Cmd_GetCustomList },
|
||||
{ "hpkval", 1, Cmd_GetCustomList },
|
||||
{ "listdemo", 1, Cmd_GetDemoList, },
|
||||
{ "load", 1, Cmd_GetSavesList },
|
||||
{ "map", 1, Cmd_GetMapList },
|
||||
{ "map_background", 1, Cmd_GetMapList },
|
||||
{ "movie", 1, Cmd_GetMovieList },
|
||||
{ "mp3", 1, Cmd_GetCDList },
|
||||
{ "music", 1, Cmd_GetMusicList, },
|
||||
{ "play", 1, Cmd_GetSoundList },
|
||||
{ "playdemo", 1, Cmd_GetDemoList, },
|
||||
{ "timedemo", 1, Cmd_GetDemoList, },
|
||||
{ "listdemo", 1, Cmd_GetDemoList, },
|
||||
{ "playvol", 1, Cmd_GetSoundList },
|
||||
{ "reset", 1, Cmd_GetCvarsList },
|
||||
{ "save", 1, Cmd_GetSavesList },
|
||||
{ "set", 1, Cmd_GetCvarsList },
|
||||
{ "timedemo", 1, Cmd_GetDemoList },
|
||||
{ "toggle", 1, Cmd_GetCvarsList },
|
||||
{ "hpkval", 1, Cmd_GetCustomList },
|
||||
{ "hpklist", 1, Cmd_GetCustomList },
|
||||
{ "hpkextract", 1, Cmd_GetCustomList },
|
||||
{ "entpatch", 1, Cmd_GetMapList },
|
||||
{ "music", 1, Cmd_GetMusicList, },
|
||||
{ "movie", 1, Cmd_GetMovieList },
|
||||
{ "exec", 1, Cmd_GetConfigList },
|
||||
#if !XASH_DEDICATED
|
||||
{ "give", 1, Cmd_GetItemsList },
|
||||
{ "drop", 1, Cmd_GetItemsList },
|
||||
{ "bind", 1, Cmd_GetKeysList },
|
||||
{ "unbind", 1, Cmd_GetKeysList },
|
||||
{ "bind", 2, Cmd_GetCommandsList },
|
||||
#endif
|
||||
{ "game", 1, Cmd_GetGamesList },
|
||||
{ "save", 1, Cmd_GetSavesList },
|
||||
{ "load", 1, Cmd_GetSavesList },
|
||||
{ "play", 1, Cmd_GetSoundList },
|
||||
{ "map", 1, Cmd_GetMapList },
|
||||
{ "cd", 1, Cmd_GetCDList },
|
||||
{ "mp3", 1, Cmd_GetCDList },
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1203,7 +1160,6 @@ void Con_CompleteCommand( field_t *field )
|
||||
{
|
||||
field_t temp;
|
||||
string filename;
|
||||
qboolean toggle = false;
|
||||
qboolean nextcmd;
|
||||
int i;
|
||||
|
||||
@@ -1238,8 +1194,8 @@ void Con_CompleteCommand( field_t *field )
|
||||
con.shortestMatch[0] = 0;
|
||||
|
||||
// find matching commands and variables
|
||||
Cmd_LookupCmds( &toggle, &con, (setpair_t)Con_AddCommandToList );
|
||||
Cvar_LookupVars( 0, &toggle, &con, (setpair_t)Con_AddCommandToList );
|
||||
Cmd_LookupCmds( NULL, &con, (setpair_t)Con_AddCommandToList );
|
||||
Cvar_LookupVars( 0, NULL, &con, (setpair_t)Con_AddCommandToList );
|
||||
|
||||
if( !con.matchCount ) return; // no matches
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
filesystem.c - game filesystem based on DP fs
|
||||
Copyright (C) 2003-2006 Mathieu Olivier
|
||||
Copyright (C) 2000-2007 DarkPlaces contributors
|
||||
@@ -16,21 +16,11 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#if XASH_SDL
|
||||
#include <SDL.h> // SDL_GetBasePath
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include "common.h"
|
||||
#include "library.h"
|
||||
#include "platform/platform.h"
|
||||
|
||||
CVAR_DEFINE_AUTO( fs_mount_hd, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount high definition content folder" );
|
||||
CVAR_DEFINE_AUTO( fs_mount_lv, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount low violence models content folder" );
|
||||
CVAR_DEFINE_AUTO( fs_mount_addon, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount addon content folder" );
|
||||
CVAR_DEFINE_AUTO( fs_mount_l10n, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount localization content folder" );
|
||||
CVAR_DEFINE_AUTO( ui_language, "english", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "selected game language" );
|
||||
|
||||
fs_api_t g_fsapi;
|
||||
fs_globals_t *FI;
|
||||
|
||||
@@ -79,23 +69,9 @@ void *FS_GetNativeObject( const char *obj )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FS_Rescan_f( void )
|
||||
static void FS_Rescan_f( void )
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
// FIXME: VFS shouldn't care about this, allow engine to mount gamedirs
|
||||
if( fs_mount_lv.value ) SetBits( flags, FS_MOUNT_LV );
|
||||
if( fs_mount_hd.value ) SetBits( flags, FS_MOUNT_HD );
|
||||
if( fs_mount_addon.value ) SetBits( flags, FS_MOUNT_ADDON );
|
||||
if( fs_mount_l10n.value ) SetBits( flags, FS_MOUNT_L10N );
|
||||
|
||||
g_fsapi.Rescan( flags, ui_language.string );
|
||||
|
||||
ClearBits( fs_mount_lv.flags, FCVAR_CHANGED );
|
||||
ClearBits( fs_mount_hd.flags, FCVAR_CHANGED );
|
||||
ClearBits( fs_mount_addon.flags, FCVAR_CHANGED );
|
||||
ClearBits( fs_mount_l10n.flags, FCVAR_CHANGED );
|
||||
ClearBits( ui_language.flags, FCVAR_CHANGED );
|
||||
FS_Rescan();
|
||||
}
|
||||
|
||||
static void FS_ClearPaths_f( void )
|
||||
@@ -260,12 +236,6 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size )
|
||||
return false;
|
||||
}
|
||||
|
||||
void FS_CheckConfig( void )
|
||||
{
|
||||
if( fs_mount_lv.value || fs_mount_hd.value || fs_mount_addon.value || fs_mount_l10n.value )
|
||||
FS_Rescan_f();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_Init
|
||||
@@ -318,11 +288,6 @@ void FS_Init( const char *basedir )
|
||||
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_make_gameinfo", FS_MakeGameInfo_f, "create gameinfo.txt for current running game" );
|
||||
|
||||
Cvar_RegisterVariable( &fs_mount_hd );
|
||||
Cvar_RegisterVariable( &fs_mount_lv );
|
||||
Cvar_RegisterVariable( &fs_mount_addon );
|
||||
Cvar_RegisterVariable( &fs_mount_l10n );
|
||||
|
||||
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
|
||||
host.gamedll[0] = 0;
|
||||
|
||||
|
||||
@@ -140,8 +140,7 @@ static void Sys_PrintUsage( const char *exename )
|
||||
|
||||
"\nCommon options:\n"
|
||||
O("-dev [level] ", "set log verbosity 0-2")
|
||||
O("-log [file name] ", "write log to \"engine.log\" or [file name] if specified")
|
||||
O("-logtime ", "enable writing timestamps to the log file")
|
||||
O("-log ", "write log to \"engine.log\"")
|
||||
O("-nowriteconfig ", "disable config save")
|
||||
O("-noch ", "disable crashhandler")
|
||||
#if XASH_WIN32 // !!!!
|
||||
@@ -202,6 +201,7 @@ static void Sys_PrintUsage( const char *exename )
|
||||
O("-daemonize ", "run engine as a daemon")
|
||||
#endif
|
||||
#if XASH_SDL == 2
|
||||
O("-sdl_joy_old_api ","use SDL legacy joystick API")
|
||||
O("-sdl_renderer <n> ","use alternative SDL_Renderer for software")
|
||||
#endif // XASH_SDL
|
||||
#if XASH_ANDROID && !XASH_SDL
|
||||
@@ -698,17 +698,17 @@ static qboolean Host_Autosleep( double dt, double scale )
|
||||
static double timewindow; // allocate a time window for sleeps
|
||||
static int counter; // for debug
|
||||
static double realsleeptime;
|
||||
const double sleeptime = sleep * 0.000001;
|
||||
const double sleeptime = sleep * 0.001;
|
||||
|
||||
if( dt < targetframetime * scale )
|
||||
{
|
||||
// if we have allocated time window, try to sleep
|
||||
if( timewindow > realsleeptime )
|
||||
{
|
||||
// Platform_Sleep isn't guaranteed to sleep an exact amount of microseconds
|
||||
// Platform_Sleep isn't guaranteed to sleep an exact amount of milliseconds
|
||||
// so we measure the real sleep time and use it to decrease the window
|
||||
double t1 = Sys_DoubleTime(), t2;
|
||||
Platform_NanoSleep( sleep * 1000 ); // in usec!
|
||||
Platform_Sleep( sleep ); // in msec!
|
||||
t2 = Sys_DoubleTime();
|
||||
realsleeptime = t2 - t1;
|
||||
|
||||
@@ -1044,7 +1044,7 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
|
||||
}
|
||||
|
||||
if( !Sys_CheckParm( "-noch" ))
|
||||
Sys_SetupCrashHandler( argv[0] );
|
||||
Sys_SetupCrashHandler();
|
||||
|
||||
#if XASH_DLL_LOADER
|
||||
host.enabledll = !Sys_CheckParm( "-nodll" );
|
||||
@@ -1236,14 +1236,6 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
Cvar_Getf( "host_ver", FCVAR_READ_ONLY, "detailed info about this build", "%i " XASH_VERSION " %s %s %s", Q_buildnum(), Q_buildos(), Q_buildarch(), g_buildcommit);
|
||||
Cvar_Getf( "host_lowmemorymode", FCVAR_READ_ONLY, "indicates if engine compiled for low RAM consumption (0 - normal, 1 - low engine limits, 2 - low protocol limits)", "%i", XASH_LOW_MEMORY );
|
||||
|
||||
Cvar_Get( "host_hl25_extended_structs",
|
||||
#if SUPPORT_HL25_EXTENDED_STRUCTS
|
||||
"1",
|
||||
#else
|
||||
"0",
|
||||
#endif
|
||||
FCVAR_READ_ONLY, "indicates if engine was compiled with extended msurface_t struct" );
|
||||
|
||||
Mod_Init();
|
||||
NET_Init();
|
||||
NET_InitMasters();
|
||||
@@ -1319,7 +1311,6 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
Cmd_RemoveCommand( "setgl" );
|
||||
Cbuf_ExecStuffCmds(); // execute stuffcmds (commandline)
|
||||
SCR_CheckStartupVids(); // must be last
|
||||
FS_CheckConfig();
|
||||
|
||||
if( Sys_GetParmFromCmdLine( "-timedemo", demoname ))
|
||||
Cbuf_AddTextf( "timedemo %s\n", demoname );
|
||||
|
||||
@@ -419,21 +419,14 @@ void NET_InitMasters( void )
|
||||
|
||||
Cvar_RegisterVariable( &sv_verbose_heartbeats );
|
||||
|
||||
{ // IPv4-only
|
||||
NET_AddMaster( "mentality.rip:27010", false, false );
|
||||
NET_AddMaster( "ms2.mentality.rip:27010", false, false );
|
||||
NET_AddMaster( "ms3.mentality.rip:27010", false, false );
|
||||
}
|
||||
// keep main master always there
|
||||
NET_AddMaster( MASTERSERVER_ADR, false, false );
|
||||
NET_AddMaster( "mentality.rip:27011", false, false ); // testing server, might be offline
|
||||
NET_AddMaster( "ms2.mentality.rip:27010", false, false ); // secondary master
|
||||
|
||||
{ // IPv6-only
|
||||
NET_AddMaster( "aaaa.mentality.rip:27010", false, true );
|
||||
NET_AddMaster( "aaaa.ms2.mentality.rip:27010", false, true );
|
||||
}
|
||||
|
||||
{ // testing servers, might be offline
|
||||
NET_AddMaster( "mentality.rip:27011", false, false );
|
||||
NET_AddMaster( "aaaa.mentality.rip:27011", false, true );
|
||||
}
|
||||
NET_AddMaster( "aaaa.mentality.rip:27010", false, true ); // IPv6-only
|
||||
NET_AddMaster( "aaaa.mentality.rip:27011", false, true ); // IPv6-only, testing server, might be offline
|
||||
NET_AddMaster( "aaaa.ms2.mentality.rip:27010", false, false ); // secondary IPv6-only master
|
||||
|
||||
NET_LoadMasters( );
|
||||
}
|
||||
|
||||
@@ -382,7 +382,8 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p
|
||||
for( j = numbones - 1; j >= 0; j-- )
|
||||
{
|
||||
i = boneused[j];
|
||||
R_StudioCalcBones( frame, s, &pbone[i], &panim[i], adj, pos[i], q[i] );
|
||||
R_StudioCalcBoneQuaternion( frame, s, &pbone[i], &panim[i], adj, q[i] );
|
||||
R_StudioCalcBonePosition( frame, s, &pbone[i], &panim[i], adj, pos[i] );
|
||||
}
|
||||
|
||||
if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f;
|
||||
@@ -723,7 +724,7 @@ void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean i
|
||||
{
|
||||
for( k = 0; k < pseqdesc->numframes; k++ )
|
||||
{
|
||||
R_StudioCalcBones( k, 0, &pbones[j], panim, NULL, pos, NULL );
|
||||
R_StudioCalcBonePosition( k, 0, &pbones[j], panim, NULL, pos );
|
||||
Mod_StudioBoundVertex( vert_mins, vert_maxs, &bone_count, pos );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,16 +844,7 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
|
||||
if( curfile->downloaded >= curfile->size )
|
||||
{
|
||||
// chunked files are finalized in FileSaveReceivedData
|
||||
if( curfile->compressed && !curfile->chunked )
|
||||
{
|
||||
curfile->pfn_process = HTTP_FileDecompress;
|
||||
curfile->success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
HTTP_FreeFile( curfile, false ); // success
|
||||
}
|
||||
HTTP_FreeFile( curfile, false ); // success
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,24 +151,17 @@ static inline qboolean NET_IsSocketValid( int socket )
|
||||
|
||||
void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr )
|
||||
{
|
||||
memcpy( &ip6[0], adr->ip6_0, 2 );
|
||||
memcpy( &ip6[2], adr->ip6_1, 14 );
|
||||
memcpy( ip6, adr->ip6, sizeof( adr->ip6 ));
|
||||
}
|
||||
|
||||
void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 )
|
||||
{
|
||||
memcpy( adr->ip6_0, &ip6[0], 2 );
|
||||
memcpy( adr->ip6_1, &ip6[2], 14 );
|
||||
memcpy( adr->ip6, ip6, sizeof( adr->ip6 ));
|
||||
}
|
||||
|
||||
static int NET_NetadrIP6Compare( const netadr_t *a, const netadr_t *b )
|
||||
{
|
||||
uint8_t ip6_a[16], ip6_b[16];
|
||||
|
||||
NET_NetadrToIP6Bytes( ip6_a, a );
|
||||
NET_NetadrToIP6Bytes( ip6_b, b );
|
||||
|
||||
return memcmp( ip6_a, ip6_b, sizeof( ip6_a ));
|
||||
return memcmp( a->ip6, b->ip6, sizeof( a->ip6 ));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -178,29 +171,27 @@ NET_NetadrToSockadr
|
||||
*/
|
||||
static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s )
|
||||
{
|
||||
netadrtype_t type = NET_NetadrType( a );
|
||||
|
||||
memset( s, 0, sizeof( *s ));
|
||||
|
||||
if( type == NA_BROADCAST )
|
||||
if( a->type == NA_BROADCAST )
|
||||
{
|
||||
s->ss_family = AF_INET;
|
||||
((struct sockaddr_in *)s)->sin_port = a->port;
|
||||
((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST;
|
||||
}
|
||||
else if( type == NA_IP )
|
||||
else if( a->type == NA_IP )
|
||||
{
|
||||
s->ss_family = AF_INET;
|
||||
((struct sockaddr_in *)s)->sin_port = a->port;
|
||||
((struct sockaddr_in *)s)->sin_addr.s_addr = a->ip4;
|
||||
}
|
||||
else if( type == NA_IP6 )
|
||||
else if( a->type6 == NA_IP6 )
|
||||
{
|
||||
s->ss_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)s)->sin6_port = a->port;
|
||||
NET_NetadrToIP6Bytes(((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, a );
|
||||
}
|
||||
else if( type == NA_MULTICAST_IP6 )
|
||||
else if( a->type6 == NA_MULTICAST_IP6 )
|
||||
{
|
||||
s->ss_family = AF_INET6;
|
||||
((struct sockaddr_in6 *)s)->sin6_port = a->port;
|
||||
@@ -217,13 +208,13 @@ static void NET_SockadrToNetadr( const struct sockaddr_storage *s, netadr_t *a )
|
||||
{
|
||||
if( s->ss_family == AF_INET )
|
||||
{
|
||||
NET_NetadrSetType( a, NA_IP );
|
||||
a->type = NA_IP;
|
||||
a->ip4 = ((struct sockaddr_in *)s)->sin_addr.s_addr;
|
||||
a->port = ((struct sockaddr_in *)s)->sin_port;
|
||||
}
|
||||
else if( s->ss_family == AF_INET6 )
|
||||
{
|
||||
NET_NetadrSetType( a, NA_IP6 );
|
||||
a->type6 = NA_IP6;
|
||||
NET_IP6BytesToNetadr( a, ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr );
|
||||
a->port = ((struct sockaddr_in6 *)s)->sin6_port;
|
||||
}
|
||||
@@ -558,8 +549,8 @@ qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen )
|
||||
// try to parse as IPv6 first
|
||||
if( ParseIPv6Addr( copy, ip6, NULL, NULL ))
|
||||
{
|
||||
NET_NetadrSetType( adr, NA_IP6 );
|
||||
NET_IP6BytesToNetadr( adr, ip6 );
|
||||
adr->type6 = NA_IP6;
|
||||
|
||||
if( !hasCIDR )
|
||||
*prefixlen = 128;
|
||||
@@ -629,7 +620,7 @@ qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen )
|
||||
adr->ip4 = ntohl( mask );
|
||||
}
|
||||
|
||||
NET_NetadrSetType( adr, NA_IP );
|
||||
adr->type = NA_IP;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -643,11 +634,10 @@ NET_AdrToString
|
||||
const char *NET_AdrToString( const netadr_t a )
|
||||
{
|
||||
static char s[64];
|
||||
netadrtype_t type = NET_NetadrType( &a );
|
||||
|
||||
if( type == NA_LOOPBACK )
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return "loopback";
|
||||
if( type == NA_IP6 || type == NA_MULTICAST_IP6 )
|
||||
if( a.type6 == NA_IP6 || a.type6 == NA_MULTICAST_IP6 )
|
||||
{
|
||||
uint8_t ip6[16];
|
||||
|
||||
@@ -671,11 +661,10 @@ NET_BaseAdrToString
|
||||
const char *NET_BaseAdrToString( const netadr_t a )
|
||||
{
|
||||
static char s[64];
|
||||
netadrtype_t type = NET_NetadrType( &a );
|
||||
|
||||
if( type == NA_LOOPBACK )
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return "loopback";
|
||||
if( type == NA_IP6 || type == NA_MULTICAST_IP6 )
|
||||
if( a.type6 == NA_IP6 || a.type6 == NA_MULTICAST_IP6 )
|
||||
{
|
||||
uint8_t ip6[16];
|
||||
|
||||
@@ -700,19 +689,16 @@ Compares without the port
|
||||
*/
|
||||
qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b )
|
||||
{
|
||||
netadrtype_t type_a = NET_NetadrType( &a );
|
||||
netadrtype_t type_b = NET_NetadrType( &b );
|
||||
|
||||
if( type_a != type_b )
|
||||
if( a.type6 != b.type6 )
|
||||
return false;
|
||||
|
||||
if( type_a == NA_LOOPBACK )
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return true;
|
||||
|
||||
if( type_a == NA_IP )
|
||||
if( a.type == NA_IP )
|
||||
return a.ip4 == b.ip4;
|
||||
|
||||
if( type_a == NA_IP6 )
|
||||
if( a.type6 == NA_IP6 )
|
||||
{
|
||||
if( !NET_NetadrIP6Compare( &a, &b ))
|
||||
return true;
|
||||
@@ -721,6 +707,36 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b )
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
NET_CompareClassBAdr
|
||||
|
||||
Compare local masks
|
||||
====================
|
||||
*/
|
||||
qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b )
|
||||
{
|
||||
if( a.type6 != b.type6 )
|
||||
return false;
|
||||
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return true;
|
||||
|
||||
if( a.type == NA_IP )
|
||||
{
|
||||
if( a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] )
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: we don't check for IPv6 here
|
||||
// this check is very dumb and only used for LAN restriction
|
||||
// Actual check is in IsReservedAdr
|
||||
|
||||
// for real mask compare use NET_CompareAdrByMask
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
NET_CompareAdrByMask
|
||||
@@ -730,13 +746,10 @@ Checks if adr is a part of subnet
|
||||
*/
|
||||
qboolean NET_CompareAdrByMask( const netadr_t a, const netadr_t b, uint prefixlen )
|
||||
{
|
||||
netadrtype_t type_a = NET_NetadrType( &a );
|
||||
netadrtype_t type_b = NET_NetadrType( &b );
|
||||
|
||||
if( type_a != type_b || type_a == NA_LOOPBACK )
|
||||
if( a.type6 != b.type6 || a.type == NA_LOOPBACK )
|
||||
return false;
|
||||
|
||||
if( type_a == NA_IP )
|
||||
if( a.type == NA_IP )
|
||||
{
|
||||
uint32_t ipa = htonl( a.ip4 );
|
||||
uint32_t ipb = htonl( b.ip4 );
|
||||
@@ -744,7 +757,7 @@ qboolean NET_CompareAdrByMask( const netadr_t a, const netadr_t b, uint prefixle
|
||||
if(( ipa & (( 0xFFFFFFFFU ) << ( 32 - prefixlen ))) == ipb )
|
||||
return true;
|
||||
}
|
||||
else if( type_a == NA_IP6 )
|
||||
else if( a.type6 == NA_IP6 )
|
||||
{
|
||||
uint16_t a_[8], b_[8];
|
||||
size_t check = prefixlen / 16;
|
||||
@@ -785,13 +798,11 @@ Check for reserved ip's
|
||||
*/
|
||||
qboolean NET_IsReservedAdr( netadr_t a )
|
||||
{
|
||||
netadrtype_t type_a = NET_NetadrType( &a );
|
||||
|
||||
if( type_a == NA_LOOPBACK )
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return true;
|
||||
|
||||
// Following checks was imported from GameNetworkingSockets library
|
||||
if( type_a == NA_IP )
|
||||
if( a.type == NA_IP )
|
||||
{
|
||||
if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved
|
||||
( a.ip[0] == 127 ) || // 127.x.x.x
|
||||
@@ -803,7 +814,7 @@ qboolean NET_IsReservedAdr( netadr_t a )
|
||||
}
|
||||
}
|
||||
|
||||
if( type_a == NA_IP6 )
|
||||
if( a.type6 == NA_IP6 )
|
||||
{
|
||||
uint8_t ip6[16];
|
||||
|
||||
@@ -836,23 +847,20 @@ Compare full address
|
||||
*/
|
||||
qboolean NET_CompareAdr( const netadr_t a, const netadr_t b )
|
||||
{
|
||||
netadrtype_t type_a = NET_NetadrType( &a );
|
||||
netadrtype_t type_b = NET_NetadrType( &b );
|
||||
|
||||
if( type_a != type_b )
|
||||
if( a.type6 != b.type6 )
|
||||
return false;
|
||||
|
||||
if( type_a == NA_LOOPBACK )
|
||||
if( a.type == NA_LOOPBACK )
|
||||
return true;
|
||||
|
||||
if( type_a == NA_IP )
|
||||
if( a.type == NA_IP )
|
||||
{
|
||||
if( a.ip4 == b.ip4 && a.port == b.port )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( type_a == NA_IP6 )
|
||||
if( a.type6 == NA_IP6 )
|
||||
{
|
||||
if( a.port == b.port && !NET_NetadrIP6Compare( &a, &b ))
|
||||
return true;
|
||||
@@ -875,13 +883,9 @@ int NET_CompareAdrSort( const void *_a, const void *_b )
|
||||
{
|
||||
const netadr_t *a = _a, *b = _b;
|
||||
int porta, portb, portdiff, addrdiff;
|
||||
netadrtype_t type_a, type_b;
|
||||
|
||||
type_a = NET_NetadrType( a );
|
||||
type_b = NET_NetadrType( b );
|
||||
|
||||
if( type_a != type_b )
|
||||
return bound( -1, (int)type_a - (int)type_b, 1 );
|
||||
if( a->type6 != b->type6 )
|
||||
return bound( -1, (int)a->type6 - (int)b->type6, 1 );
|
||||
|
||||
porta = ntohs( a->port );
|
||||
portb = ntohs( b->port );
|
||||
@@ -892,7 +896,7 @@ int NET_CompareAdrSort( const void *_a, const void *_b )
|
||||
else
|
||||
portdiff = 0;
|
||||
|
||||
switch( type_a )
|
||||
switch( a->type6 )
|
||||
{
|
||||
case NA_IP6:
|
||||
if(( addrdiff = NET_NetadrIP6Compare( a, b )))
|
||||
@@ -900,7 +904,14 @@ int NET_CompareAdrSort( const void *_a, const void *_b )
|
||||
// fallthrough
|
||||
case NA_MULTICAST_IP6:
|
||||
return portdiff;
|
||||
}
|
||||
|
||||
// don't check for full type earlier, as it's value depends on v6 address
|
||||
if( a->type != b->type )
|
||||
return bound( -1, (int)a->type - (int)b->type, 1 );
|
||||
|
||||
switch( a->type )
|
||||
{
|
||||
case NA_IP:
|
||||
if(( addrdiff = memcmp( a->ip, b->ip, sizeof( a->ipx ))))
|
||||
return addrdiff;
|
||||
@@ -935,7 +946,7 @@ static qboolean NET_StringToAdrEx( const char *string, netadr_t *adr, int family
|
||||
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ))
|
||||
{
|
||||
NET_NetadrSetType( adr, NA_LOOPBACK );
|
||||
adr->type = NA_LOOPBACK;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -960,7 +971,7 @@ net_gai_state_t NET_StringToAdrNB( const char *string, netadr_t *adr, qboolean v
|
||||
|
||||
if( !Q_stricmp( string, "localhost" ) || !Q_stricmp( string, "loopback" ))
|
||||
{
|
||||
NET_NetadrSetType( adr, NA_LOOPBACK );
|
||||
adr->type = NA_LOOPBACK;
|
||||
return NET_EAI_OK;
|
||||
}
|
||||
|
||||
@@ -1006,7 +1017,7 @@ static qboolean NET_GetLoopPacket( netsrc_t sock, netadr_t *from, byte *data, si
|
||||
*length = loop->msgs[i].datalen;
|
||||
|
||||
memset( from, 0, sizeof( *from ));
|
||||
NET_NetadrSetType( from, NA_LOOPBACK );
|
||||
from->type = NA_LOOPBACK;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1512,7 +1523,7 @@ static int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t
|
||||
total_sent += size;
|
||||
len -= size;
|
||||
packet_number++;
|
||||
Platform_NanoSleep( 100 * 1000 );
|
||||
Platform_Sleep( 1 );
|
||||
}
|
||||
|
||||
return total_sent;
|
||||
@@ -1535,20 +1546,19 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t
|
||||
int ret;
|
||||
struct sockaddr_storage addr = { 0 };
|
||||
SOCKET net_socket = 0;
|
||||
netadrtype_t type = NET_NetadrType( &to );
|
||||
|
||||
if( !net.initialized || type == NA_LOOPBACK )
|
||||
if( !net.initialized || to.type == NA_LOOPBACK )
|
||||
{
|
||||
NET_SendLoopPacket( sock, length, data, to );
|
||||
return;
|
||||
}
|
||||
else if( type == NA_BROADCAST || type == NA_IP )
|
||||
else if( to.type == NA_BROADCAST || to.type == NA_IP )
|
||||
{
|
||||
net_socket = net.ip_sockets[sock];
|
||||
if( !NET_IsSocketValid( net_socket ))
|
||||
return;
|
||||
}
|
||||
else if( type == NA_MULTICAST_IP6 || type == NA_IP6 )
|
||||
else if( to.type6 == NA_MULTICAST_IP6 || to.type6 == NA_IP6 )
|
||||
{
|
||||
net_socket = net.ip6_sockets[sock];
|
||||
if( !NET_IsSocketValid( net_socket ))
|
||||
@@ -1556,7 +1566,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t
|
||||
}
|
||||
else
|
||||
{
|
||||
Host_Error( "%s: bad address type %i (%i, %i)\n", __func__, to.type, to.ip6_0[0], to.ip6_0[1] );
|
||||
Host_Error( "%s: bad address type %i (%i)\n", __func__, to.type, to.type6 );
|
||||
}
|
||||
|
||||
NET_NetadrToSockadr( &to, &addr );
|
||||
@@ -1572,7 +1582,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t
|
||||
return;
|
||||
|
||||
// some PPP links don't allow broadcasts
|
||||
if( err == WSAEADDRNOTAVAIL && ( type == NA_BROADCAST || type == NA_MULTICAST_IP6 ))
|
||||
if( err == WSAEADDRNOTAVAIL && ( to.type == NA_BROADCAST || to.type6 == NA_MULTICAST_IP6 ))
|
||||
return;
|
||||
|
||||
if( Host_IsDedicated( ))
|
||||
@@ -2027,7 +2037,6 @@ void NET_Init( void )
|
||||
Cvar_RegisterVariable( &net_fakelag );
|
||||
Cvar_RegisterVariable( &net_fakeloss );
|
||||
Cvar_RegisterVariable( &net_resolve_debug );
|
||||
Cvar_RegisterVariable( &net_clockwindow );
|
||||
|
||||
Q_snprintf( cmd, sizeof( cmd ), "%i", PORT_SERVER );
|
||||
Cvar_FullSet( "hostport", cmd, FCVAR_READ_ONLY );
|
||||
|
||||
@@ -64,6 +64,7 @@ void NET_Config( qboolean net_enable, qboolean changeport );
|
||||
const char *NET_AdrToString( const netadr_t a ) RETURNS_NONNULL;
|
||||
const char *NET_BaseAdrToString( const netadr_t a ) RETURNS_NONNULL;
|
||||
qboolean NET_IsReservedAdr( netadr_t a );
|
||||
qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b );
|
||||
qboolean NET_StringToAdr( const char *string, netadr_t *adr );
|
||||
qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen );
|
||||
net_gai_state_t NET_StringToAdrNB( const char *string, netadr_t *adr, qboolean v6only );
|
||||
@@ -79,7 +80,7 @@ void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr );
|
||||
|
||||
static inline qboolean NET_IsLocalAddress( netadr_t adr )
|
||||
{
|
||||
return NET_NetadrType( &adr ) == NA_LOOPBACK;
|
||||
return adr.type == NA_LOOPBACK ? true : false;
|
||||
}
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
|
||||
@@ -69,6 +69,7 @@ GNU General Public License for more details.
|
||||
// bytes will be stripped by the networking channel layer
|
||||
#define NET_MAX_MESSAGE PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 )
|
||||
|
||||
#define MASTERSERVER_ADR "mentality.rip:27010"
|
||||
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0"
|
||||
|
||||
#define PORT_MASTER 27010
|
||||
|
||||
@@ -31,14 +31,15 @@ GNU General Public License for more details.
|
||||
#define XASH_COLORIZE_CONSOLE 0
|
||||
#endif
|
||||
|
||||
static struct logdata_s {
|
||||
char title[64];
|
||||
qboolean log_active;
|
||||
qboolean log_time;
|
||||
char log_path[MAX_SYSPATH];
|
||||
FILE *logfile;
|
||||
int logfileno;
|
||||
} s_ld;
|
||||
typedef struct {
|
||||
char title[64];
|
||||
qboolean log_active;
|
||||
char log_path[MAX_SYSPATH];
|
||||
FILE *logfile;
|
||||
int logfileno;
|
||||
} LogData;
|
||||
|
||||
static LogData s_ld;
|
||||
|
||||
void Sys_DestroyConsole( void )
|
||||
{
|
||||
@@ -77,19 +78,14 @@ static void Sys_FlushLogfile( void )
|
||||
|
||||
void Sys_InitLog( void )
|
||||
{
|
||||
const char *mode;
|
||||
const char *mode;
|
||||
|
||||
if( Sys_CheckParm( "-log" ))
|
||||
if( Sys_CheckParm( "-log" ) && host.allow_console != 0 )
|
||||
{
|
||||
if( !Sys_GetParmFromCmdLine( "-log", s_ld.log_path ) || !isalnum( s_ld.log_path[0] ))
|
||||
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
|
||||
|
||||
COM_DefaultExtension( s_ld.log_path, ".log", sizeof( s_ld.log_path ));
|
||||
s_ld.log_active = true;
|
||||
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
|
||||
}
|
||||
|
||||
s_ld.log_time = Sys_CheckParm( "-logtime" );
|
||||
|
||||
if( host.change_game && host.type != HOST_DEDICATED )
|
||||
mode = "a";
|
||||
else mode = "w";
|
||||
@@ -105,7 +101,7 @@ void Sys_InitLog( void )
|
||||
|
||||
if ( !s_ld.logfile )
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s: can't create log file %s: %s\n", __func__, s_ld.log_path, strerror( errno ));
|
||||
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,23 +295,17 @@ void Sys_PrintLog( const char *pMsg )
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = len > 0 ? pMsg[len - 1] : 0;
|
||||
|
||||
// spew to engine.log
|
||||
if( s_ld.logfile )
|
||||
{
|
||||
if( s_ld.log_time && print_time )
|
||||
{
|
||||
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
|
||||
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
|
||||
}
|
||||
else
|
||||
{
|
||||
logtime[0] = '\0';
|
||||
logtime_len = 0;
|
||||
}
|
||||
if( !s_ld.logfile )
|
||||
return;
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
if( print_time )
|
||||
{
|
||||
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
|
||||
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
|
||||
}
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -65,8 +65,6 @@ typedef struct memheader_s
|
||||
// immediately followed by data, which is followed by a MEMHEADER_SENTINEL2 byte
|
||||
} memheader_t;
|
||||
|
||||
STATIC_CHECK_SIZEOF( memheader_t, 24, 40 );
|
||||
|
||||
typedef struct mempool_s
|
||||
{
|
||||
struct memheader_s *chain; // chain of individual memory allocations
|
||||
@@ -233,7 +231,10 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline
|
||||
void _Mem_Free( void *data, const char *filename, int fileline )
|
||||
{
|
||||
if( data == NULL )
|
||||
{
|
||||
Sys_Error( "%s: data == NULL (called at %s:%i)\n", __func__, filename, fileline );
|
||||
return;
|
||||
}
|
||||
|
||||
Mem_FreeBlock((memheader_t *)((byte *)data - sizeof( memheader_t )), filename, fileline );
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <android/log.h>
|
||||
#include "linker.h"
|
||||
|
||||
static const Elf_Sym *soinfo_elf_lookup( const struct soinfo *si, unsigned hash, const char *name )
|
||||
static const Elf_Sym *soinfo_elf_lookup( const soinfo *si, unsigned hash, const char *name )
|
||||
{
|
||||
const Elf_Sym *symtab = si->symtab;
|
||||
const char *strtab = si->strtab;
|
||||
@@ -87,14 +87,14 @@ static unsigned elfhash( const unsigned char *name )
|
||||
Binary Interface) where in Chapter 5 it discuss resolving "Shared
|
||||
Object Dependencies" in breadth first search order.
|
||||
*/
|
||||
static const Elf_Sym *dlsym_handle_lookup( const struct soinfo *si, const char *name )
|
||||
static const Elf_Sym *dlsym_handle_lookup( const soinfo *si, const char *name )
|
||||
{
|
||||
return soinfo_elf_lookup( si, elfhash((const unsigned char *)name ), name );
|
||||
}
|
||||
|
||||
void *dlsym_weak( void *handle, const char *symbol )
|
||||
extern "C" void *dlsym_weak( void *handle, const char *symbol )
|
||||
{
|
||||
const struct soinfo *found = (struct soinfo *)handle;
|
||||
const soinfo *found = (soinfo *)handle;
|
||||
const Elf_Sym *sym = dlsym_handle_lookup( found, symbol );
|
||||
|
||||
if( sym != NULL )
|
||||
@@ -31,8 +31,10 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
// #include <elf.h>
|
||||
#include <linux/elf.h>
|
||||
// #include <sys/exec_elf.h>
|
||||
// #include <link.h>
|
||||
|
||||
// a1ba: we don't really need custom linker on Android 64, because
|
||||
// it's only intended to workaround bug which persist on Android < 4.4
|
||||
@@ -67,8 +69,8 @@ struct link_map_t {
|
||||
uintptr_t l_addr;
|
||||
char *l_name;
|
||||
uintptr_t l_ld;
|
||||
struct link_map_t *l_next;
|
||||
struct link_map_t *l_prev;
|
||||
link_map_t *l_next;
|
||||
link_map_t *l_prev;
|
||||
};
|
||||
|
||||
// Values for r_debug->state
|
||||
@@ -83,7 +85,7 @@ enum {
|
||||
|
||||
#define SOINFO_NAME_LEN 128
|
||||
|
||||
typedef void (*linker_function_t)( void );
|
||||
typedef void (*linker_function_t)();
|
||||
|
||||
// Android uses REL for 32-bit but only uses RELA for 64-bit.
|
||||
#if defined( __LP64__ )
|
||||
@@ -91,6 +93,7 @@ typedef void (*linker_function_t)( void );
|
||||
#endif
|
||||
|
||||
struct soinfo {
|
||||
public:
|
||||
char name[SOINFO_NAME_LEN];
|
||||
const Elf_Phdr *phdr;
|
||||
size_t phnum;
|
||||
@@ -109,7 +112,7 @@ struct soinfo {
|
||||
uint32_t unused3; // DO NOT USE, maintained for compatibility
|
||||
#endif
|
||||
|
||||
struct soinfo *next;
|
||||
soinfo *next;
|
||||
unsigned flags;
|
||||
|
||||
const char *strtab;
|
||||
@@ -161,8 +164,8 @@ struct soinfo {
|
||||
unsigned mips_gotsym;
|
||||
#endif
|
||||
|
||||
size_t ref_count;
|
||||
struct link_map_t link_map;
|
||||
size_t ref_count;
|
||||
link_map_t link_map;
|
||||
|
||||
bool constructors_called;
|
||||
|
||||
@@ -174,6 +177,14 @@ struct soinfo {
|
||||
bool has_text_relocations;
|
||||
#endif
|
||||
bool has_DT_SYMBOLIC;
|
||||
|
||||
void CallConstructors();
|
||||
void CallDestructors();
|
||||
void CallPreInitConstructors();
|
||||
|
||||
private:
|
||||
void CallArray( const char *array_name, linker_function_t *functions, size_t count, bool reverse );
|
||||
void CallFunction( const char *function_name, linker_function_t function );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -91,6 +91,11 @@ void GAME_EXPORT Platform_SetMousePos(int x, int y)
|
||||
|
||||
}
|
||||
|
||||
int Platform_JoyInit( int numjoy )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform_EnableTextInput( qboolean enable )
|
||||
{
|
||||
keystate.chars = enable;
|
||||
|
||||
@@ -142,10 +142,17 @@ static qboolean vsync;
|
||||
|
||||
void GL_UpdateSwapInterval( void )
|
||||
{
|
||||
if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
// disable VSync while level is loading
|
||||
if( cls.state < ca_active )
|
||||
{
|
||||
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
vsync = gl_vsync.value;
|
||||
// setup vsync here
|
||||
vsync = false;
|
||||
SetBits( gl_vsync->flags, FCVAR_CHANGED );
|
||||
}
|
||||
else if( FBitSet( gl_vsync->flags, FCVAR_CHANGED ))
|
||||
{
|
||||
ClearBits( gl_vsync->flags, FCVAR_CHANGED );
|
||||
vsync = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ qboolean SNDDMA_Init( void )
|
||||
return false;
|
||||
}
|
||||
|
||||
dma.buffer = Mem_Calloc( sndpool, samples * 2 ); //allocate pcm frame buffer
|
||||
dma.buffer = Mem_Malloc( sndpool, samples * 2 ); //allocate pcm frame buffer
|
||||
dma.samplepos = 0;
|
||||
dma.samples = samples;
|
||||
dma.format.width = 2;
|
||||
|
||||
@@ -161,10 +161,17 @@ void* GL_GetProcAddress( const char *name ) // RenderAPI requirement
|
||||
|
||||
void GL_UpdateSwapInterval( void )
|
||||
{
|
||||
if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
// disable VSync while level is loading
|
||||
if( cls.state < ca_active )
|
||||
{
|
||||
// setup fb vsync here
|
||||
fb.vsync = false;
|
||||
SetBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
}
|
||||
else if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
fb.vsync = gl_vsync.value;
|
||||
fb.vsync = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +281,13 @@ void GAME_EXPORT Platform_SetMousePos(int x, int y)
|
||||
|
||||
}
|
||||
|
||||
void Platform_Vibrate( float life, char flags )
|
||||
void Platform_Vibrate(float life, char flags)
|
||||
{
|
||||
|
||||
}
|
||||
int Platform_JoyInit( int numjoy )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,7 +31,6 @@ GNU General Public License for more details.
|
||||
==============================================================================
|
||||
*/
|
||||
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 );
|
||||
void Platform_SetStatus( const char *status );
|
||||
@@ -71,9 +70,6 @@ void Android_Shutdown( void );
|
||||
#endif
|
||||
|
||||
#if XASH_WIN32
|
||||
void Win32_Init( qboolean con_showalways );
|
||||
void Win32_Shutdown( void );
|
||||
qboolean Win32_NanoSleep( int nsec );
|
||||
void Wcon_CreateConsole( qboolean con_showalways );
|
||||
void Wcon_DestroyConsole( void );
|
||||
void Wcon_InitConsoleCommands( void );
|
||||
@@ -128,7 +124,7 @@ static inline void Platform_Init( qboolean con_showalways, const char *basedir )
|
||||
#elif XASH_DOS
|
||||
DOS_Init( );
|
||||
#elif XASH_WIN32
|
||||
Win32_Init( con_showalways );
|
||||
Wcon_CreateConsole( con_showalways );
|
||||
#elif XASH_LINUX
|
||||
Linux_Init( );
|
||||
#endif
|
||||
@@ -143,7 +139,7 @@ static inline void Platform_Shutdown( void )
|
||||
#elif XASH_DOS
|
||||
DOS_Shutdown( );
|
||||
#elif XASH_WIN32
|
||||
Win32_Shutdown( );
|
||||
Wcon_DestroyConsole( );
|
||||
#elif XASH_LINUX
|
||||
Linux_Shutdown( );
|
||||
#endif
|
||||
@@ -169,28 +165,24 @@ static inline void Platform_SetupSigtermHandling( void )
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline qboolean Platform_NanoSleep( int nsec )
|
||||
static inline void Platform_Sleep( int msec )
|
||||
{
|
||||
// SDL2 doesn't have nanosleep, so use low-level functions here
|
||||
// When this code will be ported to SDL3, use SDL_DelayNS
|
||||
#if XASH_POSIX
|
||||
struct timespec ts = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = nsec, // just don't put large numbers here
|
||||
};
|
||||
return nanosleep( &ts, NULL ) == 0;
|
||||
#elif XASH_WIN32
|
||||
return Win32_NanoSleep( nsec );
|
||||
#if XASH_TIMER == TIMER_SDL
|
||||
SDL_Delay( msec );
|
||||
#elif XASH_TIMER == TIMER_POSIX
|
||||
usleep( msec * 1000 );
|
||||
#elif XASH_TIMER == TIMER_WIN32
|
||||
Sleep( msec );
|
||||
#else
|
||||
return false;
|
||||
// stub
|
||||
#endif
|
||||
}
|
||||
|
||||
#if XASH_WIN32 || XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX || XASH_APPLE
|
||||
void Sys_SetupCrashHandler( const char *argv0 );
|
||||
#if XASH_WIN32 || XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
void Sys_SetupCrashHandler( void );
|
||||
void Sys_RestoreCrashHandler( void );
|
||||
#else
|
||||
static inline void Sys_SetupCrashHandler( const char *argv0 )
|
||||
static inline void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -207,8 +199,7 @@ static inline void Sys_RestoreCrashHandler( void )
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
void Platform_Vibrate( float life, char flags ); // left for compatibility
|
||||
void Platform_Vibrate2( float time, int low_freq, int high_freq, uint flags );
|
||||
void Platform_Vibrate( float life, char flags );
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
@@ -218,27 +209,7 @@ void Platform_Vibrate2( float time, int low_freq, int high_freq, uint flags );
|
||||
==============================================================================
|
||||
*/
|
||||
// Gamepad support
|
||||
#if XASH_SDL
|
||||
int Platform_JoyInit( void ); // returns number of connected gamepads, negative if error
|
||||
void Platform_JoyShutdown( void );
|
||||
void Platform_CalibrateGamepadGyro( void );
|
||||
#else
|
||||
static inline int Platform_JoyInit( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void Platform_JoyShutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline void Platform_CalibrateGamepadGyro( void )
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
int Platform_JoyInit( int numjoy ); // returns number of connected gamepads, negative if error
|
||||
// Text input
|
||||
void Platform_EnableTextInput( qboolean enable );
|
||||
key_modifier_t Platform_GetKeyModifiers( void );
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
crashhandler.c - advanced crashhandler
|
||||
Copyright (C) 2016 Mittorn
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// on Glibc (which potentially might not be only Linux) systems we
|
||||
// have backtrace() and backtrace_symbols() calls, which replace for us
|
||||
// platform-specific code
|
||||
#if HAVE_EXECINFO
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
|
||||
void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
{
|
||||
char message[8192];
|
||||
int len, logfd, i = 0;
|
||||
int size;
|
||||
void *addrs[16];
|
||||
char **syms;
|
||||
|
||||
(void)context;
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
// safe actions first, stack and memory may be corrupted
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s-%s, %s-%s)\n",
|
||||
Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch() );
|
||||
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD && !XASH_APPLE // they don't have si_ptr
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
|
||||
#else
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
|
||||
#endif
|
||||
|
||||
write( STDERR_FILENO, message, len );
|
||||
|
||||
// now get log fd and write trace directly to log
|
||||
logfd = Sys_LogFileNo();
|
||||
write( logfd, message, len );
|
||||
|
||||
size = backtrace( addrs, sizeof( addrs ) / sizeof( addrs[0] ));
|
||||
syms = backtrace_symbols( addrs, size );
|
||||
|
||||
for( i = 0; i < size && syms; i++ )
|
||||
{
|
||||
size_t symlen = Q_strlen( syms[i] );
|
||||
char ch = '\n';
|
||||
|
||||
write( logfd, syms[i], symlen );
|
||||
write( logfd, &ch, 1 );
|
||||
|
||||
write( STDERR_FILENO, syms[i], symlen );
|
||||
write( STDERR_FILENO, &ch, 1 );
|
||||
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "%2d: %s\n", i, syms[i] );
|
||||
}
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
#if !XASH_DEDICATED
|
||||
IN_SetMouseGrab( false );
|
||||
#endif
|
||||
host.crashed = true;
|
||||
Platform_MessageBox( "Xash Error", message, false );
|
||||
|
||||
// log saved, now we can try to save configs and close log correctly, it may crash
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed();
|
||||
host.status = HOST_CRASHED;
|
||||
|
||||
Sys_Quit( "crashed" );
|
||||
}
|
||||
|
||||
#endif // HAVE_EXECINFO
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
crashhandler.c - advanced crashhandler
|
||||
Copyright (C) 2016 Mittorn
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#if HAVE_LIBBACKTRACE
|
||||
#include <signal.h>
|
||||
#include "common.h"
|
||||
#include "backtrace.h"
|
||||
#include "input.h"
|
||||
|
||||
static struct backtrace_state *g_bt_state;
|
||||
static qboolean enable_libbacktrace;
|
||||
|
||||
static void Sys_BacktraceError( void *data, const char *msg, int errnum )
|
||||
{
|
||||
if( errnum < 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "no symbol info, no libbacktrace\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Con_Printf( S_ERROR "libbacktrace error: %s (%d)\n", msg, errnum );
|
||||
|
||||
enable_libbacktrace = false;
|
||||
}
|
||||
|
||||
struct print_data
|
||||
{
|
||||
char *message;
|
||||
size_t message_size;
|
||||
int len;
|
||||
int idx;
|
||||
int logfd;
|
||||
};
|
||||
|
||||
static void Sys_AppendPrint( struct print_data *pd, const char *fmt, ... )
|
||||
{
|
||||
va_list va;
|
||||
int len;
|
||||
|
||||
va_start( va, fmt );
|
||||
len = Q_vsnprintf( pd->message, pd->message_size, fmt, va );
|
||||
va_end( va );
|
||||
|
||||
if( len > 0 )
|
||||
{
|
||||
char ch = '\n';
|
||||
|
||||
write( pd->logfd, pd->message, len );
|
||||
write( pd->logfd, &ch, 1 );
|
||||
|
||||
write( STDERR_FILENO, pd->message, len );
|
||||
write( STDERR_FILENO, &ch, 1 );
|
||||
|
||||
pd->message += len;
|
||||
pd->len += len;
|
||||
pd->message_size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
static void Sys_BacktracePrintError( void *data, const char *msg, int errnum )
|
||||
{
|
||||
struct print_data *pd = data;
|
||||
Sys_AppendPrint( pd, "%2d: error: %s (%d)\n", pd->idx++, msg, errnum );
|
||||
}
|
||||
|
||||
static void Sys_BacktracePrintSyminfo( void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize )
|
||||
{
|
||||
struct print_data *pd = data;
|
||||
Dl_info dlinfo = { 0 };
|
||||
const char *module_name;
|
||||
|
||||
if( dladdr((void *)pc, &dlinfo ))
|
||||
module_name = dlinfo.dli_fname;
|
||||
else module_name = NULL;
|
||||
|
||||
if( symname )
|
||||
{
|
||||
if( module_name )
|
||||
Sys_AppendPrint( pd, "%2d: <%s+%d> (%s)\n", pd->idx++, symname, pc - symval, module_name );
|
||||
else
|
||||
Sys_AppendPrint( pd, "%2d: <%s+%d>\n", pd->idx++, symname, pc - symval );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( module_name )
|
||||
Sys_AppendPrint( pd, "%2d: %p (%s)\n", pd->idx++, pc, module_name );
|
||||
else
|
||||
Sys_AppendPrint( pd, "%2d: %p\n", pd->idx++, pc );
|
||||
}
|
||||
}
|
||||
|
||||
static int Sys_BacktracePrintFull( void *data, uintptr_t pc, const char *filename, int lineno, const char *function )
|
||||
{
|
||||
struct print_data *pd = data;
|
||||
Dl_info dlinfo = { 0 };
|
||||
const char *module_name;
|
||||
|
||||
if( dladdr((void *)pc, &dlinfo ))
|
||||
module_name = dlinfo.dli_fname;
|
||||
else module_name = NULL;
|
||||
|
||||
if( filename && lineno >= 0 && function )
|
||||
{
|
||||
filename = COM_FileWithoutPath( filename );
|
||||
|
||||
if( module_name )
|
||||
Sys_AppendPrint( pd, "%2d: %s (%s:%d) (%s)\n", pd->idx++, function, filename, lineno, module_name );
|
||||
else
|
||||
Sys_AppendPrint( pd, "%2d: %s (%s:%d)\n", pd->idx++, function, filename, lineno );
|
||||
}
|
||||
else
|
||||
{
|
||||
backtrace_syminfo( g_bt_state, pc, Sys_BacktracePrintSyminfo, Sys_BacktraceError, data );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sys_CrashLibbacktrace( int signal, siginfo_t *si, void *context )
|
||||
{
|
||||
char message[8192];
|
||||
int len, logfd;
|
||||
struct print_data pd = { .idx = 0 };
|
||||
|
||||
(void)context;
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
// safe actions first, stack and memory may be corrupted
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s-%s, %s-%s)\n",
|
||||
Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch() );
|
||||
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD && !XASH_APPLE // they don't have si_ptr
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
|
||||
#else
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
|
||||
#endif
|
||||
|
||||
write( STDERR_FILENO, message, len );
|
||||
|
||||
// now get log fd and write trace directly to log
|
||||
pd.logfd = logfd = Sys_LogFileNo();
|
||||
write( logfd, message, len );
|
||||
|
||||
pd.message = message + len;
|
||||
pd.message_size = sizeof( message ) - len;
|
||||
pd.len = 0;
|
||||
|
||||
backtrace_full( g_bt_state, 1, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
#if !XASH_DEDICATED
|
||||
IN_SetMouseGrab( false );
|
||||
#endif
|
||||
host.crashed = true;
|
||||
Platform_MessageBox( "Xash Error", message, false );
|
||||
|
||||
// log saved, now we can try to save configs and close log correctly, it may crash
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed();
|
||||
host.status = HOST_CRASHED;
|
||||
|
||||
Sys_Quit( "crashed" );
|
||||
}
|
||||
|
||||
qboolean Sys_SetupLibbacktrace( const char *argv0 )
|
||||
{
|
||||
enable_libbacktrace = true;
|
||||
g_bt_state = backtrace_create_state( argv0, true, Sys_BacktraceError, NULL );
|
||||
return g_bt_state != NULL && enable_libbacktrace;
|
||||
}
|
||||
|
||||
#endif // HAVE_EXECINFO
|
||||
@@ -18,22 +18,13 @@ GNU General Public License for more details.
|
||||
// while this is mostly POSIX-compatible functions
|
||||
// the contents of ucontext_t is platform-dependent
|
||||
// before adding new OS here, check platform.h
|
||||
#define _XOPEN_SOURCE 1 // required for ucontext
|
||||
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX || XASH_APPLE
|
||||
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
#ifndef XASH_OPENBSD
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include "library.h"
|
||||
#include "input.h"
|
||||
|
||||
void Sys_Crash( int signal, siginfo_t *si, void *context );
|
||||
void Sys_CrashLibbacktrace( int signal, siginfo_t *si, void *context );
|
||||
qboolean Sys_SetupLibbacktrace( const char *argv0 );
|
||||
static struct sigaction oldFilter;
|
||||
|
||||
#if !HAVE_EXECINFO
|
||||
|
||||
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
|
||||
#define STACK_DUMP_STR "Stack dump:\n"
|
||||
@@ -42,25 +33,27 @@ static struct sigaction oldFilter;
|
||||
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
|
||||
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
|
||||
|
||||
static struct sigaction oldFilter;
|
||||
|
||||
static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
|
||||
{
|
||||
Dl_info dlinfo;
|
||||
if( len <= 0 )
|
||||
return 0; // overflow
|
||||
|
||||
if( dladdr( addr, &dlinfo ))
|
||||
{
|
||||
if( dlinfo.dli_sname )
|
||||
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
|
||||
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
|
||||
if( dladdr( addr, &dlinfo ))
|
||||
{
|
||||
if( dlinfo.dli_sname )
|
||||
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
|
||||
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
|
||||
}
|
||||
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
|
||||
}
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
|
||||
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
|
||||
}
|
||||
|
||||
void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
static void Sys_Crash( int signal, siginfo_t *si, void *context)
|
||||
{
|
||||
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
|
||||
char message[8192];
|
||||
@@ -72,10 +65,6 @@ void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
#endif
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
#if XASH_AMD64
|
||||
#if XASH_FREEBSD
|
||||
pc = (void*)ucontext->uc_mcontext.mc_rip;
|
||||
@@ -123,10 +112,10 @@ void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
#endif
|
||||
|
||||
// safe actions first, stack and memory may be corrupted
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s-%s, %s-%s)\n",
|
||||
Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch() );
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
|
||||
Q_buildnum(), g_buildcommit, Q_buildos(), Q_buildarch() );
|
||||
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD && !XASH_APPLE
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
|
||||
#else
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
|
||||
@@ -134,6 +123,10 @@ void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
|
||||
write( STDERR_FILENO, message, len );
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
// now get log fd and write trace directly to log
|
||||
logfd = Sys_LogFileNo();
|
||||
write( logfd, message, len );
|
||||
@@ -193,8 +186,8 @@ void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
#if !XASH_DEDICATED
|
||||
IN_SetMouseGrab( false );
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif
|
||||
host.crashed = true;
|
||||
Platform_MessageBox( "Xash Error", message, false );
|
||||
@@ -207,27 +200,15 @@ void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
Sys_Quit( "crashed" );
|
||||
}
|
||||
|
||||
#endif // !HAVE_EXECINFO
|
||||
|
||||
void Sys_SetupCrashHandler( const char *argv0 )
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
struct sigaction act = { 0 };
|
||||
#if HAVE_LIBBACKTRACE
|
||||
if( Sys_SetupLibbacktrace( argv0 ))
|
||||
{
|
||||
act.sa_sigaction = Sys_CrashLibbacktrace;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
act.sa_sigaction = Sys_Crash;
|
||||
}
|
||||
act.sa_sigaction = Sys_Crash;
|
||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigaction( SIGSEGV, &act, &oldFilter );
|
||||
sigaction( SIGABRT, &act, &oldFilter );
|
||||
sigaction( SIGBUS, &act, &oldFilter );
|
||||
sigaction( SIGILL, &act, &oldFilter );
|
||||
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
|
||||
@@ -174,10 +174,5 @@ double Platform_DoubleTime( void )
|
||||
#endif
|
||||
return (double) ts.tv_sec + (double) ts.tv_nsec/1000000000.0;
|
||||
}
|
||||
|
||||
void Platform_Sleep( int msec )
|
||||
{
|
||||
usleep( msec * 1000 );
|
||||
}
|
||||
#endif // XASH_TIMER == TIMER_POSIX
|
||||
|
||||
|
||||
@@ -88,8 +88,52 @@ GNU General Public License for more details.
|
||||
#define SDL_SCANCODE_PRINTSCREEN SDLK_PRINT
|
||||
#define SDL_SCANCODE_UNKNOWN SDLK_UNKNOWN
|
||||
#define SDL_GetScancodeName( x ) "unknown"
|
||||
#define SDL_JoystickID Uint8
|
||||
#endif
|
||||
|
||||
static int SDLash_GameControllerButtonMapping[] =
|
||||
{
|
||||
#if XASH_NSWITCH // devkitPro/SDL has inverted Nintendo layout for SDL_GameController
|
||||
K_B_BUTTON, K_A_BUTTON, K_Y_BUTTON, K_X_BUTTON,
|
||||
#else
|
||||
K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON,
|
||||
#endif
|
||||
K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON,
|
||||
K_LSTICK, K_RSTICK,
|
||||
K_L1_BUTTON, K_R1_BUTTON,
|
||||
K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT,
|
||||
K_MISC_BUTTON,
|
||||
K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON,
|
||||
K_TOUCHPAD,
|
||||
};
|
||||
|
||||
// Swap axis to follow default axis binding:
|
||||
// LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft
|
||||
static int SDLash_GameControllerAxisMapping[] =
|
||||
{
|
||||
JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX,
|
||||
JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY,
|
||||
JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY,
|
||||
JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||
JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||
};
|
||||
|
||||
static qboolean SDLash_IsInstanceIDAGameController( SDL_JoystickID joyId )
|
||||
{
|
||||
#if !SDL_VERSION_ATLEAST( 2, 0, 4 )
|
||||
// HACKHACK: if we're not initialized g_joy, then we're probably using gamecontroller api
|
||||
// so return true
|
||||
if( !g_joy )
|
||||
return true;
|
||||
return false;
|
||||
#else
|
||||
if( SDL_GameControllerFromInstanceID( joyId ) != NULL )
|
||||
return true;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SDLash_KeyEvent
|
||||
@@ -364,6 +408,50 @@ static void SDLash_ActiveEvent( int gain )
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
static size_t num_open_game_controllers = 0;
|
||||
|
||||
static void SDLash_GameController_Add( int index )
|
||||
{
|
||||
extern convar_t joy_enable; // private to input system
|
||||
SDL_GameController *controller;
|
||||
|
||||
if( !joy_enable.value )
|
||||
return;
|
||||
|
||||
controller = SDL_GameControllerOpen( index );
|
||||
if( !controller )
|
||||
{
|
||||
Con_Reportf( "Failed to open SDL GameController %d: %s\n", index, SDL_GetError( ) );
|
||||
SDL_ClearError( );
|
||||
return;
|
||||
}
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 6 )
|
||||
Con_Reportf( "Added controller: %s (%i:%i:%i)\n",
|
||||
SDL_GameControllerName( controller ),
|
||||
SDL_GameControllerGetVendor( controller ),
|
||||
SDL_GameControllerGetProduct( controller ),
|
||||
SDL_GameControllerGetProductVersion( controller ));
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 6 )
|
||||
|
||||
++num_open_game_controllers;
|
||||
if( num_open_game_controllers == 1 )
|
||||
Joy_AddEvent( );
|
||||
}
|
||||
|
||||
|
||||
static void SDLash_GameController_Remove( SDL_JoystickID joystick_id )
|
||||
{
|
||||
Con_Reportf( "Removed controller %i\n", joystick_id );
|
||||
|
||||
// `Joy_RemoveEvent` sets `joy_found` to `0`.
|
||||
// We only want to do this when all the game controllers have been removed.
|
||||
--num_open_game_controllers;
|
||||
if( num_open_game_controllers == 0 )
|
||||
Joy_RemoveEvent( );
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
=============
|
||||
SDLash_EventFilter
|
||||
@@ -391,6 +479,28 @@ static void SDLash_EventHandler( SDL_Event *event )
|
||||
SDLash_KeyEvent( event->key );
|
||||
break;
|
||||
|
||||
/* Joystick events */
|
||||
case SDL_JOYAXISMOTION:
|
||||
if ( !SDLash_IsInstanceIDAGameController( event->jaxis.which ))
|
||||
Joy_AxisMotionEvent( event->jaxis.axis, event->jaxis.value );
|
||||
break;
|
||||
|
||||
case SDL_JOYBALLMOTION:
|
||||
if ( !SDLash_IsInstanceIDAGameController( event->jball.which ))
|
||||
Joy_BallMotionEvent( event->jball.ball, event->jball.xrel, event->jball.yrel );
|
||||
break;
|
||||
|
||||
case SDL_JOYHATMOTION:
|
||||
if ( !SDLash_IsInstanceIDAGameController( event->jhat.which ))
|
||||
Joy_HatMotionEvent( event->jhat.hat, event->jhat.value );
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
if ( !SDLash_IsInstanceIDAGameController( event->jbutton.which ))
|
||||
Joy_ButtonEvent( event->jbutton.button, event->jbutton.state );
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
Sys_Quit( "caught SDL_QUIT" );
|
||||
break;
|
||||
@@ -458,17 +568,46 @@ static void SDLash_EventHandler( SDL_Event *event )
|
||||
SDLash_InputEvent( event->text );
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
Joy_AddEvent();
|
||||
break;
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
Joy_RemoveEvent();
|
||||
break;
|
||||
|
||||
/* GameController API */
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
{
|
||||
if( !Joy_IsActive( ))
|
||||
break;
|
||||
|
||||
if( event->caxis.axis >= 0 && event->caxis.axis < ARRAYSIZE( SDLash_GameControllerAxisMapping ))
|
||||
{
|
||||
Joy_KnownAxisMotionEvent( SDLash_GameControllerAxisMapping[event->caxis.axis], event->caxis.value );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
{
|
||||
if( !Joy_IsActive( ))
|
||||
break;
|
||||
|
||||
// TODO: Use joyinput funcs, for future multiple gamepads support
|
||||
if( event->cbutton.button >= 0 && event->cbutton.button < ARRAYSIZE( SDLash_GameControllerButtonMapping ))
|
||||
{
|
||||
Key_Event( SDLash_GameControllerButtonMapping[event->cbutton.button], event->cbutton.state );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
SDLash_GameController_Add( event->cdevice.which );
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERTOUCHPADDOWN:
|
||||
case SDL_CONTROLLERTOUCHPADMOTION:
|
||||
case SDL_CONTROLLERTOUCHPADUP:
|
||||
case SDL_CONTROLLERSENSORUPDATE:
|
||||
SDLash_HandleGameControllerEvent( event );
|
||||
SDLash_GameController_Remove( event->cdevice.which );
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
@@ -481,19 +620,19 @@ static void SDLash_EventHandler( SDL_Event *event )
|
||||
switch( event->window.event )
|
||||
{
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
{
|
||||
char val[32];
|
||||
if( vid_fullscreen.value == WINDOW_MODE_WINDOWED )
|
||||
{
|
||||
char val[32];
|
||||
|
||||
Q_snprintf( val, sizeof( val ), "%d", event->window.data1 );
|
||||
Cvar_DirectSet( &window_xpos, val );
|
||||
Q_snprintf( val, sizeof( val ), "%d", event->window.data1 );
|
||||
Cvar_DirectSet( &window_xpos, val );
|
||||
|
||||
Q_snprintf( val, sizeof( val ), "%d", event->window.data2 );
|
||||
Cvar_DirectSet( &window_ypos, val );
|
||||
Q_snprintf( val, sizeof( val ), "%d", event->window.data2 );
|
||||
Cvar_DirectSet( &window_ypos, val );
|
||||
|
||||
if ( vid_fullscreen.value == WINDOW_MODE_WINDOWED )
|
||||
Cvar_DirectSet( &vid_maximized, "0" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
host.status = HOST_SLEEP;
|
||||
Cvar_DirectSet( &vid_maximized, "0" );
|
||||
|
||||
@@ -28,16 +28,14 @@ void GL_InitExtensions( void );
|
||||
qboolean GL_DeleteContext( void );
|
||||
void VID_SaveWindowSize( int width, int height, qboolean maximized );
|
||||
|
||||
// joystick events
|
||||
extern SDL_Joystick *g_joy;
|
||||
|
||||
//
|
||||
// in_sdl.c
|
||||
//
|
||||
void SDLash_InitCursors( void );
|
||||
void SDLash_FreeCursors( void );
|
||||
|
||||
//
|
||||
// joy_sdl.c
|
||||
//
|
||||
void SDLash_HandleGameControllerEvent( SDL_Event *ev );
|
||||
|
||||
#endif // XASH_SDL
|
||||
#endif // KEYWRAPPER_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
in_sdl.c - SDL input component
|
||||
Copyright (C) 2018-2025 a1batross
|
||||
vid_sdl.c - SDL input component
|
||||
Copyright (C) 2018 a1batross
|
||||
|
||||
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
|
||||
@@ -12,6 +12,7 @@ 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.
|
||||
*/
|
||||
#if !XASH_DEDICATED
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common.h"
|
||||
@@ -23,6 +24,7 @@ GNU General Public License for more details.
|
||||
#include "sound.h"
|
||||
#include "vid_common.h"
|
||||
|
||||
SDL_Joystick *g_joy = NULL;
|
||||
#if !SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
#define SDL_WarpMouseInWindow( win, x, y ) SDL_WarpMouse( ( x ), ( y ) )
|
||||
#else
|
||||
@@ -33,12 +35,6 @@ static struct
|
||||
} cursors;
|
||||
#endif
|
||||
|
||||
static struct
|
||||
{
|
||||
int x, y;
|
||||
qboolean pushed;
|
||||
} in_visible_cursor_pos;
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_GetMousePos
|
||||
@@ -130,6 +126,20 @@ void Platform_SetClipboardText( const char *buffer )
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_Vibrate
|
||||
|
||||
=============
|
||||
*/
|
||||
void Platform_Vibrate( float time, char flags )
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 9 )
|
||||
if( g_joy )
|
||||
SDL_JoystickRumble( g_joy, 0xFFFF, 0xFFFF, time * 1000.0f );
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 9 )
|
||||
}
|
||||
|
||||
#if !XASH_PSVITA
|
||||
|
||||
/*
|
||||
@@ -147,6 +157,120 @@ void Platform_EnableTextInput( qboolean enable )
|
||||
|
||||
#endif // !XASH_PSVITA
|
||||
|
||||
/*
|
||||
=============
|
||||
SDLash_JoyInit_Old
|
||||
|
||||
=============
|
||||
*/
|
||||
static int SDLash_JoyInit_Old( int numjoy )
|
||||
{
|
||||
int num;
|
||||
int i;
|
||||
|
||||
Con_Reportf( "Joystick: SDL\n" );
|
||||
|
||||
if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK &&
|
||||
SDL_InitSubSystem( SDL_INIT_JOYSTICK ) )
|
||||
{
|
||||
Con_Reportf( "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( g_joy )
|
||||
{
|
||||
SDL_JoystickClose( g_joy );
|
||||
}
|
||||
|
||||
num = SDL_NumJoysticks();
|
||||
|
||||
if( num > 0 )
|
||||
Con_Reportf( "%i joysticks found:\n", num );
|
||||
else
|
||||
{
|
||||
Con_Reportf( "No joystick found.\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
for( i = 0; i < num; i++ )
|
||||
Con_Reportf( "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) );
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
|
||||
Con_Reportf( "Pass +set joy_index N to command line, where N is number, to select active joystick\n" );
|
||||
|
||||
g_joy = SDL_JoystickOpen( numjoy );
|
||||
|
||||
if( !g_joy )
|
||||
{
|
||||
Con_Reportf( "Failed to select joystick: %s\n", SDL_GetError( ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
Con_Reportf( "Selected joystick: %s\n"
|
||||
"\tAxes: %i\n"
|
||||
"\tHats: %i\n"
|
||||
"\tButtons: %i\n"
|
||||
"\tBalls: %i\n",
|
||||
SDL_JoystickName( g_joy ), SDL_JoystickNumAxes( g_joy ), SDL_JoystickNumHats( g_joy ),
|
||||
SDL_JoystickNumButtons( g_joy ), SDL_JoystickNumBalls( g_joy ) );
|
||||
|
||||
SDL_GameControllerEventState( SDL_DISABLE );
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
SDL_JoystickEventState( SDL_ENABLE );
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
/*
|
||||
=============
|
||||
SDLash_JoyInit_New
|
||||
|
||||
=============
|
||||
*/
|
||||
static int SDLash_JoyInit_New( int numjoy )
|
||||
{
|
||||
int count, numJoysticks, i;
|
||||
|
||||
Con_Reportf( "Joystick: SDL GameController API\n" );
|
||||
if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER &&
|
||||
SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) )
|
||||
{
|
||||
Con_Reportf( "Failed to initialize SDL GameController API: %s\n", SDL_GetError() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_GameControllerAddMappingsFromFile( "controllermappings.txt" );
|
||||
|
||||
count = 0;
|
||||
numJoysticks = SDL_NumJoysticks();
|
||||
for ( i = 0; i < numJoysticks; i++ )
|
||||
if( SDL_IsGameController( i ) )
|
||||
++count;
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_JoyInit
|
||||
|
||||
=============
|
||||
*/
|
||||
int Platform_JoyInit( int numjoy )
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
// SDL_Joystick is now an old API
|
||||
// SDL_GameController is preferred
|
||||
if( !Sys_CheckParm( "-sdl_joy_old_api" ) )
|
||||
return SDLash_JoyInit_New(numjoy);
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
return SDLash_JoyInit_Old(numjoy);
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
SDLash_InitCursors
|
||||
@@ -209,6 +333,11 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
|
||||
{
|
||||
qboolean visible;
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( !cursors.initialized )
|
||||
return;
|
||||
#endif
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case dc_user:
|
||||
@@ -221,7 +350,7 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
|
||||
}
|
||||
|
||||
// never disable cursor in touch emulation mode
|
||||
if( !visible && Touch_WantVisibleCursor( ))
|
||||
if( !visible && Touch_Emulated( ))
|
||||
return;
|
||||
|
||||
host.mouse_visible = visible;
|
||||
@@ -230,28 +359,11 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( host.mouse_visible )
|
||||
{
|
||||
if( cursors.initialized )
|
||||
SDL_SetCursor( cursors.cursors[type] );
|
||||
|
||||
SDL_SetCursor( cursors.cursors[type] );
|
||||
SDL_ShowCursor( true );
|
||||
|
||||
// restore the last mouse position
|
||||
if( in_visible_cursor_pos.pushed )
|
||||
{
|
||||
SDL_WarpMouseInWindow( host.hWnd, in_visible_cursor_pos.x, in_visible_cursor_pos.y );
|
||||
in_visible_cursor_pos.pushed = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// save last mouse position and warp it to the center
|
||||
if( !in_visible_cursor_pos.pushed )
|
||||
{
|
||||
SDL_GetMouseState( &in_visible_cursor_pos.x, &in_visible_cursor_pos.y );
|
||||
SDL_WarpMouseInWindow( host.hWnd, host.window_center_x, host.window_center_y );
|
||||
in_visible_cursor_pos.pushed = true;
|
||||
}
|
||||
|
||||
SDL_ShowCursor( false );
|
||||
}
|
||||
#else
|
||||
@@ -306,3 +418,5 @@ key_modifier_t Platform_GetKeyModifiers( void )
|
||||
return KeyModifier_None;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // XASH_DEDICATED
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
joy_sdl.c - SDL gamepads
|
||||
Copyright (C) 2018-2025 a1batross
|
||||
|
||||
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 <SDL.h>
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
#include "common.h"
|
||||
#include "keydefs.h"
|
||||
#include "input.h"
|
||||
#include "client.h"
|
||||
#include "events.h"
|
||||
|
||||
static const int g_button_mapping[] =
|
||||
{
|
||||
#if XASH_NSWITCH // devkitPro/SDL has inverted Nintendo layout for SDL_GameController
|
||||
K_B_BUTTON, K_A_BUTTON, K_Y_BUTTON, K_X_BUTTON,
|
||||
#else
|
||||
K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON,
|
||||
#endif
|
||||
K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON,
|
||||
K_LSTICK, K_RSTICK,
|
||||
K_L1_BUTTON, K_R1_BUTTON,
|
||||
K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT,
|
||||
K_MISC_BUTTON,
|
||||
K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON,
|
||||
K_TOUCHPAD,
|
||||
};
|
||||
|
||||
// Swap axis to follow default axis binding:
|
||||
static const engineAxis_t g_axis_mapping[] =
|
||||
{
|
||||
JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX,
|
||||
JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY,
|
||||
JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY,
|
||||
JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||
JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||
};
|
||||
|
||||
static SDL_JoystickID g_current_gamepad_id = -1; // used to send rumble to
|
||||
static SDL_GameController *g_current_gamepad;
|
||||
static SDL_GameController **g_gamepads;
|
||||
static size_t g_num_gamepads;
|
||||
|
||||
#define CALIBRATION_TIME 10.0f
|
||||
|
||||
static struct
|
||||
{
|
||||
float time;
|
||||
vec3_t data;
|
||||
vec3_t calibrated_values;
|
||||
int samples;
|
||||
} gyrocal;
|
||||
|
||||
static void SDLash_RestartCalibration( void )
|
||||
{
|
||||
Joy_SetCalibrationState( JOY_NOT_CALIBRATED );
|
||||
|
||||
memset( &gyrocal, 0, sizeof( gyrocal ));
|
||||
|
||||
gyrocal.time = host.realtime + CALIBRATION_TIME;
|
||||
|
||||
Con_Printf( "Starting gyroscope calibration...\n" );
|
||||
}
|
||||
|
||||
static void SDLash_FinalizeCalibration( void )
|
||||
{
|
||||
float data_rate = 10.0f; // let's say we're polling at 10Hz?
|
||||
int min_samples;
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 16 )
|
||||
data_rate = SDL_GameControllerGetSensorDataRate( g_current_gamepad, SDL_SENSOR_GYRO );
|
||||
if( !data_rate )
|
||||
data_rate = 10.0f;
|
||||
#endif
|
||||
|
||||
min_samples = Q_rint( CALIBRATION_TIME * data_rate * 0.75f );
|
||||
|
||||
// we waited for few seconds and got too few samples
|
||||
if( gyrocal.samples <= min_samples )
|
||||
{
|
||||
Joy_SetCalibrationState( JOY_FAILED_TO_CALIBRATE );
|
||||
return;
|
||||
}
|
||||
|
||||
VectorScale( gyrocal.data, 1.0f / gyrocal.samples, gyrocal.calibrated_values );
|
||||
Joy_SetCalibrationState( JOY_CALIBRATED );
|
||||
|
||||
Con_Printf( "Calibration done. Result: %f %f %f\n", gyrocal.calibrated_values[0], gyrocal.calibrated_values[1], gyrocal.calibrated_values[2] );
|
||||
|
||||
gyrocal.time = 0.0f;
|
||||
}
|
||||
|
||||
static void SDLash_GameControllerAddMappings( const char *name )
|
||||
{
|
||||
fs_offset_t len = 0;
|
||||
byte *p = FS_LoadFile( name, &len, false );
|
||||
|
||||
if( !p )
|
||||
return;
|
||||
|
||||
if( len > 0 && len < INT32_MAX ) // function accepts int, SDL3 fixes this
|
||||
{
|
||||
SDL_RWops *rwops = SDL_RWFromConstMem( p, (int)len );
|
||||
SDL_GameControllerAddMappingsFromRW( rwops, true );
|
||||
}
|
||||
|
||||
Mem_Free( p );
|
||||
}
|
||||
|
||||
static void SDLash_SetActiveGameController( SDL_JoystickID id )
|
||||
{
|
||||
SDL_GameController *oldgc;
|
||||
|
||||
if( g_current_gamepad_id == id )
|
||||
return;
|
||||
|
||||
g_current_gamepad_id = id;
|
||||
|
||||
oldgc = g_current_gamepad;
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
|
||||
SDL_GameControllerSetSensorEnabled( oldgc, SDL_SENSOR_GYRO, SDL_FALSE );
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 14 )
|
||||
|
||||
if( id < 0 )
|
||||
{
|
||||
g_current_gamepad = NULL;
|
||||
Joy_SetCapabilities( false );
|
||||
Joy_SetCalibrationState( JOY_NOT_CALIBRATED );
|
||||
}
|
||||
else
|
||||
{
|
||||
qboolean have_gyro = false;
|
||||
|
||||
g_current_gamepad = SDL_GameControllerFromInstanceID( id );
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
|
||||
have_gyro = SDL_GameControllerHasSensor( g_current_gamepad, SDL_SENSOR_GYRO );
|
||||
|
||||
if( have_gyro )
|
||||
{
|
||||
SDL_GameControllerSetSensorEnabled( g_current_gamepad, SDL_SENSOR_GYRO, SDL_TRUE );
|
||||
SDLash_RestartCalibration();
|
||||
}
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 14 )
|
||||
|
||||
Joy_SetCapabilities( have_gyro );
|
||||
}
|
||||
}
|
||||
|
||||
static void SDLash_GameControllerAdded( int device_index )
|
||||
{
|
||||
SDL_GameController *gc;
|
||||
SDL_GameController **list;
|
||||
|
||||
gc = SDL_GameControllerOpen( device_index );
|
||||
if( !gc )
|
||||
{
|
||||
Con_Printf( S_ERROR "SDL_GameControllerOpen: %s\n", SDL_GetError( ));
|
||||
return;
|
||||
}
|
||||
|
||||
// this "game controller" only exists on Android within emulator and tries to map
|
||||
// keyboard events into game controller events, which as you can expect, doesn't
|
||||
// work and I don't understand the intention here. When debugging Xash in Android
|
||||
// Studio emulator, just enable hardware input passthrough.
|
||||
#if XASH_ANDROID
|
||||
if( !Q_strcmp( SDL_GameControllerName( gc ), "qwerty2" ))
|
||||
{
|
||||
SDL_GameControllerClose( gc );
|
||||
return;
|
||||
}
|
||||
#endif // XASH_ANDROID
|
||||
|
||||
list = Mem_Realloc( host.mempool, g_gamepads, sizeof( *list ) * ( g_num_gamepads + 1 ));
|
||||
list[g_num_gamepads++] = gc;
|
||||
|
||||
g_gamepads = list;
|
||||
|
||||
// set as current device if none other set
|
||||
if( g_current_gamepad_id < 0 )
|
||||
{
|
||||
SDL_Joystick *joy = SDL_GameControllerGetJoystick( gc );
|
||||
|
||||
if( joy )
|
||||
SDLash_SetActiveGameController( SDL_JoystickInstanceID( joy ));
|
||||
}
|
||||
|
||||
Con_Printf( "Detected \"%s\" game controller.\nMapping string: %s\n", SDL_GameControllerName( gc ), SDL_GameControllerMapping( gc ));
|
||||
}
|
||||
|
||||
static void SDLash_GameControllerRemoved( SDL_JoystickID id )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( id == g_current_gamepad_id )
|
||||
SDLash_SetActiveGameController( -1 );
|
||||
|
||||
// now close the device
|
||||
for( i = 0; i < g_num_gamepads; i++ )
|
||||
{
|
||||
SDL_GameController *gc = g_gamepads[i];
|
||||
SDL_Joystick *joy;
|
||||
|
||||
if( !gc )
|
||||
continue;
|
||||
|
||||
joy = SDL_GameControllerGetJoystick( gc );
|
||||
|
||||
if( !joy )
|
||||
continue;
|
||||
|
||||
if( SDL_JoystickInstanceID( joy ) == id )
|
||||
{
|
||||
Con_Printf( "Game controller \"%s\" was disconnected\n", SDL_GameControllerName( gc ));
|
||||
|
||||
SDL_GameControllerClose( gc );
|
||||
g_gamepads[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SDLash_GameControllerSensorUpdate( SDL_ControllerSensorEvent sensor )
|
||||
{
|
||||
vec3_t data;
|
||||
|
||||
if( sensor.which != g_current_gamepad_id )
|
||||
return;
|
||||
|
||||
if( sensor.sensor != SDL_SENSOR_GYRO )
|
||||
return;
|
||||
|
||||
if( gyrocal.time != 0.0f )
|
||||
{
|
||||
if( host.realtime > gyrocal.time )
|
||||
SDLash_FinalizeCalibration();
|
||||
|
||||
VectorAdd( gyrocal.data, sensor.data, gyrocal.data );
|
||||
gyrocal.samples++;
|
||||
|
||||
Joy_SetCalibrationState( JOY_CALIBRATING );
|
||||
return;
|
||||
}
|
||||
|
||||
VectorSubtract( sensor.data, gyrocal.calibrated_values, data );
|
||||
Joy_GyroEvent( data );
|
||||
}
|
||||
|
||||
void SDLash_HandleGameControllerEvent( SDL_Event *ev )
|
||||
{
|
||||
int x;
|
||||
|
||||
switch( ev->type )
|
||||
{
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
SDLash_SetActiveGameController( ev->caxis.which );
|
||||
x = ev->caxis.axis;
|
||||
if( x >= 0 && x < ARRAYSIZE( g_axis_mapping ))
|
||||
Joy_AxisMotionEvent( g_axis_mapping[x], ev->caxis.value );
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
SDLash_SetActiveGameController( ev->cbutton.which );
|
||||
x = ev->cbutton.button;
|
||||
if( x >= 0 && x < ARRAYSIZE( g_button_mapping ))
|
||||
Key_Event( g_button_mapping[x], ev->cbutton.state );
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
SDLash_GameControllerRemoved( ev->cdevice.which );
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
SDLash_GameControllerAdded( ev->cdevice.which );
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
|
||||
case SDL_CONTROLLERSENSORUPDATE:
|
||||
SDLash_GameControllerSensorUpdate( ev->csensor );
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Platform_CalibrateGamepadGyro( void )
|
||||
{
|
||||
SDLash_RestartCalibration();
|
||||
}
|
||||
|
||||
void Platform_Vibrate2( float time, int val1, int val2, uint flags )
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 9 )
|
||||
SDL_GameController *gc = g_current_gamepad;
|
||||
Uint32 ms;
|
||||
|
||||
if( g_current_gamepad_id < 0 || !gc )
|
||||
return;
|
||||
|
||||
if( val1 < 0 )
|
||||
val1 = COM_RandomLong( 0x7FFF, 0xFFFF );
|
||||
|
||||
if( val2 < 0 )
|
||||
val2 = COM_RandomLong( 0x7FFF, 0xFFFF );
|
||||
|
||||
ms = (Uint32)ceil( time );
|
||||
SDL_GameControllerRumble( gc, val1, val2, ms );
|
||||
#endif // SDL_VERSION_ATLEAST( 2, 0, 9 )
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_Vibrate
|
||||
|
||||
=============
|
||||
*/
|
||||
void Platform_Vibrate( float time, char flags )
|
||||
{
|
||||
Platform_Vibrate2( time, -1, -1, flags );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_JoyInit
|
||||
|
||||
=============
|
||||
*/
|
||||
int Platform_JoyInit( void )
|
||||
{
|
||||
int count, numJoysticks, i;
|
||||
|
||||
Con_Reportf( "Joystick: SDL GameController API\n" );
|
||||
if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER &&
|
||||
SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ))
|
||||
{
|
||||
Con_Reportf( "Failed to initialize SDL GameController API: %s\n", SDL_GetError( ));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDLash_GameControllerAddMappings( "gamecontrollerdb.txt" ); // shipped in extras.pk3
|
||||
SDLash_GameControllerAddMappings( "controllermappings.txt" );
|
||||
|
||||
count = 0;
|
||||
numJoysticks = SDL_NumJoysticks();
|
||||
for ( i = 0; i < numJoysticks; i++ )
|
||||
{
|
||||
if( SDL_IsGameController( i ))
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Platform_JoyShutdown
|
||||
|
||||
=============
|
||||
*/
|
||||
void Platform_JoyShutdown( void )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
SDLash_SetActiveGameController( -1 );
|
||||
|
||||
for( i = 0; i < g_num_gamepads; i++ )
|
||||
{
|
||||
if( !g_gamepads[i] )
|
||||
continue;
|
||||
|
||||
SDL_GameControllerClose( g_gamepads[i] );
|
||||
g_gamepads[i] = NULL;
|
||||
}
|
||||
|
||||
Mem_Free( g_gamepads );
|
||||
g_gamepads = NULL;
|
||||
g_num_gamepads = 0;
|
||||
|
||||
SDL_QuitSubSystem( SDL_INIT_GAMECONTROLLER );
|
||||
}
|
||||
#else // SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
void Platform_Vibrate( float time, char flags )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Platform_JoyInit( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform_JoyShutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -175,7 +175,7 @@ qboolean SNDDMA_Init( void )
|
||||
if( !samplecount )
|
||||
samplecount = 0x8000;
|
||||
dma.samples = samplecount * obtained.channels;
|
||||
dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 );
|
||||
dma.buffer = Mem_Malloc( sndpool, dma.samples * 2 );
|
||||
dma.samplepos = 0;
|
||||
|
||||
sdl_format = obtained.format;
|
||||
|
||||
@@ -32,11 +32,6 @@ double Platform_DoubleTime( void )
|
||||
CurrentTime = SDL_GetPerformanceCounter();
|
||||
return (double)( CurrentTime - g_ClockStart ) / (double)( g_PerformanceFrequency );
|
||||
}
|
||||
|
||||
void Platform_Sleep( int msec )
|
||||
{
|
||||
SDL_Delay( msec );
|
||||
}
|
||||
#endif // XASH_TIMER == TIMER_SDL
|
||||
|
||||
#if XASH_MESSAGEBOX == MSGBOX_SDL
|
||||
@@ -114,11 +109,8 @@ void SDLash_Init( const char *basedir )
|
||||
host.type = HOST_DEDICATED;
|
||||
}
|
||||
|
||||
#if SDL_MAJOR_VERSION >= 2
|
||||
SDL_SetHint( SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0" );
|
||||
SDL_SetHint( SDL_HINT_MOUSE_TOUCH_EVENTS, "0" );
|
||||
SDL_SetHint( SDL_HINT_TOUCH_MOUSE_EVENTS, "0" );
|
||||
|
||||
#if XASH_SDL == 2
|
||||
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
|
||||
SDL_StopTextInput();
|
||||
#endif // XASH_SDL == 2
|
||||
|
||||
|
||||
@@ -257,8 +257,7 @@ static void R_InitVideoModes( void )
|
||||
{
|
||||
char buf[MAX_VA_STRING];
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
SDL_Point point = { window_xpos.value, window_ypos.value };
|
||||
int displayIndex = SDL_GetPointDisplayIndex( &point );
|
||||
int displayIndex = 0; // TODO: handle multiple displays somehow
|
||||
int i, modes;
|
||||
|
||||
num_vidmodes = 0;
|
||||
@@ -488,7 +487,13 @@ GL_UpdateSwapInterval
|
||||
void GL_UpdateSwapInterval( void )
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
// disable VSync while level is loading
|
||||
if( cls.state < ca_active )
|
||||
{
|
||||
SDL_GL_SetSwapInterval( 0 );
|
||||
SetBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
}
|
||||
else if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
|
||||
@@ -730,24 +735,6 @@ static qboolean VID_CreateWindowWithSafeGL( const char *wndname, int xpos, int y
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean RectFitsInDisplay( const SDL_Rect *rect, const SDL_Rect *display )
|
||||
{
|
||||
return rect->x >= display->x
|
||||
&& rect->y >= display->y
|
||||
&& rect->x + rect->w <= display->x + display->w
|
||||
&& rect->y + rect->h <= display->y + display->h;
|
||||
}
|
||||
// Function to check if the rectangle fits in any display
|
||||
static qboolean RectFitsInAnyDisplay( const SDL_Rect *rect, const SDL_Rect *display_rects, int num_displays )
|
||||
{
|
||||
for( int i = 0; i < num_displays; i++ )
|
||||
{
|
||||
if( RectFitsInDisplay( rect, &display_rects[i] ))
|
||||
return true; // Rectangle fits in this display
|
||||
}
|
||||
return false; // Rectangle does not fit in any display
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VID_CreateWindow
|
||||
@@ -760,8 +747,6 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
|
||||
qboolean maximized = vid_maximized.value != 0.0f;
|
||||
Uint32 wndFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_MOUSE_FOCUS;
|
||||
int xpos, ypos;
|
||||
int num_displays = SDL_GetNumVideoDisplays();
|
||||
SDL_Rect rect = { window_xpos.value, window_ypos.value, width, height };
|
||||
|
||||
Q_strncpy( wndname, GI->title, sizeof( wndname ));
|
||||
|
||||
@@ -772,43 +757,35 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
|
||||
|
||||
if( window_mode == WINDOW_MODE_WINDOWED )
|
||||
{
|
||||
SDL_Rect *display_rects = ( SDL_Rect * )malloc( num_displays * sizeof( SDL_Rect ));
|
||||
|
||||
SDL_Rect r;
|
||||
|
||||
SetBits( wndFlags, SDL_WINDOW_RESIZABLE );
|
||||
if( maximized )
|
||||
SetBits( wndFlags, SDL_WINDOW_MAXIMIZED );
|
||||
|
||||
if( !display_rects )
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 5 )
|
||||
if( SDL_GetDisplayUsableBounds( 0, &r ) < 0 &&
|
||||
SDL_GetDisplayBounds( 0, &r ) < 0 )
|
||||
#else
|
||||
if( SDL_GetDisplayBounds( 0, &r ) < 0 )
|
||||
#endif
|
||||
{
|
||||
Con_Printf( S_ERROR "Failed to allocate memory for display rects!\n" );
|
||||
xpos = SDL_WINDOWPOS_UNDEFINED;
|
||||
ypos = SDL_WINDOWPOS_UNDEFINED;
|
||||
Con_Reportf( S_ERROR "%s: SDL_GetDisplayBounds failed: %s\n", __func__, SDL_GetError( ));
|
||||
xpos = SDL_WINDOWPOS_CENTERED;
|
||||
ypos = SDL_WINDOWPOS_CENTERED;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 0; i < num_displays; i++ )
|
||||
{
|
||||
if( SDL_GetDisplayBounds( i, &display_rects[i] ) != 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "Failed to get bounds for display %d! SDL_Error: %s\n", i, SDL_GetError());
|
||||
display_rects[i] = ( SDL_Rect ){ 0, 0, 0, 0 };
|
||||
}
|
||||
}
|
||||
// Check if the rectangle fits in any display
|
||||
if( !RectFitsInAnyDisplay( &rect, display_rects, num_displays ))
|
||||
{
|
||||
// Rectangle doesn't fit in any display, center it
|
||||
xpos = window_xpos.value;
|
||||
ypos = window_ypos.value;
|
||||
|
||||
// don't create window outside of usable display space
|
||||
if( xpos < r.x || xpos + width > r.x + r.w )
|
||||
xpos = SDL_WINDOWPOS_CENTERED;
|
||||
|
||||
if( ypos < r.y || ypos + height > r.y + r.h )
|
||||
ypos = SDL_WINDOWPOS_CENTERED;
|
||||
Con_Printf( S_ERROR "Rectangle does not fit in any display. Centering window.\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
xpos = rect.x;
|
||||
ypos = rect.y;
|
||||
}
|
||||
}
|
||||
free( display_rects );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -818,16 +795,7 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
|
||||
else
|
||||
SetBits( wndFlags, SDL_WINDOW_FULLSCREEN_DESKTOP );
|
||||
SetBits( wndFlags, SDL_WINDOW_BORDERLESS );
|
||||
if ( window_xpos.value < 0 || window_ypos.value < 0 )
|
||||
{
|
||||
xpos = SDL_WINDOWPOS_UNDEFINED;
|
||||
ypos = SDL_WINDOWPOS_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
xpos = window_xpos.value;
|
||||
ypos = window_ypos.value;
|
||||
}
|
||||
xpos = ypos = 0;
|
||||
}
|
||||
|
||||
if( !VID_CreateWindowWithSafeGL( wndname, xpos, ypos, width, height, wndFlags ))
|
||||
@@ -1050,8 +1018,7 @@ qboolean R_Init_Video( const int type )
|
||||
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
SDL_DisplayMode displayMode;
|
||||
SDL_Point point = { window_xpos.value, window_ypos.value };
|
||||
SDL_GetCurrentDisplayMode( SDL_GetPointDisplayIndex( &point ), &displayMode );
|
||||
SDL_GetCurrentDisplayMode( 0, &displayMode );
|
||||
refState.desktopBitsPixel = SDL_BITSPERPIXEL( displayMode.format );
|
||||
#else
|
||||
refState.desktopBitsPixel = 16;
|
||||
|
||||
@@ -49,6 +49,10 @@ typedef struct
|
||||
qboolean inputEnabled;
|
||||
qboolean consoleVisible;
|
||||
qboolean attached;
|
||||
|
||||
// log stuff
|
||||
qboolean log_active;
|
||||
char log_path[MAX_SYSPATH];
|
||||
} WinConData;
|
||||
|
||||
static WinConData s_wcd;
|
||||
@@ -498,13 +502,19 @@ create win32 console
|
||||
*/
|
||||
void Wcon_CreateConsole( qboolean con_showalways )
|
||||
{
|
||||
if( Sys_CheckParm( "-log" ))
|
||||
s_wcd.log_active = true;
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
{
|
||||
Q_strncpy( s_wcd.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path ));
|
||||
}
|
||||
else // dedicated console
|
||||
{
|
||||
Q_strncpy( s_wcd.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path ));
|
||||
s_wcd.log_active = true; // always make log
|
||||
}
|
||||
|
||||
s_wcd.attached = ( AttachConsole( ATTACH_PARENT_PROCESS ) != 0 );
|
||||
@@ -586,8 +596,10 @@ void Wcon_DestroyConsole( void )
|
||||
// last text message into console or log
|
||||
Con_Reportf( "%s: Unloading xash.dll\n", __func__ );
|
||||
|
||||
Sys_CloseLog( NULL );
|
||||
|
||||
if( !s_wcd.attached )
|
||||
{
|
||||
{
|
||||
if( s_wcd.hWnd )
|
||||
{
|
||||
ShowWindow( s_wcd.hWnd, SW_HIDE );
|
||||
|
||||
@@ -14,15 +14,11 @@ GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "input.h"
|
||||
|
||||
#define DBGHELP 1 // we always enable dbghelp.dll on Windows targets
|
||||
|
||||
#if XASH_SDL
|
||||
#include <SDL.h>
|
||||
#endif // XASH_SDL
|
||||
|
||||
#if DBGHELP
|
||||
|
||||
#include <winnt.h>
|
||||
#include <dbghelp.h>
|
||||
#include <psapi.h>
|
||||
@@ -272,8 +268,8 @@ static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
|
||||
// check to avoid recursive call
|
||||
host.crashed = true;
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
IN_SetMouseGrab( false );
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif // XASH_SDL
|
||||
|
||||
#if DBGHELP
|
||||
@@ -322,7 +318,7 @@ static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
void Sys_SetupCrashHandler( const char *argv0 )
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
|
||||
oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
|
||||
|
||||
@@ -18,8 +18,6 @@ GNU General Public License for more details.
|
||||
#include "server.h"
|
||||
#include <shellapi.h>
|
||||
|
||||
HANDLE g_waitable_timer;
|
||||
|
||||
#if XASH_TIMER == TIMER_WIN32
|
||||
double Platform_DoubleTime( void )
|
||||
{
|
||||
@@ -36,74 +34,8 @@ double Platform_DoubleTime( void )
|
||||
|
||||
return (double)( CurrentTime.QuadPart - g_ClockStart.QuadPart ) / (double)( g_PerformanceFrequency.QuadPart );
|
||||
}
|
||||
|
||||
void Platform_Sleep( int msec )
|
||||
{
|
||||
Sleep( msec );
|
||||
}
|
||||
#endif // XASH_TIMER == TIMER_WIN32
|
||||
|
||||
void Win32_Init( qboolean con_showalways )
|
||||
{
|
||||
HMODULE hModule = LoadLibrary( "kernel32.dll" );
|
||||
if( hModule )
|
||||
{
|
||||
HANDLE ( __stdcall *pfnCreateWaitableTimerExW)( LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess );
|
||||
|
||||
if(( pfnCreateWaitableTimerExW = (void *)GetProcAddress( hModule, "CreateWaitableTimerExW" )))
|
||||
{
|
||||
g_waitable_timer = pfnCreateWaitableTimerExW(
|
||||
NULL,
|
||||
NULL,
|
||||
0x1 /* CREATE_WAITABLE_TIMER_MANUAL_RESET */ | 0x2 /* CREATE_WAITABLE_TIMER_HIGH_RESOLUTION */,
|
||||
0x0002 /* TIMER_MODIFY_STATE */ | SYNCHRONIZE | DELETE
|
||||
);
|
||||
}
|
||||
|
||||
FreeLibrary( hModule );
|
||||
}
|
||||
|
||||
#if 0 // FIXME: creates object but doesn't wait for specific time for me on Windows 10, with the code above commented
|
||||
if( !g_waitable_timer )
|
||||
g_waitable_timer = CreateWaitableTimer( NULL, TRUE, NULL );
|
||||
#endif
|
||||
|
||||
Wcon_CreateConsole( con_showalways );
|
||||
}
|
||||
|
||||
void Win32_Shutdown( void )
|
||||
{
|
||||
Wcon_DestroyConsole( );
|
||||
|
||||
if( g_waitable_timer )
|
||||
{
|
||||
CloseHandle( g_waitable_timer );
|
||||
g_waitable_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean Win32_NanoSleep( int nsec )
|
||||
{
|
||||
LARGE_INTEGER ts;
|
||||
|
||||
if( !g_waitable_timer )
|
||||
return false;
|
||||
|
||||
ts.QuadPart = -nsec / 100;
|
||||
|
||||
if( !SetWaitableTimer( g_waitable_timer, &ts, 0, NULL, NULL, FALSE ))
|
||||
{
|
||||
CloseHandle( g_waitable_timer );
|
||||
g_waitable_timer = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( WaitForSingleObject( g_waitable_timer, Q_max( 1, nsec / 1000000 )) != WAIT_OBJECT_0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean Platform_DebuggerPresent( void )
|
||||
{
|
||||
return IsDebuggerPresent();
|
||||
|
||||
@@ -348,7 +348,7 @@ typedef struct ref_api_s
|
||||
void (*Con_NXPrintf)( struct con_nprint_s *info, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
||||
void (*CL_CenterPrint)( const char *s, float y );
|
||||
void (*Con_DrawStringLen)( const char *pText, int *length, int *height );
|
||||
int (*Con_DrawString)( int x, int y, const char *string, const rgba_t setColor );
|
||||
int (*Con_DrawString)( int x, int y, const char *string, rgba_t setColor );
|
||||
void (*CL_DrawCenterPrint)( void );
|
||||
|
||||
// entity management
|
||||
|
||||
@@ -205,62 +205,69 @@ typedef struct
|
||||
|
||||
typedef struct sv_client_s
|
||||
{
|
||||
cl_state_t state;
|
||||
cl_upload_t upstate; // uploading state
|
||||
char name[32]; // extracted from userinfo, color string allowed
|
||||
uint flags; // client flags, some info
|
||||
uint extensions; // protocol extensions
|
||||
cl_state_t state;
|
||||
cl_upload_t upstate; // uploading state
|
||||
char name[32]; // extracted from userinfo, color string allowed
|
||||
uint flags; // client flags, some info
|
||||
CRC32_t crcValue;
|
||||
|
||||
char hashedcdkey[34]; // MD5 hash is 32 hex #'s, plus trailing 0
|
||||
char userinfo[MAX_INFO_STRING]; // name, etc (received from client)
|
||||
char physinfo[MAX_INFO_STRING]; // set on server (transmit to client)
|
||||
char useragent[MAX_INFO_STRING];
|
||||
char userinfo[MAX_INFO_STRING]; // name, etc (received from client)
|
||||
char physinfo[MAX_INFO_STRING]; // set on server (transmit to client)
|
||||
|
||||
byte ignorecmdtime_warned; // did we warn our server operator in the log for this batch of commands?
|
||||
byte m_bLoopback; // does this client want to hear his own voice?
|
||||
uint listeners; // which other clients does this guy's voice stream go to?
|
||||
netchan_t netchan;
|
||||
int chokecount; // number of messages rate supressed
|
||||
int delta_sequence; // -1 = no compression.
|
||||
|
||||
double next_messagetime; // time when we should send next world state update
|
||||
double next_checkpingtime; // time to send all players pings to client
|
||||
double next_sendinfotime; // time to send info about all players
|
||||
double cl_updaterate; // client requested updaterate
|
||||
double timebase; // client timebase
|
||||
double connection_started;
|
||||
|
||||
char hashedcdkey[34]; // MD5 hash is 32 hex #'s, plus trailing 0
|
||||
|
||||
customization_t customdata; // player customization linked list
|
||||
resource_t resourcesonhand;
|
||||
resource_t resourcesneeded; // <mapname.res> from client (server downloading)
|
||||
usercmd_t lastcmd; // for filling in big drops
|
||||
|
||||
double connecttime;
|
||||
double cmdtime;
|
||||
double ignorecmdtime;
|
||||
|
||||
int packet_loss;
|
||||
float latency;
|
||||
|
||||
int ignored_ents; // if visibility list is full we should know how many entities will be ignored
|
||||
edict_t *edict; // EDICT_NUM(clientnum+1)
|
||||
edict_t *pViewEntity; // svc_setview member
|
||||
edict_t *viewentity[MAX_VIEWENTS]; // list of portal cameras in player PVS
|
||||
int num_viewents; // num of portal cameras that can merge PVS
|
||||
|
||||
qboolean m_bLoopback; // Does this client want to hear his own voice?
|
||||
uint listeners; // which other clients does this guy's voice stream go to?
|
||||
|
||||
// the datagram is written to by sound calls, prints, temp ents, etc.
|
||||
// it can be harmlessly overflowed.
|
||||
sizebuf_t datagram;
|
||||
byte datagram_buf[MAX_DATAGRAM];
|
||||
|
||||
client_frame_t *frames; // updates can be delta'd from here
|
||||
event_state_t events; // delta-updated events cycle
|
||||
|
||||
int challenge; // challenge of this user, randomly generated
|
||||
int userid; // identifying number on server
|
||||
int extensions;
|
||||
char useragent[MAX_INFO_STRING];
|
||||
|
||||
int ignorecmdtime_warns; // how many times client time was faster than server during this session
|
||||
int userid; // identifying number on server
|
||||
qboolean ignorecmdtime_warned; // did we warn our server operator in the log for this batch of commands?
|
||||
|
||||
netchan_t netchan;
|
||||
sizebuf_t datagram; // the datagram is written to by sound calls, prints, temp ents, etc.
|
||||
byte datagram_buf[MAX_DATAGRAM]; // it can be harmlessly overflowed.
|
||||
|
||||
int chokecount; // number of messages rate supressed
|
||||
int delta_sequence; // -1 = no compression.
|
||||
|
||||
double next_messagetime; // time when we should send next world state update
|
||||
double next_checkpingtime; // time to send all players pings to client
|
||||
double next_sendinfotime; // time to send info about all players
|
||||
double cl_updaterate; // client requested updaterate
|
||||
double timebase; // client timebase
|
||||
double connection_started;
|
||||
|
||||
customization_t customdata; // player customization linked list
|
||||
resource_t resourcesonhand;
|
||||
resource_t resourcesneeded; // <mapname.res> from client (server downloading)
|
||||
usercmd_t lastcmd; // for filling in big drops
|
||||
|
||||
int packet_loss;
|
||||
double connecttime;
|
||||
double cmdtime;
|
||||
double ignorecmdtime;
|
||||
float latency;
|
||||
|
||||
int ignored_ents; // if visibility list is full we should know how many entities will be ignored
|
||||
edict_t *edict; // EDICT_NUM(clientnum+1)
|
||||
edict_t *pViewEntity; // svc_setview member
|
||||
edict_t *viewentity[MAX_VIEWENTS]; // list of portal cameras in player PVS
|
||||
int num_viewents; // num of portal cameras that can merge PVS
|
||||
|
||||
int userinfo_change_attempts;
|
||||
double fullupdate_next_calltime;
|
||||
double userinfo_next_changetime;
|
||||
double userinfo_penalty;
|
||||
|
||||
client_frame_t *frames; // updates can be delta'd from here
|
||||
event_state_t events; // delta-updated events cycle
|
||||
int userinfo_change_attempts;
|
||||
} sv_client_t;
|
||||
|
||||
/*
|
||||
@@ -272,6 +279,19 @@ typedef struct sv_client_s
|
||||
a program error, like an overflowed reliable buffer
|
||||
=============================================================================
|
||||
*/
|
||||
// MAX_CHALLENGES is made large to prevent a denial
|
||||
// of service attack that could cycle all of them
|
||||
// out before legitimate users connected
|
||||
#define MAX_CHALLENGES 1024
|
||||
|
||||
typedef struct
|
||||
{
|
||||
netadr_t adr;
|
||||
double time;
|
||||
int challenge;
|
||||
qboolean connected;
|
||||
} challenge_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[32]; // in GoldSrc max name length is 12
|
||||
@@ -365,7 +385,7 @@ typedef struct
|
||||
entity_state_t *baselines; // [GI->max_edicts]
|
||||
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
|
||||
|
||||
uint32_t challenge_salt[16]; // pregenerated random numbers for generating challenged based on IP's MD5 address
|
||||
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
|
||||
|
||||
sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching
|
||||
byte *testpacket_buf; // check for NULL if testpacket is available
|
||||
|
||||
@@ -86,54 +86,38 @@ flood the server with invalid connection IPs. With a
|
||||
challenge, they must give a valid IP address.
|
||||
=================
|
||||
*/
|
||||
static int SV_GetChallenge( netadr_t from, qboolean *error )
|
||||
static void SV_GetChallenge( netadr_t from )
|
||||
{
|
||||
const netadrtype_t type = NET_NetadrType( &from );
|
||||
MD5Context_t ctx;
|
||||
byte digest[16];
|
||||
int i, oldest = 0;
|
||||
double oldestTime;
|
||||
|
||||
*error = false;
|
||||
oldestTime = 0x7fffffff;
|
||||
|
||||
MD5Init( &ctx );
|
||||
|
||||
switch( type )
|
||||
// see if we already have a challenge for this ip
|
||||
for( i = 0; i < MAX_CHALLENGES; i++ )
|
||||
{
|
||||
case NA_IP:
|
||||
MD5Update( &ctx, from.ip, sizeof( from.ip ));
|
||||
break;
|
||||
case NA_IPX:
|
||||
MD5Update( &ctx, from.ipx, sizeof( from.ipx ));
|
||||
break;
|
||||
case NA_IP6:
|
||||
{
|
||||
byte ip6[16];
|
||||
NET_NetadrToIP6Bytes( ip6, &from );
|
||||
MD5Update( &ctx, ip6, sizeof( ip6 ));
|
||||
break;
|
||||
}
|
||||
case NA_LOOPBACK:
|
||||
return 0;
|
||||
default:
|
||||
*error = true;
|
||||
return 0;
|
||||
if( !svs.challenges[i].connected && NET_CompareAdr( from, svs.challenges[i].adr ))
|
||||
break;
|
||||
|
||||
if( svs.challenges[i].time < oldestTime )
|
||||
{
|
||||
oldestTime = svs.challenges[i].time;
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
|
||||
MD5Update( &ctx, (byte *)svs.challenge_salt, sizeof( svs.challenge_salt ));
|
||||
MD5Final( digest, &ctx );
|
||||
|
||||
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
|
||||
}
|
||||
|
||||
static void SV_SendChallenge( netadr_t from )
|
||||
{
|
||||
qboolean error = false;
|
||||
int challenge = SV_GetChallenge( from, &error );
|
||||
|
||||
if( error )
|
||||
return;
|
||||
if( i == MAX_CHALLENGES )
|
||||
{
|
||||
// this is the first time this client has asked for a challenge
|
||||
svs.challenges[oldest].challenge = (COM_RandomLong( 0, 0x7FFF ) << 16) | COM_RandomLong( 0, 0xFFFF );
|
||||
svs.challenges[oldest].adr = from;
|
||||
svs.challenges[oldest].time = host.realtime;
|
||||
svs.challenges[oldest].connected = false;
|
||||
i = oldest;
|
||||
}
|
||||
|
||||
// send it back
|
||||
Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CHALLENGE" %i", challenge );
|
||||
Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, S2C_CHALLENGE" %i", svs.challenges[i].challenge );
|
||||
}
|
||||
|
||||
static int SV_GetFragmentSize( void *pcl, fragsize_t mode )
|
||||
@@ -227,16 +211,35 @@ Make sure connecting client is not spoofing
|
||||
*/
|
||||
static int SV_CheckChallenge( netadr_t from, int challenge )
|
||||
{
|
||||
qboolean error = false;
|
||||
int challenge2 = SV_GetChallenge( from, &error );
|
||||
int i;
|
||||
|
||||
if( error || challenge2 != challenge )
|
||||
// see if the challenge is valid
|
||||
// don't care if it is a local address.
|
||||
if( NET_IsLocalAddress( from ))
|
||||
return 1;
|
||||
|
||||
for( i = 0; i < MAX_CHALLENGES; i++ )
|
||||
{
|
||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||
return false;
|
||||
if( NET_CompareAdr( from, svs.challenges[i].adr ))
|
||||
{
|
||||
if( challenge == svs.challenges[i].challenge )
|
||||
break; // valid challenge
|
||||
#if 0
|
||||
// g-cont. this breaks multiple connections from single machine
|
||||
SV_RejectConnection( from, "bad challenge %i\n", challenge );
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if( i == MAX_CHALLENGES )
|
||||
{
|
||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||
return 0;
|
||||
}
|
||||
svs.challenges[i].connected = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -250,7 +253,7 @@ static int SV_CheckIPRestrictions( netadr_t from )
|
||||
{
|
||||
if( sv_lan.value )
|
||||
{
|
||||
if( !NET_IsReservedAdr( from ))
|
||||
if( !NET_CompareClassBAdr( from, net_local ) && !NET_IsReservedAdr( from ))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -264,36 +267,23 @@ Get slot # and set client_t pointer for player, if possible
|
||||
We don't do this search on a "reconnect, we just reuse the slot
|
||||
================
|
||||
*/
|
||||
static sv_client_t *SV_FindEmptySlot( void )
|
||||
static int SV_FindEmptySlot( netadr_t from, int *pslot, sv_client_t **ppClient )
|
||||
{
|
||||
int i;
|
||||
sv_client_t *cl;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < svs.maxclients; i++ )
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
{
|
||||
if( svs.clients[i].state == cs_free )
|
||||
return &svs.clients[i];
|
||||
if( cl->state == cs_free )
|
||||
{
|
||||
*ppClient = cl;
|
||||
*pslot = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void SV_MaybeNotifyPlayerCountChange( const sv_client_t *cl, const char *address )
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
// if this was the first client on the server, or the last client
|
||||
// the server can hold, send a heartbeat to the master.
|
||||
for( i = 0; i < svs.maxclients; i++ )
|
||||
{
|
||||
if( svs.clients[i].state >= cs_connected )
|
||||
count++;
|
||||
}
|
||||
|
||||
if( count == 1 || count == svs.maxclients )
|
||||
NET_MasterClear();
|
||||
|
||||
Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n",
|
||||
cl->name, cl->userid, (int)( cl - svs.clients ), address );
|
||||
SV_RejectConnection( from, "server is full\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -305,14 +295,15 @@ A connection request that did not come from the master
|
||||
*/
|
||||
static void SV_ConnectClient( netadr_t from )
|
||||
{
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
char protinfo[MAX_INFO_STRING];
|
||||
client_frame_t *frames;
|
||||
sv_client_t *newcl = NULL;
|
||||
int qport, version;
|
||||
int i;
|
||||
int challenge;
|
||||
const char *s;
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
char protinfo[MAX_INFO_STRING];
|
||||
sv_client_t *cl, *newcl = NULL;
|
||||
qboolean reconnect = false;
|
||||
int nClientSlot = 0;
|
||||
int qport, version;
|
||||
int i, count = 0;
|
||||
int challenge;
|
||||
const char *s;
|
||||
int extensions;
|
||||
uint netchan_flags = 0;
|
||||
|
||||
@@ -330,6 +321,39 @@ static void SV_ConnectClient( netadr_t from )
|
||||
return;
|
||||
}
|
||||
|
||||
challenge = Q_atoi( Cmd_Argv( 2 )); // get challenge
|
||||
|
||||
// see if the challenge is valid (local clients don't need to challenge)
|
||||
if( !SV_CheckChallenge( from, challenge ))
|
||||
return;
|
||||
|
||||
s = Cmd_Argv( 3 ); // protocol info
|
||||
|
||||
if( !Info_IsValid( s ))
|
||||
{
|
||||
SV_RejectConnection( from, "invalid protinfo in connect command\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Q_strncpy( protinfo, s, sizeof( protinfo ));
|
||||
|
||||
if( !SV_ProcessUserAgent( from, protinfo ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// extract qport from protocol info
|
||||
qport = Q_atoi( Info_ValueForKey( protinfo, "qport" ));
|
||||
|
||||
s = Info_ValueForKey( protinfo, "uuid" );
|
||||
if( Q_strlen( s ) != 32 )
|
||||
{
|
||||
SV_RejectConnection( from, "invalid authentication certificate length\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ) );
|
||||
|
||||
// LAN servers restrict to class b IP addresses
|
||||
if( !SV_CheckIPRestrictions( from ))
|
||||
{
|
||||
@@ -337,31 +361,9 @@ static void SV_ConnectClient( netadr_t from )
|
||||
return;
|
||||
}
|
||||
|
||||
challenge = Q_atoi( Cmd_Argv( 2 )); // get challenge
|
||||
|
||||
// see if the challenge is valid (local clients don't need to challenge)
|
||||
if( !SV_CheckChallenge( from, challenge ))
|
||||
return;
|
||||
|
||||
s = Cmd_Argv( 3 );
|
||||
if( Q_strlen( s ) > sizeof( protinfo ) || !Info_IsValid( s ))
|
||||
{
|
||||
SV_RejectConnection( from, "invalid protinfo in connect command\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Q_strncpy( protinfo, s, sizeof( protinfo )); // protocol info
|
||||
|
||||
if( !SV_ProcessUserAgent( from, protinfo ))
|
||||
return;
|
||||
|
||||
// extract qport from protocol info
|
||||
qport = Q_atoi( Info_ValueForKey( protinfo, "qport" ));
|
||||
extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ));
|
||||
|
||||
s = Cmd_Argv( 4 ); // user info
|
||||
|
||||
if( Q_strlen( s ) > sizeof( userinfo ) || !Info_IsValid( s ))
|
||||
if( Q_strlen( s ) > MAX_INFO_STRING || !Info_IsValid( s ))
|
||||
{
|
||||
SV_RejectConnection( from, "invalid userinfo in connect command\n" );
|
||||
return;
|
||||
@@ -380,51 +382,51 @@ static void SV_ConnectClient( netadr_t from )
|
||||
}
|
||||
|
||||
// if there is already a slot for this ip, reuse it
|
||||
for( i = 0; i < svs.maxclients; i++ )
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
{
|
||||
sv_client_t *cl = &svs.clients[i];
|
||||
|
||||
if( cl->state == cs_free || cl->state == cs_zombie )
|
||||
continue;
|
||||
|
||||
if( NET_CompareBaseAdr( from, cl->netchan.remote_address ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port ))
|
||||
{
|
||||
reconnect = true;
|
||||
newcl = cl;
|
||||
Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A reconnecting client will re-use the slot found above when checking for reconnection.
|
||||
// the slot will be wiped clean.
|
||||
if( !newcl )
|
||||
if( !reconnect )
|
||||
{
|
||||
// connect the client if there are empty slots.
|
||||
newcl = SV_FindEmptySlot();
|
||||
|
||||
if( !newcl )
|
||||
{
|
||||
SV_RejectConnection( from, "server is full\n" );
|
||||
if( !SV_FindEmptySlot( from, &nClientSlot, &newcl ))
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from ));
|
||||
}
|
||||
|
||||
// find a client slot
|
||||
ASSERT( newcl != NULL );
|
||||
|
||||
// build a new connection
|
||||
// accept the new client
|
||||
|
||||
sv.current_client = newcl;
|
||||
frames = Mem_Realloc( host.mempool, newcl->frames, sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
|
||||
memset( frames, 0, sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
|
||||
SV_ClearResourceLists( newcl );
|
||||
|
||||
memset( newcl, 0, sizeof( *newcl ));
|
||||
|
||||
newcl->edict = EDICT_NUM(( newcl - svs.clients ) + 1 );
|
||||
newcl->frames = frames;
|
||||
newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 );
|
||||
newcl->challenge = challenge; // save challenge for checksumming
|
||||
if( newcl->frames ) Mem_Free( newcl->frames );
|
||||
newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
|
||||
newcl->userid = g_userid++; // create unique userid
|
||||
newcl->state = cs_connected;
|
||||
newcl->extensions = FBitSet( extensions, NET_EXT_SPLITSIZE );
|
||||
newcl->extensions = extensions & (NET_EXT_SPLITSIZE);
|
||||
Q_strncpy( newcl->useragent, protinfo, sizeof( newcl->useragent ));
|
||||
|
||||
// reset viewentities (from previous level)
|
||||
memset( newcl->viewentity, 0, sizeof( newcl->viewentity ));
|
||||
newcl->num_viewents = 0;
|
||||
// HACKHACK: can hear all players by default to avoid issues
|
||||
// with server.dll without voice game manager
|
||||
newcl->listeners = -1;
|
||||
@@ -449,18 +451,44 @@ static void SV_ConnectClient( netadr_t from )
|
||||
newcl->connection_started = host.realtime;
|
||||
newcl->cl_updaterate = 0.05; // 20 fps as default
|
||||
newcl->delta_sequence = -1;
|
||||
newcl->flags = 0;
|
||||
|
||||
// reset any remaining events
|
||||
memset( &newcl->events, 0, sizeof( newcl->events ));
|
||||
|
||||
// parse some info from the info strings (this can override cl_updaterate)
|
||||
Q_strncpy( newcl->userinfo, userinfo, sizeof( newcl->userinfo ));
|
||||
newcl->fullupdate_next_calltime = 0;
|
||||
newcl->userinfo_next_changetime = 0;
|
||||
newcl->userinfo_penalty = 0;
|
||||
newcl->userinfo_change_attempts = 0;
|
||||
|
||||
SV_UserinfoChanged( newcl );
|
||||
|
||||
SV_ClearResourceLists( newcl );
|
||||
#if 0
|
||||
memset( &newcl->resourcesneeded, 0, sizeof( resource_t ));
|
||||
memset( &newcl->resourcesonhand, 0, sizeof( resource_t ));
|
||||
newcl->resourcesneeded.pNext = newcl->resourcesneeded.pPrev = &newcl->resourcesneeded;
|
||||
newcl->resourcesonhand.pNext = newcl->resourcesonhand.pPrev = &newcl->resourcesonhand;
|
||||
#endif
|
||||
newcl->next_messagetime = host.realtime + newcl->cl_updaterate;
|
||||
newcl->next_sendinfotime = 0.0;
|
||||
newcl->ignored_ents = 0;
|
||||
newcl->chokecount = 0;
|
||||
|
||||
// reset stats
|
||||
newcl->next_checkpingtime = -1.0;
|
||||
newcl->packet_loss = 0;
|
||||
|
||||
SV_MaybeNotifyPlayerCountChange( newcl, NET_AdrToString( newcl->netchan.remote_address ));
|
||||
// if this was the first client on the server, or the last client
|
||||
// the server can hold, send a heartbeat to the master.
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
if( cl->state >= cs_connected ) count++;
|
||||
|
||||
Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n", newcl->name, newcl->userid, i, NET_AdrToString( newcl->netchan.remote_address ));
|
||||
|
||||
if( count == 1 || count == svs.maxclients )
|
||||
NET_MasterClear();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -472,21 +500,25 @@ A connection request that came from the game module
|
||||
*/
|
||||
edict_t *GAME_EXPORT SV_FakeConnect( const char *netname )
|
||||
{
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
int i, count = 0;
|
||||
sv_client_t *cl;
|
||||
|
||||
// find a client slot
|
||||
cl = SV_FindEmptySlot();
|
||||
|
||||
if( !cl )
|
||||
return NULL; // server is full
|
||||
|
||||
userinfo[0] = '\0';
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
int i, count = 0;
|
||||
sv_client_t *cl;
|
||||
|
||||
if( !COM_CheckString( netname ))
|
||||
netname = "Bot";
|
||||
|
||||
// find a client slot
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
{
|
||||
if( cl->state == cs_free )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == svs.maxclients )
|
||||
return NULL; // server is full
|
||||
|
||||
userinfo[0] = '\0';
|
||||
|
||||
// setup fake client params
|
||||
Info_SetValueForKey( userinfo, "name", netname, sizeof( userinfo ));
|
||||
Info_SetValueForKey( userinfo, "model", "gordon", sizeof( userinfo ));
|
||||
@@ -496,26 +528,34 @@ edict_t *GAME_EXPORT SV_FakeConnect( const char *netname )
|
||||
// build a new connection
|
||||
// accept the new client
|
||||
sv.current_client = cl;
|
||||
if( cl->frames )
|
||||
Mem_Free( cl->frames ); // fakeclients doesn't have frames
|
||||
SV_ClearResourceLists( cl );
|
||||
|
||||
memset( cl, 0, sizeof( *cl ));
|
||||
if( cl->frames ) Mem_Free( cl->frames ); // fakeclients doesn't have frames
|
||||
memset( cl, 0, sizeof( sv_client_t ));
|
||||
|
||||
cl->state = cs_spawned;
|
||||
cl->edict = EDICT_NUM(( cl - svs.clients ) + 1 );
|
||||
cl->userid = g_userid++; // create unique userid
|
||||
cl->edict = EDICT_NUM( (cl - svs.clients) + 1 );
|
||||
cl->userid = g_userid++; // create unique userid
|
||||
SetBits( cl->flags, FCL_FAKECLIENT );
|
||||
|
||||
// parse some info from the info strings
|
||||
Q_strncpy( cl->userinfo, userinfo, sizeof( cl->userinfo ));
|
||||
|
||||
SV_UserinfoChanged( cl );
|
||||
SetBits( cl->flags, FCL_RESEND_USERINFO );
|
||||
cl->next_sendinfotime = 0.0;
|
||||
|
||||
// if this was the first client on the server, or the last client
|
||||
// the server can hold, send a heartbeat to the master.
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
if( cl->state >= cs_connected ) count++;
|
||||
cl = sv.current_client;
|
||||
|
||||
Log_Printf( "\"%s<%i><%i><>\" connected, address \"local\"\n", cl->name, cl->userid, i );
|
||||
|
||||
SetBits( cl->edict->v.flags, FL_CLIENT|FL_FAKECLIENT ); // mark it as fakeclient
|
||||
cl->connection_started = host.realtime;
|
||||
cl->state = cs_spawned;
|
||||
|
||||
SV_MaybeNotifyPlayerCountChange( cl, "local" );
|
||||
if( count == 1 || count == svs.maxclients )
|
||||
NET_MasterClear();
|
||||
|
||||
return cl->edict;
|
||||
}
|
||||
@@ -745,7 +785,7 @@ const char *SV_GetClientIDString( sv_client_t *cl )
|
||||
if( sv_lan.value )
|
||||
return "ID_LAN";
|
||||
|
||||
Q_snprintf( result, sizeof( result ), "ID_%s", cl->hashedcdkey );
|
||||
Q_snprintf( result, sizeof( result ), "ID_%s", MD5_Print( (byte *)cl->hashedcdkey ));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -814,7 +854,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||
( packetsize > FRAGMENT_MAX_SIZE ))
|
||||
{
|
||||
// skip the test and just get challenge
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -822,7 +862,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||
ofs = packetsize - svs.testpacket_filepos - 1;
|
||||
if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen ))
|
||||
{
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1745,7 +1785,7 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl )
|
||||
if( host.realtime < cl->userinfo_next_changetime + cl->userinfo_penalty * sv_userinfo_penalty_multiplier.value )
|
||||
{
|
||||
// player changes userinfo too quick! ignore!
|
||||
if( host.realtime < cl->userinfo_next_changetime && cl->userinfo_change_attempts > 0 )
|
||||
if( host.realtime < cl->userinfo_next_changetime )
|
||||
{
|
||||
Con_Reportf( "%s: ignore userinfo update for %s: penalty %f, attempts %i\n",
|
||||
__func__, cl->name, cl->userinfo_penalty, cl->userinfo_change_attempts );
|
||||
@@ -1756,15 +1796,15 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl )
|
||||
}
|
||||
|
||||
// they spammed too fast, increase penalty
|
||||
if( cl->userinfo_change_attempts >= (int)sv_userinfo_penalty_attempts.value )
|
||||
if( cl->userinfo_change_attempts > sv_userinfo_penalty_attempts.value )
|
||||
{
|
||||
Con_Reportf( "%s: penalty set %f for %s\n", __func__,
|
||||
cl->userinfo_penalty, cl->name );
|
||||
cl->userinfo_penalty *= sv_userinfo_penalty_multiplier.value;
|
||||
cl->userinfo_change_attempts = 0;
|
||||
|
||||
Con_Reportf( "%s: penalty set %f for %s\n", __func__, cl->userinfo_penalty, cl->name );
|
||||
}
|
||||
|
||||
cl->userinfo_next_changetime = host.realtime + cl->userinfo_penalty * sv_userinfo_penalty_multiplier.value;
|
||||
cl->userinfo_next_changetime = host.realtime + cl->userinfo_penalty;
|
||||
|
||||
return allow;
|
||||
}
|
||||
@@ -1868,10 +1908,13 @@ static void SV_UserinfoChanged( sv_client_t *cl )
|
||||
|
||||
val = Info_ValueForKey( cl->userinfo, "cl_updaterate" );
|
||||
|
||||
if( COM_CheckString( val ))
|
||||
if( COM_CheckString( val ) )
|
||||
{
|
||||
float rate = Q_atoi( val );
|
||||
cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, rate, sv_maxupdaterate.value );
|
||||
if( Q_atoi( val ) != 0 )
|
||||
{
|
||||
cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, Q_atoi( val ), sv_maxupdaterate.value );
|
||||
}
|
||||
else cl->cl_updaterate = 0.0;
|
||||
}
|
||||
|
||||
// call prog code to allow overrides
|
||||
@@ -3136,7 +3179,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||
}
|
||||
else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE ))
|
||||
{
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
}
|
||||
else if( !Q_strcmp( pcmd, C2S_CONNECT ))
|
||||
{
|
||||
@@ -3169,8 +3212,10 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||
if( len > 0 )
|
||||
Netchan_OutOfBand( NS_SERVER, from, len, (byte*)buf );
|
||||
}
|
||||
else if( sv_log_outofband.value )
|
||||
else
|
||||
{
|
||||
Con_DPrintf( S_ERROR "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3301,10 +3346,6 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg )
|
||||
SV_RunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
|
||||
}
|
||||
|
||||
// was player kicked? stop here
|
||||
if( cl->state <= cs_zombie )
|
||||
return;
|
||||
|
||||
cl->lastcmd = cmds[0];
|
||||
|
||||
// adjust latency time by 1/2 last client frame since
|
||||
|
||||
@@ -573,7 +573,7 @@ static void SV_Kick_f( void )
|
||||
sv_client_t *cl;
|
||||
const char *param;
|
||||
|
||||
if( Cmd_Argc() < 2 )
|
||||
if( Cmd_Argc() != 2 )
|
||||
{
|
||||
Con_Printf( S_USAGE "kick <#id|name> [reason]\n" );
|
||||
return;
|
||||
|
||||
@@ -302,7 +302,7 @@ static int SV_FilterToString( char *dest, size_t size, qboolean config, ipfilter
|
||||
|
||||
static qboolean SV_IPFilterIncludesIPFilter( ipfilter_t *a, ipfilter_t *b )
|
||||
{
|
||||
if( NET_NetadrType( &a->adr ) != NET_NetadrType( &b->adr ))
|
||||
if( a->adr.type6 != b->adr.type6 )
|
||||
return false;
|
||||
|
||||
// can't include bigger subnet in small
|
||||
@@ -359,7 +359,7 @@ qboolean SV_CheckIP( netadr_t *adr )
|
||||
if( entry->endTime && host.realtime > entry->endTime )
|
||||
continue; // expired
|
||||
|
||||
switch( NET_NetadrType( &entry->adr ))
|
||||
switch( entry->adr.type6 )
|
||||
{
|
||||
case NA_IP:
|
||||
case NA_IP6:
|
||||
|
||||
@@ -2379,7 +2379,7 @@ void GAME_EXPORT pfnClientCommand( edict_t* pEdict, char* szFmt, ... )
|
||||
if( sv.state != ss_active )
|
||||
return; // early out
|
||||
|
||||
if(( cl = SV_ClientFromEdict( pEdict, false )) == NULL )
|
||||
if(( cl = SV_ClientFromEdict( pEdict, true )) == NULL )
|
||||
{
|
||||
Con_Printf( S_ERROR "stuffcmd: client is not spawned!\n" );
|
||||
return;
|
||||
@@ -4600,7 +4600,7 @@ static void GAME_EXPORT pfnQueryClientCvarValue( const edict_t *player, const ch
|
||||
if( !COM_CheckString( cvarName ))
|
||||
return;
|
||||
|
||||
if(( cl = SV_ClientFromEdict( player, false )) != NULL )
|
||||
if(( cl = SV_ClientFromEdict( player, true )) != NULL )
|
||||
{
|
||||
MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue );
|
||||
MSG_WriteString( &cl->netchan.message, cvarName );
|
||||
@@ -4627,7 +4627,7 @@ static void GAME_EXPORT pfnQueryClientCvarValue2( const edict_t *player, const c
|
||||
if( !COM_CheckString( cvarName ))
|
||||
return;
|
||||
|
||||
if(( cl = SV_ClientFromEdict( player, false )) != NULL )
|
||||
if(( cl = SV_ClientFromEdict( player, true )) != NULL )
|
||||
{
|
||||
MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue2 );
|
||||
MSG_WriteLong( &cl->netchan.message, requestID );
|
||||
|
||||
@@ -1027,9 +1027,6 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
|
||||
svs.timestart = Sys_DoubleTime();
|
||||
svs.spawncount++; // any partially connected client will be restarted
|
||||
|
||||
for( i = 0; i < ARRAYSIZE( svs.challenge_salt ); i++ )
|
||||
svs.challenge_salt[i] = COM_RandomLong( 0, 0x7FFFFFFE );
|
||||
|
||||
cycle = Cvar_VariableString( "mapchangecfgfile" );
|
||||
|
||||
if( COM_CheckString( cycle ))
|
||||
|
||||
@@ -106,7 +106,7 @@ CVAR_DEFINE_AUTO( sv_skyvec_y, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight dir
|
||||
CVAR_DEFINE_AUTO( sv_skyvec_z, "0", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "skylight direction by z-axis" );
|
||||
CVAR_DEFINE_AUTO( sv_wateralpha, "1", FCVAR_MOVEVARS|FCVAR_UNLOGGED, "world surfaces water transparency factor. 1.0 - solid, 0.0 - fully transparent" );
|
||||
CVAR_DEFINE_AUTO( sv_background_freeze, "1", FCVAR_ARCHIVE, "freeze player movement on background maps (e.g. to prevent falling)" );
|
||||
static CVAR_DEFINE_AUTO( showtriggers, "0", FCVAR_LATCH|FCVAR_TEMPORARY, "debug cvar shows triggers" );
|
||||
static CVAR_DEFINE_AUTO( showtriggers, "0", FCVAR_LATCH, "debug cvar shows triggers" );
|
||||
static CVAR_DEFINE_AUTO( sv_airmove, "1", FCVAR_SERVER, "obsolete, compatibility issues" );
|
||||
static CVAR_DEFINE_AUTO( sv_version, "", FCVAR_READ_ONLY, "engine version string" );
|
||||
CVAR_DEFINE_AUTO( hostname, "", FCVAR_PRINTABLEONLY, "name of current host" );
|
||||
@@ -189,15 +189,24 @@ void SV_UpdateMovevars( qboolean initialize )
|
||||
if( !initialize && !host.movevars_changed )
|
||||
return;
|
||||
|
||||
// NOTE: Natural Selection mod on ns_machina map that uses model as sky
|
||||
// it sets the value to 4000000 that even exceeds the coord limit, but
|
||||
// it's fine until the value fits in "zmax" delta field
|
||||
// However, some stupid mappers set an insane value like 999999999 which
|
||||
// overflows delta. In this case, just clamp it to something bigger
|
||||
if( sv_zmax.value < 256.0f )
|
||||
Cvar_DirectSet( &sv_zmax, "256" );
|
||||
else if( sv_zmax.value > 16777216.0f ) // 2^24
|
||||
Cvar_DirectSet( &sv_zmax, "16777216" );
|
||||
// NOTE: this breaks Natural Selection mod on ns_machina map that uses model as sky
|
||||
// it sets the value to 4000000 that even exceeds the coord limit
|
||||
#if 0
|
||||
// check range
|
||||
if( sv_zmax.value < 256.0f ) Cvar_SetValue( "sv_zmax", 256.0f );
|
||||
|
||||
// clamp it right
|
||||
if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD ))
|
||||
{
|
||||
if( sv_zmax.value > 131070.0f )
|
||||
Cvar_SetValue( "sv_zmax", 131070.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( sv_zmax.value > 32767.0f )
|
||||
Cvar_SetValue( "sv_zmax", 32767.0f );
|
||||
}
|
||||
#endif
|
||||
|
||||
svgame.movevars.gravity = sv_gravity.value;
|
||||
svgame.movevars.stopspeed = sv_stopspeed.value;
|
||||
@@ -763,31 +772,6 @@ qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent )
|
||||
{
|
||||
const char *input_devices_str = Info_ValueForKey( useragent, "d" );
|
||||
const char *id = Info_ValueForKey( useragent, "uuid" );
|
||||
size_t len, i;
|
||||
|
||||
len = Q_strlen( id );
|
||||
if( len != 32 )
|
||||
{
|
||||
SV_RejectConnection( from, "invalid authentication certificate\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
for( i = 0; i < len; i++ )
|
||||
{
|
||||
char c = id[i];
|
||||
|
||||
if( !isdigit( id[i] ) && !( c >= 'a' && c <= 'f' ))
|
||||
{
|
||||
SV_RejectConnection( from, "invalid authentication certificate\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( SV_CheckID( id ))
|
||||
{
|
||||
SV_RejectConnection( from, "You are banned!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !sv_allow_noinputdevices.value && ( !input_devices_str || !input_devices_str[0] ) )
|
||||
{
|
||||
@@ -821,6 +805,17 @@ qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent )
|
||||
}
|
||||
}
|
||||
|
||||
if( id )
|
||||
{
|
||||
qboolean banned = SV_CheckID( id );
|
||||
|
||||
if( banned )
|
||||
{
|
||||
SV_RejectConnection( from, "You are banned!\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -892,10 +892,6 @@ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed )
|
||||
vec3_t oldvel;
|
||||
usercmd_t cmd;
|
||||
|
||||
// if the player got kicked, do not process commands
|
||||
if( cl->state <= cs_zombie )
|
||||
return;
|
||||
|
||||
clent = cl->edict;
|
||||
cmd = *ucmd;
|
||||
|
||||
@@ -952,6 +948,12 @@ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed )
|
||||
clent->v.light_level = ucmd->lightlevel;
|
||||
if( ucmd->impulse ) clent->v.impulse = ucmd->impulse;
|
||||
|
||||
if( ucmd->impulse == 204 )
|
||||
{
|
||||
// force client.dll update
|
||||
SV_RefreshUserinfo();
|
||||
}
|
||||
|
||||
svgame.globals->time = cl->timebase;
|
||||
svgame.dllFuncs.pfnPlayerPreThink( clent );
|
||||
SV_PlayerRunThink( clent, frametime, cl->timebase );
|
||||
|
||||
@@ -601,7 +601,7 @@ static void SV_FindTouchedLeafs( edict_t *ent, model_t *mod, mnode_t *node, int
|
||||
// add an efrag if the node is a leaf
|
||||
if( node->contents < 0 )
|
||||
{
|
||||
if( ent->num_leafs >= MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )))
|
||||
if( ent->num_leafs > MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )))
|
||||
{
|
||||
// continue counting leafs,
|
||||
// so we know how many it's overrun
|
||||
|
||||
387
engine/wscript
387
engine/wscript
@@ -5,15 +5,22 @@
|
||||
from waflib import Logs
|
||||
from waflib.extras import pthread
|
||||
import os
|
||||
from copy import copy
|
||||
|
||||
top = '.'
|
||||
|
||||
EXECINFO_TEST = '''#include <execinfo.h>
|
||||
FFMPEG_CHECK_FRAGMENT='''
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/avutil.h>
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
backtrace(0, 0);
|
||||
backtrace_symbols(0, 0);
|
||||
swresample_version();
|
||||
avformat_version();
|
||||
avcodec_version();
|
||||
swscale_version();
|
||||
avutil_version();
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
@@ -45,87 +52,117 @@ def options(opt):
|
||||
grp.add_option('--enable-ffmpeg', action = 'store_true', dest = 'FFMPEG', default = False,
|
||||
help = '') # hidden option, does nothing
|
||||
|
||||
grp.add_option('--enable-ffmpeg', action = 'store_true', dest = 'FFMPEG', default = False,
|
||||
help = 'enable ffmpeg based movie playback')
|
||||
|
||||
opt.load('sdl2')
|
||||
|
||||
def find_sdl(conf):
|
||||
err_msg = '%s not available! If you want to build dedicated server, specify --dedicated'
|
||||
if conf.env.DEST_OS in ['nswitch', 'psvita']:
|
||||
err_msg = '%s not available!'
|
||||
|
||||
conf.load('sdl2')
|
||||
if conf.options.SDL3:
|
||||
if not conf.env.HAVE_SDL3:
|
||||
conf.fatal(err_msg % 'SDL3')
|
||||
conf.env.XASH_SDL = 3
|
||||
else:
|
||||
if not conf.env.HAVE_SDL2:
|
||||
conf.fatal(err_msg % 'SDL2')
|
||||
conf.env.XASH_SDL = 2
|
||||
|
||||
def configure(conf):
|
||||
# Common dependencies, will be linked to both dedicated server and client
|
||||
if conf.env.DEST_OS == 'linux':
|
||||
conf.check_cc(lib='rt')
|
||||
if conf.env.MAGX:
|
||||
conf.define('XASH_CRASHHANDLER', 0)
|
||||
elif conf.env.DEST_OS == 'haiku':
|
||||
conf.env.LIB_HAIKU = ['network']
|
||||
conf.env.LIBPATH_HAIKU = ['/boot/system/lib']
|
||||
elif conf.env.DEST_OS == 'wasi':
|
||||
conf.options.NO_ASYNC_RESOLVE = True
|
||||
conf.env.CFLAGS += ['-mllvm', '-wasm-enable-sjlj']
|
||||
elif conf.env.DEST_OS == 'sunos':
|
||||
conf.check_cc(lib='socket')
|
||||
# check for dedicated server build
|
||||
if conf.options.DEDICATED:
|
||||
conf.define('XASH_DEDICATED', 1)
|
||||
elif conf.env.DEST_OS == 'dos':
|
||||
conf.options.STATIC = True
|
||||
conf.options.NO_ASYNC_RESOLVE = True
|
||||
if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ):
|
||||
conf.define('XASH_NO_NETWORK',1)
|
||||
elif conf.env.DEST_OS == 'nswitch':
|
||||
# re-enable undefined reference errors
|
||||
conf.env.CXXFLAGS += ['-Wl,--no-undefined']
|
||||
conf.env.CFLAGS += ['-Wl,--no-undefined']
|
||||
# allow the SDL2 sanity check to complete properly by linking in lm normally
|
||||
conf.env.LDFLAGS += ['-lm']
|
||||
conf.load('sdl2')
|
||||
if not conf.env.HAVE_SDL2:
|
||||
conf.fatal('SDL2 not availiable! Install switch-sdl2!')
|
||||
conf.define('XASH_SDL', 2)
|
||||
# then remove them to avoid them getting linked to shared objects
|
||||
conf.env.LIB_SDL2.remove('stdc++') # remove libstdc++ linking from SDL2, we link it later
|
||||
conf.env.LDFLAGS.remove('-lm')
|
||||
elif conf.env.DEST_OS == 'psvita':
|
||||
# allow the SDL2 sanity check to complete properly by linking in some deps missing from the pkg-config file
|
||||
extra_libs = ['-lstdc++', '-lm', '-lSceShaccCgExt', '-lSceShaccCg_stub', '-ltaihen_stub', '-lSceKernelDmacMgr_stub']
|
||||
conf.env.LDFLAGS += extra_libs
|
||||
conf.load('sdl2')
|
||||
if not conf.env.HAVE_SDL2:
|
||||
conf.fatal('SDL2 not availiable! Install switch-sdl2!')
|
||||
conf.define('XASH_SDL', 2)
|
||||
# then remove them to avoid them getting linked to shared objects
|
||||
for lib in extra_libs:
|
||||
conf.env.LDFLAGS.remove(lib)
|
||||
elif conf.options.FBDEV_SW:
|
||||
# unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support
|
||||
# conf.define('XASH_FBDEV', 1)
|
||||
conf.check_cc( lib = 'asound' )
|
||||
elif conf.options.SDL12:
|
||||
conf.define('XASH_SDL', 12)
|
||||
conf.check_cfg(package='sdl', args='--cflags --libs', uselib_store='SDL2' )
|
||||
conf.env.HAVE_SDL2 = True
|
||||
else:
|
||||
conf.load('sdl2')
|
||||
if conf.options.SDL3:
|
||||
if not conf.env.HAVE_SDL3:
|
||||
conf.fatal('SDL3 not available! If you want to build dedicated server, specify --dedicated')
|
||||
conf.define('XASH_SDL', 3)
|
||||
else:
|
||||
if not conf.env.HAVE_SDL2:
|
||||
conf.fatal('SDL2 not available! If you want to build dedicated server, specify --dedicated')
|
||||
conf.define('XASH_SDL', 2)
|
||||
|
||||
if conf.env.DEST_OS == 'haiku':
|
||||
conf.env.LIB_HAIKU = ['network']
|
||||
conf.env.LIBPATH_HAIKU = ['/boot/system/lib']
|
||||
|
||||
if conf.env.DEST_OS == 'wasi':
|
||||
conf.options.NO_ASYNC_RESOLVE = True
|
||||
conf.env.append_unique('CFLAGS', '-mllvm')
|
||||
conf.env.append_unique('CFLAGS', '-wasm-enable-sjlj')
|
||||
|
||||
if conf.env.DEST_OS == 'sunos':
|
||||
conf.check_cc(lib='socket')
|
||||
|
||||
if conf.options.STATIC:
|
||||
conf.env.STATIC = True
|
||||
conf.define('XASH_NO_LIBDL',1)
|
||||
|
||||
if not conf.env.DEST_OS in ['win32', 'android'] and not conf.options.NO_ASYNC_RESOLVE:
|
||||
conf.check_pthreads(mode='c')
|
||||
|
||||
if conf.env.DEST_OS == 'linux':
|
||||
conf.check_cc(lib='rt')
|
||||
|
||||
if hasattr(conf.options, 'DLLEMU'):
|
||||
conf.define_cond('XASH_DLL_LOADER', conf.options.DLLEMU)
|
||||
|
||||
if conf.env.MAGX:
|
||||
conf.define('XASH_CRASHHANDLER', 0)
|
||||
|
||||
conf.env.ENGINE_TESTS = conf.options.ENGINE_TESTS
|
||||
|
||||
if conf.options.ENGINE_FUZZ:
|
||||
conf.env.append_unique('CFLAGS', '-fsanitize=fuzzer-no-link')
|
||||
conf.env.append_unique('LINKFLAGS', '-fsanitize=fuzzer')
|
||||
|
||||
# Client-only dependencies
|
||||
if conf.env.CLIENT:
|
||||
if conf.env.DEST_OS in ['nswitch', 'psvita']:
|
||||
extra_libs = ['-lm']
|
||||
if conf.env.DEST_OS == 'psvita':
|
||||
extra_libs = ['-lstdc++', '-lm', '-lSceShaccCgExt', '-lSceShaccCg_stub', '-ltaihen_stub', '-lSceKernelDmacMgr_stub']
|
||||
|
||||
conf.env.append_unique('CXXFLAGS', ['-Wl,--no-undefined'])
|
||||
conf.env.append_unique('CFLAGS', ['-Wl,--no-undefined'])
|
||||
|
||||
conf.env.LDFLAGS += extra_libs
|
||||
find_sdl(conf)
|
||||
for lib in extra_libs: conf.env.LDFLAGS.remove(lib)
|
||||
|
||||
if conf.env.DEST_OS == 'nswitch': # remove libstdc++ linking from SDL2, we link it later
|
||||
conf.env.LIB_SDL2.remove('stdc++')
|
||||
elif conf.env.DEST_OS == 'linux' and conf.options.FBDEV_SW:
|
||||
conf.check_cc(lib='asound')
|
||||
elif conf.options.SDL12: # TODO: move to sdl2.py
|
||||
conf.check_cfg(package='sdl', args='--cflags --libs', uselib_store='SDL2')
|
||||
conf.env.XASH_SDL = 12
|
||||
conf.env.HAVE_SDL2 = True
|
||||
else:
|
||||
find_sdl(conf)
|
||||
|
||||
|
||||
if conf.options.STATIC:
|
||||
conf.env.STATIC = True
|
||||
conf.define('XASH_NO_LIBDL', 1)
|
||||
|
||||
if not conf.env.DEST_OS in ['win32', 'android'] and not conf.options.NO_ASYNC_RESOLVE:
|
||||
conf.check_pthreads(mode='c')
|
||||
|
||||
if hasattr(conf.options, 'DLLEMU'):
|
||||
conf.define_cond('XASH_DLL_LOADER', conf.options.DLLEMU)
|
||||
|
||||
conf.check_cc(fragment=EXECINFO_TEST, msg='Checking for glibc backtrace()', mandatory=False, define_name='HAVE_EXECINFO')
|
||||
|
||||
conf.define('ENGINE_DLL', 1)
|
||||
conf.define_cond('XASH_ENGINE_TESTS', conf.options.ENGINE_TESTS)
|
||||
|
||||
if conf.options.FFMPEG:
|
||||
# add ffmpeg libraries into single uselib package
|
||||
conf.check_cfg(package='libswresample', uselib_store='SWRESAMPLE', args='--cflags --libs')
|
||||
conf.check_cfg(package='libavformat', uselib_store='AVFORMAT', args='--cflags --libs')
|
||||
conf.check_cfg(package='libavcodec', uselib_store='AVCODEC', args='--cflags --libs')
|
||||
conf.check_cfg(package='libswscale', uselib_store='SWSCALE', args='--cflags --libs')
|
||||
conf.check_cfg(package='libavutil', uselib_store='AVUTIL', args='--cflags --libs')
|
||||
|
||||
# validate ffmpeg libs installation
|
||||
conf.check_cc(fragment=FFMPEG_CHECK_FRAGMENT, use='SWRESAMPLE AVCODEC AVFORMAT AVUTIL SWSCALE', msg='Checking for ffmpeg sanity')
|
||||
|
||||
conf.env.FFMPEG = True
|
||||
conf.define('HAVE_FFMPEG', True)
|
||||
|
||||
conf.define_cond('XASH_ENGINE_TESTS', conf.env.ENGINE_TESTS)
|
||||
conf.define_cond('XASH_STATIC_LIBS', conf.env.STATIC_LINKING)
|
||||
conf.define_cond('XASH_CUSTOM_SWAP', conf.options.CUSTOM_SWAP)
|
||||
conf.define_cond('XASH_ENABLE_MAIN', conf.env.DISABLE_LAUNCHER)
|
||||
conf.define_cond('XASH_NO_ASYNC_NS_RESOLVE', conf.options.NO_ASYNC_RESOLVE)
|
||||
conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1
|
||||
|
||||
@@ -136,103 +173,147 @@ def build(bld):
|
||||
# public includes for renderers and utils use
|
||||
bld(name = 'engine_includes', export_includes = '. common common/imagelib', use = 'filesystem_includes')
|
||||
|
||||
libs = ['engine_includes', 'public', 'dllemu', 'werror', 'backtrace']
|
||||
includes = ['server', 'client', 'client/vgui', 'common/soundlib']
|
||||
is_cxx_link = False
|
||||
libs = [ 'engine_includes', 'public', 'dllemu', 'werror' ]
|
||||
|
||||
# basic build: dedicated only
|
||||
source = bld.path.ant_glob(['common/*.c', 'common/imagelib/*.c', 'common/soundlib/*.c', 'server/*.c'])
|
||||
source = bld.path.ant_glob([
|
||||
'common/*.c',
|
||||
'common/imagelib/*.c',
|
||||
'common/soundlib/*.c',
|
||||
'server/*.c'])
|
||||
|
||||
# include platform-specific sources, this shall not fail if directory doesn't exist
|
||||
source += bld.path.ant_glob('platform/%s/*.c' % bld.env.DEST_OS)
|
||||
|
||||
# Android _is_ Linux based, after all
|
||||
if bld.env.DEST_OS == 'android':
|
||||
source += bld.path.ant_glob('platform/linux/*.c')
|
||||
|
||||
# include common POSIX conformant code
|
||||
if bld.env.DEST_OS not in ['win32', 'dos']:
|
||||
source += bld.path.ant_glob('platform/posix/*.c')
|
||||
|
||||
# include sources for optional features
|
||||
if bld.get_define('XASH_CUSTOM_SWAP'):
|
||||
source += ['platform/misc/kmalloc.c', 'platform/misc/sbrk.c']
|
||||
|
||||
if bld.get_define('XASH_STATIC_LIBS'):
|
||||
source += ['platform/misc/lib_static.c']
|
||||
if bld.env.ENGINE_TESTS:
|
||||
source += bld.path.ant_glob(['tests/*.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'win32':
|
||||
libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32']
|
||||
elif bld.env.DEST_OS == 'nswitch':
|
||||
libs += ['SOLDER']
|
||||
libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ]
|
||||
source += bld.path.ant_glob(['platform/win32/*.c'])
|
||||
elif bld.env.DEST_OS not in ['dos', 'nswitch', 'psvita']: #posix
|
||||
libs += [ 'M', 'RT', 'PTHREAD', 'ASOUND']
|
||||
if not bld.env.STATIC:
|
||||
libs += ['DL']
|
||||
source += bld.path.ant_glob(['platform/posix/*.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'linux':
|
||||
source += bld.path.ant_glob(['platform/linux/*.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'irix':
|
||||
source += bld.path.ant_glob(['platform/irix/*.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'dos':
|
||||
source += bld.path.ant_glob(['platform/dos/*.c'])
|
||||
source += bld.path.ant_glob(['platform/stub/s_stub.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'haiku':
|
||||
libs.append('HAIKU')
|
||||
|
||||
if bld.get_define('XASH_CUSTOM_SWAP'):
|
||||
source += bld.path.ant_glob(['platform/misc/kmalloc.c', 'platform/misc/sbrk.c'])
|
||||
|
||||
if bld.env.STATIC_LINKING:
|
||||
source += bld.path.ant_glob(['platform/misc/lib_static.c'])
|
||||
is_cxx_link = True
|
||||
|
||||
if bld.env.HAVE_SDL2 or bld.env.HAVE_SDL3:
|
||||
libs.append('SDL3' if bld.env.HAVE_SDL3 else 'SDL2')
|
||||
source += bld.path.ant_glob(['platform/sdl/*.c'])
|
||||
|
||||
if bld.get_define('HAVE_FFMPEG'):
|
||||
libs.append('SWRESAMPLE')
|
||||
libs.append('AVCODEC')
|
||||
libs.append('AVFORMAT')
|
||||
libs.append('AVUTIL')
|
||||
libs.append('SWSCALE')
|
||||
|
||||
if bld.env.MAGX:
|
||||
libs.append('MAGX')
|
||||
is_cxx_link = True
|
||||
|
||||
if bld.env.DEST_OS == 'android':
|
||||
libs += ['LOG']
|
||||
source += bld.path.ant_glob(['platform/android/*.cpp', 'platform/android/*.c', 'platform/linux/*.c'])
|
||||
|
||||
if bld.env.DEST_OS == 'nswitch':
|
||||
libs += [ 'SOLDER' ]
|
||||
source += bld.path.ant_glob(['platform/posix/*.c'])
|
||||
source += bld.path.ant_glob(['platform/nswitch/*.c'])
|
||||
# HACK: link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting
|
||||
# we can't do this right away because std::filesystem will complain about not having pathconf(),
|
||||
# which we have defined in sys_nswitch.c
|
||||
bld.env.LDFLAGS += ['-v', '-Wl,--whole-archive', '-lstdc++', '-Wl,--no-whole-archive', '-lm']
|
||||
elif bld.env.DEST_OS == 'psvita':
|
||||
libs += ['VRTLD']
|
||||
|
||||
if bld.env.DEST_OS == 'psvita':
|
||||
libs += [ 'VRTLD' ]
|
||||
source += bld.path.ant_glob(['platform/posix/*.c'])
|
||||
source += bld.path.ant_glob(['platform/psvita/*.c'])
|
||||
# HACK: link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting
|
||||
# also link in all the funky dependencies that aren't in SDL2's LDFLAGS
|
||||
bld.env.LDFLAGS += ['-Wl,--whole-archive', '-lstdc++', '-lpthread', '-Wl,--no-whole-archive',
|
||||
'-lm', '-lSceShaccCgExt', '-lkubridge_stub', '-ltaihen_stub', '-lSceShaccCg_stub', '-lSceKernelModulemgr_stub', '-lSceSblSsMgr_stub', '-lSceVshBridge_stub', '-lSceKernelDmacMgr_stub',
|
||||
bld.env.LDFLAGS += [
|
||||
'-Wl,--whole-archive',
|
||||
'-lstdc++',
|
||||
'-lpthread',
|
||||
'-Wl,--no-whole-archive',
|
||||
'-lm',
|
||||
'-lSceShaccCgExt',
|
||||
'-lkubridge_stub',
|
||||
'-ltaihen_stub',
|
||||
'-lSceShaccCg_stub',
|
||||
'-lSceKernelModulemgr_stub',
|
||||
'-lSceSblSsMgr_stub',
|
||||
'-lSceVshBridge_stub',
|
||||
'-lSceKernelDmacMgr_stub'
|
||||
]
|
||||
else: # POSIX
|
||||
libs += ['M', 'RT', 'PTHREAD', 'ASOUND', 'HAIKU', 'MAGX', 'LOG', 'SOCKET']
|
||||
if not bld.env.STATIC:
|
||||
libs += ['DL']
|
||||
|
||||
if bld.env.SERVER:
|
||||
# TODO: avoid possible name collision when client is built without launcher
|
||||
# but dedicated server is enabled. They're both called 'xash' in this case
|
||||
bld.program(
|
||||
source = copy(source),
|
||||
target = 'xash',
|
||||
use = copy(libs),
|
||||
includes = includes,
|
||||
defines = 'XASH_ENABLE_MAIN=1 XASH_DEDICATED=1',
|
||||
rpath = bld.env.DEFAULT_RPATH,
|
||||
install_path = bld.env.BINDIR,
|
||||
)
|
||||
if bld.env.DEST_OS == 'sunos':
|
||||
libs.append('SOCKET')
|
||||
|
||||
if bld.env.CLIENT:
|
||||
defines = []
|
||||
if bld.env.XASH_SDL:
|
||||
libs += ['SDL3' if bld.env.HAVE_SDL3 else 'SDL2']
|
||||
source += bld.path.ant_glob(['platform/sdl/*.c'])
|
||||
defines += ['XASH_SDL=%d' % bld.env.XASH_SDL]
|
||||
# add client files
|
||||
if not bld.env.DEDICATED:
|
||||
source += bld.path.ant_glob([
|
||||
'client/*.c',
|
||||
'client/vgui/*.c',
|
||||
'client/avi/*.c',
|
||||
'client/soundlib/*.c',
|
||||
'client/soundlib/libmpg/*.c',
|
||||
])
|
||||
is_cxx_link = True
|
||||
libs += ['bzip2', 'MultiEmulator', 'opus', 'opusfile', 'vorbis', 'vorbisfile']
|
||||
|
||||
# add client files
|
||||
if not bld.env.DEDICATED:
|
||||
source += bld.path.ant_glob('client/**/*.c')
|
||||
libs += ['bzip2', 'MultiEmulator', 'opus', 'opusfile', 'vorbis', 'vorbisfile']
|
||||
rpath = bld.env.DEFAULT_RPATH
|
||||
|
||||
# Android port is linked differently
|
||||
if bld.env.DEST_OS == 'android':
|
||||
f = bld.shlib
|
||||
install_path = bld.env.LIBDIR
|
||||
defines += ['XASH_ENABLE_MAIN=1', 'XASH_SDLMAIN=1']
|
||||
elif not bld.env.LAUNCHER:
|
||||
f = bld.program
|
||||
install_path = bld.env.BINDIR
|
||||
defines += ['XASH_ENABLE_MAIN=1']
|
||||
# Switch and PSVita have custom parameters
|
||||
if bld.env.DEST_OS in ['nswitch', 'psvita']:
|
||||
rpath = None
|
||||
install_path = None
|
||||
features = 'cxx c cxxprogram cprogram'
|
||||
# Always build as shared library on Android
|
||||
elif bld.env.DISABLE_LAUNCHER and bld.env.DEST_OS != 'android':
|
||||
install_path = bld.env.BINDIR
|
||||
if bld.env.STATIC:
|
||||
features = 'cxx c cxxprogram_static cprogram_static'
|
||||
elif is_cxx_link:
|
||||
features = 'cxx c cxxprogram cprogram'
|
||||
else:
|
||||
f = bld.shlib
|
||||
install_path = bld.env.LIBDIR
|
||||
features = 'c cprogram'
|
||||
else:
|
||||
install_path = bld.env.LIBDIR
|
||||
if is_cxx_link:
|
||||
features = 'cxx c cxxshlib cshlib'
|
||||
else:
|
||||
features = 'c cshlib'
|
||||
|
||||
if bld.env.DEST_OS in ['nswitch', 'psvita']:
|
||||
install_path = None
|
||||
|
||||
f(source = source,
|
||||
target = 'xash',
|
||||
includes = includes,
|
||||
features = 'cxx c',
|
||||
use = libs,
|
||||
defines = defines,
|
||||
install_path = install_path,
|
||||
special_install_path = bld.env.BINDIR,
|
||||
nacp = 'platform/nswitch/xash3d-fwgs.nacp',
|
||||
icon = 'platform/nswitch/icon.jpg',
|
||||
sce_sys = 'platform/psvita/sce_sys',
|
||||
title_id = 'XASH10000',
|
||||
app_name = 'xash3d-fwgs',
|
||||
rpath = bld.env.DEFAULT_RPATH,
|
||||
)
|
||||
bld(source = source,
|
||||
target = 'xash',
|
||||
features = features,
|
||||
includes = 'server client client/vgui common/soundlib',
|
||||
use = libs,
|
||||
install_path = install_path,
|
||||
nro_install_path = bld.env.BINDIR,
|
||||
nacp = 'platform/nswitch/xash3d-fwgs.nacp',
|
||||
icon = 'platform/nswitch/icon.jpg',
|
||||
sce_sys = 'platform/psvita/sce_sys',
|
||||
title_id = 'XASH10000',
|
||||
app_name = 'xash3d-fwgs',
|
||||
rpath = rpath,
|
||||
)
|
||||
|
||||
@@ -59,9 +59,8 @@ char fs_rootdir[MAX_SYSPATH];
|
||||
searchpath_t *fs_writepath;
|
||||
|
||||
static searchpath_t *fs_searchpaths = NULL; // chain
|
||||
static char fs_basedir[MAX_SYSPATH]; // base game directory
|
||||
static char fs_gamedir[MAX_SYSPATH]; // game current directory
|
||||
static string fs_language;
|
||||
static char fs_basedir[MAX_SYSPATH]; // base game directory
|
||||
static char fs_gamedir[MAX_SYSPATH]; // game current directory
|
||||
|
||||
// add archives in specific order PAK -> PK3 -> WAD
|
||||
// so raw WADs takes precedence over WADs included into PAKs and PK3s
|
||||
@@ -1250,39 +1249,14 @@ void FS_AddGameHierarchy( const char *dir, uint flags )
|
||||
if( isGameDir )
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/" DEFAULT_DOWNLOADED_DIRECTORY, dir );
|
||||
FS_AddGameDirectory( buf, FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
FS_AddGameDirectory( buf, FS_NOWRITE_PATH | FS_CUSTOM_PATH );
|
||||
}
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/", dir );
|
||||
FS_AddGameDirectory( buf, flags );
|
||||
|
||||
if( FBitSet( flags, FS_MOUNT_HD ))
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s_hd/", dir );
|
||||
FS_AddGameDirectory( buf, flags|FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FS_MOUNT_ADDON ))
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s_addon/", dir );
|
||||
FS_AddGameDirectory( buf, flags|FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FS_MOUNT_LV ))
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s_lv/", dir );
|
||||
FS_AddGameDirectory( buf, flags|FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FS_MOUNT_L10N ) && COM_CheckStringEmpty( fs_language ) && Q_isalpha( fs_language ))
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s_%s/", dir, fs_language );
|
||||
FS_AddGameDirectory( buf, flags|FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
}
|
||||
|
||||
if( isGameDir )
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/" DEFAULT_CUSTOM_DIRECTORY, dir );
|
||||
FS_AddGameDirectory( buf, FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
FS_AddGameDirectory( buf, FS_NOWRITE_PATH | FS_CUSTOM_PATH );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1291,35 +1265,29 @@ void FS_AddGameHierarchy( const char *dir, uint flags )
|
||||
FS_Rescan
|
||||
================
|
||||
*/
|
||||
void FS_Rescan( uint32_t flags, const char *language )
|
||||
void FS_Rescan( void )
|
||||
{
|
||||
const char *str;
|
||||
const int extrasFlags = FS_NOWRITE_PATH | FS_CUSTOM_PATH;
|
||||
Con_Reportf( "%s( %s )\n", __func__, GI->title );
|
||||
|
||||
FS_ClearSearchPath();
|
||||
|
||||
flags &= FS_MOUNT_HD|FS_MOUNT_LV|FS_MOUNT_ADDON|FS_MOUNT_L10N;
|
||||
|
||||
if( FBitSet( flags, FS_MOUNT_L10N ))
|
||||
Q_strncpy( fs_language, language, sizeof( fs_language ));
|
||||
else
|
||||
fs_language[0] = 0;
|
||||
|
||||
str = getenv( "XASH3D_EXTRAS_PAK1" );
|
||||
if( COM_CheckString( str ))
|
||||
FS_MountArchive_Fullpath( str, FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
FS_MountArchive_Fullpath( str, extrasFlags );
|
||||
|
||||
str = getenv( "XASH3D_EXTRAS_PAK2" );
|
||||
if( COM_CheckString( str ))
|
||||
FS_MountArchive_Fullpath( str, FS_NOWRITE_PATH|FS_CUSTOM_PATH );
|
||||
FS_MountArchive_Fullpath( str, extrasFlags );
|
||||
|
||||
if( Q_stricmp( GI->basedir, GI->gamefolder ))
|
||||
FS_AddGameHierarchy( GI->basedir, flags );
|
||||
FS_AddGameHierarchy( GI->basedir, 0 );
|
||||
if( Q_stricmp( GI->basedir, GI->falldir ) && Q_stricmp( GI->gamefolder, GI->falldir ))
|
||||
FS_AddGameHierarchy( GI->falldir, flags );
|
||||
FS_AddGameHierarchy( GI->falldir, 0 );
|
||||
|
||||
GI->added = true;
|
||||
FS_AddGameHierarchy( GI->gamefolder, FS_GAMEDIR_PATH | flags );
|
||||
FS_AddGameHierarchy( GI->gamefolder, FS_GAMEDIR_PATH );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1364,7 +1332,7 @@ void FS_LoadGameInfo( const char *rootfolder )
|
||||
FS_CreatePath( buf );
|
||||
}
|
||||
|
||||
FS_Rescan( 0, NULL ); // create new filesystem
|
||||
FS_Rescan(); // create new filesystem
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1607,7 +1575,6 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
||||
Q_strncpy( fs_gamedir, gamedir, sizeof( fs_gamedir ));
|
||||
Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir ));
|
||||
Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir ));
|
||||
fs_language[0] = 0;
|
||||
|
||||
// validate user input
|
||||
if( COM_CheckStringEmpty( fs_rodir ) && !Q_stricmp( fs_rodir, fs_rootdir ))
|
||||
@@ -1729,17 +1696,12 @@ FS_Shutdown
|
||||
void FS_ShutdownStdio( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
// release gamedirs
|
||||
for( i = 0; i < FI.numgames; i++ )
|
||||
{
|
||||
if( FI.games[i] )
|
||||
{
|
||||
Mem_Free( FI.games[i] );
|
||||
FI.games[i] = NULL;
|
||||
}
|
||||
}
|
||||
FI.numgames = 0;
|
||||
|
||||
FS_ClearSearchPath(); // release all wad files too
|
||||
Mem_FreePool( &fs_mempool );
|
||||
@@ -3119,10 +3081,6 @@ qboolean FS_Rename( const char *oldname, const char *newname )
|
||||
char oldname2[MAX_SYSPATH], newname2[MAX_SYSPATH], oldpath[MAX_SYSPATH], newpath[MAX_SYSPATH];
|
||||
int ret;
|
||||
|
||||
// a1ba: disallow path traversal
|
||||
if( FS_CheckNastyPath( oldname ) || FS_CheckNastyPath( newname ))
|
||||
return false;
|
||||
|
||||
if( !fs_writepath )
|
||||
return false;
|
||||
|
||||
@@ -3171,10 +3129,6 @@ qboolean GAME_EXPORT FS_Delete( const char *path )
|
||||
char path2[MAX_SYSPATH], real_path[MAX_SYSPATH];
|
||||
int ret;
|
||||
|
||||
// a1ba: disallow path traversal
|
||||
if( FS_CheckNastyPath( path ))
|
||||
return false;
|
||||
|
||||
if( !fs_writepath || !COM_CheckString( path ))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -48,11 +48,6 @@ enum
|
||||
FS_SKIP_ARCHIVED_WADS = BIT( 5 ), // don't mount wads inside archives automatically
|
||||
FS_LOAD_PACKED_WAD = BIT( 6 ), // this wad is packed inside other archive
|
||||
|
||||
FS_MOUNT_HD = BIT( 7 ), // mount high definition content folder
|
||||
FS_MOUNT_LV = BIT( 8 ), // mount low violence content folder
|
||||
FS_MOUNT_ADDON = BIT( 9 ), // mount addon folder
|
||||
FS_MOUNT_L10N = BIT( 10 ), // mount localization folder
|
||||
|
||||
FS_GAMEDIRONLY_SEARCH_FLAGS = FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH
|
||||
};
|
||||
|
||||
@@ -156,7 +151,7 @@ typedef struct fs_api_t
|
||||
void (*ShutdownStdio)( void );
|
||||
|
||||
// search path utils
|
||||
void (*Rescan)( uint32_t flags, const char *language );
|
||||
void (*Rescan)( void );
|
||||
void (*ClearSearchPath)( void );
|
||||
void (*AllowDirectPaths)( qboolean enable );
|
||||
void (*AddGameDirectory)( const char *dir, uint flags );
|
||||
|
||||
@@ -167,7 +167,7 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
|
||||
ALLOC_CHECK( 2 ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
|
||||
// search path utils
|
||||
void FS_Rescan( uint32_t flags, const char *language );
|
||||
void FS_Rescan( void );
|
||||
void FS_ClearSearchPath( void );
|
||||
void FS_AllowDirectPaths( qboolean enable );
|
||||
void FS_AddGameDirectory( const char *dir, uint flags );
|
||||
|
||||
@@ -13,11 +13,10 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "crtlib.h"
|
||||
#include "buildenums.h"
|
||||
|
||||
static const char *const mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
static const char *mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
static const char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
int Q_buildnum_date( const char *date )
|
||||
@@ -39,31 +38,9 @@ int Q_buildnum_date( const char *date )
|
||||
b = d + (int)((y - 1) * 365.25f );
|
||||
|
||||
if((( y % 4 ) == 0 ) && m > 1 )
|
||||
{
|
||||
b += 1;
|
||||
b -= 41728; // Apr 1 2015
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int Q_buildnum_iso( const char *date )
|
||||
{
|
||||
int y, m, d, b, i;
|
||||
|
||||
if( sscanf( date, "%d-%d-%d", &y, &m, &d ) != 3 || y <= 1900 || m <= 0 || d <= 0 )
|
||||
return -1;
|
||||
|
||||
// fixup day and month
|
||||
m--;
|
||||
d--;
|
||||
|
||||
for( i = 0; i < m; i++ )
|
||||
d += mond[i];
|
||||
|
||||
y -= 1900;
|
||||
b = d + (int)((y - 1) * 365.25f );
|
||||
|
||||
if((( y % 4 ) == 0 ) && m > 1 )
|
||||
b += 1;
|
||||
}
|
||||
b -= 41728; // Apr 1 2015
|
||||
|
||||
return b;
|
||||
@@ -80,13 +57,8 @@ int Q_buildnum( void )
|
||||
{
|
||||
static int b = 0;
|
||||
|
||||
if( b ) return b;
|
||||
|
||||
if( COM_CheckString( g_buildcommit_date ))
|
||||
b = Q_buildnum_iso( g_buildcommit_date );
|
||||
|
||||
if( b <= 0 )
|
||||
b = Q_buildnum_date( g_build_date );
|
||||
if( !b )
|
||||
b = Q_buildnum_date( __DATE__ );
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -15,5 +15,4 @@ GNU General Public License for more details.
|
||||
|
||||
const char *g_buildcommit = XASH_BUILD_COMMIT;
|
||||
const char *g_buildbranch = XASH_BUILD_BRANCH;
|
||||
const char *g_buildcommit_date = XASH_BUILD_COMMIT_DATE;
|
||||
const char *g_build_date = __DATE__;
|
||||
|
||||
|
||||
@@ -339,9 +339,6 @@ int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list ar
|
||||
{
|
||||
int result;
|
||||
|
||||
if( unlikely( buffersize == 0 ))
|
||||
return -1; // report as overflow
|
||||
|
||||
#ifndef _MSC_VER
|
||||
result = vsnprintf( buffer, buffersize, format, args );
|
||||
#else
|
||||
|
||||
@@ -57,7 +57,6 @@ enum
|
||||
//
|
||||
int Q_buildnum( void );
|
||||
int Q_buildnum_date( const char *date );
|
||||
int Q_buildnum_iso( const char *date );
|
||||
int Q_buildnum_compat( void );
|
||||
const char *Q_PlatformStringByID( const int platform );
|
||||
const char *Q_buildos( void );
|
||||
@@ -65,8 +64,6 @@ const char *Q_ArchitectureStringByID( const int arch, const uint abi, const int
|
||||
const char *Q_buildarch( void );
|
||||
extern const char *g_buildcommit;
|
||||
extern const char *g_buildbranch;
|
||||
extern const char *g_build_date;
|
||||
extern const char *g_buildcommit_date;
|
||||
|
||||
//
|
||||
// crtlib.c
|
||||
@@ -141,29 +138,24 @@ static inline char Q_tolower( const char in )
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline qboolean Q_istype( const char *str, int (*istype)( int c ))
|
||||
static inline qboolean Q_isdigit( const char *str )
|
||||
{
|
||||
if( likely( str && *str ))
|
||||
{
|
||||
while( istype( *str )) str++;
|
||||
while( isdigit( *str )) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline qboolean Q_isdigit( const char *str )
|
||||
{
|
||||
return Q_istype( str, isdigit );
|
||||
}
|
||||
|
||||
static inline qboolean Q_isalpha( const char *str )
|
||||
{
|
||||
return Q_istype( str, isalpha );
|
||||
}
|
||||
|
||||
static inline qboolean Q_isspace( const char *str )
|
||||
{
|
||||
return Q_istype( str, isspace );
|
||||
if( likely( str && *str ))
|
||||
{
|
||||
while( isspace( *str ) ) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int Q_strcmp( const char *s1, const char *s2 )
|
||||
|
||||
@@ -180,14 +180,8 @@ int main( void )
|
||||
if( Q_buildnum_date( "Apr 02 2015" ) != 1 )
|
||||
return 201;
|
||||
|
||||
if( Q_buildnum_date( "Apr 02 2015" ) != Q_buildnum_iso( "2015-04-02 21:19:10 +0300" ))
|
||||
return 202;
|
||||
|
||||
if( Q_buildnum_date( "Apr 17 2023" ) != 2938 )
|
||||
return 203;
|
||||
|
||||
if( Q_buildnum_date( "Apr 17 2023" ) != Q_buildnum_iso( "2023-04-17 21:19:10 +0300" ))
|
||||
return 204;
|
||||
return 202;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user