Compare commits

..

16 Commits

Author SHA1 Message Date
Alibek Omarov
004af02e99 engine: avi: comment out debug messages 2025-01-29 11:13:29 +03:00
Alibek Omarov
8ad56eb085 Merge branch 'master' into avi 2025-01-29 11:12:38 +03:00
Alibek Omarov
32def35408 engine: avi: remove stub file 2025-01-23 18:56:44 +03:00
Alibek Omarov
952ccb1247 engine: avi: very dumb pause 2025-01-23 18:55:48 +03:00
Alibek Omarov
6f0fb594e7 Merge branch 'master' 2025-01-23 18:42:00 +03:00
Alibek Omarov
8b4b0479c8 ci: enable ffmpeg by default for Windows and Linux x86 targets 2024-12-13 03:04:32 +03:00
Alibek Omarov
c8bf7e5630 engine: avi: get rid of stubs file, temporarily put these stubs into avi_ffmpeg.c 2024-12-13 03:04:32 +03:00
Alibek Omarov
4987080e1e engine: client: remove unused S_RawSamples wrapper function 2024-12-13 03:04:32 +03:00
Alibek Omarov
1d78e400ff engine: sound: increase MAX_RAW_SAMPLES to 16384 to fit an audio track coming from the movies 2024-12-13 03:04:32 +03:00
Alibek Omarov
2d1a885954 common: render_api: use movie_state_s in AVI functions for easier type checking 2024-12-13 03:04:32 +03:00
Alibek Omarov
43ba2e747b engine: avi: mention SDL3 and FTEQW developers which ffmpeg integrations in their projects helped me to understand the ffmpeg API 2024-12-13 03:04:32 +03:00
Alibek Omarov
eefb823ee2 engine: client: remove legacy movie audio track streaming functions 2024-12-13 03:04:32 +03:00
Alibek Omarov
a3c33e0fa9 engine: client: sound: make S_RawSampleStereo public to other parts of engine 2024-12-13 03:04:32 +03:00
Alibek Omarov
16fe6b1b1a engine: client: avi: remove VFW-based player 2024-12-13 03:04:32 +03:00
Alibek Omarov
4275829bd5 engine: client: partially rework how AVI playback works in Xash. Add ffmpeg-based player.
This intentionally will not build without --enable-ffmpeg for now.

We add new functions to the RenderAPI called AVI_Think and AVI_SetParm. Game
developer is supposed to call AVI_Think each frame and set video playback
parameters through AVI_SetParm function.

What's tested:
* Intro cinematics.
* Looping videos.
* Playing back an hour long video.
* API extension (PrimeXT's avi branch at https://github.com/a1batross/PrimeXT).

What's broken:
* There is no way to seek a video file, only rewind to the start.
2024-12-13 03:04:32 +03:00
Alibek Omarov
1884f0a3ce engine: client: avi: add new macro XASH_AVI and corresponding backend select macros (AVI_NULL and AVI_FFMPEG) 2024-12-09 00:27:40 +03:00
125 changed files with 4405 additions and 5442 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -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

View File

@@ -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/'
)

View File

@@ -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).

View File

@@ -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

View File

@@ -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" />
[![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
[![Discord Server](https://img.shields.io/discord/355697768582610945?logo=Discord&label=International%20Discord%20chat)](http://fwgsdiscord.mentality.rip/) [![Russian speakers Telegram Chat](https://img.shields.io/badge/Russian_speakers_Telegram_chat-gray?logo=Telegram)](https://t.me/flyingwithgauss) \
[![Download Daily Build](https://img.shields.io/badge/downloads-testing-orange)](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
[![builds.sr.ht status](https://builds.sr.ht/~a1batross/xash3d-fwgs.svg)](https://builds.sr.ht/~a1batross/xash3d-fwgs?) [![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) [![Discord Server](https://img.shields.io/discord/355697768582610945.svg)](http://fwgsdiscord.mentality.rip/) \
[![Download Stable](https://img.shields.io/badge/download-stable-yellow)](https://github.com/FWGS/xash3d-fwgs/releases/latest) [![Download Testing](https://img.shields.io/badge/downloads-testing-orange)](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`.
* 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`.
* 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`.
* 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`.
* 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`.

View File

@@ -1,17 +1,17 @@
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")
@@ -20,20 +20,30 @@ else()
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
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)

View File

@@ -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")

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 );

View File

@@ -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 ))

View File

@@ -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

View 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 );
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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, &current_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__ );
}

View File

@@ -173,7 +173,7 @@ static int CL_CalcTabStop( const cl_font_t *font, int x )
return stop;
}
int CL_DrawCharacter( float x, float y, int number, const rgba_t color, 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;
@@ -231,7 +231,7 @@ int CL_DrawCharacter( float x, float y, int number, const rgba_t color, cl_font_
return font->charWidths[number];
}
int CL_DrawString( float x, float y, const char *s, const 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 )
{
rgba_t current_color;
int draw_len = 0;
@@ -287,7 +287,7 @@ int CL_DrawString( float x, float y, const char *s, const rgba_t color, cl_font_
return draw_len;
}
int CL_DrawStringf( 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];

View File

@@ -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"
@@ -310,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;
@@ -1703,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 )
@@ -3431,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 )

View File

@@ -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 )

View File

@@ -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,7 +94,7 @@ 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" );
@@ -1063,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;
@@ -1074,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 );
@@ -1183,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
@@ -1225,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
@@ -1561,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 ));
@@ -1731,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 );
}
@@ -2485,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
@@ -3144,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;
}
@@ -3156,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 )
@@ -3497,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" );

View File

@@ -92,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,
@@ -111,7 +106,7 @@ static const mobile_engfuncs_t gMobileEngfuncs =
pfnDrawScaledCharacter,
Sys_Warn,
Sys_GetNativeObject,
pfnSetCustomClientID,
ID_SetCustomClientID,
pfnParseFileSafe
};

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -737,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 );
@@ -807,11 +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, int number, const rgba_t color, cl_font_t *font, int flags );
int CL_DrawString( float x, float y, const char *s, const rgba_t color, cl_font_t *font, 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, const rgba_t color, int flags, const char *fmt, ... ) FORMAT_CHECK( 6 );
int CL_DrawStringf( cl_font_t *font, float x, float y, rgba_t color, int flags, const char *fmt, ... ) FORMAT_CHECK( 6 );
//
@@ -844,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 )
@@ -869,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 )
@@ -879,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 )
@@ -1107,7 +1107,7 @@ 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 );
@@ -1214,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;

View File

@@ -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 );
@@ -759,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 );
}
@@ -843,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
@@ -876,7 +873,7 @@ void Con_Print( const char *txt )
if( cr_pending )
{
Con_DeleteLastLine();
cr_pending = false;
cr_pending = 0;
}
c = *txt;
@@ -889,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;
}
@@ -940,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
@@ -1490,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 ))
{
@@ -1608,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 );
}
@@ -2031,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 )
@@ -2043,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;
@@ -2118,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" );

View File

@@ -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 };

View File

@@ -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

View File

@@ -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 );
}

View File

@@ -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

View File

@@ -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;
}
}
@@ -1097,7 +1107,7 @@ static qboolean OSK_KeyEvent( int key, int down )
/*
=============
OSK_EnableTextInput
Joy_EnableTextInput
Enables built-in IME
=============

View File

@@ -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 ();

View File

@@ -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
}
}

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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

View File

@@ -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;

View File

@@ -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" );
@@ -1311,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 );

View File

@@ -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( );
}

View File

@@ -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 );
}
}

View File

@@ -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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}
/*

View File

@@ -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 );
}

View File

@@ -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 )

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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 )

View File

@@ -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

View File

@@ -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" );

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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:

View File

@@ -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 );

View File

@@ -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 ))

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -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

View File

@@ -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,
)

View File

@@ -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;

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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;
}

View File

@@ -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__;

View File

@@ -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

View File

@@ -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 )

View File

@@ -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;
}

View File

@@ -114,35 +114,27 @@ uint32_t Q_DecodeUTF16( utfstate_t *s, uint32_t in )
return 0;
}
static size_t Q_CodepointLength( uint32_t ch )
{
if( ch <= 0x7fu )
return 1;
else if( ch <= 0x7ffu )
return 2;
else if( ch <= 0xffffu )
return 3;
return 4;
}
size_t Q_EncodeUTF8( char dst[4], uint32_t ch )
{
switch( Q_CodepointLength( ch ))
if( ch <= 0x7fu )
{
case 1:
dst[0] = ch;
return 1;
case 2:
}
else if( ch <= 0x7ffu )
{
dst[0] = 0xc0u | (( ch >> 6 ) & 0x1fu );
dst[1] = 0x80u | (( ch ) & 0x3fu );
return 2;
case 3:
}
else if( ch <= 0xffffu )
{
dst[0] = 0xe0u | (( ch >> 12 ) & 0x0fu );
dst[1] = 0x80u | (( ch >> 6 ) & 0x3fu );
dst[2] = 0x80u | (( ch ) & 0x3fu );
return 3;
}
dst[0] = 0xf0u | (( ch >> 18 ) & 0x07u );
dst[1] = 0x80u | (( ch >> 12 ) & 0x3fu );
dst[2] = 0x80u | (( ch >> 6 ) & 0x3fu );
@@ -171,6 +163,18 @@ size_t Q_UTF8Length( const char *s )
return len;
}
static size_t Q_CodepointLength( uint32_t ch )
{
if( ch <= 0x7fu )
return 1;
else if( ch <= 0x7ffu )
return 2;
else if( ch <= 0xffffu )
return 3;
return 4;
}
size_t Q_UTF16ToUTF8( char *dst, size_t dstsize, const uint16_t *src, size_t srcsize )
{
utfstate_t state = { 0 };

View File

@@ -3,7 +3,6 @@
# mittorn, 2018
from waflib import Logs, Configure
from waflib.extras import gitversion
import os
top = '.'
@@ -61,16 +60,6 @@ def export_define(conf, define, value=1):
def simple_check(conf, fragment, msg, mandatory=False, **kw):
return conf.check_cc(fragment=fragment, msg='Checking for %s' % msg, mandatory=mandatory, **kw)
@Configure.conf
def get_git_commit_date(conf):
node = conf.srcnode.find_node('.git')
if not node:
Logs.debug('can\'t find .git in conf.srcnode')
return None
return gitversion.run_git(conf, ['log', '-1', '--format=%ci'])
def options(opt):
opt.add_option('--validate-target', action='store', dest='VALIDATE_TARGET', default=None,
help='development option, needs --enable-tests flag')
@@ -78,11 +67,6 @@ def options(opt):
def configure(conf):
# private to libpublic
conf.load('gitversion')
conf.start_msg('Git commit date')
conf.env.GIT_COMMIT_DATE = conf.get_git_commit_date()
conf.end_msg(conf.env.GIT_COMMIT_DATE)
conf.env.VALIDATE_TARGET = conf.options.VALIDATE_TARGET
# need to expose it for everyone using libpublic headers
@@ -137,7 +121,7 @@ def build(bld):
# build it separately to slightly improve rebuild times
bld.stlib(source = 'build_vcs.c',
target = 'build_vcs',
defines = ['XASH_BUILD_COMMIT=\"%s\"' % bld.env.GIT_VERSION, 'XASH_BUILD_BRANCH=\"%s\"' % bld.env.GIT_BRANCH, 'XASH_BUILD_COMMIT_DATE=\"%s\"' % bld.env.GIT_COMMIT_DATE])
defines = ['XASH_BUILD_COMMIT=\"%s\"' % bld.env.GIT_VERSION, 'XASH_BUILD_BRANCH=\"%s\"' % bld.env.GIT_BRANCH])
bld.stlib(source = bld.path.ant_glob('*.c', excl='build_vcs.c'),
target = 'public',

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