mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 19:45:05 +08:00
Compare commits
61 Commits
mbedtls
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e2b51f5a3 | ||
|
|
2086dc6128 | ||
|
|
5c4dd797ce | ||
|
|
e7636d580f | ||
|
|
64c1413278 | ||
|
|
7eb1220b19 | ||
|
|
a260b4c143 | ||
|
|
cb5146bb48 | ||
|
|
0365e645df | ||
|
|
d1143b4b5c | ||
|
|
aa4bdf0732 | ||
|
|
700a5333af | ||
|
|
ad25458c5a | ||
|
|
8f6ff1b64f | ||
|
|
3abb39f172 | ||
|
|
6a02b77163 | ||
|
|
a3c26affa3 | ||
|
|
95df96e668 | ||
|
|
bdce95a31b | ||
|
|
eec9609354 | ||
|
|
a58f2ac13b | ||
|
|
e704247f9d | ||
|
|
07a096d04e | ||
|
|
f5077a13b9 | ||
|
|
d414eeb246 | ||
|
|
5daf686a82 | ||
|
|
635b382c1d | ||
|
|
7fbd461f83 | ||
|
|
eb0ff8e631 | ||
|
|
f50e61403f | ||
|
|
2cf8c63631 | ||
|
|
e09773c912 | ||
|
|
3a15357dc8 | ||
|
|
2127735456 | ||
|
|
e0ab44213a | ||
|
|
f2166a2c9d | ||
|
|
009855c193 | ||
|
|
f0ea3a194a | ||
|
|
9f2b8954e9 | ||
|
|
2166dbde66 | ||
|
|
4c109a0c6c | ||
|
|
36c71c2610 | ||
|
|
e2d6b7ab28 | ||
|
|
8ccb9dea3f | ||
|
|
34467bfe9a | ||
|
|
b1d9eef896 | ||
|
|
c2ac632446 | ||
|
|
365c126de6 | ||
|
|
44313e624c | ||
|
|
2f776a6173 | ||
|
|
190fa1bab8 | ||
|
|
3366ad06d0 | ||
|
|
57206b3651 | ||
|
|
d9ecd03b4a | ||
|
|
2de4d0d364 | ||
|
|
cc3889146c | ||
|
|
09f5886751 | ||
|
|
fb5b4fed6e | ||
|
|
1e31fbcd51 | ||
|
|
b2a5f0db5a | ||
|
|
abd7b5ccdd |
113
.clang-format
Normal file
113
.clang-format
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
Language: Cpp
|
||||||
|
Standard: c++17
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
TabWidth: 4
|
||||||
|
IndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: None
|
||||||
|
NamespaceIndentation: None
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: true
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: Always
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
BeforeWhile: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
MaxEmptyLinesToKeep: 2
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
KeepEmptyLinesAtEOF: false
|
||||||
|
SeparateDefinitionBlocks: Always
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
LineEnding: LF
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortEnumsOnASingleLine: false
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
PointerAlignment: Right
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
ReferenceAlignment: Pointer
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpacesInParens: Custom
|
||||||
|
SpacesInParensOptions:
|
||||||
|
ExceptDoubleParentheses: true
|
||||||
|
InCStyleCasts: false
|
||||||
|
InConditionalStatements: true
|
||||||
|
InEmptyParentheses: false
|
||||||
|
Other: true
|
||||||
|
# XashCollapseParens: true
|
||||||
|
SpaceBeforeParens: Never
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpacesInAngles: Never
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: -1
|
||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignArrayOfStructures: None
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignConsecutiveBitFields:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: false
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: false
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments:
|
||||||
|
Kind: Always
|
||||||
|
OverEmptyLines: 0
|
||||||
|
ColumnLimit: 0
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: Never
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
InsertBraces: false
|
||||||
|
RemoveBracesLLVM: false
|
||||||
|
FixNamespaceComments: false
|
||||||
1
.github/workflows/c-cpp.yml
vendored
1
.github/workflows/c-cpp.yml
vendored
@@ -70,6 +70,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SDL_VERSION: 2.32.10
|
SDL_VERSION: 2.32.10
|
||||||
FFMPEG_VERSION: 8.1
|
FFMPEG_VERSION: 8.1
|
||||||
|
YY_THUNKS_VERSION: v1.2.1
|
||||||
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
||||||
GH_CPU_OS: ${{ matrix.targetos }}
|
GH_CPU_OS: ${{ matrix.targetos }}
|
||||||
GH_CROSSCOMPILING: ${{ matrix.cross }}
|
GH_CROSSCOMPILING: ${{ matrix.cross }}
|
||||||
|
|||||||
2
3rdparty/gl4es/wscript
vendored
2
3rdparty/gl4es/wscript
vendored
@@ -16,6 +16,8 @@ def build(bld):
|
|||||||
cflags = []
|
cflags = []
|
||||||
if bld.env.COMPILER_CC != 'msvc':
|
if bld.env.COMPILER_CC != 'msvc':
|
||||||
cflags += ['-w', '-fvisibility=hidden', '-std=gnu99']
|
cflags += ['-w', '-fvisibility=hidden', '-std=gnu99']
|
||||||
|
else:
|
||||||
|
cflags += ['-wd4098']
|
||||||
bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']),
|
bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']),
|
||||||
target = 'gl4es',
|
target = 'gl4es',
|
||||||
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],
|
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],
|
||||||
|
|||||||
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: 623c34db69...510c30c51a
2
3rdparty/vgui_support
vendored
2
3rdparty/vgui_support
vendored
Submodule 3rdparty/vgui_support updated: e021fb5e7f...d2aaec107d
59
3rdparty/yy-thunks/wscript
vendored
Normal file
59
3rdparty/yy-thunks/wscript
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
from waflib import Task, TaskGen, Logs
|
||||||
|
|
||||||
|
OBJ_NAME = 'YY_Thunks_for_WinXP.obj'
|
||||||
|
OBJ_SHA256 = '51a13c4b667c82181d357e274a50308a273be63f5f85ae958465774857721573'
|
||||||
|
DLL_LINKFLAGS = ['-ENTRY:DllMainCRTStartupForYY_Thunks', '-alternatename:_YY_ThunksOriginalDllMainCRTStartup@12=__DllMainCRTStartup@12']
|
||||||
|
|
||||||
|
class copy_yy_thunks(Task.Task):
|
||||||
|
color = 'CYAN'
|
||||||
|
def run(self):
|
||||||
|
return self.outputs[0].write(self.inputs[0].read('rb'), 'wb')
|
||||||
|
|
||||||
|
@TaskGen.feature('yy_thunks')
|
||||||
|
def add_yy_thunks_obj(self):
|
||||||
|
src = self.path.find_node(OBJ_NAME)
|
||||||
|
out = self.path.find_or_declare(OBJ_NAME)
|
||||||
|
self.compiled_tasks = [self.create_task('copy_yy_thunks', src, out)]
|
||||||
|
|
||||||
|
@TaskGen.feature('cshlib', 'cxxshlib')
|
||||||
|
@TaskGen.after_method('process_use')
|
||||||
|
def apply_yy_thunks_dll(self):
|
||||||
|
if 'yy_thunks' not in self.to_list(getattr(self, 'use', [])):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.bld.get_tgen_by_name('yy_thunks')
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
self.env.append_value('LINKFLAGS', DLL_LINKFLAGS)
|
||||||
|
|
||||||
|
def options(opt):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
if conf.env.DEST_OS != 'win32' or conf.env.COMPILER_CC != 'msvc':
|
||||||
|
return
|
||||||
|
|
||||||
|
if conf.env.MSVC_TARGETS[0] not in ['amd64_x86', 'x86']:
|
||||||
|
return
|
||||||
|
|
||||||
|
obj = conf.path.find_node(OBJ_NAME)
|
||||||
|
if not obj:
|
||||||
|
Logs.warn('YY-Thunks: %s not found, XP compatibility disabled' % OBJ_NAME)
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(obj.abspath(), 'rb') as f:
|
||||||
|
digest = hashlib.sha256(f.read()).hexdigest()
|
||||||
|
|
||||||
|
if digest != OBJ_SHA256:
|
||||||
|
conf.fatal('%s checksum mismatch: expected %s, got %s' % (OBJ_NAME, OBJ_SHA256, digest))
|
||||||
|
|
||||||
|
conf.env.YY_THUNKS = True
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
if bld.env.YY_THUNKS:
|
||||||
|
bld(name='yy_thunks', features='yy_thunks')
|
||||||
@@ -29,9 +29,7 @@ Initial Xash3D SDL2/Linux port author, Xash3D FWGS engine maintainer, creator of
|
|||||||
|
|
||||||
## [$_Vladislav](https://github.com/Vladislav4KZ)
|
## [$_Vladislav](https://github.com/Vladislav4KZ)
|
||||||
|
|
||||||
[YaPB Project](https://github.com/yapb) member, [Xash3D FWGS](https://github.com/FWGS/xash3d-fwgs) tester,
|
[YaPB Project](https://github.com/yapb) member and [contributor](https://github.com/yapb/yapb/commits?author=Vladislav4KZ) ([Docs](https://github.com/yapb/docs), [Nav Graphs](https://github.com/yapb/graph), [RU translation](https://github.com/yapb/yapb/blob/master/cfg/addons/yapb/conf/lang/ru_lang.cfg)), Xash3D FWGS [tester](https://github.com/FWGS/xash3d-fwgs/issues?q=is%3Aissue%20state%3Aopen%20author%3AVladislav4KZ) and [contributor](https://github.com/FWGS/xash3d-fwgs/commits?author=Vladislav4KZ) (gyro-aim support, scr-parser, etc.), [CS16Client](https://github.com/Velaron/cs16-client) [contributor](https://github.com/Velaron/cs16-client/commits?author=Vladislav4KZ).
|
||||||
editor of the [Official YaPB Documentation](https://github.com/yapb/docs) (in English and Russian), curator of the [YaPB Graph Database](https://github.com/yapb/graph) and author of [YaPB Waypoint/Graph Pack](https://gamebanana.com/mods/40087).
|
|
||||||
|
|
||||||
Also does the Russian localization of text, images (gfx/shell), as well as additional menu buttons in English used in Xash3D FWGS for various mods.
|
* [Boosty page](https://boosty.to/rasstaman1337)
|
||||||
|
* [PayPal link](https://paypal.me/rasstaman1337)
|
||||||
* [Boosty page](https://boosty.to/rasstaman1337)
|
|
||||||
@@ -12,14 +12,18 @@ All connectionless packets have `\xff\xff\xff\xff` (32-bit integer set to all on
|
|||||||
|
|
||||||
- [Any](#any)
|
- [Any](#any)
|
||||||
- [Any to Any](#any-to-any)
|
- [Any to Any](#any-to-any)
|
||||||
- [`A2A_NETINFO`](#a2a_netinfo)
|
|
||||||
- [`A2A_INFO`](#a2a_info)
|
|
||||||
- [`A2A_PING` and `A2A_GOLDSRC_PING`](#a2a_ping-and-a2a_goldsrc_ping)
|
- [`A2A_PING` and `A2A_GOLDSRC_PING`](#a2a_ping-and-a2a_goldsrc_ping)
|
||||||
- [`A2A_ACK` and `A2A_GOLDSRC_ACK`](#a2a_ack-and-a2a_goldsrc_ack)
|
- [`A2A_ACK` and `A2A_GOLDSRC_ACK`](#a2a_ack-and-a2a_goldsrc_ack)
|
||||||
|
- [Deprecated queries](#deprecated-queries)
|
||||||
|
- [`A2A_NETINFO`](#a2a_netinfo)
|
||||||
|
- [`A2A_INFO`](#a2a_info)
|
||||||
- [Any to Client](#any-to-client)
|
- [Any to Client](#any-to-client)
|
||||||
- [`A2C_PRINT` and `A2C_GOLDSRC_PRINT`](#a2c_print-and-a2c_goldsrc_print)
|
- [`A2C_PRINT` and `A2C_GOLDSRC_PRINT`](#a2c_print-and-a2c_goldsrc_print)
|
||||||
- [Any to Server and Server to Any](#any-to-server-and-server-to-any)
|
- [Any to Server and Server to Any](#any-to-server-and-server-to-any)
|
||||||
- [`A2S_GOLDSRC_INFO`, `A2S_GOLDSRC_RULES` and `A2S_GOLDSRC_PLAYERS` and their responses](#a2s_goldsrc_info-a2s_goldsrc_rules-and-a2s_goldsrc_players-and-their-responses)
|
- [GoldSrc server queries](#goldsrc-server-queries)
|
||||||
|
- [`A2S_GOLDSRC_INFO`](#a2s_goldsrc_info)
|
||||||
|
- [`A2S_GOLDSRC_PLAYERS`](#a2s_goldsrc_players)
|
||||||
|
- [`A2S_GOLDSRC_RULES`](#a2s_goldsrc_rules)
|
||||||
- [Any to Master](#any-to-master)
|
- [Any to Master](#any-to-master)
|
||||||
- [`S2M_SCAN_REQUEST`](#s2m_scan_request)
|
- [`S2M_SCAN_REQUEST`](#s2m_scan_request)
|
||||||
- [Master to Any](#master-to-any)
|
- [Master to Any](#master-to-any)
|
||||||
@@ -50,7 +54,23 @@ All connectionless packets have `\xff\xff\xff\xff` (32-bit integer set to all on
|
|||||||
|
|
||||||
### Any to Any
|
### Any to Any
|
||||||
|
|
||||||
#### `A2A_NETINFO`
|
#### `A2A_PING` and `A2A_GOLDSRC_PING`
|
||||||
|
|
||||||
|
Simple ping message.
|
||||||
|
* Request, in ASCII: `ping` or `i` in GoldSrc format
|
||||||
|
* Response is always `A2A_ACK` or `A2A_GOLDSRC_ACK`
|
||||||
|
|
||||||
|
#### `A2A_ACK` and `A2A_GOLDSRC_ACK`
|
||||||
|
|
||||||
|
Simple ack message.
|
||||||
|
* Request, in ASCII: `ack` or `j` in GoldSrc format
|
||||||
|
* Response: none
|
||||||
|
|
||||||
|
#### Deprecated queries
|
||||||
|
|
||||||
|
These text-based Xash protocol queries are deprecated in favor of [GoldSrc server queries](#goldsrc-server-queries), which Xash3D FWGS servers implement as well. New implementations must not send them. The server still responds to `A2A_INFO` for compatibility with older clients, `A2A_NETINFO` is not handled anymore.
|
||||||
|
|
||||||
|
##### `A2A_NETINFO`
|
||||||
|
|
||||||
Used to implement Half-Life's NetAPI.
|
Used to implement Half-Life's NetAPI.
|
||||||
* Request message, in ASCII: `netinfo <version> <context> <request_id>`
|
* Request message, in ASCII: `netinfo <version> <context> <request_id>`
|
||||||
@@ -81,7 +101,7 @@ Possible requests and response formats:
|
|||||||
* On any other server will respond with `neterror` set to `undefined`
|
* On any other server will respond with `neterror` set to `undefined`
|
||||||
* If protocol version doesn't match, server responds with `neterror` set to `protocol`
|
* If protocol version doesn't match, server responds with `neterror` set to `protocol`
|
||||||
|
|
||||||
#### `A2A_INFO`
|
##### `A2A_INFO`
|
||||||
|
|
||||||
Used to request server's details.
|
Used to request server's details.
|
||||||
* Request message, in ASCII: `info <version>`
|
* Request message, in ASCII: `info <version>`
|
||||||
@@ -101,18 +121,6 @@ Used to request server's details.
|
|||||||
- `password` set to 1 if server is protected with password otherwise 0
|
- `password` set to 1 if server is protected with password otherwise 0
|
||||||
- `host` is server's name
|
- `host` is server's name
|
||||||
|
|
||||||
#### `A2A_PING` and `A2A_GOLDSRC_PING`
|
|
||||||
|
|
||||||
Simple ping message.
|
|
||||||
* Request, in ASCII: `ping` or `i` in GoldSrc format
|
|
||||||
* Response is always `A2A_ACK` or `A2A_GOLDSRC_ACK`
|
|
||||||
|
|
||||||
#### `A2A_ACK` and `A2A_GOLDSRC_ACK`
|
|
||||||
|
|
||||||
Simple ack message.
|
|
||||||
* Request, in ASCII: `ack` or `j` in GoldSrc format
|
|
||||||
* Response: none
|
|
||||||
|
|
||||||
### Any to Client
|
### Any to Client
|
||||||
|
|
||||||
#### `A2C_PRINT` and `A2C_GOLDSRC_PRINT`
|
#### `A2C_PRINT` and `A2C_GOLDSRC_PRINT`
|
||||||
@@ -124,9 +132,62 @@ Simple print message.
|
|||||||
|
|
||||||
### Any to Server and Server to Any
|
### Any to Server and Server to Any
|
||||||
|
|
||||||
#### `A2S_GOLDSRC_INFO`, `A2S_GOLDSRC_RULES` and `A2S_GOLDSRC_PLAYERS` and their responses
|
#### GoldSrc server queries
|
||||||
|
|
||||||
These match Source Engine Query messages used in GoldSrc and Source engine and implemented for compatibility with existing tools. Read documentation for them on VDC: https://developer.valvesoftware.com/wiki/Server_queries
|
These match Source Engine Query messages used in GoldSrc and Source engine and implemented for compatibility with existing server browsers and monitoring tools. The client uses them to query GoldSrc servers in the server browser, and as text-based Xash protocol queries are [deprecated](#deprecated-queries), it will use them to query Xash servers as well. Read documentation for them on VDC: https://developer.valvesoftware.com/wiki/Server_queries
|
||||||
|
|
||||||
|
Unlike other connectionless messages these are binary: integers are little-endian, floats are 32-bit IEEE 754 and strings are null-terminated.
|
||||||
|
|
||||||
|
`A2S_GOLDSRC_PLAYERS` and `A2S_GOLDSRC_RULES` requests carry a 32-bit challenge, `A2S_GOLDSRC_INFO` may have one appended after the query string. When no challenge is known yet, `\xff\xff\xff\xff` is sent in it's place. The server may answer with a challenge instead of the data: byte `A` (`0x41`) followed by a 32-bit challenge value, 9 bytes total with the header. The request is then repeated with the received challenge. Don't confuse this message with textual `S2C_GOLDSRC_CHALLENGE` used during connection, they only share the first byte.
|
||||||
|
|
||||||
|
Responses that don't fit into a single packet (usually rules) come from GoldSrc servers split into fragments. Such packets start with `\xfe\xff\xff\xff` instead of `\xff\xff\xff\xff`, followed by a 32-bit sequence number, same in all fragments of one response, and a byte with total fragment count in the lower 4 bits and this fragment's index in the upper 4 bits. Fragment payloads concatenated in index order form a normal `\xff\xff\xff\xff` message. Xash servers never split query responses and send them as single datagrams.
|
||||||
|
|
||||||
|
#### `A2S_GOLDSRC_INFO`
|
||||||
|
|
||||||
|
Used to request server's details.
|
||||||
|
|
||||||
|
* Request: `TSource Engine Query\0`, optionally followed by a 32-bit challenge. Xash servers ignore it.
|
||||||
|
* Response: byte `I` (`S2A_GOLDSRC_INFO`), followed by:
|
||||||
|
- byte: protocol version, 48 on GoldSrc, 49 on Xash3D FWGS
|
||||||
|
- string: server's name
|
||||||
|
- string: server's current level
|
||||||
|
- string: game directory name
|
||||||
|
- string: game description
|
||||||
|
- int16: Steam AppID, always 0 on Xash servers
|
||||||
|
- byte: total player count, bots included
|
||||||
|
- byte: max players limit
|
||||||
|
- byte: bot count
|
||||||
|
- byte: server type, `d` for dedicated or `l` for listen server
|
||||||
|
- byte: server's operating system, `w` for Windows, `l` for Linux, `m` for macOS
|
||||||
|
- byte: set to 1 if server is protected with password otherwise 0
|
||||||
|
- byte: set to 1 if server is VAC secured, Xash servers put the `secure` value from gameinfo here
|
||||||
|
- string: server's version
|
||||||
|
|
||||||
|
Same as the Source engine response format but without the extra data flag and its fields.
|
||||||
|
|
||||||
|
Legacy GoldSrc servers respond with the obsolete `m` format (`S2A_GOLDSRC_LEGACY_INFO`) instead, see VDC for its layout. Xash servers never send it but the client understands it and assumes protocol 48.
|
||||||
|
|
||||||
|
#### `A2S_GOLDSRC_PLAYERS`
|
||||||
|
|
||||||
|
Used to request server's player list.
|
||||||
|
|
||||||
|
* Request: byte `U` (`0x55`), then a 32-bit challenge.
|
||||||
|
* Response: byte `D` (`S2A_GOLDSRC_PLAYERS`), followed by a byte with total player count, then for each player:
|
||||||
|
- byte: player's index. Xash servers send sequential indexes starting from 0, GoldSrc servers always send 0 here.
|
||||||
|
- string: player's name
|
||||||
|
- int32: player's kill count
|
||||||
|
- float: time in seconds the player is connected, -1.0 for bots on Xash servers
|
||||||
|
|
||||||
|
Xash server doesn't respond at all if the player list is empty, the server is protected with password or exposing the player list is disabled with `sv_expose_player_list` cvar.
|
||||||
|
|
||||||
|
#### `A2S_GOLDSRC_RULES`
|
||||||
|
|
||||||
|
Used to request server's game rules.
|
||||||
|
|
||||||
|
* Request: byte `V` (`0x56`), then a 32-bit challenge.
|
||||||
|
* Response: byte `E` (`S2A_GOLDSRC_RULES`), followed by an int16 with total rule count, then rule name and rule value strings for each.
|
||||||
|
|
||||||
|
Only server cvars (`FCVAR_SERVER`) are sent. Values of protected cvars (`FCVAR_PROTECTED`) are replaced with `1` if the value is set otherwise `0`. If there are no cvars to expose, server doesn't respond at all.
|
||||||
|
|
||||||
### Any to Master
|
### Any to Master
|
||||||
|
|
||||||
@@ -154,7 +215,7 @@ These match Source Engine Query messages used in GoldSrc and Source engine and i
|
|||||||
- `reserved` is single reserved 8-bit byte
|
- `reserved` is single reserved 8-bit byte
|
||||||
- Following is list of IP addresses in binary form: 6-bytes for IPv4 address + port and 18-bytes for IPv6 address + port
|
- Following is list of IP addresses in binary form: 6-bytes for IPv4 address + port and 18-bytes for IPv6 address + port
|
||||||
- If port is 0, it means the end of list, otherwise it's the last IP used for pagination (not implemented in Xash3D)
|
- If port is 0, it means the end of list, otherwise it's the last IP used for pagination (not implemented in Xash3D)
|
||||||
* Response: client doesn't make response to master server but might request `A2S_INFO` from servers
|
* Response: client doesn't make response to master server but might request `A2S_GOLDSRC_INFO` from servers
|
||||||
|
|
||||||
## Master and game server
|
## Master and game server
|
||||||
|
|
||||||
|
|||||||
@@ -28,17 +28,18 @@ used; servers register out of band.
|
|||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
UTF-8 text, tokenized with `COM_ParseFileSafe` (whitespace separates,
|
UTF-8 text, parsed line by line. Each line is tokenized with
|
||||||
`//` and `#` start line comments, `"..."` quotes a token). One directive
|
`COM_ParseFileSafe` (whitespace separates, `//` and `#` start line
|
||||||
per record:
|
comments, `"..."` quotes a token). Blank lines and comment-only lines
|
||||||
|
are ignored. One directive per line:
|
||||||
|
|
||||||
* `ip <address>` — Xash3D server (protocol 49).
|
* `ip <address>` — Xash3D server (protocol 49).
|
||||||
* `gs <address>` — GoldSrc server (protocol 48).
|
* `gs <address>` — GoldSrc server (protocol 48).
|
||||||
|
|
||||||
`<address>` is parsed by `NET_StringToAdr` (`1.2.3.4:27015`,
|
`<address>` is parsed by `NET_StringToAdr` (`1.2.3.4:27015`,
|
||||||
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Unknown
|
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Lines
|
||||||
directives are skipped together with one operand so new keywords can be
|
starting with an unknown directive are skipped entirely, so new keywords
|
||||||
added without breaking older clients.
|
with any number of operands can be added without breaking older clients.
|
||||||
|
|
||||||
`Content-Type` is not inspected, `text/plain; charset=utf-8` expected.
|
`Content-Type` is not inspected, `text/plain; charset=utf-8` expected.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
> [!CAUTION]
|
||||||
|
> **Only download Xash3D FWGS from official sources.** Third-party builds, "modded launchers", "optimized" repacks and random mirrors are frequently bundled with malware, miners, spyware and credential stealers. We cannot vouch for anything we did not build. Get official binaries only from the [releases page](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous).
|
||||||
|
|
||||||
# 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" />
|
# Xash3D FWGS Engine <img align="right" width="128" height="128" src="https://github.com/FWGS/xash3d-fwgs/raw/master/game_launch/icon-xash-material.png" alt="Xash3D FWGS icon" />
|
||||||
[](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
|
[](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
|
||||||
[](http://xash.su/discord/) [](https://t.me/flyingwithgauss) \
|
[](http://xash.su/discord/) [](https://t.me/flyingwithgauss) \
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||||||
typedef struct usercmd_s
|
typedef struct usercmd_s
|
||||||
{
|
{
|
||||||
int16_t lerp_msec; // added in HL
|
int16_t lerp_msec; // added in HL
|
||||||
int8_t msec; // added in QW
|
uint8_t msec; // added in QW
|
||||||
uint8_t pad1;
|
uint8_t pad1;
|
||||||
vec3_t viewangles;
|
vec3_t viewangles;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const char *CL_MsgInfo( int cmd )
|
|||||||
// get engine message name
|
// get engine message name
|
||||||
const char *svc_string = NULL;
|
const char *svc_string = NULL;
|
||||||
|
|
||||||
switch( cls.legacymode )
|
switch( cls.net_protocol )
|
||||||
{
|
{
|
||||||
case PROTO_CURRENT:
|
case PROTO_CURRENT:
|
||||||
svc_string = svc_strings[cmd];
|
svc_string = svc_strings[cmd];
|
||||||
@@ -146,7 +146,7 @@ static void CL_WriteErrorMessage( int current_count, sizebuf_t *msg )
|
|||||||
|
|
||||||
FS_Write( fp, &cls.starting_count, sizeof( int ));
|
FS_Write( fp, &cls.starting_count, sizeof( int ));
|
||||||
FS_Write( fp, ¤t_count, sizeof( int ));
|
FS_Write( fp, ¤t_count, sizeof( int ));
|
||||||
FS_Write( fp, &cls.legacymode, sizeof( cls.legacymode ));
|
FS_Write( fp, &cls.net_protocol, sizeof( cls.net_protocol ));
|
||||||
FS_Write( fp, MSG_GetData( msg ), MSG_GetMaxBytes( msg ));
|
FS_Write( fp, MSG_GetData( msg ), MSG_GetMaxBytes( msg ));
|
||||||
FS_Close( fp );
|
FS_Close( fp );
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ void CL_ReplayBufferDat_f( void )
|
|||||||
FS_Read( f, ¤t_count, sizeof( current_count ));
|
FS_Read( f, ¤t_count, sizeof( current_count ));
|
||||||
FS_Read( f, &protocol, sizeof( protocol ));
|
FS_Read( f, &protocol, sizeof( protocol ));
|
||||||
|
|
||||||
cls.legacymode = protocol;
|
cls.net_protocol = protocol;
|
||||||
|
|
||||||
len = FS_Read( f, buffer, sizeof( buffer ));
|
len = FS_Read( f, buffer, sizeof( buffer ));
|
||||||
FS_Close( f );
|
FS_Close( f );
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ static void CL_WriteDemoHeader( const char *name )
|
|||||||
|
|
||||||
demo.header.id = IDEMOHEADER;
|
demo.header.id = IDEMOHEADER;
|
||||||
demo.header.dem_protocol = DEMO_PROTOCOL;
|
demo.header.dem_protocol = DEMO_PROTOCOL;
|
||||||
demo.header.net_protocol = CL_GetDemoNetProtocol( cls.legacymode );
|
demo.header.net_protocol = CL_GetDemoNetProtocol( cls.net_protocol );
|
||||||
demo.header.host_fps = host_maxfps.value ? bound( MIN_FPS, host_maxfps.value, maxfps ) : maxfps;
|
demo.header.host_fps = host_maxfps.value ? bound( MIN_FPS, host_maxfps.value, maxfps ) : maxfps;
|
||||||
Q_strncpy( demo.header.mapname, clgame.mapname, sizeof( demo.header.mapname ));
|
Q_strncpy( demo.header.mapname, clgame.mapname, sizeof( demo.header.mapname ));
|
||||||
Q_strncpy( demo.header.comment, clgame.maptitle, sizeof( demo.header.comment ));
|
Q_strncpy( demo.header.comment, clgame.maptitle, sizeof( demo.header.comment ));
|
||||||
@@ -723,7 +723,7 @@ static void CL_DemoStartPlayback( int mode )
|
|||||||
|
|
||||||
demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message
|
demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message
|
||||||
|
|
||||||
CL_SetupNetchanForProtocol( cls.legacymode );
|
CL_SetupNetchanForProtocol( cls.net_protocol );
|
||||||
|
|
||||||
memset( demo.cmds, 0, sizeof( demo.cmds ));
|
memset( demo.cmds, 0, sizeof( demo.cmds ));
|
||||||
demo.angle_position = 1;
|
demo.angle_position = 1;
|
||||||
@@ -1555,7 +1555,7 @@ void CL_PlayDemo_f( void )
|
|||||||
|
|
||||||
if( neg ) cls.forcetrack = -cls.forcetrack;
|
if( neg ) cls.forcetrack = -cls.forcetrack;
|
||||||
CL_DemoStartPlayback( DEMO_QUAKE1 );
|
CL_DemoStartPlayback( DEMO_QUAKE1 );
|
||||||
cls.legacymode = PROTO_QUAKE;
|
cls.net_protocol = PROTO_QUAKE;
|
||||||
return; // quake demo is started
|
return; // quake demo is started
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1591,7 +1591,7 @@ void CL_PlayDemo_f( void )
|
|||||||
CL_DemoStartPlayback( DEMO_XASH3D );
|
CL_DemoStartPlayback( DEMO_XASH3D );
|
||||||
|
|
||||||
// must be after DemoStartPlayback, as CL_Disconnect_f resets the protocol
|
// must be after DemoStartPlayback, as CL_Disconnect_f resets the protocol
|
||||||
cls.legacymode = CL_GetProtocolFromDemo( demo.header.net_protocol );
|
cls.net_protocol = CL_GetProtocolFromDemo( demo.header.net_protocol );
|
||||||
|
|
||||||
// g-cont. is this need?
|
// g-cont. is this need?
|
||||||
Q_strncpy( cls.servername, demoname, sizeof( cls.servername ));
|
Q_strncpy( cls.servername, demoname, sizeof( cls.servername ));
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ static CVAR_DEFINE_AUTO( cl_logoext, "bmp", FCVAR_ARCHIVE, "temporary cvar to te
|
|||||||
static CVAR_DEFINE( cl_logoupdate, "@cl_logoupdate", "0", 0, "set by menu to trigger clan logo update" );
|
static CVAR_DEFINE( cl_logoupdate, "@cl_logoupdate", "0", 0, "set by menu to trigger clan logo update" );
|
||||||
CVAR_DEFINE_AUTO( cl_logomaxdim, "96", FCVAR_ARCHIVE, "maximum decal dimension" );
|
CVAR_DEFINE_AUTO( cl_logomaxdim, "96", FCVAR_ARCHIVE, "maximum decal dimension" );
|
||||||
static CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" );
|
static CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" );
|
||||||
|
static CVAR_DEFINE_AUTO( cl_require_challenge_echo, "-1", FCVAR_ARCHIVE, "reject connect packets that don't echo challenge, protects against spoofed servers but breaks connection to old servers (-1 = engine default)" );
|
||||||
|
|
||||||
CVAR_DEFINE( cl_draw_particles, "r_drawparticles", "1", FCVAR_CHEAT, "render particles" );
|
CVAR_DEFINE( cl_draw_particles, "r_drawparticles", "1", FCVAR_CHEAT, "render particles" );
|
||||||
CVAR_DEFINE( cl_draw_tracers, "r_drawtracers", "1", FCVAR_CHEAT, "render tracers" );
|
CVAR_DEFINE( cl_draw_tracers, "r_drawtracers", "1", FCVAR_CHEAT, "render tracers" );
|
||||||
@@ -171,7 +172,7 @@ int CL_IsDevOverviewMode( void )
|
|||||||
|
|
||||||
connprotocol_t CL_Protocol( void )
|
connprotocol_t CL_Protocol( void )
|
||||||
{
|
{
|
||||||
return cls.legacymode;
|
return cls.net_protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats )
|
void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats )
|
||||||
@@ -235,7 +236,7 @@ static void CL_CreateResourceList( void )
|
|||||||
Cvar_DirectSet( &cl_logoext, "bmp" );
|
Cvar_DirectSet( &cl_logoext, "bmp" );
|
||||||
|
|
||||||
Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.%s", cl_logoext.string );
|
Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.%s", cl_logoext.string );
|
||||||
if( cls.legacymode == PROTO_GOLDSRC )
|
if( cls.net_protocol == PROTO_GOLDSRC )
|
||||||
{
|
{
|
||||||
CL_ConvertImageToWAD3( szFileName );
|
CL_ConvertImageToWAD3( szFileName );
|
||||||
Q_strncpy( szFileName, "tempdecal.wad", sizeof( szFileName ));
|
Q_strncpy( szFileName, "tempdecal.wad", sizeof( szFileName ));
|
||||||
@@ -335,7 +336,7 @@ static void CL_CheckClientState( void )
|
|||||||
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
|
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
|
||||||
Netchan_ReportFlow( &cls.netchan );
|
Netchan_ReportFlow( &cls.netchan );
|
||||||
|
|
||||||
if( cls.legacymode == PROTO_GOLDSRC )
|
if( cls.net_protocol == PROTO_GOLDSRC )
|
||||||
{
|
{
|
||||||
CL_ServerCommand(true, "specmode 4\n");
|
CL_ServerCommand(true, "specmode 4\n");
|
||||||
CL_ServerCommand(true, "specmode 4\n");
|
CL_ServerCommand(true, "specmode 4\n");
|
||||||
@@ -868,7 +869,7 @@ static void CL_WritePacket( void )
|
|||||||
byte data[MAX_CMD_BUFFER] = { 0 };
|
byte data[MAX_CMD_BUFFER] = { 0 };
|
||||||
runcmd_t *pcmd;
|
runcmd_t *pcmd;
|
||||||
int numbackup, maxbackup, maxcmds;
|
int numbackup, maxbackup, maxcmds;
|
||||||
const connprotocol_t proto = cls.legacymode;
|
const connprotocol_t proto = cls.net_protocol;
|
||||||
|
|
||||||
// FIXME: on Xash protocol we don't send move commands until ca_active
|
// FIXME: on Xash protocol we don't send move commands until ca_active
|
||||||
// to prevent outgoing_command outrun incoming_acknowledged
|
// to prevent outgoing_command outrun incoming_acknowledged
|
||||||
@@ -1374,7 +1375,7 @@ static void CL_CheckForResend( void )
|
|||||||
cls.state = ca_connecting;
|
cls.state = ca_connecting;
|
||||||
Q_strncpy( cls.servername, "localhost", sizeof( cls.servername ));
|
Q_strncpy( cls.servername, "localhost", sizeof( cls.servername ));
|
||||||
NET_NetadrSetType( &cls.serveradr, NA_LOOPBACK );
|
NET_NetadrSetType( &cls.serveradr, NA_LOOPBACK );
|
||||||
cls.legacymode = PROTO_CURRENT;
|
cls.net_protocol = PROTO_CURRENT;
|
||||||
|
|
||||||
// we don't need a challenge on the localhost
|
// we don't need a challenge on the localhost
|
||||||
CL_SendConnectPacket( PROTO_CURRENT, 0 );
|
CL_SendConnectPacket( PROTO_CURRENT, 0 );
|
||||||
@@ -1449,7 +1450,7 @@ static void CL_CheckForResend( void )
|
|||||||
// but tell us that test is allowed
|
// but tell us that test is allowed
|
||||||
// in this case, just send connect packet and hope for the best
|
// in this case, just send connect packet and hope for the best
|
||||||
if( cls.bandwidth_test.passed || cls.bandwidth_test.failed )
|
if( cls.bandwidth_test.passed || cls.bandwidth_test.failed )
|
||||||
CL_SendConnectPacket( cls.legacymode, cls.bandwidth_test.challenge );
|
CL_SendConnectPacket( cls.net_protocol, cls.bandwidth_test.challenge );
|
||||||
else
|
else
|
||||||
CL_SendBandwidthTest( adr, false );
|
CL_SendBandwidthTest( adr, false );
|
||||||
}
|
}
|
||||||
@@ -1512,7 +1513,7 @@ static void CL_Connect_f( void )
|
|||||||
Key_SetKeyDest( key_console );
|
Key_SetKeyDest( key_console );
|
||||||
|
|
||||||
cls.state = ca_connecting;
|
cls.state = ca_connecting;
|
||||||
cls.legacymode = proto;
|
cls.net_protocol = proto;
|
||||||
Q_strncpy( cls.servername, server, sizeof( cls.servername ));
|
Q_strncpy( cls.servername, server, sizeof( cls.servername ));
|
||||||
cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately
|
cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately
|
||||||
cls.max_fragment_size = FRAGMENT_MAX_SIZE; // guess a we can establish connection with maximum fragment size
|
cls.max_fragment_size = FRAGMENT_MAX_SIZE; // guess a we can establish connection with maximum fragment size
|
||||||
@@ -1717,7 +1718,7 @@ static void CL_Reconnect( qboolean setup_netchan )
|
|||||||
{
|
{
|
||||||
if( setup_netchan )
|
if( setup_netchan )
|
||||||
{
|
{
|
||||||
CL_SetupNetchanForProtocol( cls.legacymode );
|
CL_SetupNetchanForProtocol( cls.net_protocol );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1762,7 +1763,7 @@ void CL_Disconnect( void )
|
|||||||
CL_Stop_f();
|
CL_Stop_f();
|
||||||
|
|
||||||
// send a disconnect message to the server
|
// send a disconnect message to the server
|
||||||
CL_SendDisconnectMessage( cls.legacymode );
|
CL_SendDisconnectMessage( cls.net_protocol );
|
||||||
SteamBroker_TerminateGameConnection();
|
SteamBroker_TerminateGameConnection();
|
||||||
CL_ClearState ();
|
CL_ClearState ();
|
||||||
|
|
||||||
@@ -1780,7 +1781,7 @@ void CL_Disconnect( void )
|
|||||||
cls.connect_retry = 0;
|
cls.connect_retry = 0;
|
||||||
memset( &cls.bandwidth_test, 0, sizeof( cls.bandwidth_test ));
|
memset( &cls.bandwidth_test, 0, sizeof( cls.bandwidth_test ));
|
||||||
cls.signon = 0;
|
cls.signon = 0;
|
||||||
cls.legacymode = PROTO_CURRENT;
|
cls.net_protocol = PROTO_CURRENT;
|
||||||
|
|
||||||
// back to menu in non-developer mode
|
// back to menu in non-developer mode
|
||||||
if( host_developer.value || cls.key_dest == key_menu )
|
if( host_developer.value || cls.key_dest == key_menu )
|
||||||
@@ -1808,7 +1809,7 @@ void CL_Crashed( void )
|
|||||||
CL_Stop_f(); // stop any demos
|
CL_Stop_f(); // stop any demos
|
||||||
|
|
||||||
// send a disconnect message to the server
|
// send a disconnect message to the server
|
||||||
CL_SendDisconnectMessage( cls.legacymode );
|
CL_SendDisconnectMessage( cls.net_protocol );
|
||||||
|
|
||||||
Host_WriteOpenGLConfig();
|
Host_WriteOpenGLConfig();
|
||||||
Host_WriteConfig(); // write config
|
Host_WriteConfig(); // write config
|
||||||
@@ -1914,7 +1915,7 @@ static void CL_Reconnect_f( void )
|
|||||||
|
|
||||||
if( !COM_StringEmptyOrNULL( cls.servername ))
|
if( !COM_StringEmptyOrNULL( cls.servername ))
|
||||||
{
|
{
|
||||||
connprotocol_t proto = cls.legacymode;
|
connprotocol_t proto = cls.net_protocol;
|
||||||
|
|
||||||
if( cls.state >= ca_connected )
|
if( cls.state >= ca_connected )
|
||||||
CL_Disconnect();
|
CL_Disconnect();
|
||||||
@@ -1923,7 +1924,7 @@ static void CL_Reconnect_f( void )
|
|||||||
cls.demonum = cls.movienum = -1; // not in the demo loop now
|
cls.demonum = cls.movienum = -1; // not in the demo loop now
|
||||||
cls.state = ca_connecting;
|
cls.state = ca_connecting;
|
||||||
cls.signon = 0;
|
cls.signon = 0;
|
||||||
cls.legacymode = proto; // don't change protocol
|
cls.net_protocol = proto; // don't change protocol
|
||||||
|
|
||||||
Con_Printf( "reconnecting...\n" );
|
Con_Printf( "reconnecting...\n" );
|
||||||
}
|
}
|
||||||
@@ -2072,23 +2073,61 @@ static void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
|
|||||||
UI_AddServerToList( from, infostring );
|
UI_AddServerToList( from, infostring );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static net_request_t *CL_NetRequestFind( netadr_t from, int type )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
|
{
|
||||||
|
net_request_t *nr = &clgame.net_requests[i];
|
||||||
|
|
||||||
|
if( !nr->pfnFunc || nr->resp.type != type )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( NET_CompareAdr( nr->resp.remote_address, from ))
|
||||||
|
return nr;
|
||||||
|
|
||||||
|
// broadcast requests accept a response from anyone
|
||||||
|
if( NET_NetadrType( &nr->resp.remote_address ) == NA_BROADCAST )
|
||||||
|
return nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CL_NetRequestComplete( net_request_t *nr, netadr_t from, const char *response )
|
||||||
|
{
|
||||||
|
netadr_t request_adr = nr->resp.remote_address;
|
||||||
|
|
||||||
|
nr->resp.response = (void *)response;
|
||||||
|
nr->resp.remote_address = from;
|
||||||
|
nr->resp.error = NET_SUCCESS;
|
||||||
|
nr->resp.ping = host.realtime - nr->timesend;
|
||||||
|
|
||||||
|
nr->pfnFunc( &nr->resp );
|
||||||
|
|
||||||
|
if( FBitSet( nr->flags, FNETAPI_MULTIPLE_RESPONSE ))
|
||||||
|
nr->resp.remote_address = request_adr;
|
||||||
|
else memset( nr, 0, sizeof( *nr )); // done
|
||||||
|
}
|
||||||
|
|
||||||
static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolean legacy_format )
|
static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolean legacy_format )
|
||||||
{
|
{
|
||||||
static char s[512+8];
|
static char s[512+8];
|
||||||
int p, numcl, maxcl, password, remaining, bots;
|
int p, numcl, maxcl, password, bots;
|
||||||
string host, map, gamedir, version;
|
string host, map, gamedir, version;
|
||||||
char *replace;
|
|
||||||
|
|
||||||
// set to beginning but skip header
|
// set to beginning but skip header
|
||||||
MSG_SeekToBit( msg, (sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
MSG_SeekToBit( msg, (sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||||
|
|
||||||
if( legacy_format )
|
if( legacy_format )
|
||||||
{
|
{
|
||||||
string address;
|
|
||||||
int mod;
|
int mod;
|
||||||
|
|
||||||
p = MSG_ReadByte( msg );
|
// FIXME: this is invalid, `m` servers might be geniune 47 proto servers
|
||||||
Q_strncpy( address, MSG_ReadString( msg ), sizeof( address ));
|
// but many servers that reply with legacy format are 47/48
|
||||||
|
// so at least let the user to connect them
|
||||||
|
p = PROTOCOL_GOLDSRC_VERSION;
|
||||||
|
|
||||||
|
MSG_ReadString( msg ); // address
|
||||||
Q_strncpy( host, MSG_ReadString( msg ), sizeof( host ));
|
Q_strncpy( host, MSG_ReadString( msg ), sizeof( host ));
|
||||||
Q_strncpy( map, MSG_ReadString( msg ), sizeof( map ));
|
Q_strncpy( map, MSG_ReadString( msg ), sizeof( map ));
|
||||||
Q_strncpy( gamedir, MSG_ReadString( msg ), sizeof( gamedir ));
|
Q_strncpy( gamedir, MSG_ReadString( msg ), sizeof( gamedir ));
|
||||||
@@ -2159,7 +2198,7 @@ static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolea
|
|||||||
|
|
||||||
// write host last so we can try to cut off too long hostnames
|
// write host last so we can try to cut off too long hostnames
|
||||||
// TODO: value size limit for infostrings
|
// TODO: value size limit for infostrings
|
||||||
remaining = sizeof( s ) - Q_strlen( s ) - sizeof( "\\host\\" ) - 1;
|
int remaining = sizeof( s ) - Q_strlen( s ) - sizeof( "\\host\\" ) - 1;
|
||||||
if( remaining < 0 )
|
if( remaining < 0 )
|
||||||
{
|
{
|
||||||
// should never happen?
|
// should never happen?
|
||||||
@@ -2167,103 +2206,205 @@ static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolea
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *replace;
|
||||||
while(( replace = Q_strpbrk( host, "\\\"" )))
|
while(( replace = Q_strpbrk( host, "\\\"" )))
|
||||||
{
|
|
||||||
*replace = ' '; // find a better replacement?
|
*replace = ' '; // find a better replacement?
|
||||||
}
|
|
||||||
|
|
||||||
Info_SetValueForKey( s, "host", host, sizeof( s ));
|
Info_SetValueForKey( s, "host", host, sizeof( s ));
|
||||||
|
|
||||||
|
// tell mainui about server
|
||||||
UI_AddServerToList( from, s );
|
UI_AddServerToList( from, s );
|
||||||
|
|
||||||
|
// now process NetAPI requst
|
||||||
|
net_request_t *nr = CL_NetRequestFind( from, NETAPI_REQUEST_DETAILS );
|
||||||
|
|
||||||
|
if( !nr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
s[0] = 0;
|
||||||
|
Info_SetValueForKey( s, "gamedir", gamedir, sizeof( s ));
|
||||||
|
Info_SetValueForKeyf( s, "current", sizeof( s ), "%i", numcl );
|
||||||
|
Info_SetValueForKeyf( s, "max", sizeof( s ), "%i", maxcl );
|
||||||
|
Info_SetValueForKey( s, "map", map, sizeof( s ));
|
||||||
|
Info_SetValueForKey( s, "hostname", host, sizeof( s ));
|
||||||
|
|
||||||
|
CL_NetRequestComplete( nr, from, s );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================
|
=================
|
||||||
CL_ParseNETInfoMessage
|
CL_NetRequestSend
|
||||||
|
|
||||||
Handle a reply from a netinfo
|
|
||||||
=================
|
=================
|
||||||
*/
|
*/
|
||||||
static void CL_ParseNETInfoMessage( netadr_t from, const char *s )
|
qboolean CL_NetRequestSend( net_request_t *nr )
|
||||||
{
|
{
|
||||||
net_request_t *nr = NULL;
|
byte buf[64];
|
||||||
static char infostring[MAX_PRINT_MSG];
|
sizebuf_t msg;
|
||||||
int i, context, type;
|
|
||||||
int errorBits = 0;
|
|
||||||
const char *val;
|
|
||||||
|
|
||||||
context = Q_atoi( Cmd_Argv( 1 ));
|
if( nr->resp.type == NETAPI_REQUEST_PING )
|
||||||
type = Q_atoi( Cmd_Argv( 2 ));
|
|
||||||
|
|
||||||
// find request with specified context and type
|
|
||||||
for( i = 0; i < MAX_REQUESTS; i++ )
|
|
||||||
{
|
{
|
||||||
if( clgame.net_requests[i].resp.context == context && clgame.net_requests[i].resp.type == type )
|
Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, A2A_GOLDSRC_PING );
|
||||||
{
|
return true;
|
||||||
nr = &clgame.net_requests[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found, ignore
|
MSG_Init( &msg, "NetRequest", buf, sizeof( buf ));
|
||||||
if( nr == NULL )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// find the payload
|
switch( nr->resp.type )
|
||||||
s = Q_strchr( s, ' ' ); // skip netinfo
|
|
||||||
if( !s )
|
|
||||||
return;
|
|
||||||
|
|
||||||
s = Q_strchr( s + 1, ' ' ); // skip challenge
|
|
||||||
if( !s )
|
|
||||||
return;
|
|
||||||
|
|
||||||
s = Q_strchr( s + 1, ' ' ); // skip type
|
|
||||||
if( s )
|
|
||||||
s++; // skip final whitespace
|
|
||||||
else if( type != NETAPI_REQUEST_PING ) // ping have no payload, and that's ok
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( s )
|
|
||||||
{
|
{
|
||||||
if( s[0] == '\\' )
|
case NETAPI_REQUEST_DETAILS:
|
||||||
{
|
MSG_WriteString( &msg, A2S_GOLDSRC_INFO );
|
||||||
// check for errors
|
break;
|
||||||
val = Info_ValueForKey( s, "neterror" );
|
case NETAPI_REQUEST_PLAYERS:
|
||||||
|
MSG_WriteByte( &msg, A2S_GOLDSRC_PLAYERS );
|
||||||
if( !Q_stricmp( val, "protocol" ))
|
break;
|
||||||
SetBits( errorBits, NET_ERROR_PROTO_UNSUPPORTED );
|
case NETAPI_REQUEST_RULES:
|
||||||
else if( !Q_stricmp( val, "undefined" ))
|
MSG_WriteByte( &msg, A2S_GOLDSRC_RULES );
|
||||||
SetBits( errorBits, NET_ERROR_UNDEFINED );
|
break;
|
||||||
else if( !Q_stricmp( val, "forbidden" ))
|
default:
|
||||||
SetBits( errorBits, NET_ERROR_FORBIDDEN );
|
return false;
|
||||||
|
|
||||||
CL_FixupColorStringsForInfoString( s, infostring, sizeof( infostring ));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Q_strncpy( infostring, s, sizeof( infostring ));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
infostring[0] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup the answer
|
MSG_WriteLong( &msg, nr->challenge );
|
||||||
nr->resp.response = infostring;
|
|
||||||
nr->resp.remote_address = from;
|
|
||||||
nr->resp.error = NET_SUCCESS;
|
|
||||||
nr->resp.ping = host.realtime - nr->timesend;
|
|
||||||
|
|
||||||
if( nr->timeout <= host.realtime )
|
Netchan_OutOfBand( NS_CLIENT, nr->resp.remote_address, MSG_GetNumBytesWritten( &msg ), MSG_GetData( &msg ));
|
||||||
SetBits( nr->resp.error, NET_ERROR_TIMEOUT );
|
|
||||||
SetBits( nr->resp.error, errorBits ); // misc error bits
|
|
||||||
|
|
||||||
nr->pfnFunc( &nr->resp );
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if( !FBitSet( nr->flags, FNETAPI_MULTIPLE_RESPONSE ))
|
/*
|
||||||
memset( nr, 0, sizeof( *nr )); // done
|
=================
|
||||||
|
CL_HasActiveNetRequest
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
qboolean CL_HasActiveNetRequest( netadr_t from )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
|
{
|
||||||
|
const net_request_t *nr = &clgame.net_requests[i];
|
||||||
|
|
||||||
|
if( !nr->pfnFunc )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( nr->resp.type != NETAPI_REQUEST_RULES && nr->resp.type != NETAPI_REQUEST_PLAYERS )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( NET_CompareAdr( nr->resp.remote_address, from ))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_NetRequestChallenge
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static void CL_NetRequestChallenge( netadr_t from, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
MSG_SeekToBit( msg, ( sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||||
|
int challenge = MSG_ReadLong( msg );
|
||||||
|
|
||||||
|
// give the challenge to the first request without one,
|
||||||
|
// the server sends a challenge per received query
|
||||||
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
|
{
|
||||||
|
net_request_t *nr = &clgame.net_requests[i];
|
||||||
|
|
||||||
|
if( !nr->pfnFunc || nr->resp.type == NETAPI_REQUEST_PING )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( nr->challenge != -1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !NET_CompareAdr( nr->resp.remote_address, from ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nr->challenge = challenge;
|
||||||
|
CL_NetRequestSend( nr );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_NetRequestPing
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static void CL_NetRequestPing( netadr_t from )
|
||||||
|
{
|
||||||
|
net_request_t *nr = CL_NetRequestFind( from, NETAPI_REQUEST_PING );
|
||||||
|
|
||||||
|
if( !nr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
CL_NetRequestComplete( nr, from, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_NetRequestPlayers
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static void CL_NetRequestPlayers( netadr_t from, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
static char s[MAX_PRINT_MSG];
|
||||||
|
net_request_t *nr = CL_NetRequestFind( from, NETAPI_REQUEST_PLAYERS );
|
||||||
|
|
||||||
|
if( !nr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MSG_SeekToBit( msg, ( sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||||
|
|
||||||
|
int count = MSG_ReadByte( msg );
|
||||||
|
|
||||||
|
s[0] = 0;
|
||||||
|
for( int i = 0; i < count && !MSG_CheckOverflow( msg ); i++ )
|
||||||
|
{
|
||||||
|
MSG_ReadByte( msg ); // index, GoldSrc servers always send 0 here
|
||||||
|
Info_SetValueForKey( s, va( "p%iname", i ), MSG_ReadString( msg ), sizeof( s ));
|
||||||
|
Info_SetValueForKeyf( s, va( "p%ifrags", i ), sizeof( s ), "%i", MSG_ReadLong( msg ));
|
||||||
|
Info_SetValueForKeyf( s, va( "p%itime", i ), sizeof( s ), "%f", MSG_ReadFloat( msg ));
|
||||||
|
}
|
||||||
|
Info_SetValueForKeyf( s, "players", sizeof( s ), "%i", count );
|
||||||
|
|
||||||
|
if( MSG_CheckOverflow( msg ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CL_NetRequestComplete( nr, from, s );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
CL_NetRequestRules
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static void CL_NetRequestRules( netadr_t from, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
static char s[MAX_PRINT_MSG];
|
||||||
|
net_request_t *nr = CL_NetRequestFind( from, NETAPI_REQUEST_RULES );
|
||||||
|
|
||||||
|
if( !nr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
MSG_SeekToBit( msg, ( sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||||
|
|
||||||
|
int count = MSG_ReadShort( msg );
|
||||||
|
|
||||||
|
s[0] = 0;
|
||||||
|
for( int i = 0; i < count && !MSG_CheckOverflow( msg ); i++ )
|
||||||
|
{
|
||||||
|
string key;
|
||||||
|
|
||||||
|
Q_strncpy( key, MSG_ReadString( msg ), sizeof( key ));
|
||||||
|
Info_SetValueForKey( s, key, MSG_ReadString( msg ), sizeof( s ));
|
||||||
|
}
|
||||||
|
Info_SetValueForKeyf( s, "rules", sizeof( s ), "%i", count );
|
||||||
|
|
||||||
|
if( MSG_CheckOverflow( msg ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CL_NetRequestComplete( nr, from, s );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2275,14 +2416,13 @@ check for timeouts
|
|||||||
*/
|
*/
|
||||||
static void CL_ProcessNetRequests( void )
|
static void CL_ProcessNetRequests( void )
|
||||||
{
|
{
|
||||||
net_request_t *nr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// find a request with specified context
|
// find a request with specified context
|
||||||
for( i = 0; i < MAX_REQUESTS; i++ )
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
{
|
{
|
||||||
nr = &clgame.net_requests[i];
|
net_request_t *nr = &clgame.net_requests[i];
|
||||||
if( !nr->pfnFunc ) continue; // not used
|
|
||||||
|
if( !nr->pfnFunc )
|
||||||
|
continue; // not used
|
||||||
|
|
||||||
if( nr->timeout <= host.realtime )
|
if( nr->timeout <= host.realtime )
|
||||||
{
|
{
|
||||||
@@ -2443,6 +2583,29 @@ static void CL_ClientConnect( connprotocol_t proto, const char *c, netadr_t from
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *challenge_str = Info_ValueForKey( Cmd_Argv( 1 ), "challenge" );
|
||||||
|
|
||||||
|
if( COM_StringEmpty( challenge_str ))
|
||||||
|
{
|
||||||
|
qboolean require_challenge;
|
||||||
|
|
||||||
|
if( cl_require_challenge_echo.value < 0 )
|
||||||
|
require_challenge = false; // FIXME: flip once most servers migrate to newer engine version
|
||||||
|
else
|
||||||
|
require_challenge = !!cl_require_challenge_echo.value;
|
||||||
|
|
||||||
|
if( require_challenge )
|
||||||
|
{
|
||||||
|
Con_Printf( S_WARN "%s: %s did not echo challenge, ignoring (possible spoof)\n", __func__, NET_AdrToString( from ));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( Q_atoi( challenge_str ) != cls.bandwidth_test.challenge )
|
||||||
|
{
|
||||||
|
Con_Printf( S_WARN "%s: challenge mismatch from %s, ignoring (possible spoof)\n", __func__, NET_AdrToString( from ));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( cls.netchan_pending_cookie != 0 )
|
if( cls.netchan_pending_cookie != 0 )
|
||||||
{
|
{
|
||||||
int server_extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" ));
|
int server_extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" ));
|
||||||
@@ -2497,8 +2660,16 @@ static void CL_Print( const char *c, const char *args, netadr_t from, sizebuf_t
|
|||||||
Con_Printf( "%s%c", s, s[Q_strlen( s ) - 1] != '\n' ? '\n' : '\0' );
|
Con_Printf( "%s%c", s, s[Q_strlen( s ) - 1] != '\n' ? '\n' : '\0' );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CL_Challenge( const char *c, netadr_t from )
|
static void CL_Challenge( const char *c, netadr_t from, sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
|
// connection challenge is text and always comes as literal A00000000,
|
||||||
|
// query challenge is binary: challenge type byte and the challenge value
|
||||||
|
if( c[0] == S2C_GOLDSRC_CHALLENGE[0] && Q_strcmp( c, S2C_GOLDSRC_CHALLENGE ))
|
||||||
|
{
|
||||||
|
CL_NetRequestChallenge( from, msg );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if( cls.state != ca_connecting )
|
if( cls.state != ca_connecting )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2508,7 +2679,7 @@ static void CL_Challenge( const char *c, netadr_t from )
|
|||||||
// try to autodetect protocol by challenge response
|
// try to autodetect protocol by challenge response
|
||||||
if( !Q_strcmp( c, S2C_GOLDSRC_CHALLENGE ))
|
if( !Q_strcmp( c, S2C_GOLDSRC_CHALLENGE ))
|
||||||
{
|
{
|
||||||
cls.legacymode = PROTO_GOLDSRC;
|
cls.net_protocol = PROTO_GOLDSRC;
|
||||||
|
|
||||||
cls.steam_auth = Q_atoi( Cmd_Argv( 2 )) == 3;
|
cls.steam_auth = Q_atoi( Cmd_Argv( 2 )) == 3;
|
||||||
|
|
||||||
@@ -2524,14 +2695,14 @@ static void CL_Challenge( const char *c, netadr_t from )
|
|||||||
|
|
||||||
cls.bandwidth_test.challenge = Q_atoi( Cmd_Argv( 1 ));
|
cls.bandwidth_test.challenge = Q_atoi( Cmd_Argv( 1 ));
|
||||||
|
|
||||||
if( cls.legacymode == PROTO_CURRENT && cl_test_bandwidth.value && !cls.bandwidth_test.passed )
|
if( cls.net_protocol == PROTO_CURRENT && cl_test_bandwidth.value && !cls.bandwidth_test.passed )
|
||||||
{
|
{
|
||||||
// when connecting to old server or server that has bandwidth test disabled
|
// when connecting to old server or server that has bandwidth test disabled
|
||||||
// it might be more preferrable to have some sane fragment size
|
// it might be more preferrable to have some sane fragment size
|
||||||
if( !Q_atoi( Cmd_Argv( 2 )))
|
if( !Q_atoi( Cmd_Argv( 2 )))
|
||||||
{
|
{
|
||||||
Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE );
|
Cvar_SetValue( "cl_dlmax", FRAGMENT_DEFAULT_SIZE );
|
||||||
CL_SendConnectPacket( cls.legacymode, cls.bandwidth_test.challenge );
|
CL_SendConnectPacket( cls.net_protocol, cls.bandwidth_test.challenge );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2541,7 +2712,7 @@ static void CL_Challenge( const char *c, netadr_t from )
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// challenge from the server we are connecting to
|
// challenge from the server we are connecting to
|
||||||
CL_SendConnectPacket( cls.legacymode, cls.bandwidth_test.challenge );
|
CL_SendConnectPacket( cls.net_protocol, cls.bandwidth_test.challenge );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2686,7 +2857,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
|||||||
// server connection
|
// server connection
|
||||||
if( !Q_strcmp( c, S2C_GOLDSRC_CONNECTION ) || !Q_strcmp( c, S2C_CONNECTION ))
|
if( !Q_strcmp( c, S2C_GOLDSRC_CONNECTION ) || !Q_strcmp( c, S2C_CONNECTION ))
|
||||||
{
|
{
|
||||||
CL_ClientConnect( cls.legacymode, c, from );
|
CL_ClientConnect( cls.net_protocol, c, from );
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( c, A2A_INFO ))
|
else if( !Q_strcmp( c, A2A_INFO ))
|
||||||
{
|
{
|
||||||
@@ -2700,9 +2871,13 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
|||||||
{
|
{
|
||||||
CL_ParseGoldSrcStatusMessage( from, msg, true );
|
CL_ParseGoldSrcStatusMessage( from, msg, true );
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( c, A2A_NETINFO ))
|
else if( c[0] == S2A_GOLDSRC_PLAYERS )
|
||||||
{
|
{
|
||||||
CL_ParseNETInfoMessage( from, args ); // server responding to a status broadcast
|
CL_NetRequestPlayers( from, msg );
|
||||||
|
}
|
||||||
|
else if( c[0] == S2A_GOLDSRC_RULES )
|
||||||
|
{
|
||||||
|
CL_NetRequestRules( from, msg );
|
||||||
}
|
}
|
||||||
else if( c[0] == A2C_GOLDSRC_PRINT || !Q_strcmp( c, A2C_PRINT ))
|
else if( c[0] == A2C_GOLDSRC_PRINT || !Q_strcmp( c, A2C_PRINT ))
|
||||||
{
|
{
|
||||||
@@ -2722,11 +2897,11 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
|||||||
}
|
}
|
||||||
else if( !Q_strcmp( c, A2A_ACK ) || !Q_strcmp( c, A2A_GOLDSRC_ACK ))
|
else if( !Q_strcmp( c, A2A_ACK ) || !Q_strcmp( c, A2A_GOLDSRC_ACK ))
|
||||||
{
|
{
|
||||||
// no-op
|
CL_NetRequestPing( from ); // server responding to a NetAPI ping
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( c, S2C_CHALLENGE ) || !Q_strcmp( c, S2C_GOLDSRC_CHALLENGE ))
|
else if( !Q_strcmp( c, S2C_CHALLENGE ) || c[0] == S2C_GOLDSRC_CHALLENGE[0] )
|
||||||
{
|
{
|
||||||
CL_Challenge( c, from );
|
CL_Challenge( c, from, msg );
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( c, S2C_REJECT ) || c[0] == S2C_GOLDSRC_REJECT || c[0] == S2C_GOLDSRC_REJECT_BADPASSWORD )
|
else if( !Q_strcmp( c, S2C_REJECT ) || c[0] == S2C_GOLDSRC_REJECT || c[0] == S2C_GOLDSRC_REJECT_BADPASSWORD )
|
||||||
{
|
{
|
||||||
@@ -2806,7 +2981,7 @@ static void CL_ReadNetMessage( void )
|
|||||||
size_t curSize;
|
size_t curSize;
|
||||||
void (*parsefn)( sizebuf_t *msg );
|
void (*parsefn)( sizebuf_t *msg );
|
||||||
|
|
||||||
switch( cls.legacymode )
|
switch( cls.net_protocol )
|
||||||
{
|
{
|
||||||
case PROTO_QUAKE:
|
case PROTO_QUAKE:
|
||||||
parsefn = CL_ParseQuakeMessage;
|
parsefn = CL_ParseQuakeMessage;
|
||||||
@@ -3102,7 +3277,7 @@ void CL_ProcessFile( qboolean successfully_received, const char *filename )
|
|||||||
MSG_Init( &msg, "Resource Registration", msg_buf, sizeof( msg_buf ));
|
MSG_Init( &msg, "Resource Registration", msg_buf, sizeof( msg_buf ));
|
||||||
|
|
||||||
if( CL_PrecacheResources( ))
|
if( CL_PrecacheResources( ))
|
||||||
CL_RegisterResources( &msg, cls.legacymode );
|
CL_RegisterResources( &msg, cls.net_protocol );
|
||||||
|
|
||||||
if( MSG_GetNumBytesWritten( &msg ) > 0 )
|
if( MSG_GetNumBytesWritten( &msg ) > 0 )
|
||||||
{
|
{
|
||||||
@@ -3162,7 +3337,7 @@ tell server about changed userinfo
|
|||||||
*/
|
*/
|
||||||
void CL_UpdateInfo( const char *key, const char *value )
|
void CL_UpdateInfo( const char *key, const char *value )
|
||||||
{
|
{
|
||||||
switch( cls.legacymode )
|
switch( cls.net_protocol )
|
||||||
{
|
{
|
||||||
case PROTO_GOLDSRC:
|
case PROTO_GOLDSRC:
|
||||||
if( cl_advertise_engine_in_name.value && !Q_stricmp( key, "name" ) && Q_strnicmp( value, "[Xash3D]", 8 ))
|
if( cl_advertise_engine_in_name.value && !Q_stricmp( key, "name" ) && Q_strnicmp( value, "[Xash3D]", 8 ))
|
||||||
@@ -3495,6 +3670,7 @@ static void CL_InitLocal( void )
|
|||||||
Cvar_RegisterVariable( &cl_logoupdate );
|
Cvar_RegisterVariable( &cl_logoupdate );
|
||||||
Cvar_RegisterVariable( &cl_logomaxdim );
|
Cvar_RegisterVariable( &cl_logomaxdim );
|
||||||
Cvar_RegisterVariable( &cl_test_bandwidth );
|
Cvar_RegisterVariable( &cl_test_bandwidth );
|
||||||
|
Cvar_RegisterVariable( &cl_require_challenge_echo );
|
||||||
|
|
||||||
Voice_RegisterCvars();
|
Voice_RegisterCvars();
|
||||||
VGui_RegisterCvars();
|
VGui_RegisterCvars();
|
||||||
|
|||||||
@@ -445,6 +445,7 @@ typedef struct
|
|||||||
double timeout;
|
double timeout;
|
||||||
double timesend; // time when request was sended
|
double timesend; // time when request was sended
|
||||||
int flags; // FNETAPI_MULTIPLE_RESPONSE etc
|
int flags; // FNETAPI_MULTIPLE_RESPONSE etc
|
||||||
|
int challenge; // GoldSrc query challenge, -1 until received
|
||||||
} net_request_t;
|
} net_request_t;
|
||||||
|
|
||||||
// new versions of client dlls have a single export with all callbacks
|
// new versions of client dlls have a single export with all callbacks
|
||||||
@@ -647,7 +648,7 @@ typedef struct
|
|||||||
string internetservers_customfilter;
|
string internetservers_customfilter;
|
||||||
|
|
||||||
// multiprotocol support
|
// multiprotocol support
|
||||||
connprotocol_t legacymode;
|
connprotocol_t net_protocol;
|
||||||
int extensions;
|
int extensions;
|
||||||
|
|
||||||
netadr_t serveradr;
|
netadr_t serveradr;
|
||||||
@@ -793,6 +794,7 @@ void CL_ClearState( void );
|
|||||||
void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats );
|
void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats );
|
||||||
void CL_SendGoldSrcConnectPacket( netadr_t adr, int challenge, const void *ticket, size_t ticketlen );
|
void CL_SendGoldSrcConnectPacket( netadr_t adr, int challenge, const void *ticket, size_t ticketlen );
|
||||||
void CL_NotifyServerListResponse( void );
|
void CL_NotifyServerListResponse( void );
|
||||||
|
qboolean CL_NetRequestSend( net_request_t *nr );
|
||||||
|
|
||||||
//
|
//
|
||||||
// cl_demo.c
|
// cl_demo.c
|
||||||
|
|||||||
@@ -3364,44 +3364,60 @@ NetAPI_SendRequest
|
|||||||
*/
|
*/
|
||||||
static void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double timeout, netadr_t *remote_address, net_api_response_func_t response )
|
static void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double timeout, netadr_t *remote_address, net_api_response_func_t response )
|
||||||
{
|
{
|
||||||
net_request_t *nr = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if( !response )
|
if( !response )
|
||||||
{
|
{
|
||||||
Con_DPrintf( S_ERROR "%s: no callbcak specified for request with context %i!\n", __func__, context );
|
Con_DPrintf( S_ERROR "%s: no callback specified for request with context %i!\n", __func__, context );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: call pfnFunc and tell client.dll about errors?
|
||||||
|
|
||||||
if( NET_NetadrType( remote_address ) == NA_IPX || NET_NetadrType( remote_address ) == NA_BROADCAST_IPX )
|
if( NET_NetadrType( remote_address ) == NA_IPX || NET_NetadrType( remote_address ) == NA_BROADCAST_IPX )
|
||||||
return; // IPX no longer support
|
return; // IPX no longer support
|
||||||
|
|
||||||
if( request == NETAPI_REQUEST_SERVERLIST )
|
switch( request )
|
||||||
|
{
|
||||||
|
case NETAPI_REQUEST_SERVERLIST:
|
||||||
return; // no support for server list requests
|
return; // no support for server list requests
|
||||||
|
case NETAPI_REQUEST_PING:
|
||||||
|
case NETAPI_REQUEST_RULES:
|
||||||
|
case NETAPI_REQUEST_PLAYERS:
|
||||||
|
case NETAPI_REQUEST_DETAILS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Con_Printf( S_ERROR "%s: unknown request type %d, context %i\n", __func__, request, context );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// find a free request
|
// find a free request
|
||||||
for( i = 0; i < MAX_REQUESTS; i++ )
|
net_request_t *nr = NULL, *oldest_nr = NULL;
|
||||||
|
int i;
|
||||||
|
double max_timeout = 0;
|
||||||
|
for( i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
{
|
{
|
||||||
nr = &clgame.net_requests[i];
|
nr = &clgame.net_requests[i];
|
||||||
if( !nr->pfnFunc ) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( i == MAX_REQUESTS )
|
if(( host.realtime - nr->timesend ) > max_timeout )
|
||||||
{
|
|
||||||
double max_timeout = 0;
|
|
||||||
|
|
||||||
// no free requests? use oldest
|
|
||||||
for( i = 0, nr = NULL; i < MAX_REQUESTS; i++ )
|
|
||||||
{
|
{
|
||||||
if(( host.realtime - clgame.net_requests[i].timesend ) > max_timeout )
|
max_timeout = host.realtime - nr->timesend;
|
||||||
{
|
oldest_nr = nr;
|
||||||
max_timeout = host.realtime - clgame.net_requests[i].timesend;
|
|
||||||
nr = &clgame.net_requests[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !nr->pfnFunc )
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert( nr != NULL );
|
if( i == ARRAYSIZE( clgame.net_requests ))
|
||||||
|
{
|
||||||
|
// no free requests? use oldest
|
||||||
|
nr = oldest_nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !nr )
|
||||||
|
{
|
||||||
|
Con_Printf( S_ERROR "%s: no free requests\n", __func__ );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// clear slot
|
// clear slot
|
||||||
memset( nr, 0, sizeof( *nr ));
|
memset( nr, 0, sizeof( *nr ));
|
||||||
@@ -3414,9 +3430,10 @@ static void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags,
|
|||||||
nr->resp.type = request;
|
nr->resp.type = request;
|
||||||
nr->resp.remote_address = *remote_address;
|
nr->resp.remote_address = *remote_address;
|
||||||
nr->flags = flags;
|
nr->flags = flags;
|
||||||
|
nr->challenge = -1;
|
||||||
|
|
||||||
// local servers request
|
if( !CL_NetRequestSend( nr ))
|
||||||
Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, A2A_NETINFO" %i %i %i", PROTOCOL_VERSION, context, request );
|
Con_Printf( S_ERROR "%s: failed to send net request for type %d with context %i\n", __func__, request, context );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3428,7 +3445,7 @@ NetAPI_CancelRequest
|
|||||||
static void GAME_EXPORT NetAPI_CancelRequest( int context )
|
static void GAME_EXPORT NetAPI_CancelRequest( int context )
|
||||||
{
|
{
|
||||||
// find a specified request
|
// find a specified request
|
||||||
for( int i = 0; i < MAX_REQUESTS; i++ )
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
{
|
{
|
||||||
net_request_t *nr = &clgame.net_requests[i];
|
net_request_t *nr = &clgame.net_requests[i];
|
||||||
|
|
||||||
@@ -3456,7 +3473,7 @@ NetAPI_CancelAllRequests
|
|||||||
void GAME_EXPORT NetAPI_CancelAllRequests( void )
|
void GAME_EXPORT NetAPI_CancelAllRequests( void )
|
||||||
{
|
{
|
||||||
// tell the user about cancel
|
// tell the user about cancel
|
||||||
for( int i = 0; i < MAX_REQUESTS; i++ )
|
for( int i = 0; i < ARRAYSIZE( clgame.net_requests ); i++ )
|
||||||
{
|
{
|
||||||
net_request_t *nr = &clgame.net_requests[i];
|
net_request_t *nr = &clgame.net_requests[i];
|
||||||
if( !nr->pfnFunc ) continue; // not used
|
if( !nr->pfnFunc ) continue; // not used
|
||||||
|
|||||||
@@ -706,6 +706,9 @@ static void R_CollectRendererNames( void )
|
|||||||
#if XASH_REF_GLES3COMPAT_ENABLED
|
#if XASH_REF_GLES3COMPAT_ENABLED
|
||||||
"gles3compat",
|
"gles3compat",
|
||||||
#endif
|
#endif
|
||||||
|
#if XASH_REF_DX2_ENABLED
|
||||||
|
"dx2",
|
||||||
|
#endif
|
||||||
#if XASH_REF_SOFT_ENABLED
|
#if XASH_REF_SOFT_ENABLED
|
||||||
"soft",
|
"soft",
|
||||||
#endif
|
#endif
|
||||||
@@ -729,6 +732,9 @@ static void R_CollectRendererNames( void )
|
|||||||
#if XASH_REF_GLES3COMPAT_ENABLED
|
#if XASH_REF_GLES3COMPAT_ENABLED
|
||||||
"GLES3 (gl2_shim)",
|
"GLES3 (gl2_shim)",
|
||||||
#endif
|
#endif
|
||||||
|
#if XASH_REF_DX2_ENABLED
|
||||||
|
"DirectX 2",
|
||||||
|
#endif
|
||||||
#if XASH_REF_SOFT_ENABLED
|
#if XASH_REF_SOFT_ENABLED
|
||||||
"Software",
|
"Software",
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ void CL_BatchResourceRequest( qboolean initialize )
|
|||||||
{
|
{
|
||||||
if( done_downloading && CL_PrecacheResources( ))
|
if( done_downloading && CL_PrecacheResources( ))
|
||||||
{
|
{
|
||||||
CL_RegisterResources( &msg, cls.legacymode );
|
CL_RegisterResources( &msg, cls.net_protocol );
|
||||||
}
|
}
|
||||||
|
|
||||||
Netchan_CreateFragments( &cls.netchan, &msg );
|
Netchan_CreateFragments( &cls.netchan, &msg );
|
||||||
@@ -1186,8 +1186,8 @@ void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto )
|
|||||||
int type = MSG_ReadUBitLong( msg, 2 );
|
int type = MSG_ReadUBitLong( msg, 2 );
|
||||||
int delta_type;
|
int delta_type;
|
||||||
|
|
||||||
if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
|
if( FBitSet( type, ENTITY_BEAM )) delta_type = DT_CUSTOM_ENTITY_STATE_T;
|
||||||
else if( type != ENTITY_NORMAL ) delta_type = DT_CUSTOM_ENTITY_STATE_T;
|
else if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
|
||||||
else delta_type = DT_ENTITY_STATE_T;
|
else delta_type = DT_ENTITY_STATE_T;
|
||||||
|
|
||||||
Delta_ReadGSFields( msg, delta_type, &ent->prevstate, &ent->baseline, 1.0f );
|
Delta_ReadGSFields( msg, delta_type, &ent->prevstate, &ent->baseline, 1.0f );
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ static void CL_ParseSoundPacketGS( sizebuf_t *msg )
|
|||||||
float attn;
|
float attn;
|
||||||
if( FBitSet( flags, SND_ATTENUATION ))
|
if( FBitSet( flags, SND_ATTENUATION ))
|
||||||
attn = (float)MSG_ReadByte( msg ) / 64.0f;
|
attn = (float)MSG_ReadByte( msg ) / 64.0f;
|
||||||
else attn = ATTN_NONE;
|
else attn = 1.0f;
|
||||||
|
|
||||||
int chan = MSG_ReadUBitLong( msg, 3 );
|
int chan = MSG_ReadUBitLong( msg, 3 );
|
||||||
int entnum = MSG_ReadUBitLong( msg, MAX_GOLDSRC_ENTITY_BITS );
|
int entnum = MSG_ReadUBitLong( msg, MAX_GOLDSRC_ENTITY_BITS );
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ void FS_FreeStream( stream_t *stream )
|
|||||||
stream->format->freefunc( stream );
|
stream->format->freefunc( stream );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XASH_ENGINE_TESTS
|
#if XASH_LLVM_LIBFUZZER
|
||||||
#define IMPLEMENT_SOUNDLIB_FUZZ_TARGET( export, target ) \
|
#define IMPLEMENT_SOUNDLIB_FUZZ_TARGET( export, target ) \
|
||||||
int EXPORT export( const uint8_t *Data, size_t Size ); \
|
int EXPORT export( const uint8_t *Data, size_t Size ); \
|
||||||
int EXPORT export( const uint8_t *Data, size_t Size ) \
|
int EXPORT export( const uint8_t *Data, size_t Size ) \
|
||||||
@@ -294,4 +294,4 @@ int EXPORT export( const uint8_t *Data, size_t Size ) \
|
|||||||
|
|
||||||
IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadMPG, Sound_LoadMPG )
|
IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadMPG, Sound_LoadMPG )
|
||||||
IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadWAV, Sound_LoadWAV )
|
IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadWAV, Sound_LoadWAV )
|
||||||
#endif
|
#endif // XASH_LLVM_LIBFUZZER
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ static qboolean Sound_ParseID3Tag( const byte *buffer, fs_offset_t filesize )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XASH_ENGINE_TESTS
|
#if XASH_LLVM_LIBFUZZER
|
||||||
int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size );
|
int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size );
|
||||||
int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size )
|
int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size )
|
||||||
{
|
{
|
||||||
@@ -193,7 +193,7 @@ int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size )
|
|||||||
Sound_ParseID3Tag( Data, Size );
|
Sound_ParseID3Tag( Data, Size );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // XASH_LLVM_LIBFUZZER
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================================================================
|
=================================================================
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
void *context; // handle to GL rendering context
|
void *context; // handle to GL rendering context
|
||||||
ref_safegl_context_t safe;
|
ref_safegl_context_t safe;
|
||||||
qboolean software;
|
ref_graphic_apis_t type;
|
||||||
} glwstate_t;
|
} glwstate_t;
|
||||||
|
|
||||||
extern glwstate_t glw_state;
|
extern glwstate_t glw_state;
|
||||||
|
|||||||
@@ -701,6 +701,10 @@ void Test_RunImagelib( void )
|
|||||||
|
|
||||||
Z_Free( rgb.buffer );
|
Z_Free( rgb.buffer );
|
||||||
}
|
}
|
||||||
|
#endif // XASH_ENGINE_TESTS
|
||||||
|
|
||||||
|
#if XASH_LLVM_LIBFUZZER
|
||||||
|
#include "sprite.h"
|
||||||
|
|
||||||
#define IMPLEMENT_IMAGELIB_FUZZ_TARGET( export, target ) \
|
#define IMPLEMENT_IMAGELIB_FUZZ_TARGET( export, target ) \
|
||||||
int EXPORT export( const uint8_t *Data, size_t Size ); \
|
int EXPORT export( const uint8_t *Data, size_t Size ); \
|
||||||
@@ -724,4 +728,29 @@ IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadPNG, Image_LoadPNG )
|
|||||||
IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadDDS, Image_LoadDDS )
|
IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadDDS, Image_LoadDDS )
|
||||||
IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadTGA, Image_LoadTGA )
|
IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadTGA, Image_LoadTGA )
|
||||||
|
|
||||||
#endif /* XASH_ENGINE_TESTS */
|
int EXPORT Fuzz_Image_LoadSPR( const uint8_t *Data, size_t Size );
|
||||||
|
int EXPORT Fuzz_Image_LoadSPR( const uint8_t *Data, size_t Size )
|
||||||
|
{
|
||||||
|
rgbdata_t *rgb;
|
||||||
|
|
||||||
|
// the real caller passes only the pixel data size, the frame header is extra
|
||||||
|
if( Size < sizeof( dspriteframe_t ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
host.type = HOST_NORMAL;
|
||||||
|
Memory_Init();
|
||||||
|
Image_Init();
|
||||||
|
|
||||||
|
image.hint = IL_HINT_Q1; // installs the Quake palette so the decoder runs
|
||||||
|
|
||||||
|
if( Image_LoadSPR( "#internal.spr", Data, Size - sizeof( dspriteframe_t )))
|
||||||
|
{
|
||||||
|
rgb = ImagePack( "#internal.spr" );
|
||||||
|
FS_FreeImage( rgb );
|
||||||
|
}
|
||||||
|
|
||||||
|
Image_Shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // XASH_LLVM_LIBFUZZER
|
||||||
|
|||||||
@@ -262,10 +262,10 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, fs_offset_t filesi
|
|||||||
image.width = pin.width;
|
image.width = pin.width;
|
||||||
image.height = pin.height;
|
image.height = pin.height;
|
||||||
|
|
||||||
if( filesize < image.width * image.height )
|
if( filesize < (fs_offset_t)image.width * image.height )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if( filesize == ( image.width * image.height * 4 ))
|
if( filesize == (fs_offset_t)image.width * image.height * 4 )
|
||||||
truecolor = true;
|
truecolor = true;
|
||||||
|
|
||||||
// sorry, can't validate palette rendermode
|
// sorry, can't validate palette rendermode
|
||||||
|
|||||||
@@ -225,33 +225,33 @@ void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto )
|
|||||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO " %i", PROTOCOL_VERSION );
|
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO " %i", PROTOCOL_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int NET_ParseMasterStaticBody( char *body )
|
static int NET_ParseMasterStaticBody( const byte *body, size_t size )
|
||||||
{
|
{
|
||||||
char token[MAX_TOKEN];
|
char line[1024];
|
||||||
char *pfile = body;
|
int offset = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
while(( pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL )))
|
while( Q_memfgets( (byte *)body, size, &offset, line, sizeof( line )) != NULL )
|
||||||
{
|
{
|
||||||
|
char token[MAX_TOKEN];
|
||||||
|
char *pfile = line;
|
||||||
qboolean gs;
|
qboolean gs;
|
||||||
|
netadr_t adr = { 0 };
|
||||||
|
|
||||||
|
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
||||||
|
if( !pfile || token[0] == '\0' )
|
||||||
|
continue;
|
||||||
|
|
||||||
if( !Q_strcmp( token, "ip" ))
|
if( !Q_strcmp( token, "ip" ))
|
||||||
gs = false;
|
gs = false;
|
||||||
else if( !Q_strcmp( token, "gs" ))
|
else if( !Q_strcmp( token, "gs" ))
|
||||||
gs = true;
|
gs = true;
|
||||||
else
|
else
|
||||||
{
|
|
||||||
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
|
||||||
if( !pfile )
|
|
||||||
break;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
||||||
if( !pfile )
|
if( !pfile )
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
netadr_t adr = { 0 };
|
|
||||||
|
|
||||||
if( !NET_StringToAdr( token, &adr ))
|
if( !NET_StringToAdr( token, &adr ))
|
||||||
{
|
{
|
||||||
@@ -288,16 +288,9 @@ static void NET_MasterStaticResponse( const char *url, qboolean success, const b
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP buffer isn't NUL-terminated; COM_ParseFileSafe needs one.
|
|
||||||
char *body = Mem_Malloc( host.mempool, size + 1 );
|
|
||||||
memcpy( body, data, size );
|
|
||||||
body[size] = 0;
|
|
||||||
|
|
||||||
NET_Config( true, false ); // allow remote sends
|
NET_Config( true, false ); // allow remote sends
|
||||||
|
|
||||||
int count = NET_ParseMasterStaticBody( body );
|
int count = NET_ParseMasterStaticBody( data, size );
|
||||||
|
|
||||||
Mem_Free( body );
|
|
||||||
|
|
||||||
Con_Reportf( "masterstatic: %s yielded %d server(s)\n", url, count );
|
Con_Reportf( "masterstatic: %s yielded %d server(s)\n", url, count );
|
||||||
|
|
||||||
@@ -516,7 +509,7 @@ static master_t *NET_AddMaster( const char *addr )
|
|||||||
|
|
||||||
static void NET_AddMaster_f( void )
|
static void NET_AddMaster_f( void )
|
||||||
{
|
{
|
||||||
if( Cmd_Argc() != 2 )
|
if( Cmd_Argc() != 2 && Cmd_Argc() != 3 )
|
||||||
{
|
{
|
||||||
Msg( S_USAGE "addmaster <address> [gs]\n");
|
Msg( S_USAGE "addmaster <address> [gs]\n");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -62,14 +62,19 @@ static byte *Mod_SwapSpriteFrame( byte *p, byte *end, int bytes )
|
|||||||
p += sizeof( frame );
|
p += sizeof( frame );
|
||||||
|
|
||||||
// skip pixel data
|
// skip pixel data
|
||||||
if( p + frame.width * frame.height * bytes > end )
|
if( frame.width < 0 || frame.height < 0 )
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
p += frame.width * frame.height * bytes;
|
uint64_t pixels = (uint64_t)frame.width * frame.height * bytes;
|
||||||
|
|
||||||
|
if( pixels > (size_t)( end - p ))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p += pixels;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte *Mod_SwapSpriteGroup( byte *p, byte *end, int bytes )
|
static byte *Mod_SwapSpriteGroup( byte *p, byte *end, int bytes, int min_frames )
|
||||||
{
|
{
|
||||||
dspritegroup_t group;
|
dspritegroup_t group;
|
||||||
|
|
||||||
@@ -84,7 +89,7 @@ static byte *Mod_SwapSpriteGroup( byte *p, byte *end, int bytes )
|
|||||||
// swap intervals
|
// swap intervals
|
||||||
int numframes = group.numframes;
|
int numframes = group.numframes;
|
||||||
|
|
||||||
if( p + numframes * sizeof( dspriteinterval_t ) > end )
|
if( numframes < min_frames || (size_t)numframes > (size_t)( end - p ) / sizeof( dspriteinterval_t ))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for( int i = 0; i < numframes; i++ )
|
for( int i = 0; i < numframes; i++ )
|
||||||
@@ -144,18 +149,26 @@ static qboolean Mod_SwapSprite( void *buffer, size_t buffersize, int *out_versio
|
|||||||
|
|
||||||
short numi = LittleShort( *(short *)p );
|
short numi = LittleShort( *(short *)p );
|
||||||
*(short *)p = numi;
|
*(short *)p = numi;
|
||||||
p += sizeof( short ) + numi * 3;
|
p += sizeof( short );
|
||||||
|
|
||||||
|
if( numi < 0 || p + numi * 3 > end )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
p += numi * 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( numframes < 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
*out_version = version;
|
*out_version = version;
|
||||||
int bytes = ( version == SPRITE_VERSION_32 ) ? 4 : 1;
|
int bytes = ( version == SPRITE_VERSION_32 ) ? 4 : 1;
|
||||||
|
|
||||||
// swap all frames
|
// swap all frames
|
||||||
for( int i = 0; i < numframes && p && p < end; i++ )
|
for( int i = 0; i < numframes; i++ )
|
||||||
{
|
{
|
||||||
dframetype_t frametype;
|
dframetype_t frametype;
|
||||||
|
|
||||||
@@ -173,12 +186,18 @@ static qboolean Mod_SwapSprite( void *buffer, size_t buffersize, int *out_versio
|
|||||||
p = Mod_SwapSpriteFrame( p, end, bytes );
|
p = Mod_SwapSpriteFrame( p, end, bytes );
|
||||||
break;
|
break;
|
||||||
case FRAME_GROUP:
|
case FRAME_GROUP:
|
||||||
|
p = Mod_SwapSpriteGroup( p, end, bytes, 1 );
|
||||||
|
break;
|
||||||
case FRAME_ANGLED:
|
case FRAME_ANGLED:
|
||||||
p = Mod_SwapSpriteGroup( p, end, bytes );
|
// angled groups are indexed by 0..7 at render time
|
||||||
|
p = Mod_SwapSpriteGroup( p, end, bytes, 8 );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !p )
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -229,17 +248,23 @@ void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolea
|
|||||||
psprite->texFormat = SPR_ADDITIVE;
|
psprite->texFormat = SPR_ADDITIVE;
|
||||||
psprite->numframes = mod->numframes = pinq1->numframes;
|
psprite->numframes = mod->numframes = pinq1->numframes;
|
||||||
psprite->facecull = SPR_CULL_FRONT;
|
psprite->facecull = SPR_CULL_FRONT;
|
||||||
psprite->radius = pinq1->boundingradius;
|
float radius = floorf( pinq1->boundingradius );
|
||||||
|
if( radius >= (float)INT_MAX )
|
||||||
|
psprite->radius = INT_MAX;
|
||||||
|
else if( radius < 0.0f || IS_NAN( radius ))
|
||||||
|
psprite->radius = 0;
|
||||||
|
else
|
||||||
|
psprite->radius = (int)radius;
|
||||||
psprite->synctype = pinq1->synctype;
|
psprite->synctype = pinq1->synctype;
|
||||||
|
|
||||||
// LadyHavoc: hack to allow sprites to be non-fullbright
|
// LadyHavoc: hack to allow sprites to be non-fullbright
|
||||||
if( Q_strchr( mod->name, '!' ))
|
if( Q_strchr( mod->name, '!' ))
|
||||||
psprite->texFormat = SPR_ALPHTEST;
|
psprite->texFormat = SPR_ALPHTEST;
|
||||||
|
|
||||||
mod->mins[0] = mod->mins[1] = -pinq1->bounds[0] * 0.5f;
|
|
||||||
mod->maxs[0] = mod->maxs[1] = pinq1->bounds[0] * 0.5f;
|
mod->maxs[0] = mod->maxs[1] = pinq1->bounds[0] * 0.5f;
|
||||||
mod->mins[2] = -pinq1->bounds[1] * 0.5f;
|
mod->mins[0] = mod->mins[1] = -mod->maxs[0];
|
||||||
mod->maxs[2] = pinq1->bounds[1] * 0.5f;
|
mod->maxs[2] = pinq1->bounds[1] * 0.5f;
|
||||||
|
mod->mins[2] = -mod->maxs[2];
|
||||||
}
|
}
|
||||||
else // if( version == SPRITE_VERSION_HL )
|
else // if( version == SPRITE_VERSION_HL )
|
||||||
{
|
{
|
||||||
@@ -263,10 +288,10 @@ void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolea
|
|||||||
psprite->radius = pinhl->boundingradius;
|
psprite->radius = pinhl->boundingradius;
|
||||||
psprite->synctype = pinhl->synctype;
|
psprite->synctype = pinhl->synctype;
|
||||||
|
|
||||||
mod->mins[0] = mod->mins[1] = -pinhl->bounds[0] * 0.5f;
|
|
||||||
mod->maxs[0] = mod->maxs[1] = pinhl->bounds[0] * 0.5f;
|
mod->maxs[0] = mod->maxs[1] = pinhl->bounds[0] * 0.5f;
|
||||||
mod->mins[2] = -pinhl->bounds[1] * 0.5f;
|
mod->mins[0] = mod->mins[1] = -mod->maxs[0];
|
||||||
mod->maxs[2] = pinhl->bounds[1] * 0.5f;
|
mod->maxs[2] = pinhl->bounds[1] * 0.5f;
|
||||||
|
mod->mins[2] = -mod->maxs[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
if( loaded )
|
if( loaded )
|
||||||
@@ -283,3 +308,34 @@ void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolea
|
|||||||
Mod_SpriteLoadTextures( mod, buffer );
|
Mod_SpriteLoadTextures( mod, buffer );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if XASH_LLVM_LIBFUZZER
|
||||||
|
int EXPORT Fuzz_Mod_LoadSpriteModel( const uint8_t *Data, size_t Size );
|
||||||
|
int EXPORT Fuzz_Mod_LoadSpriteModel( const uint8_t *Data, size_t Size )
|
||||||
|
{
|
||||||
|
model_t mod = { .name = "#internal.spr", .needload = NL_NEEDS_LOADED };
|
||||||
|
qboolean loaded = false;
|
||||||
|
byte *buf;
|
||||||
|
|
||||||
|
if( Size == 0 )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Memory_Init();
|
||||||
|
|
||||||
|
// dedicated mode exercises the parser/validator without needing a renderer
|
||||||
|
host.type = HOST_DEDICATED;
|
||||||
|
host.mempool = Mem_AllocPool( "fuzzing pool" );
|
||||||
|
|
||||||
|
// the loader byteswaps the buffer in place, so hand it a writable copy
|
||||||
|
buf = Mem_Malloc( host.mempool, Size );
|
||||||
|
memcpy( buf, Data, Size );
|
||||||
|
|
||||||
|
Mod_LoadSpriteModel( &mod, buf, Size, &loaded );
|
||||||
|
|
||||||
|
if( mod.mempool )
|
||||||
|
Mem_FreePool( &mod.mempool );
|
||||||
|
Mem_FreePool( &host.mempool );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // XASH_LLVM_LIBFUZZER
|
||||||
|
|||||||
@@ -653,7 +653,7 @@ void Mod_NeedCRC( const char *name, qboolean needCRC )
|
|||||||
else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM );
|
else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XASH_ENGINE_TESTS
|
#if XASH_LLVM_LIBFUZZER
|
||||||
|
|
||||||
static const uint8_t *fuzz_data;
|
static const uint8_t *fuzz_data;
|
||||||
static size_t fuzz_size;
|
static size_t fuzz_size;
|
||||||
@@ -689,4 +689,4 @@ int EXPORT Fuzz_Mod_LoadModel( const uint8_t *Data, size_t Size )
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // XASH_ENGINE_TESTS
|
#endif // XASH_LLVM_LIBFUZZER
|
||||||
|
|||||||
@@ -1364,9 +1364,14 @@ static qboolean NET_QueuePacket( int net_socket, netsrc_t sock, netadr_t *from,
|
|||||||
// check for split message
|
// check for split message
|
||||||
if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET )
|
if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET )
|
||||||
{
|
{
|
||||||
if( !CL_IsFromConnectingServer( *from ))
|
if( CL_IsFromConnectingServer( *from ))
|
||||||
return false;
|
return NET_GetLong( data, ret, length, CL_GetSplitSize( ), CL_Protocol( ));
|
||||||
return NET_GetLong( data, ret, length, CL_GetSplitSize( ), CL_Protocol( ));
|
|
||||||
|
// GoldSrc servers (e.g. ReUnion) can split large query responses
|
||||||
|
if( CL_HasActiveNetRequest( *from ))
|
||||||
|
return NET_GetLong( data, ret, length, 1400, PROTO_GOLDSRC );
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ void NET_GetLocalAddress( netadr_t *ip4, netadr_t *ip6 );
|
|||||||
#if !XASH_DEDICATED
|
#if !XASH_DEDICATED
|
||||||
size_t CL_GetSplitSize( void );
|
size_t CL_GetSplitSize( void );
|
||||||
qboolean CL_IsFromConnectingServer( netadr_t from );
|
qboolean CL_IsFromConnectingServer( netadr_t from );
|
||||||
|
qboolean CL_HasActiveNetRequest( netadr_t from );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void HTTP_AddCustomServer( const char *url );
|
void HTTP_AddCustomServer( const char *url );
|
||||||
|
|||||||
@@ -319,7 +319,6 @@ extern const char *const svc_goldsrc_strings[svc_lastmsg+1];
|
|||||||
#define A2A_PING "ping" // reply with A2A_ACK
|
#define A2A_PING "ping" // reply with A2A_ACK
|
||||||
#define A2A_ACK "ack" // no-op
|
#define A2A_ACK "ack" // no-op
|
||||||
#define A2A_INFO "info" // different format for client and server, see code
|
#define A2A_INFO "info" // different format for client and server, see code
|
||||||
#define A2A_NETINFO "netinfo" // different format for client and server, see code
|
|
||||||
#define A2A_GOLDSRC_PING "i" // reply with A2A_GOLDSRC_ACK
|
#define A2A_GOLDSRC_PING "i" // reply with A2A_GOLDSRC_ACK
|
||||||
#define A2A_GOLDSRC_ACK "j" // no-op
|
#define A2A_GOLDSRC_ACK "j" // no-op
|
||||||
|
|
||||||
|
|||||||
@@ -13,41 +13,22 @@
|
|||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SDL_syswm.h"
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include "dlfcn.h"
|
|
||||||
|
|
||||||
#define XASHLIB "@rpath/libxash.dylib"
|
#define XASHLIB "@rpath/libxash.dylib"
|
||||||
|
|
||||||
|
|
||||||
@interface XashPromptAlertViewDelegate : NSObject <UIAlertViewDelegate>
|
|
||||||
|
|
||||||
@property (nonatomic, assign) int *button;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation XashPromptAlertViewDelegate
|
|
||||||
|
|
||||||
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
|
|
||||||
*_button = buttonIndex;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int szArgc;
|
int szArgc;
|
||||||
char **szArgv;
|
char **szArgv;
|
||||||
char *g_szLibrarySuffix;
|
|
||||||
float g_iOSVer;
|
float g_iOSVer;
|
||||||
bool isdark;
|
|
||||||
|
|
||||||
const char *IOS_GetDocsDir(void)
|
//UI vars
|
||||||
|
UIAlertController *alert;
|
||||||
|
UIViewController *controller;
|
||||||
|
bool finishedDialog = false;
|
||||||
|
|
||||||
|
const char *IOS_GetDocsDir( void )
|
||||||
{
|
{
|
||||||
if( g_iOSVer >= 8.0 )
|
|
||||||
{
|
|
||||||
static const char *dir = NULL;
|
static const char *dir = NULL;
|
||||||
|
|
||||||
if( dir )
|
if( dir )
|
||||||
@@ -61,23 +42,9 @@ const char *IOS_GetDocsDir(void)
|
|||||||
NSLog(@"IOS_GetDocsDir: %s", dir);
|
NSLog(@"IOS_GetDocsDir: %s", dir);
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static char dir[1024];
|
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
||||||
NSString *basePath = paths.firstObject;
|
|
||||||
[[NSFileManager defaultManager] createDirectoryAtPath:basePath withIntermediateDirectories:YES attributes:nil error:nil];
|
|
||||||
strcpy(dir,[basePath UTF8String]);
|
|
||||||
mkdir(dir,777);
|
|
||||||
|
|
||||||
NSLog(@"IOS_GetDocsDir: %s", dir);
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *IOS_GetExecDir(void)
|
const char *IOS_GetExecDir( void )
|
||||||
{
|
{
|
||||||
static const char *dir = NULL;
|
static const char *dir = NULL;
|
||||||
|
|
||||||
@@ -87,7 +54,7 @@ const char *IOS_GetExecDir(void)
|
|||||||
NSString *executableDirctory = [[NSBundle mainBundle] bundlePath];
|
NSString *executableDirctory = [[NSBundle mainBundle] bundlePath];
|
||||||
|
|
||||||
dir = [executableDirctory fileSystemRepresentation];
|
dir = [executableDirctory fileSystemRepresentation];
|
||||||
NSLog(@"IOS_GetDocsDir: %s", dir);
|
NSLog(@"IOS_GetExecDir: %s", dir);
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
@@ -99,43 +66,44 @@ typedef struct settings_s
|
|||||||
{
|
{
|
||||||
unsigned char magic;
|
unsigned char magic;
|
||||||
char args[1024];
|
char args[1024];
|
||||||
unsigned int port;
|
|
||||||
char suffix[32];
|
|
||||||
unsigned int ftpserver;
|
|
||||||
} settings_t;
|
} settings_t;
|
||||||
@interface ButtonHandler :NSObject
|
|
||||||
@property (nonatomic, assign) int *button1;
|
|
||||||
@end
|
|
||||||
@implementation ButtonHandler
|
|
||||||
|
|
||||||
|
settings_t settings;
|
||||||
|
FILE *settingsfile;
|
||||||
|
char settingspath[256];
|
||||||
|
|
||||||
-(void) buttonClicked:(UIButton*)sender
|
static void SaveSettings( void )
|
||||||
{
|
{
|
||||||
*_button1 = 0;
|
if( (settingsfile = fopen( settingspath, "wb" )) )
|
||||||
|
{
|
||||||
|
strlcpy( settings.args, [alert.textFields.firstObject.text UTF8String], 1024 );
|
||||||
|
settings.magic = 111;
|
||||||
|
|
||||||
|
fwrite( &settings, sizeof(settings), 1, settingsfile );
|
||||||
|
fclose( settingsfile );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@end
|
|
||||||
void IOS_PrepareView(void)
|
static void IOS_PrepareView( void )
|
||||||
{
|
{
|
||||||
ButtonHandler *handler = [[ButtonHandler alloc] init];
|
|
||||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
UIViewController *controller = [[UIViewController alloc] init];
|
controller = [[UIViewController alloc] init];
|
||||||
[[controller view] setBackgroundColor:[UIColor grayColor]];
|
[controller.view setBackgroundColor:[UIColor grayColor]];
|
||||||
[window setRootViewController:controller];
|
[window setRootViewController:controller];
|
||||||
[window makeKeyAndVisible];
|
[window makeKeyAndVisible];
|
||||||
if([[controller traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark && g_iOSVer >= 13.0) isdark = true; else isdark = false;
|
alert = [UIAlertController alertControllerWithTitle:@"Xash3D FWGS"
|
||||||
#if 0
|
message:nil
|
||||||
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
|
preferredStyle:UIAlertControllerStyleAlert];
|
||||||
int button1 = -1;
|
|
||||||
handler.button1 = &button1;
|
|
||||||
|
|
||||||
[button addTarget:handler action:@selector(buttonClicked:) forControlEvents:UIControlEventValueChanged];
|
UIAlertAction* startAction = [UIAlertAction actionWithTitle:@"Start" style:UIAlertActionStyleDefault
|
||||||
@autoreleasepool {
|
handler:^(UIAlertAction *action) { finishedDialog = true; }];
|
||||||
while( button1 == -1 ) {
|
UIAlertAction* exitAction = [UIAlertAction actionWithTitle:@"Exit" style:UIAlertActionStyleDefault
|
||||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
handler:^(UIAlertAction *action) {
|
||||||
}
|
SaveSettings();
|
||||||
}
|
NSLog(@"Exit Selected\n");
|
||||||
[[controller view] addSubview:button];
|
exit(0); }];
|
||||||
#endif
|
[alert addAction:startAction];
|
||||||
|
[alert addAction:exitAction];
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOS_LaunchDialog( void )
|
void IOS_LaunchDialog( void )
|
||||||
@@ -148,91 +116,48 @@ void IOS_LaunchDialog( void )
|
|||||||
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted){}];
|
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted){}];
|
||||||
|
|
||||||
IOS_PrepareView();
|
IOS_PrepareView();
|
||||||
int button = -1, bExit, bStart;
|
|
||||||
UIAlertView * alert = [[UIAlertView alloc] init];
|
|
||||||
bExit = [alert addButtonWithTitle:@"Exit"];
|
|
||||||
bStart = [alert addButtonWithTitle:@"Start"];
|
|
||||||
XashPromptAlertViewDelegate *delegate = [[XashPromptAlertViewDelegate alloc] init];
|
|
||||||
delegate.button = &button;
|
|
||||||
|
|
||||||
alert.delegate = delegate;
|
|
||||||
|
|
||||||
const char *docsDir = IOS_GetDocsDir();
|
const char *docsDir = IOS_GetDocsDir();
|
||||||
|
|
||||||
//set working directory to documents so logs can be generated there
|
//set working directory to documents so logs can be generated there
|
||||||
NSString *workingDir = [NSString stringWithUTF8String:IOS_GetDocsDir()];
|
[[NSFileManager defaultManager]
|
||||||
[[NSFileManager defaultManager] changeCurrentDirectoryPath:workingDir];
|
changeCurrentDirectoryPath:[NSString stringWithUTF8String:IOS_GetDocsDir()]];
|
||||||
|
|
||||||
FILE *settingsfile;
|
snprintf( settingspath, sizeof( settingspath ), "%s/settings.bin", docsDir );
|
||||||
char settingspath[256];
|
|
||||||
snprintf(settingspath, sizeof(settingspath), "%s/settings.bin", docsDir );
|
|
||||||
settingspath[255] = 0;
|
settingspath[255] = 0;
|
||||||
settings_t settings;
|
|
||||||
|
|
||||||
[alert setTransform:CGAffineTransformMakeTranslation(0,109)];
|
|
||||||
|
|
||||||
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
|
|
||||||
|
|
||||||
UILabel *argstitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 30)];
|
|
||||||
[argstitle setText:@"Command-line arguments:"];
|
|
||||||
|
|
||||||
UITextField *args = [[UITextField alloc] initWithFrame:CGRectMake(0, 30, 300, 30)];
|
|
||||||
[args setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]];
|
|
||||||
if(isdark) [args setBackgroundColor:[[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1]];
|
|
||||||
|
|
||||||
|
|
||||||
UITextField *suffix = [[UITextField alloc] initWithFrame:CGRectMake(140, 90, 160, 30 )];
|
|
||||||
[suffix setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]];
|
|
||||||
if(isdark) [suffix setBackgroundColor:[[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1]];
|
|
||||||
|
|
||||||
UILabel *suffixtitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 90, 140, 30)];
|
|
||||||
[suffixtitle setText:@"Library suffix"];
|
|
||||||
|
|
||||||
[scroll addSubview:argstitle];
|
|
||||||
[scroll addSubview:args];
|
|
||||||
[scroll addSubview:suffix];
|
|
||||||
[scroll addSubview:suffixtitle];
|
|
||||||
|
|
||||||
settingsfile = fopen( settingspath, "rb" );
|
settingsfile = fopen( settingspath, "rb" );
|
||||||
if( settingsfile && ( fread(&settings, sizeof( settings ), 1, settingsfile ) == 1 ) && ( settings.magic == SETTINGS_MAGIC ) )
|
bool ret = false;
|
||||||
|
if ( settingsfile )
|
||||||
{
|
{
|
||||||
settings.args[1023] = 0;
|
ret = fread( &settings, sizeof( settings ), 1, settingsfile ) == 1;
|
||||||
settings.suffix[31] = 0;
|
|
||||||
[args setText:@(settings.args)];
|
|
||||||
[suffix setText:@(settings.suffix)];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[args setText:@"-dev 2 -log"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll.contentSize=CGSizeMake(250, 200);
|
|
||||||
[alert setValue:scroll forKey:@"accessoryView"];
|
|
||||||
|
|
||||||
[alert show];
|
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||||
|
[textField setPlaceholder:@"Launch Options"];
|
||||||
|
if (ret && ( settings.magic == SETTINGS_MAGIC ))
|
||||||
|
{
|
||||||
|
[textField setText:@(settings.args)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[textField setText:@"-dev 2 -log"];
|
||||||
|
}
|
||||||
|
[textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[controller presentViewController:alert animated:YES completion:nil];
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
while( button == -1 ) {
|
while( !finishedDialog ) {
|
||||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (settingsfile = fopen( settingspath, "wb" )) )
|
NSArray *argv = [alert.textFields.firstObject.text componentsSeparatedByString:@" "];
|
||||||
{
|
|
||||||
strlcpy(settings.args, [args.text UTF8String], 1024);
|
|
||||||
strlcpy(settings.suffix, [suffix.text UTF8String], 32 );
|
|
||||||
settings.magic = 111;
|
|
||||||
|
|
||||||
fwrite(&settings, sizeof(settings), 1, settingsfile);
|
|
||||||
fclose(settingsfile);
|
|
||||||
}
|
|
||||||
if( button == bExit )
|
|
||||||
{
|
|
||||||
printf("Exit selected\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *argv = [ args.text componentsSeparatedByString:@" " ];
|
SaveSettings();
|
||||||
|
|
||||||
int count = [argv count];
|
int count = [argv count];
|
||||||
szArgv = calloc( count + 2, sizeof( char* ) );
|
szArgv = calloc( count + 2, sizeof( char* ) );
|
||||||
@@ -244,16 +169,6 @@ void IOS_LaunchDialog( void )
|
|||||||
szArgc = count + 1;
|
szArgc = count + 1;
|
||||||
szArgv[count + 1] = 0;
|
szArgv[count + 1] = 0;
|
||||||
|
|
||||||
if( [suffix.text length] )
|
|
||||||
g_szLibrarySuffix = strdup([suffix.text UTF8String]);
|
|
||||||
|
|
||||||
alert.delegate = nil;
|
|
||||||
|
|
||||||
[args release];
|
|
||||||
[argstitle release];
|
|
||||||
[suffix release];
|
|
||||||
[suffixtitle release];
|
|
||||||
|
|
||||||
[alert release];
|
[alert release];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +181,7 @@ char *IOS_GetUDID( void )
|
|||||||
return udid;
|
return udid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOS_Log(const char *text)
|
void IOS_Log( const char *text )
|
||||||
{
|
{
|
||||||
NSLog(@"Xash: %@", [NSString stringWithUTF8String:text]);
|
NSLog(@"Xash: %@", [NSString stringWithUTF8String:text]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
|
|||||||
if( window_mode != WINDOW_MODE_WINDOWED )
|
if( window_mode != WINDOW_MODE_WINDOWED )
|
||||||
SetBits( flags, SDL_FULLSCREEN|SDL_HWSURFACE );
|
SetBits( flags, SDL_FULLSCREEN|SDL_HWSURFACE );
|
||||||
|
|
||||||
if( !glw_state.software )
|
if( glw_state.type == REF_GL )
|
||||||
SetBits( flags, SDL_OPENGL );
|
SetBits( flags, SDL_OPENGL );
|
||||||
|
|
||||||
if( !VID_CreateWindowWithSafeGL( wndname, 0, 0, width, height, flags ))
|
if( !VID_CreateWindowWithSafeGL( wndname, 0, 0, width, height, flags ))
|
||||||
@@ -381,10 +381,10 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
|
|
||||||
refState.desktopBitsPixel = 16;
|
refState.desktopBitsPixel = 16;
|
||||||
|
|
||||||
|
glw_state.type = type;
|
||||||
switch( type )
|
switch( type )
|
||||||
{
|
{
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
glw_state.software = true;
|
|
||||||
break;
|
break;
|
||||||
case REF_GL:
|
case REF_GL:
|
||||||
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) )
|
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) )
|
||||||
@@ -399,6 +399,8 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case REF_D3D:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
||||||
break;
|
break;
|
||||||
@@ -417,6 +419,7 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
ref.dllFuncs.GL_InitExtensions();
|
ref.dllFuncs.GL_InitExtensions();
|
||||||
break;
|
break;
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
|
case REF_D3D:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ void GL_UpdateSwapInterval( void )
|
|||||||
{
|
{
|
||||||
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||||
|
|
||||||
if( SDL_GL_SetSwapInterval( gl_vsync.value ) < 0 )
|
if( glw_state.context && SDL_GL_SetSwapInterval( gl_vsync.value ) < 0 )
|
||||||
Con_Reportf( S_ERROR "SDL_GL_SetSwapInterval: %s\n", SDL_GetError( ));
|
Con_Reportf( S_ERROR "SDL_GL_SetSwapInterval: %s\n", SDL_GetError( ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ static void VID_GetWindowSizeInPixels( SDL_Window *window, SDL_Renderer *rendere
|
|||||||
#if SDL_VERSION_ATLEAST( 2, 26, 0 )
|
#if SDL_VERSION_ATLEAST( 2, 26, 0 )
|
||||||
SDL_GetWindowSizeInPixels( window, w, h );
|
SDL_GetWindowSizeInPixels( window, w, h );
|
||||||
#else
|
#else
|
||||||
if( glw_state.software )
|
if( glw_state.type != REF_GL )
|
||||||
SDL_GetRendererOutputSize( renderer, w, h );
|
SDL_GetRendererOutputSize( renderer, w, h );
|
||||||
else
|
else
|
||||||
SDL_GL_GetDrawableSize( window, w, h );
|
SDL_GL_GetDrawableSize( window, w, h );
|
||||||
@@ -669,7 +669,7 @@ static rserr_t VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
SetBits( flags, SDL_WINDOW_ALLOW_HIGHDPI );
|
SetBits( flags, SDL_WINDOW_ALLOW_HIGHDPI );
|
||||||
#endif // !XASH_WIN32
|
#endif // !XASH_WIN32
|
||||||
|
|
||||||
if( !glw_state.software )
|
if( glw_state.type == REF_GL )
|
||||||
SetBits( flags, SDL_WINDOW_OPENGL );
|
SetBits( flags, SDL_WINDOW_OPENGL );
|
||||||
|
|
||||||
if( vid_maximized.value )
|
if( vid_maximized.value )
|
||||||
@@ -681,7 +681,7 @@ static rserr_t VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
|
|
||||||
if( !host.hWnd )
|
if( !host.hWnd )
|
||||||
{
|
{
|
||||||
err = glw_state.software ? rserr_unknown : rserr_invalid_context;
|
err = (glw_state.type == REF_GL) ? rserr_invalid_context : rserr_unknown;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,7 +698,7 @@ static rserr_t VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
SDL_ShowWindow( host.hWnd );
|
SDL_ShowWindow( host.hWnd );
|
||||||
SDL_RaiseWindow( host.hWnd );
|
SDL_RaiseWindow( host.hWnd );
|
||||||
|
|
||||||
if( glw_state.software )
|
if( glw_state.type == REF_SOFTWARE )
|
||||||
{
|
{
|
||||||
char cmd[64];
|
char cmd[64];
|
||||||
|
|
||||||
@@ -720,7 +720,7 @@ static rserr_t VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
Con_Printf( "SDL_Renderer %s initialized\n", info.name );
|
Con_Printf( "SDL_Renderer %s initialized\n", info.name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if( glw_state.type == REF_GL )
|
||||||
{
|
{
|
||||||
glw_state.context = SDL_GL_CreateContext( host.hWnd );
|
glw_state.context = SDL_GL_CreateContext( host.hWnd );
|
||||||
|
|
||||||
@@ -917,10 +917,10 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
SDL_SetHint( SDL_HINT_VIDEO_X11_XRANDR, "1" );
|
SDL_SetHint( SDL_HINT_VIDEO_X11_XRANDR, "1" );
|
||||||
SDL_SetHint( SDL_HINT_VIDEO_X11_XVIDMODE, "1" );
|
SDL_SetHint( SDL_HINT_VIDEO_X11_XVIDMODE, "1" );
|
||||||
|
|
||||||
|
glw_state.type = type;
|
||||||
switch( type )
|
switch( type )
|
||||||
{
|
{
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
glw_state.software = true;
|
|
||||||
break;
|
break;
|
||||||
case REF_GL:
|
case REF_GL:
|
||||||
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) )
|
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", safe ) )
|
||||||
@@ -935,6 +935,8 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case REF_D3D:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
||||||
break;
|
break;
|
||||||
@@ -950,6 +952,7 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
ref.dllFuncs.GL_InitExtensions();
|
ref.dllFuncs.GL_InitExtensions();
|
||||||
break;
|
break;
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
|
case REF_D3D:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,12 @@ GNU General Public License for more details.
|
|||||||
void SDLash_InitCursors( void );
|
void SDLash_InitCursors( void );
|
||||||
void SDLash_FreeCursors( void );
|
void SDLash_FreeCursors( void );
|
||||||
|
|
||||||
|
//
|
||||||
|
// sensor_sdl3.c
|
||||||
|
//
|
||||||
|
void SDLash_InitSensors( void );
|
||||||
|
void SDLash_ShutdownSensors( void );
|
||||||
|
qboolean SDLash_GyroIsAvailable( void );
|
||||||
|
void SDLash_SensorUpdate( SDL_SensorEvent sensor );
|
||||||
|
|
||||||
#endif // PLATFORM_SDL3_H
|
#endif // PLATFORM_SDL3_H
|
||||||
|
|||||||
98
engine/platform/sdl3/sensor_sdl3.c
Normal file
98
engine/platform/sdl3/sensor_sdl3.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
sensor_sdl3.c - SDL3 sensor handling
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "platform_sdl3.h"
|
||||||
|
|
||||||
|
static SDL_SensorID g_system_gyro_id = -1;
|
||||||
|
static SDL_Sensor *g_system_gyro;
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
SDLash_InitSensors
|
||||||
|
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void SDLash_InitSensors( void )
|
||||||
|
{
|
||||||
|
if( SDL_InitSubSystem( SDL_INIT_SENSOR ) == 0 )
|
||||||
|
{
|
||||||
|
int num_sensors;
|
||||||
|
SDL_SensorID *sensors = SDL_GetSensors( &num_sensors );
|
||||||
|
|
||||||
|
for( int i = 0; i < num_sensors; i++ )
|
||||||
|
{
|
||||||
|
if( SDL_GetSensorTypeForID( i ) == SDL_SENSOR_GYRO )
|
||||||
|
{
|
||||||
|
g_system_gyro = SDL_OpenSensor( i );
|
||||||
|
if( g_system_gyro )
|
||||||
|
{
|
||||||
|
g_system_gyro_id = sensors[i];
|
||||||
|
Con_Printf( "SDL: Opened built-in gyroscope: %s\n", SDL_GetSensorName( g_system_gyro ));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free( sensors );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Con_Reportf( S_ERROR "Failed to init SDL Sensor subsystem: %s\n", SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
SDLash_ShutdownSensors
|
||||||
|
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void SDLash_ShutdownSensors( void )
|
||||||
|
{
|
||||||
|
if( g_system_gyro )
|
||||||
|
{
|
||||||
|
SDL_CloseSensor( g_system_gyro );
|
||||||
|
g_system_gyro = NULL;
|
||||||
|
g_system_gyro_id = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_QuitSubSystem( SDL_INIT_SENSOR );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
SDLash_GyroIsAvailable
|
||||||
|
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
qboolean SDLash_GyroIsAvailable( void )
|
||||||
|
{
|
||||||
|
return( g_system_gyro != NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
SDLash_SensorUpdate
|
||||||
|
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void SDLash_SensorUpdate( SDL_SensorEvent sensor )
|
||||||
|
{
|
||||||
|
if( sensor.which == g_system_gyro_id )
|
||||||
|
{
|
||||||
|
IN_GyroEvent( sensor.data );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ void Platform_Sleep( int msec )
|
|||||||
{
|
{
|
||||||
SDL_Delay( msec );
|
SDL_Delay( msec );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // XASH_TIMER == TIMER_SDL
|
#endif // XASH_TIMER == TIMER_SDL
|
||||||
|
|
||||||
#if XASH_MESSAGEBOX == MSGBOX_SDL
|
#if XASH_MESSAGEBOX == MSGBOX_SDL
|
||||||
@@ -45,6 +46,7 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren
|
|||||||
{
|
{
|
||||||
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, title, message, parentMainWindow ? host.hWnd : NULL );
|
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, title, message, parentMainWindow ? host.hWnd : NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void SDLash_NanoSleep( int nsec )
|
void SDLash_NanoSleep( int nsec )
|
||||||
@@ -90,10 +92,8 @@ static void SDLCALL SDLash_LogOutputFunction( void *userdata, int category, SDL_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLash_Init( const char *basedir )
|
void SDLash_Init( void )
|
||||||
{
|
{
|
||||||
(void)basedir;
|
|
||||||
|
|
||||||
// TODO: initial state, to be filled from gameinfo!
|
// TODO: initial state, to be filled from gameinfo!
|
||||||
SDL_SetAppMetadata( XASH_ENGINE_NAME, XASH_VERSION, "su.xash.engine" );
|
SDL_SetAppMetadata( XASH_ENGINE_NAME, XASH_VERSION, "su.xash.engine" );
|
||||||
SDL_SetAppMetadataProperty( SDL_PROP_APP_METADATA_TYPE_STRING, "game" );
|
SDL_SetAppMetadataProperty( SDL_PROP_APP_METADATA_TYPE_STRING, "game" );
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ GNU General Public License for more details.
|
|||||||
#include "ref_common.h"
|
#include "ref_common.h"
|
||||||
#include "xash3d_mathlib.h"
|
#include "xash3d_mathlib.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "wrect.h"
|
||||||
#include <SDL3/SDL_system.h>
|
#include <SDL3/SDL_system.h>
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
@@ -92,8 +93,8 @@ static SDL_Window *VID_CreateWindowWithSafeGL( const char *title, SDL_Rect *rect
|
|||||||
static void VID_SaveWindowSize( SDL_Window *hWnd, int width, int height )
|
static void VID_SaveWindowSize( SDL_Window *hWnd, int width, int height )
|
||||||
{
|
{
|
||||||
qboolean maximized = FBitSet( SDL_GetWindowFlags( hWnd ), SDL_WINDOW_MAXIMIZED );
|
qboolean maximized = FBitSet( SDL_GetWindowFlags( hWnd ), SDL_WINDOW_MAXIMIZED );
|
||||||
int render_w;
|
int render_w;
|
||||||
int render_h;
|
int render_h;
|
||||||
|
|
||||||
if( !SDL_GetWindowSizeInPixels( hWnd, &render_w, &render_h ))
|
if( !SDL_GetWindowSizeInPixels( hWnd, &render_w, &render_h ))
|
||||||
{
|
{
|
||||||
@@ -110,15 +111,15 @@ static void VID_SaveWindowSize( SDL_Window *hWnd, int width, int height )
|
|||||||
static qboolean VID_CreateWindow( const int input_width, const int input_height, window_mode_t window_mode )
|
static qboolean VID_CreateWindow( const int input_width, const int input_height, window_mode_t window_mode )
|
||||||
{
|
{
|
||||||
SDL_Rect rect = { SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, input_width, input_height };
|
SDL_Rect rect = { SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, input_width, input_height };
|
||||||
Uint32 flags = SDL_WINDOW_RESIZABLE;
|
Uint32 flags = SDL_WINDOW_RESIZABLE;
|
||||||
|
|
||||||
if( !glw_state.software )
|
if( glw_state.type == REF_GL )
|
||||||
SetBits( flags, SDL_WINDOW_OPENGL );
|
SetBits( flags, SDL_WINDOW_OPENGL );
|
||||||
|
|
||||||
// probe true fullscreen first, if it fails, try borderless next
|
// probe true fullscreen first, if it fails, try borderless next
|
||||||
if( window_mode == WINDOW_MODE_FULLSCREEN )
|
if( window_mode == WINDOW_MODE_FULLSCREEN )
|
||||||
{
|
{
|
||||||
SDL_DisplayID display = SDL_GetPrimaryDisplay();
|
SDL_DisplayID display = SDL_GetPrimaryDisplay();
|
||||||
|
|
||||||
SDL_DisplayMode dm;
|
SDL_DisplayMode dm;
|
||||||
if( SDL_GetClosestFullscreenDisplayMode( display, rect.w, rect.h, 0.0f, true, &dm ))
|
if( SDL_GetClosestFullscreenDisplayMode( display, rect.w, rect.h, 0.0f, true, &dm ))
|
||||||
@@ -167,12 +168,12 @@ static qboolean VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
|
|
||||||
VID_SetWindowIcon( host.hWnd );
|
VID_SetWindowIcon( host.hWnd );
|
||||||
|
|
||||||
if( glw_state.software )
|
if( glw_state.type == REF_SOFTWARE )
|
||||||
{
|
{
|
||||||
// no support yet
|
// no support yet
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else if ( glw_state.type == REF_GL )
|
||||||
{
|
{
|
||||||
glw_state.context = SDL_GL_CreateContext( host.hWnd );
|
glw_state.context = SDL_GL_CreateContext( host.hWnd );
|
||||||
|
|
||||||
@@ -203,9 +204,44 @@ static qboolean VID_CreateWindow( const int input_width, const int input_height,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VID_Info_f( void )
|
||||||
|
{
|
||||||
|
Uint32 flags = SDL_GetWindowFlags( host.hWnd );
|
||||||
|
int width, height;
|
||||||
|
int render_width, render_height;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
SDL_GetWindowSize( host.hWnd, &width, &height );
|
||||||
|
SDL_GetWindowSizeInPixels( host.hWnd, &render_width, &render_height );
|
||||||
|
SDL_GetWindowPosition( host.hWnd, &x, &y );
|
||||||
|
|
||||||
|
Con_Printf( "Video: " S_GREEN "SDL3" S_DEFAULT "\n" );
|
||||||
|
Con_Printf( "Video driver: " S_GREEN "%s" S_DEFAULT "\n", SDL_GetCurrentVideoDriver( ));
|
||||||
|
Con_Printf( "Window size: " S_GREEN "%dx%d" S_DEFAULT " (" S_YELLOW "real %dx%d" S_DEFAULT ")\n", width, height, render_width, render_height );
|
||||||
|
Con_Printf( "Window position: " S_GREEN "%dx%d" S_DEFAULT "\n", x, y );
|
||||||
|
Con_Printf( "Window mode: %s" S_DEFAULT "\n",
|
||||||
|
FBitSet( flags, SDL_WINDOW_FULLSCREEN ) ? S_YELLOW "fullscreen"
|
||||||
|
: S_CYAN "windowed" );
|
||||||
|
Con_Printf( "Window bordered: %s" S_DEFAULT "\n", FBitSet( flags, SDL_WINDOW_BORDERLESS ) ? S_RED "false" : S_GREEN "true" );
|
||||||
|
Con_Printf( "Window resizable: %s" S_DEFAULT "\n", FBitSet( flags, SDL_WINDOW_RESIZABLE ) ? S_GREEN "true" : S_RED "false" );
|
||||||
|
Con_Printf( "Window maximized: %s" S_DEFAULT "\n", FBitSet( flags, SDL_WINDOW_MAXIMIZED ) ? S_GREEN "true" : S_RED "false" );
|
||||||
|
|
||||||
|
int display_index = SDL_GetDisplayForWindow( host.hWnd );
|
||||||
|
if( display_index >= 0 )
|
||||||
|
Con_Printf( "Window display index: " S_GREEN "%d" S_DEFAULT "\n", display_index );
|
||||||
|
else
|
||||||
|
Con_Printf( "Window display index: " S_RED "fail: " S_DEFAULT "%s\n", SDL_GetError( ));
|
||||||
|
|
||||||
|
const SDL_DisplayMode *dm = SDL_GetWindowFullscreenMode( host.hWnd );
|
||||||
|
if( dm )
|
||||||
|
Con_Printf( "Window display mode: " S_GREEN "%dx%d@%f" S_DEFAULT "\n", dm->w, dm->h, dm->refresh_rate );
|
||||||
|
else
|
||||||
|
Con_Printf( "Window display mode: " S_RED "fail: " S_DEFAULT "%s\n", SDL_GetError( ));
|
||||||
|
}
|
||||||
|
|
||||||
static void R_InitVideoModes( void )
|
static void R_InitVideoModes( void )
|
||||||
{
|
{
|
||||||
SDL_DisplayID display = SDL_GetDisplayForWindow( host.hWnd );
|
SDL_DisplayID display = SDL_GetDisplayForWindow( host.hWnd );
|
||||||
int count;
|
int count;
|
||||||
SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes( display, &count );
|
SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes( display, &count );
|
||||||
|
|
||||||
@@ -257,6 +293,32 @@ void Platform_Minimize_f( void )
|
|||||||
SDL_MinimizeWindow( host.hWnd );
|
SDL_MinimizeWindow( host.hWnd );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
platform_orientation_t Platform_GetDisplayOrientation( void )
|
||||||
|
{
|
||||||
|
if( host.hWnd )
|
||||||
|
{
|
||||||
|
int display_index = SDL_GetDisplayForWindow( host.hWnd );
|
||||||
|
if( display_index >= 0 )
|
||||||
|
{
|
||||||
|
switch( SDL_GetCurrentDisplayOrientation( display_index ))
|
||||||
|
{
|
||||||
|
case SDL_ORIENTATION_LANDSCAPE:
|
||||||
|
return ORIENTATION_LANDSCAPE;
|
||||||
|
case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
|
||||||
|
return ORIENTATION_LANDSCAPE_FLIPPED;
|
||||||
|
case SDL_ORIENTATION_PORTRAIT:
|
||||||
|
return ORIENTATION_PORTRAIT;
|
||||||
|
case SDL_ORIENTATION_PORTRAIT_FLIPPED:
|
||||||
|
return ORIENTATION_PORTRAIT_FLIPPED;
|
||||||
|
default:
|
||||||
|
return ORIENTATION_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ORIENTATION_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
void GL_UpdateSwapInterval( void )
|
void GL_UpdateSwapInterval( void )
|
||||||
{
|
{
|
||||||
if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||||
@@ -275,10 +337,10 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
if( Sys_CheckParm( "-egl" ))
|
if( Sys_CheckParm( "-egl" ))
|
||||||
Con_Printf( S_WARN "%s: -egl option is deprecated\n", __func__ );
|
Con_Printf( S_WARN "%s: -egl option is deprecated\n", __func__ );
|
||||||
|
|
||||||
|
glw_state.type = type;
|
||||||
switch( type )
|
switch( type )
|
||||||
{
|
{
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
glw_state.software = true;
|
|
||||||
break;
|
break;
|
||||||
case REF_GL:
|
case REF_GL:
|
||||||
{
|
{
|
||||||
@@ -295,6 +357,8 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case REF_D3D:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
||||||
break;
|
break;
|
||||||
@@ -307,6 +371,7 @@ qboolean R_Init_Video( ref_graphic_apis_t type )
|
|||||||
switch( type )
|
switch( type )
|
||||||
{
|
{
|
||||||
case REF_SOFTWARE:
|
case REF_SOFTWARE:
|
||||||
|
case REF_D3D:
|
||||||
break;
|
break;
|
||||||
case REF_GL:
|
case REF_GL:
|
||||||
ref.dllFuncs.GL_InitExtensions();
|
ref.dllFuncs.GL_InitExtensions();
|
||||||
@@ -341,26 +406,26 @@ int GL_SetAttribute( int attr, int val )
|
|||||||
{
|
{
|
||||||
switch( attr )
|
switch( attr )
|
||||||
{
|
{
|
||||||
#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_##name: return SDL_GL_SetAttribute( SDL_##name, val );
|
#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_ ## name: return SDL_GL_SetAttribute( SDL_ ## name, val );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MAJOR_VERSION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MAJOR_VERSION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MINOR_VERSION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MINOR_VERSION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_FLAGS );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_FLAGS );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_SHARE_WITH_CURRENT_CONTEXT );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_SHARE_WITH_CURRENT_CONTEXT );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_FRAMEBUFFER_SRGB_CAPABLE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_FRAMEBUFFER_SRGB_CAPABLE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_PROFILE_MASK );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_PROFILE_MASK );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RELEASE_BEHAVIOR );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RELEASE_BEHAVIOR );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RESET_NOTIFICATION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RESET_NOTIFICATION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_NO_ERROR );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_NO_ERROR );
|
||||||
#undef MAP_REF_API_ATTRIBUTE_TO_SDL
|
#undef MAP_REF_API_ATTRIBUTE_TO_SDL
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,26 +436,26 @@ int GL_GetAttribute( int attr, int *val )
|
|||||||
{
|
{
|
||||||
switch( attr )
|
switch( attr )
|
||||||
{
|
{
|
||||||
#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_##name: return SDL_GL_GetAttribute( SDL_##name, val );
|
#define MAP_REF_API_ATTRIBUTE_TO_SDL( name ) case REF_ ## name: return SDL_GL_GetAttribute( SDL_ ## name, val );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_RED_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_GREEN_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_BLUE_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ALPHA_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DOUBLEBUFFER );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_DEPTH_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_STENCIL_SIZE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLEBUFFERS );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_MULTISAMPLESAMPLES );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_ACCELERATED_VISUAL );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MAJOR_VERSION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MAJOR_VERSION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MINOR_VERSION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_MINOR_VERSION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_FLAGS );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_FLAGS );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_SHARE_WITH_CURRENT_CONTEXT );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_SHARE_WITH_CURRENT_CONTEXT );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_FRAMEBUFFER_SRGB_CAPABLE );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_FRAMEBUFFER_SRGB_CAPABLE );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_PROFILE_MASK );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_PROFILE_MASK );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RELEASE_BEHAVIOR );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RELEASE_BEHAVIOR );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RESET_NOTIFICATION );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_RESET_NOTIFICATION );
|
||||||
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_NO_ERROR );
|
MAP_REF_API_ATTRIBUTE_TO_SDL( GL_CONTEXT_NO_ERROR );
|
||||||
#undef MAP_REF_API_ATTRIBUTE_TO_SDL
|
#undef MAP_REF_API_ATTRIBUTE_TO_SDL
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,6 +615,9 @@ qboolean VID_SetMode( void )
|
|||||||
case rserr_invalid_mode:
|
case rserr_invalid_mode:
|
||||||
Sys_Warn( "%s: invalid mode, engine will run in %dx%d", __func__, vid_state.prev_width, vid_state.prev_height );
|
Sys_Warn( "%s: invalid mode, engine will run in %dx%d", __func__, vid_state.prev_width, vid_state.prev_height );
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
Sys_Warn( "%s: unknown error, engine will revert to %dx%d", __func__, vid_state.prev_width, vid_state.prev_height );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// revert to last successful mode
|
// revert to last successful mode
|
||||||
@@ -563,8 +631,169 @@ qboolean VID_SetMode( void )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static qboolean VID_GetDisplayBounds( SDL_DisplayID display_id, SDL_Window *hWnd, SDL_Rect *rect )
|
||||||
|
{
|
||||||
|
if( SDL_GetDisplayUsableBounds( display_id, rect ) != 0 )
|
||||||
|
{
|
||||||
|
memset( rect, 0, sizeof( *rect ));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrect_t wrc = { 0 };
|
||||||
|
if( hWnd )
|
||||||
|
{
|
||||||
|
SDL_GetWindowBordersSize( hWnd, &wrc.top, &wrc.left, &wrc.bottom, &wrc.right );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if XASH_WIN32
|
||||||
|
wrc.left = GetSystemMetrics( SM_CYSIZEFRAME );
|
||||||
|
wrc.right = wrc.bottom = wrc.left;
|
||||||
|
wrc.top = GetSystemMetrics( SM_CYSMCAPTION ) + wrc.left;
|
||||||
|
#endif // XASH_WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
rect->x += wrc.left + wrc.right;
|
||||||
|
rect->y += wrc.top + wrc.bottom;
|
||||||
|
rect->w -= ( wrc.left + wrc.right ) * 2;
|
||||||
|
rect->h -= ( wrc.top + wrc.bottom ) * 2;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rserr_t VID_SetScreenResolution( int width, int height, window_mode_t window_mode, window_mode_t prev_window_mode )
|
||||||
|
{
|
||||||
|
const SDL_DisplayID display_id = SDL_GetDisplayForWindow( host.hWnd );
|
||||||
|
int out_width, out_height;
|
||||||
|
|
||||||
|
switch( window_mode )
|
||||||
|
{
|
||||||
|
case WINDOW_MODE_BORDERLESS:
|
||||||
|
{
|
||||||
|
if( !SDL_SetWindowFullscreen( host.hWnd, false ))
|
||||||
|
{
|
||||||
|
Con_PrintSDLError( "SDL_SetWindowFullscreen" );
|
||||||
|
|
||||||
|
// there is no "invalid mode" for borderless fullscreen as there is
|
||||||
|
// no video mode change to begin with
|
||||||
|
return rserr_invalid_fullscreen;
|
||||||
|
}
|
||||||
|
if( !SDL_SetWindowBordered( host.hWnd, false ))
|
||||||
|
{
|
||||||
|
Con_PrintSDLError( "SDL_SetWindowBordered" );
|
||||||
|
|
||||||
|
return rserr_invalid_fullscreen;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WINDOW_MODE_FULLSCREEN:
|
||||||
|
{
|
||||||
|
const SDL_DisplayMode want = { .w = width, .h = height };
|
||||||
|
SDL_DisplayMode **modes;
|
||||||
|
const SDL_DisplayMode *got = 0;
|
||||||
|
int mode_count;
|
||||||
|
|
||||||
|
// return "invalid mode" if we are switching between video modes in fullscreen mode
|
||||||
|
// or "invalid fullscreen" if we are switching from windowed to fullscreen
|
||||||
|
const rserr_t appropriate_err = prev_window_mode == WINDOW_MODE_WINDOWED ? rserr_invalid_fullscreen : rserr_invalid_mode;
|
||||||
|
|
||||||
|
modes = SDL_GetFullscreenDisplayModes( display_id, &mode_count );
|
||||||
|
for( int i = 0; i < mode_count; i++ )
|
||||||
|
{
|
||||||
|
if( modes[i]->w == want.w && modes[i]->h == want.h )
|
||||||
|
{
|
||||||
|
got = modes[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !got )
|
||||||
|
{
|
||||||
|
// Fallback to native
|
||||||
|
got = SDL_GetDesktopDisplayMode( display_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !SDL_SetWindowFullscreenMode( host.hWnd, got ))
|
||||||
|
{
|
||||||
|
Con_PrintSDLError( "SDL_SetWindowFullscreenMode" );
|
||||||
|
SDL_free( modes );
|
||||||
|
return appropriate_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !SDL_SetWindowFullscreen( host.hWnd, true ))
|
||||||
|
{
|
||||||
|
Con_PrintSDLError( "SDL_SetWindowFullscreen" );
|
||||||
|
SDL_free( modes );
|
||||||
|
return appropriate_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_free( modes );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WINDOW_MODE_WINDOWED:
|
||||||
|
if( !SDL_SetWindowFullscreen( host.hWnd, false ))
|
||||||
|
{
|
||||||
|
Con_PrintSDLError( "SDL_SetWindowFullscreen" );
|
||||||
|
|
||||||
|
// TODO: appropriate error type when going back from fullscreen to windowed?
|
||||||
|
return rserr_unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetWindowResizable( host.hWnd, true );
|
||||||
|
SDL_SetWindowBordered( host.hWnd, true );
|
||||||
|
|
||||||
|
if( !FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED ))
|
||||||
|
{
|
||||||
|
const SDL_DisplayMode *dm;
|
||||||
|
qboolean center_window = false;
|
||||||
|
|
||||||
|
if( !( dm = SDL_GetDesktopDisplayMode( display_id )) && width >= dm->w && height >= dm->h )
|
||||||
|
{
|
||||||
|
SDL_SetWindowSize( host.hWnd, dm->w, dm->h );
|
||||||
|
// Con_Printf( "%s: activating fake fullscreen mode\n", __func__ );
|
||||||
|
center_window = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SDL_SetWindowSize( host.hWnd, width, height );
|
||||||
|
|
||||||
|
SDL_Rect r;
|
||||||
|
if( VID_GetDisplayBounds( display_id, host.hWnd, &r ) >= 0 )
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
SDL_GetWindowPosition( host.hWnd, &x, &y );
|
||||||
|
|
||||||
|
if( x <= r.x || y <= r.y )
|
||||||
|
center_window = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( center_window )
|
||||||
|
SDL_SetWindowPosition( host.hWnd, SDL_WINDOWPOS_CENTERED_DISPLAY( display_id ), SDL_WINDOWPOS_CENTERED_DISPLAY( display_id ));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Con_Printf( S_WARN "%s: got invalid mode %d", __func__, window_mode );
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GetWindowSize( host.hWnd, &out_width, &out_height );
|
||||||
|
|
||||||
|
Con_Printf( "%s: Setting video mode to %dx%d %s\n", __func__, out_width, out_height,
|
||||||
|
window_mode == WINDOW_MODE_BORDERLESS ? "borderless"
|
||||||
|
: window_mode == WINDOW_MODE_FULLSCREEN ? "fullscreen" : "windowed" );
|
||||||
|
|
||||||
|
VID_SaveWindowSize( host.hWnd, out_width, out_height );
|
||||||
|
|
||||||
|
// set icon that could've been lost after changing modes
|
||||||
|
VID_SetWindowIcon( host.hWnd );
|
||||||
|
|
||||||
|
return rserr_ok;
|
||||||
|
}
|
||||||
|
|
||||||
rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode )
|
rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode )
|
||||||
{
|
{
|
||||||
|
rserr_t err;
|
||||||
|
|
||||||
if( !host.hWnd )
|
if( !host.hWnd )
|
||||||
{
|
{
|
||||||
// create window if not exists
|
// create window if not exists
|
||||||
@@ -573,12 +802,13 @@ rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mod
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: change current window mode
|
err = VID_SetScreenResolution( width, height, window_mode, refState.window_mode );
|
||||||
return rserr_invalid_fullscreen;
|
if( err != rserr_ok )
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
refState.desktopBitsPixel = 24; // TODO: figure this out
|
|
||||||
refState.window_mode = window_mode;
|
refState.window_mode = window_mode;
|
||||||
|
refState.desktopBitsPixel = 24;
|
||||||
|
|
||||||
return rserr_ok;
|
return rserr_ok;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -542,6 +542,8 @@ void SV_UpdateServerInfo( void );
|
|||||||
void SV_EndRedirect( host_redirect_t *rd );
|
void SV_EndRedirect( host_redirect_t *rd );
|
||||||
void SV_RejectConnection( netadr_t from, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
void SV_RejectConnection( netadr_t from, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
||||||
void SV_GetPlayerCount( int *clients, int *bots );
|
void SV_GetPlayerCount( int *clients, int *bots );
|
||||||
|
int SV_CreateChallenge( netadr_t from, qboolean *error );
|
||||||
|
qboolean SV_ValidateChallenge( netadr_t from, int challenge );
|
||||||
|
|
||||||
static inline qboolean SV_HavePassword( void )
|
static inline qboolean SV_HavePassword( void )
|
||||||
{
|
{
|
||||||
@@ -701,6 +703,6 @@ int SV_LightForEntity( edict_t *pEdict );
|
|||||||
//
|
//
|
||||||
// sv_query.c
|
// sv_query.c
|
||||||
//
|
//
|
||||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from );
|
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from, sizebuf_t *msg );
|
||||||
|
|
||||||
#endif//SERVER_H
|
#endif//SERVER_H
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ GNU General Public License for more details.
|
|||||||
#include "const.h"
|
#include "const.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "net_encode.h"
|
#include "net_encode.h"
|
||||||
#include "net_api.h"
|
|
||||||
|
|
||||||
// challenges are valid for two consecutive windows of this size (max lifetime ~10s).
|
// challenges are valid for two consecutive windows of this size (max lifetime ~10s).
|
||||||
#define CHALLENGE_WINDOW_SECONDS 5
|
#define CHALLENGE_WINDOW_SECONDS 5
|
||||||
@@ -110,11 +109,43 @@ static int SV_GetChallenge( netadr_t from, uint32_t time_window, qboolean *error
|
|||||||
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
|
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SV_SendChallenge( netadr_t from, qboolean skip_bandwidth_test )
|
/*
|
||||||
|
=================
|
||||||
|
SV_CreateChallenge
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
int SV_CreateChallenge( netadr_t from, qboolean *error )
|
||||||
|
{
|
||||||
|
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
||||||
|
|
||||||
|
return SV_GetChallenge( from, time_window, error );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
SV_ValidateChallenge
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
qboolean SV_ValidateChallenge( netadr_t from, int challenge )
|
||||||
{
|
{
|
||||||
qboolean error = false;
|
qboolean error = false;
|
||||||
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
||||||
int challenge = SV_GetChallenge( from, time_window, &error );
|
|
||||||
|
// accept the current window and the previous one so challenges issued just
|
||||||
|
// before a window boundary remain valid for the full expected lifetime
|
||||||
|
if( SV_GetChallenge( from, time_window, &error ) == challenge && !error )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( SV_GetChallenge( from, time_window - 1, &error ) == challenge && !error )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SV_SendChallenge( netadr_t from, qboolean skip_bandwidth_test )
|
||||||
|
{
|
||||||
|
qboolean error = false;
|
||||||
|
int challenge = SV_CreateChallenge( from, &error );
|
||||||
|
|
||||||
if( error )
|
if( error )
|
||||||
return;
|
return;
|
||||||
@@ -214,15 +245,7 @@ Make sure connecting client is not spoofing
|
|||||||
*/
|
*/
|
||||||
static int SV_CheckChallenge( netadr_t from, int challenge )
|
static int SV_CheckChallenge( netadr_t from, int challenge )
|
||||||
{
|
{
|
||||||
qboolean error = false;
|
if( SV_ValidateChallenge( from, challenge ))
|
||||||
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
|
||||||
|
|
||||||
// accept the current window and the previous one so challenges issued just
|
|
||||||
// before a window boundary remain valid for the full expected lifetime
|
|
||||||
if( SV_GetChallenge( from, time_window, &error ) == challenge && !error )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if( SV_GetChallenge( from, time_window - 1, &error ) == challenge && !error )
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||||
@@ -466,6 +489,7 @@ static void SV_ConnectClient( netadr_t from )
|
|||||||
protinfo[0] = '\0';
|
protinfo[0] = '\0';
|
||||||
Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", newcl->extensions );
|
Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", newcl->extensions );
|
||||||
Info_SetValueForKey( protinfo, "cheats", sv_cheats.value ? "1" : "0", sizeof( protinfo ));
|
Info_SetValueForKey( protinfo, "cheats", sv_cheats.value ? "1" : "0", sizeof( protinfo ));
|
||||||
|
Info_SetValueForKeyf( protinfo, "challenge", sizeof( protinfo ), "%d", challenge );
|
||||||
if( FBitSet( newcl->extensions, NET_EXT_NETCHAN_COOKIE ))
|
if( FBitSet( newcl->extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||||
Info_SetValueForKeyf( protinfo, "cookie", sizeof( protinfo ), "%016"PRIx64, netchan_cookie );
|
Info_SetValueForKeyf( protinfo, "cookie", sizeof( protinfo ), "%016"PRIx64, netchan_cookie );
|
||||||
|
|
||||||
@@ -951,111 +975,6 @@ static void SV_ConnectNatClient( netadr_t from )
|
|||||||
SV_Info( to, PROTOCOL_VERSION );
|
SV_Info( to, PROTOCOL_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
================
|
|
||||||
SV_BuildNetAnswer
|
|
||||||
|
|
||||||
Responds with long info for local and broadcast requests
|
|
||||||
================
|
|
||||||
*/
|
|
||||||
static void SV_BuildNetAnswer( netadr_t from )
|
|
||||||
{
|
|
||||||
const cvar_t *cv;
|
|
||||||
char string[4096];
|
|
||||||
int version;
|
|
||||||
int context;
|
|
||||||
int type;
|
|
||||||
int count = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// ignore in single player
|
|
||||||
if( svs.maxclients == 1 || !svs.initialized )
|
|
||||||
return;
|
|
||||||
|
|
||||||
version = Q_atoi( Cmd_Argv( 1 ));
|
|
||||||
context = Q_atoi( Cmd_Argv( 2 ));
|
|
||||||
type = Q_atoi( Cmd_Argv( 3 ));
|
|
||||||
|
|
||||||
string[0] = 0;
|
|
||||||
|
|
||||||
if( version != PROTOCOL_VERSION )
|
|
||||||
{
|
|
||||||
// send error unsupported protocol
|
|
||||||
Info_SetValueForKey( string, "neterror", "protocol", sizeof( string ));
|
|
||||||
Netchan_OutOfBandPrint( NS_SERVER, from, A2A_NETINFO" %i %i %s\n", context, type, string );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( type )
|
|
||||||
{
|
|
||||||
case NETAPI_REQUEST_PING:
|
|
||||||
break;
|
|
||||||
case NETAPI_REQUEST_RULES:
|
|
||||||
for( cv = Cvar_GetList( ); cv; cv = cv->next )
|
|
||||||
{
|
|
||||||
if( !FBitSet( cv->flags, FCVAR_SERVER ))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( FBitSet( cv->flags, FCVAR_PROTECTED ))
|
|
||||||
{
|
|
||||||
if( !COM_StringEmpty( cv->string ) && Q_stricmp( cv->string, "none" ))
|
|
||||||
Info_SetValueForKey( string, cv->name, "1", sizeof( string ));
|
|
||||||
else Info_SetValueForKey( string, cv->name, "0", sizeof( string ));
|
|
||||||
}
|
|
||||||
else Info_SetValueForKey( string, cv->name, cv->string, sizeof( string ));
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Info_SetValueForKeyf( string, "rules", sizeof( string ), "%i", count );
|
|
||||||
break;
|
|
||||||
case NETAPI_REQUEST_PLAYERS:
|
|
||||||
if( !sv_expose_player_list.value || SV_HavePassword( ))
|
|
||||||
{
|
|
||||||
Info_SetValueForKey( string, "neterror", "forbidden", sizeof( string ));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for( i = 0; i < svs.maxclients; i++ )
|
|
||||||
{
|
|
||||||
const sv_client_t *cl = &svs.clients[i];
|
|
||||||
|
|
||||||
if( cl->state < cs_connected )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Info_SetValueForKey( string, va( "p%iname", count ), cl->name, sizeof( string ));
|
|
||||||
Info_SetValueForKeyf( string, va( "p%ifrags", count ), sizeof( string ), "%i", (int)cl->edict->v.frags );
|
|
||||||
Info_SetValueForKeyf( string, va( "p%itime", count ), sizeof( string ), "%f", host.realtime - cl->connection_started );
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Info_SetValueForKeyf( string, "players", sizeof( string ), "%i", count );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NETAPI_REQUEST_DETAILS:
|
|
||||||
for( i = 0; i < svs.maxclients; i++ )
|
|
||||||
{
|
|
||||||
if( svs.clients[i].state >= cs_connected )
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// should match SV_SourceQuery_Details
|
|
||||||
Info_SetValueForKey( string, "hostname", hostname.string, sizeof( string ));
|
|
||||||
Info_SetValueForKey( string, "gamedir", GI->gamefolder, sizeof( string ));
|
|
||||||
Info_SetValueForKeyf( string, "current", sizeof( string ), "%i", count );
|
|
||||||
Info_SetValueForKeyf( string, "max", sizeof( string ), "%i", svs.maxclients );
|
|
||||||
Info_SetValueForKey( string, "map", sv.name, sizeof( string ));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// send error undefined request type
|
|
||||||
Info_SetValueForKey( string, "neterror", "undefined", sizeof( string ));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Netchan_OutOfBandPrint( NS_SERVER, from, A2A_NETINFO" %i %i %s\n", context, type, string );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
================
|
================
|
||||||
Rcon_Validate
|
Rcon_Validate
|
||||||
@@ -3243,11 +3162,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
|||||||
// `pcmd` points only to the first word from the query string.
|
// `pcmd` points only to the first word from the query string.
|
||||||
if( !Q_strcmp( args, A2S_GOLDSRC_INFO ) || pcmd[0] == A2S_GOLDSRC_PLAYERS || pcmd[0] == A2S_GOLDSRC_RULES )
|
if( !Q_strcmp( args, A2S_GOLDSRC_INFO ) || pcmd[0] == A2S_GOLDSRC_PLAYERS || pcmd[0] == A2S_GOLDSRC_RULES )
|
||||||
{
|
{
|
||||||
SV_SourceQuery_HandleConnnectionlessPacket( args, from );
|
SV_SourceQuery_HandleConnnectionlessPacket( args, from, msg );
|
||||||
}
|
|
||||||
else if( !Q_strcmp( pcmd, A2A_NETINFO ))
|
|
||||||
{
|
|
||||||
SV_BuildNetAnswer( from );
|
|
||||||
}
|
}
|
||||||
else if( !Q_strcmp( pcmd, A2A_INFO ))
|
else if( !Q_strcmp( pcmd, A2A_INFO ))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -520,8 +520,7 @@ static void SV_SetupPMove( playermove_t *pmove, sv_client_t *cl, usercmd_t *ucmd
|
|||||||
vec3_t absmin, absmax;
|
vec3_t absmin, absmax;
|
||||||
edict_t *clent = cl->edict;
|
edict_t *clent = cl->edict;
|
||||||
|
|
||||||
svgame.globals->frametime = (ucmd->msec * 0.001f);
|
pmove->frametime = ucmd->msec * 0.001f;
|
||||||
|
|
||||||
pmove->player_index = NUM_FOR_EDICT( clent ) - 1;
|
pmove->player_index = NUM_FOR_EDICT( clent ) - 1;
|
||||||
pmove->multiplayer = (svs.maxclients > 1) ? true : false;
|
pmove->multiplayer = (svs.maxclients > 1) ? true : false;
|
||||||
pmove->time = (float)(cl->timebase * 1000.0);
|
pmove->time = (float)(cl->timebase * 1000.0);
|
||||||
|
|||||||
@@ -165,12 +165,49 @@ static void SV_SourceQuery_Players( netadr_t from )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
SV_SourceQuery_CheckChallenge
|
||||||
|
|
||||||
|
Validates the challenge that follows the query type byte.
|
||||||
|
On missing, mismatched or explicitly requested (-1) challenge,
|
||||||
|
replies with a new challenge for this address.
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
static qboolean SV_SourceQuery_CheckChallenge( netadr_t from, sizebuf_t *msg )
|
||||||
|
{
|
||||||
|
qboolean error = false;
|
||||||
|
|
||||||
|
MSG_SeekToBit( msg, ( sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||||
|
int challenge = MSG_ReadLong( msg );
|
||||||
|
|
||||||
|
if( !MSG_CheckOverflow( msg ) && challenge != -1 && SV_ValidateChallenge( from, challenge ))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
challenge = SV_CreateChallenge( from, &error );
|
||||||
|
if( error )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sizebuf_t buf;
|
||||||
|
char answer[16];
|
||||||
|
|
||||||
|
MSG_Init( &buf, "QueryChallenge", answer, sizeof( answer ));
|
||||||
|
|
||||||
|
MSG_WriteDword( &buf, 0xFFFFFFFFU );
|
||||||
|
MSG_WriteByte( &buf, S2C_GOLDSRC_CHALLENGE[0] );
|
||||||
|
MSG_WriteLong( &buf, challenge );
|
||||||
|
|
||||||
|
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
==================
|
==================
|
||||||
SV_SourceQuery_HandleConnnectionlessPacket
|
SV_SourceQuery_HandleConnnectionlessPacket
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from )
|
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from, sizebuf_t *msg )
|
||||||
{
|
{
|
||||||
if( !Q_strcmp( c, A2S_GOLDSRC_INFO ))
|
if( !Q_strcmp( c, A2S_GOLDSRC_INFO ))
|
||||||
{
|
{
|
||||||
@@ -179,10 +216,12 @@ void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from )
|
|||||||
else switch( c[0] )
|
else switch( c[0] )
|
||||||
{
|
{
|
||||||
case A2S_GOLDSRC_RULES:
|
case A2S_GOLDSRC_RULES:
|
||||||
SV_SourceQuery_Rules( from );
|
if( SV_SourceQuery_CheckChallenge( from, msg ))
|
||||||
|
SV_SourceQuery_Rules( from );
|
||||||
break;
|
break;
|
||||||
case A2S_GOLDSRC_PLAYERS:
|
case A2S_GOLDSRC_PLAYERS:
|
||||||
SV_SourceQuery_Players( from );
|
if( SV_SourceQuery_CheckChallenge( from, msg ))
|
||||||
|
SV_SourceQuery_Players( from );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ def options(opt):
|
|||||||
grp.add_option('--enable-static-binary', action = 'store_true', dest = 'STATIC', default = False,
|
grp.add_option('--enable-static-binary', action = 'store_true', dest = 'STATIC', default = False,
|
||||||
help = 'build static binary(not recommended, --single-binary required) [default: %(default)s]')
|
help = 'build static binary(not recommended, --single-binary required) [default: %(default)s]')
|
||||||
|
|
||||||
grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False,
|
|
||||||
help = 'add LLVM libFuzzer [default: %(default)s]' )
|
|
||||||
|
|
||||||
grp.add_option('--enable-ffmpeg', action = 'store_true', dest = 'FFMPEG', default = False,
|
grp.add_option('--enable-ffmpeg', action = 'store_true', dest = 'FFMPEG', default = False,
|
||||||
help = 'enable ffmpeg based movie playback [default: %(default)s')
|
help = 'enable ffmpeg based movie playback [default: %(default)s')
|
||||||
|
|
||||||
@@ -89,9 +86,10 @@ def configure(conf):
|
|||||||
elif conf.env.DEST_OS == 'dos':
|
elif conf.env.DEST_OS == 'dos':
|
||||||
conf.options.STATIC = True
|
conf.options.STATIC = True
|
||||||
|
|
||||||
if conf.options.ENGINE_FUZZ:
|
if conf.options.ENABLE_FUZZER:
|
||||||
|
conf.define('XASH_LLVM_LIBFUZZER', 1)
|
||||||
conf.env.append_unique('CFLAGS', '-fsanitize=fuzzer-no-link')
|
conf.env.append_unique('CFLAGS', '-fsanitize=fuzzer-no-link')
|
||||||
conf.env.append_unique('LINKFLAGS', '-fsanitize=fuzzer')
|
conf.env.append_unique('LINKFLAGS', '-fsanitize=fuzzer-no-link')
|
||||||
|
|
||||||
# Client-only dependencies
|
# Client-only dependencies
|
||||||
if conf.env.CLIENT:
|
if conf.env.CLIENT:
|
||||||
@@ -161,7 +159,7 @@ def build(bld):
|
|||||||
# public includes for renderers and utils use
|
# public includes for renderers and utils use
|
||||||
bld(name = 'engine_includes', export_includes = '. common common/imagelib', use = 'filesystem_includes')
|
bld(name = 'engine_includes', export_includes = '. common common/imagelib', use = 'filesystem_includes')
|
||||||
|
|
||||||
libs = ['engine_includes', 'public', 'werror', 'backtrace_custom', 'library_suffix', 'build_vcs', 'mbedtls']
|
libs = ['engine_includes', 'public', 'werror', 'backtrace_custom', 'library_suffix', 'build_vcs', 'mbedtls', 'yy_thunks']
|
||||||
includes = ['server', 'client', 'client/vgui', 'common/soundlib', 'platform']
|
includes = ['server', 'client', 'client/vgui', 'common/soundlib', 'platform']
|
||||||
|
|
||||||
# basic build: dedicated only
|
# basic build: dedicated only
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ GNU General Public License for more details.
|
|||||||
// GoldSrc Directories and ID
|
// GoldSrc Directories and ID
|
||||||
// GAME gamedir
|
// GAME gamedir
|
||||||
// GAMECONFIG gamedir (rodir integration?)
|
// GAMECONFIG gamedir (rodir integration?)
|
||||||
// GAMEDOWNLOAD gamedir_downloads (gamedir/downloads for us)
|
// GAMEDOWNLOAD gamedir_downloads
|
||||||
// GAME_FALLBACK liblist.gam's fallback_dir
|
// GAME_FALLBACK liblist.gam's fallback_dir
|
||||||
// ROOT and BASE rootdir
|
// ROOT and BASE rootdir
|
||||||
// PLATFORM platform
|
// PLATFORM platform
|
||||||
@@ -41,14 +41,22 @@ GNU General Public License for more details.
|
|||||||
char * const var = static_cast<char *>( alloca( var ## _size )); \
|
char * const var = static_cast<char *>( alloca( var ## _size )); \
|
||||||
CopyAndFixSlashes( var, ( str ), var ## _size )
|
CopyAndFixSlashes( var, ( str ), var ## _size )
|
||||||
|
|
||||||
static inline bool IsIdGamedir( const char *id )
|
static bool IsIdGamedir( const char *id )
|
||||||
{
|
{
|
||||||
return !Q_strcmp( id, "GAME" ) ||
|
return !Q_strcmp( id, "GAME" ) ||
|
||||||
!Q_strcmp( id, "GAMECONFIG" ) ||
|
!Q_strcmp( id, "GAMECONFIG" ) ||
|
||||||
!Q_strcmp( id, "GAMEDOWNLOAD" );
|
!Q_strcmp( id, "GAMEDOWNLOAD" );
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const char *IdToDir( char *dir, size_t size, const char *id )
|
static searchpath_t *IdToWritableSearchpath( const char *id )
|
||||||
|
{
|
||||||
|
if( !Q_strcmp( id, "GAME" ) || !Q_strcmp( id, "GAMECONFIG" ))
|
||||||
|
return fs_writepath;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *IdToDir( char *dir, size_t size, const char *id )
|
||||||
{
|
{
|
||||||
if( !Q_strcmp( id, "GAME" ))
|
if( !Q_strcmp( id, "GAME" ))
|
||||||
return GI->gamefolder;
|
return GI->gamefolder;
|
||||||
@@ -72,7 +80,7 @@ static inline const char *IdToDir( char *dir, size_t size, const char *id )
|
|||||||
return fs_rootdir; // give at least root directory
|
return fs_rootdir; // give at least root directory
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void CopyAndFixSlashes( char *p, const char *in, size_t size )
|
static void CopyAndFixSlashes( char *p, const char *in, size_t size )
|
||||||
{
|
{
|
||||||
Q_strncpy( p, in, size );
|
Q_strncpy( p, in, size );
|
||||||
COM_FixSlashes( p );
|
COM_FixSlashes( p );
|
||||||
@@ -147,10 +155,9 @@ public:
|
|||||||
|
|
||||||
void RemoveFile( const char *path, const char *id ) override
|
void RemoveFile( const char *path, const char *id ) override
|
||||||
{
|
{
|
||||||
char dir[MAX_VA_STRING], fullpath[MAX_VA_STRING];
|
// FIXME: handle writable downloads path
|
||||||
|
if( IdToWritableSearchpath( id ))
|
||||||
Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", IdToDir( dir, sizeof( dir ), id ), path );
|
FS_Delete( path ); // FS_Delete is aware of slashes
|
||||||
FS_Delete( fullpath ); // FS_Delete is aware of slashes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateDirHierarchy( const char *path, const char *id ) override
|
void CreateDirHierarchy( const char *path, const char *id ) override
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def configure(conf):
|
|||||||
def build(bld):
|
def build(bld):
|
||||||
bld(name = 'filesystem_includes', export_includes = '.')
|
bld(name = 'filesystem_includes', export_includes = '.')
|
||||||
|
|
||||||
libs = [ 'filesystem_includes', 'sdk_includes', 'werror' ]
|
libs = [ 'filesystem_includes', 'sdk_includes', 'werror', 'yy_thunks' ]
|
||||||
|
|
||||||
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
||||||
if bld.env.DEST_OS != 'psvita':
|
if bld.env.DEST_OS != 'psvita':
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def build(bld):
|
|||||||
|
|
||||||
bld.program(source = source,
|
bld.program(source = source,
|
||||||
target = 'xash3d', # hl.exe
|
target = 'xash3d', # hl.exe
|
||||||
use = 'sdk_includes DL USER32 SHELL32 werror',
|
use = 'sdk_includes DL USER32 SHELL32 werror yy_thunks',
|
||||||
rpath = bld.env.DEFAULT_RPATH,
|
rpath = bld.env.DEFAULT_RPATH,
|
||||||
install_path = bld.env.BINDIR,
|
install_path = bld.env.BINDIR,
|
||||||
subsystem = bld.env.MSVC_SUBSYSTEM
|
subsystem = bld.env.MSVC_SUBSYSTEM
|
||||||
|
|||||||
19
ref/dx2/dllmain.cpp
Normal file
19
ref/dx2/dllmain.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||||
|
DWORD ul_reason_for_call,
|
||||||
|
LPVOID lpReserved
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (ul_reason_for_call)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
481
ref/dx2/r_context.c
Normal file
481
ref/dx2/r_context.c
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
/*
|
||||||
|
r_context.c -- dx2 renderer context
|
||||||
|
Copyright (C) 2023-2024 a1batross
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define INITGUID
|
||||||
|
#include "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
#include "crtlib.h"
|
||||||
|
#include "r_studioint.h"
|
||||||
|
|
||||||
|
//#pragma comment(lib, "ddraw.lib")
|
||||||
|
//TODO COM_GetProcAddress
|
||||||
|
|
||||||
|
|
||||||
|
static void R_SimpleStub( void )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_SimpleStubInt( int unused )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_SimpleStubUInt( unsigned int unused )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *R_GetConfigName( void )
|
||||||
|
{
|
||||||
|
return "ref_dx2";
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean R_SetDisplayTransform( ref_screen_rotation_t rotate, int offset_x, int offset_y, float scale_x, float scale_y )
|
||||||
|
{
|
||||||
|
qboolean ret = true;
|
||||||
|
|
||||||
|
tr.rotation = rotate;
|
||||||
|
|
||||||
|
if( offset_x || offset_y )
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf("offset transform not supported\n");
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( scale_x != 1.0f || scale_y != 1.0f )
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf("scale transform not supported\n");
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_BackendStartFrame( void )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_BackendEndFrame( void )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_SetRenderMode( int mode )
|
||||||
|
{
|
||||||
|
dxc.renderMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void R_ProcessEntData( qboolean allocate, cl_entity_t *entities, unsigned int max_entities )
|
||||||
|
{
|
||||||
|
if (!allocate)
|
||||||
|
{
|
||||||
|
tr.draw_list->num_solid_entities = 0;
|
||||||
|
tr.draw_list->num_trans_entities = 0;
|
||||||
|
tr.draw_list->num_beam_entities = 0;
|
||||||
|
|
||||||
|
tr.max_entities = 0;
|
||||||
|
tr.entities = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tr.max_entities = max_entities;
|
||||||
|
tr.entities = entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gEngfuncs.drawFuncs->R_ProcessEntData)
|
||||||
|
gEngfuncs.drawFuncs->R_ProcessEntData(allocate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_SetupSky( int *skytextures )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean VID_ScreenShot( const char *filename, int shot_type )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static colorVec R_LightPoint( const float *p )
|
||||||
|
{
|
||||||
|
colorVec c = { 0 };
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int R_CreateDecalList( struct decallist_s *pList )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float R_StudioEstimateFrame( cl_entity_t *e, mstudioseqdesc_t *pseqdesc, double time )
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_StudioLerpMovement( cl_entity_t *e, double time, vec3_t origin, vec3_t angles )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean R_StudioFillAPI( struct engine_studio_api_s *api, struct r_studio_interface_s *pDefaultDraw )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static r_studio_interface_t *pStudioDraw;
|
||||||
|
|
||||||
|
static void R_StudioSetDrawInterface( struct r_studio_interface_s *pDraw )
|
||||||
|
{
|
||||||
|
pStudioDraw = pDraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_SetSkyCloudsTextures( int solidskyTexture, int alphaskyTexture )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CL_RunLightStyles( lightstyle_t *ls )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte *buffer, size_t buffersize )
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf("Mod_ProcessRenderData(%p(%s), %d, %p, %zu)\n", mod, mod->name, create, buffer, buffersize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Mod_StudioLoadTextures( model_t *mod, void *data )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CL_DrawParticles( double frametime, particle_t *particles, float partsize )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CL_DrawTracers( double frametime, particle_t *tracers )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CL_DrawBeams( int fTrans, BEAM *beams )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static intptr_t RefGetParm( int parm, int arg )
|
||||||
|
{
|
||||||
|
dx_texture_t *glt;
|
||||||
|
|
||||||
|
switch (parm)
|
||||||
|
{
|
||||||
|
case PARM_TEX_WIDTH:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->width;
|
||||||
|
case PARM_TEX_HEIGHT:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->height;
|
||||||
|
case PARM_TEX_SRC_WIDTH:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->srcWidth;
|
||||||
|
case PARM_TEX_SRC_HEIGHT:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->srcHeight;
|
||||||
|
case PARM_TEX_GLFORMAT:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return 0;
|
||||||
|
case PARM_TEX_ENCODE:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return 0;
|
||||||
|
case PARM_TEX_MIPCOUNT:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->numMips;
|
||||||
|
case PARM_TEX_DEPTH:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return 1;
|
||||||
|
#if 0
|
||||||
|
case PARM_TEX_SKYBOX:
|
||||||
|
Assert(arg >= 0 && arg < 6);
|
||||||
|
return tr.skyboxTextures[arg];
|
||||||
|
#endif
|
||||||
|
case PARM_TEX_SKYTEXNUM:
|
||||||
|
return tr.skytexturenum;
|
||||||
|
#if 0
|
||||||
|
case PARM_TEX_LIGHTMAP:
|
||||||
|
arg = bound(0, arg, MAX_LIGHTMAPS - 1);
|
||||||
|
return tr.lightmapTextures[arg];
|
||||||
|
#endif
|
||||||
|
case PARM_TEX_TARGET:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return 0;
|
||||||
|
case PARM_TEX_TEXNUM:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return arg;
|
||||||
|
case PARM_TEX_FLAGS:
|
||||||
|
glt = R_GetTexture(arg);
|
||||||
|
return glt->flags;
|
||||||
|
#if 0
|
||||||
|
case PARM_TEX_MEMORY:
|
||||||
|
return GL_TexMemory();
|
||||||
|
case PARM_ACTIVE_TMU:
|
||||||
|
return glState.activeTMU;
|
||||||
|
#endif
|
||||||
|
case PARM_LIGHTSTYLEVALUE:
|
||||||
|
arg = bound(0, arg, MAX_LIGHTSTYLES - 1);
|
||||||
|
return g_lightstylevalue[arg];
|
||||||
|
#if 0
|
||||||
|
case PARM_MAX_IMAGE_UNITS:
|
||||||
|
return GL_MaxTextureUnits();
|
||||||
|
case PARM_REBUILD_GAMMA:
|
||||||
|
return glConfig.softwareGammaUpdate;
|
||||||
|
case PARM_GL_CONTEXT_TYPE:
|
||||||
|
return glConfig.context;
|
||||||
|
case PARM_GLES_WRAPPER:
|
||||||
|
return glConfig.wrapper;
|
||||||
|
#endif
|
||||||
|
case PARM_STENCIL_ACTIVE:
|
||||||
|
return 0;//No stencil in dx2
|
||||||
|
case PARM_TEX_FILTERING:
|
||||||
|
return 0;
|
||||||
|
#if 0
|
||||||
|
if (arg < 0)
|
||||||
|
return gl_texture_nearest.value == 0.0f;
|
||||||
|
|
||||||
|
return GL_TextureFilteringEnabled(R_GetTexture(arg));
|
||||||
|
#endif
|
||||||
|
case PARM_GET_STUDIO_HDR:
|
||||||
|
//return (intptr_t)R_StudioGetHeader();
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return (*gEngfuncs.EngineGetParm)(parm, arg);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GL_Bind( int tmu, unsigned int texnum )
|
||||||
|
{
|
||||||
|
const dx_texture_t *texture;
|
||||||
|
|
||||||
|
// missed or invalid texture?
|
||||||
|
if( texnum <= 0 || texnum >= MAX_TEXTURES )
|
||||||
|
{
|
||||||
|
if( texnum != 0 )
|
||||||
|
gEngfuncs.Con_DPrintf( S_ERROR "%s: invalid texturenum %d\n", __func__, texnum );
|
||||||
|
texnum = tr.defaultTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = R_GetTexture( texnum );
|
||||||
|
|
||||||
|
//if( dxc.currentTexture == texture->d3dHandle )
|
||||||
|
// return;
|
||||||
|
|
||||||
|
dxc.currentTexture = texture->d3dHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean R_SpeedsMessage( char *out, size_t size )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_NewMap( void )
|
||||||
|
{
|
||||||
|
tr.worldmodel = gp_cl->models[1];
|
||||||
|
|
||||||
|
// clear out efrags in case the level hasn't been reloaded
|
||||||
|
for( int i = 0; i < tr.worldmodel->numleafs; i++ )
|
||||||
|
tr.worldmodel->leafs[i+1].efrags = NULL;
|
||||||
|
|
||||||
|
tr.skytexturenum = -1;
|
||||||
|
|
||||||
|
// clearing texture chains
|
||||||
|
for( int i = 0; i < tr.worldmodel->numtextures; i++ )
|
||||||
|
{
|
||||||
|
if( !tr.worldmodel->textures[i] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
texture_t *tx = tr.worldmodel->textures[i];
|
||||||
|
|
||||||
|
if( !Q_strncmp( tx->name, "sky", 3 ) && tx->width == ( tx->height * 2 ))
|
||||||
|
tr.skytexturenum = i;
|
||||||
|
|
||||||
|
tx->texturechain = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_BuildLightmaps();
|
||||||
|
|
||||||
|
if( gEngfuncs.drawFuncs->R_NewMap != NULL )
|
||||||
|
gEngfuncs.drawFuncs->R_NewMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Color4f( float r, float g, float b, float a )
|
||||||
|
{
|
||||||
|
Vector4Set(dxc.currentColor, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
|
||||||
|
{
|
||||||
|
Vector4Set(dxc.currentColor, r/255.f, g/255.f, b/255.f, a/255.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Vertex3fv( const float *worldPnt )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Vertex3f( float x, float y, float z )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CullFace( TRICULLSTYLE mode )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VGUI_SetupDrawing( qboolean rect )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_FillRenderAPI( render_api_t *api )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_FillTriAPI( triangleapi_t *api )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ref_interface_t gReffuncs =
|
||||||
|
{
|
||||||
|
.R_Init = R_Init,
|
||||||
|
.R_Shutdown = R_Shutdown,
|
||||||
|
.R_GetConfigName = R_GetConfigName,
|
||||||
|
.R_SetDisplayTransform = R_SetDisplayTransform,
|
||||||
|
|
||||||
|
.GL_SetupAttributes = GL_SetupAttributes,
|
||||||
|
.GL_InitExtensions = GL_InitExtensions,
|
||||||
|
.GL_ClearExtensions = GL_ClearExtensions,
|
||||||
|
|
||||||
|
.R_GammaChanged = R_GammaChanged,
|
||||||
|
.R_BeginFrame = R_BeginFrame,
|
||||||
|
.R_RenderScene = R_RenderScene,
|
||||||
|
.R_EndFrame = R_EndFrame,
|
||||||
|
.R_PushScene = R_SimpleStub,
|
||||||
|
.R_PopScene = R_SimpleStub,
|
||||||
|
.GL_BackendStartFrame = GL_BackendStartFrame,
|
||||||
|
.GL_BackendEndFrame = GL_BackendEndFrame,
|
||||||
|
|
||||||
|
.R_ClearScreen = R_SimpleStub,
|
||||||
|
.R_AllowFog = R_AllowFog,
|
||||||
|
.GL_SetRenderMode = GL_SetRenderMode,
|
||||||
|
|
||||||
|
.R_AddEntity = R_AddEntity,
|
||||||
|
.R_ProcessEntData = R_ProcessEntData,
|
||||||
|
|
||||||
|
.R_ShowTextures = R_ShowTextures,
|
||||||
|
|
||||||
|
.R_GetTextureOriginalBuffer = R_GetTextureOriginalBuffer,
|
||||||
|
.GL_LoadTextureFromBuffer = GL_LoadTextureFromBuffer,
|
||||||
|
.GL_ProcessTexture = GL_ProcessTexture,
|
||||||
|
.R_SetupSky = R_SetupSky,
|
||||||
|
|
||||||
|
.R_Set2DMode = R_Set2DMode,
|
||||||
|
.R_DrawStretchPic = R_DrawStretchPic,
|
||||||
|
.FillRGBA = FillRGBA,
|
||||||
|
.WorldToScreen = WorldToScreen,
|
||||||
|
|
||||||
|
.VID_ScreenShot = VID_ScreenShot,
|
||||||
|
.VID_CubemapShot = VID_CubemapShot,
|
||||||
|
|
||||||
|
.R_LightPoint = R_LightPoint,
|
||||||
|
|
||||||
|
.R_DecalShoot = R_DecalShoot,
|
||||||
|
.R_DecalRemoveAll = R_SimpleStubInt,
|
||||||
|
.R_CreateDecalList = R_CreateDecalList,
|
||||||
|
.R_ClearAllDecals = R_SimpleStub,
|
||||||
|
|
||||||
|
.R_StudioEstimateFrame = R_StudioEstimateFrame,
|
||||||
|
.R_StudioLerpMovement = R_StudioLerpMovement,
|
||||||
|
.R_StudioFillAPI = R_StudioFillAPI,
|
||||||
|
.R_StudioSetDrawInterface = R_StudioSetDrawInterface,
|
||||||
|
|
||||||
|
.R_SetSkyCloudsTextures = R_SetSkyCloudsTextures,
|
||||||
|
.GL_SubdivideSurface = GL_SubdivideSurface,
|
||||||
|
.CL_RunLightStyles = CL_RunLightStyles,
|
||||||
|
|
||||||
|
.Mod_ProcessRenderData = Mod_ProcessRenderData,
|
||||||
|
.Mod_StudioLoadTextures = Mod_StudioLoadTextures,
|
||||||
|
|
||||||
|
.CL_DrawParticles = CL_DrawParticles,
|
||||||
|
.CL_DrawTracers = CL_DrawTracers,
|
||||||
|
.CL_DrawBeams = CL_DrawBeams,
|
||||||
|
|
||||||
|
.RefGetParm = RefGetParm,
|
||||||
|
|
||||||
|
.R_GetDetailScaleForTexture = R_GetDetailScaleForTexture,
|
||||||
|
.R_SetDetailScaleForTexture = R_SetDetailScaleForTexture,
|
||||||
|
|
||||||
|
.GL_CreateTexture = GL_CreateTexture,
|
||||||
|
.GL_FindTexture = GL_FindTexture,
|
||||||
|
.GL_TextureName = GL_TextureName,
|
||||||
|
.GL_TextureData = GL_TextureData,
|
||||||
|
.GL_LoadTexture = GL_LoadTexture,
|
||||||
|
.GL_FreeTexture = GL_FreeTexture,
|
||||||
|
.R_OverrideTextureSourceSize = R_OverrideTextureSourceSize,
|
||||||
|
.GL_UpdateTexture = GL_UpdateTexture,
|
||||||
|
|
||||||
|
.GL_Bind = GL_Bind,
|
||||||
|
|
||||||
|
.GL_RenderFrame = R_RenderFrame,
|
||||||
|
.GL_OrthoBounds = GL_OrthoBounds,
|
||||||
|
.R_SpeedsMessage = R_SpeedsMessage,
|
||||||
|
.Mod_GetCurrentVis = Mod_GetCurrentVis,
|
||||||
|
.R_NewMap = R_NewMap,
|
||||||
|
.R_ClearScene = R_ClearScene,
|
||||||
|
|
||||||
|
.TriRenderMode = R_SimpleStubInt,
|
||||||
|
.Begin = R_SimpleStubInt,
|
||||||
|
.End = R_SimpleStub,
|
||||||
|
.Color4f = Color4f,
|
||||||
|
.Color4ub = Color4ub,
|
||||||
|
.Vertex3fv = Vertex3fv,
|
||||||
|
.Vertex3f = Vertex3f,
|
||||||
|
.CullFace = CullFace,
|
||||||
|
|
||||||
|
.R_FillRenderAPI = R_FillRenderAPI,
|
||||||
|
.R_FillTriAPI = R_FillTriAPI,
|
||||||
|
|
||||||
|
.VGUI_SetupDrawing = VGUI_SetupDrawing,
|
||||||
|
};
|
||||||
|
|
||||||
559
ref/dx2/r_d3d.c
Normal file
559
ref/dx2/r_d3d.c
Normal file
@@ -0,0 +1,559 @@
|
|||||||
|
/*
|
||||||
|
r_d3d.c -- dx2 renderer d3d related code
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
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 "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
|
||||||
|
CVAR_DEFINE_AUTO( r_novis, "0", 0, "ignore vis information (perfomance test)" );
|
||||||
|
CVAR_DEFINE_AUTO( r_nocull, "0", 0, "ignore frustrum culling (perfomance test)" );
|
||||||
|
CVAR_DEFINE_AUTO( r_lockpvs, "0", FCVAR_CHEAT, "lockpvs area at current point (pvs test)" );
|
||||||
|
CVAR_DEFINE_AUTO( r_lockfrustum, "0", FCVAR_CHEAT, "lock frustrum area at current point (cull test)" );
|
||||||
|
|
||||||
|
dx_globals_t tr;
|
||||||
|
dx_context_t dxc;
|
||||||
|
|
||||||
|
const char *dxResultToStr( HRESULT r )
|
||||||
|
{
|
||||||
|
if( r == DD_OK )
|
||||||
|
return "DD_OK";
|
||||||
|
if( r == DDERR_INVALIDPARAMS )
|
||||||
|
return "DDERR_INVALIDPARAMS";
|
||||||
|
if( r == DDERR_INVALIDOBJECT )
|
||||||
|
return "DDERR_INVALIDOBJECT";
|
||||||
|
if( r == DDERR_UNSUPPORTED )
|
||||||
|
return "DDERR_UNSUPPORTED";
|
||||||
|
if( r == DDERR_NOCOOPERATIVELEVELSET )
|
||||||
|
return "DDERR_NOCOOPERATIVELEVELSET";
|
||||||
|
if( r == DDERR_INVALIDCAPS )
|
||||||
|
return "DDERR_INVALIDCAPS";
|
||||||
|
if( r == DDERR_INCOMPATIBLEPRIMARY )
|
||||||
|
return "DDERR_INCOMPATIBLEPRIMARY";
|
||||||
|
if( r == DDERR_NOEXCLUSIVEMODE )
|
||||||
|
return "DDERR_NOEXCLUSIVEMODE";
|
||||||
|
if( r == DDERR_NOFLIPHW )
|
||||||
|
return "DDERR_NOFLIPHW";
|
||||||
|
if( r == DDERR_OUTOFVIDEOMEMORY )
|
||||||
|
return "DDERR_OUTOFVIDEOMEMORY";
|
||||||
|
if( r == DDERR_PRIMARYSURFACEALREADYEXISTS )
|
||||||
|
return "DDERR_PRIMARYSURFACEALREADYEXISTS";
|
||||||
|
if( r == DDERR_UNSUPPORTEDMODE )
|
||||||
|
return "DDERR_UNSUPPORTEDMODE";
|
||||||
|
if( r == DDERR_NOTFOUND )
|
||||||
|
return "DDERR_NOTFOUND";
|
||||||
|
if( r == DDERR_INVALIDRECT )
|
||||||
|
return "DDERR_INVALIDRECT";
|
||||||
|
|
||||||
|
if( r == D3DERR_VIEWPORTDATANOTSET )
|
||||||
|
return "D3DERR_VIEWPORTDATANOTSET";
|
||||||
|
if( r == D3DERR_VIEWPORTHASNODEVICE )
|
||||||
|
return "D3DERR_VIEWPORTHASNODEVICE";
|
||||||
|
if( r == D3DERR_SURFACENOTINVIDMEM )
|
||||||
|
return "D3DERR_SURFACENOTINVIDMEM";
|
||||||
|
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean DD_CreatePrimarySurface( void )
|
||||||
|
{
|
||||||
|
DDSURFACEDESC ddsd = { 0 };
|
||||||
|
memset(&ddsd, 0, sizeof(ddsd));
|
||||||
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
|
ddsd.dwFlags = DDSD_CAPS;
|
||||||
|
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &dxc.pddsPrimary, NULL));
|
||||||
|
printf("IDirectDraw::CreateSurface(PRIMARY) %p\n", dxc.pddsPrimary);
|
||||||
|
if (!dxc.pddsPrimary)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_CreateClipper(dxc.pdd, 0, &dxc.pddClipper, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::CreateClipper %p\n", dxc.pddClipper);
|
||||||
|
if (!dxc.pddClipper)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirectDrawClipper_SetHWnd(dxc.pddClipper, 0, dxc.window));
|
||||||
|
DXCheck(IDirectDrawSurface_SetClipper(dxc.pddsPrimary, dxc.pddClipper));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean DD_CreateBackSurface( void )
|
||||||
|
{
|
||||||
|
DDCAPS ddcaps =
|
||||||
|
{
|
||||||
|
.dwSize = sizeof(ddcaps)
|
||||||
|
};
|
||||||
|
DDCAPS helcaps =
|
||||||
|
{
|
||||||
|
.dwSize = sizeof(helcaps)
|
||||||
|
};
|
||||||
|
DXCheck(IDirectDraw_GetCaps(dxc.pdd, &ddcaps, &helcaps));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::GetCaps\n");
|
||||||
|
gEngfuncs.Con_Printf("ZBUFFER: %s\n", (ddcaps.ddsCaps.dwCaps & DDSCAPS_ZBUFFER) ? "- ^2enabled" : "- ^1failed");
|
||||||
|
if (ddcaps.ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
|
||||||
|
gEngfuncs.Con_Printf("ZBufferBitDepths: 0x%X\n", ddcaps.dwZBufferBitDepths);
|
||||||
|
if (helcaps.ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
|
||||||
|
gEngfuncs.Con_Printf("HEL ZBufferBitDepths: 0x%X\n", helcaps.dwZBufferBitDepths);
|
||||||
|
|
||||||
|
DDSURFACEDESC ddsd = { 0 };
|
||||||
|
memset(&ddsd, 0, sizeof(ddsd));
|
||||||
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
|
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
|
||||||
|
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY;
|
||||||
|
ddsd.dwWidth = dxc.windowWidth;
|
||||||
|
ddsd.dwHeight = dxc.windowHeight;
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &dxc.pddsBack, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::CreateSurface(BACK) %p\n", dxc.pddsBack);
|
||||||
|
if (!dxc.pddsBack)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_CreateClipper(dxc.pdd, 0, &dxc.pddBackClipper, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::CreateClipper(BACK) %p\n", dxc.pddBackClipper);
|
||||||
|
if (!dxc.pddBackClipper)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct region_t
|
||||||
|
{
|
||||||
|
RGNDATAHEADER rdh;
|
||||||
|
RECT rect;
|
||||||
|
} region;
|
||||||
|
region.rdh.dwSize = sizeof(region.rdh);
|
||||||
|
region.rdh.iType = RDH_RECTANGLES;
|
||||||
|
region.rdh.nCount = 1;
|
||||||
|
region.rdh.nRgnSize = sizeof(RECT);
|
||||||
|
region.rdh.rcBound.left = 0;
|
||||||
|
region.rdh.rcBound.top = 0;
|
||||||
|
region.rdh.rcBound.right = dxc.windowWidth;
|
||||||
|
region.rdh.rcBound.bottom = dxc.windowHeight;
|
||||||
|
memcpy(®ion.rect, ®ion.rdh.rcBound, sizeof(region.rdh.rcBound));
|
||||||
|
|
||||||
|
DXCheck(IDirectDrawClipper_SetClipList(dxc.pddBackClipper, (RGNDATA *)®ion, 0));
|
||||||
|
DXCheck(IDirectDrawSurface_SetClipper(dxc.pddsBack, dxc.pddBackClipper));
|
||||||
|
|
||||||
|
int bitDepths = ddcaps.dwZBufferBitDepths;
|
||||||
|
if (!bitDepths)
|
||||||
|
bitDepths = helcaps.dwZBufferBitDepths;
|
||||||
|
if( (ddcaps.ddsCaps.dwCaps & DDSCAPS_ZBUFFER) && bitDepths )
|
||||||
|
{
|
||||||
|
memset(&ddsd, 0, sizeof(ddsd));
|
||||||
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
|
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_ZBUFFERBITDEPTH;
|
||||||
|
ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY;
|
||||||
|
ddsd.dwWidth = dxc.windowWidth;
|
||||||
|
ddsd.dwHeight = dxc.windowHeight;
|
||||||
|
ddsd.dwZBufferBitDepth = 16;
|
||||||
|
if(bitDepths & DDBD_16 )
|
||||||
|
ddsd.dwZBufferBitDepth = 16;
|
||||||
|
else if(bitDepths & DDBD_24 )
|
||||||
|
ddsd.dwZBufferBitDepth = 24;
|
||||||
|
else if(bitDepths & DDBD_32 )
|
||||||
|
ddsd.dwZBufferBitDepth = 32;
|
||||||
|
DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &dxc.pddsZBuffer, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::CreateSurface(ZBUFFER %dbit) %p\n", ddsd.dwZBufferBitDepth, dxc.pddsZBuffer);
|
||||||
|
if( !dxc.pddsZBuffer )
|
||||||
|
return false;
|
||||||
|
DXCheck(IDirectDrawSurface_AddAttachedSurface(dxc.pddsBack, dxc.pddsZBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean D3D_Init( void )
|
||||||
|
{
|
||||||
|
DXCheck(IDirectDraw_QueryInterface(dxc.pdd, &IID_IDirect3D, (void**)&dxc.pd3d));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDraw::QueryInterface(IID_IDirect3D) %p\n", dxc.pd3d);
|
||||||
|
|
||||||
|
if( !dxc.pd3d )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirect3D_CreateViewport(dxc.pd3d, &dxc.viewport, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirect3D::CreateViewport %p\n", dxc.viewport);
|
||||||
|
|
||||||
|
if( !dxc.viewport )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean D3D_InitDevice( void )
|
||||||
|
{
|
||||||
|
if( !dxc.pd3d )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
D3DFINDDEVICESEARCH search = { 0 };
|
||||||
|
memset(&search, 0, sizeof(search));
|
||||||
|
search.dwSize = sizeof(search);
|
||||||
|
search.dwFlags = D3DFDS_COLORMODEL;
|
||||||
|
search.dcmColorModel = D3DCOLOR_RGB;
|
||||||
|
#if 0
|
||||||
|
search.dwFlags |= D3DFDS_HARDWARE;
|
||||||
|
search.bHardware = FALSE;
|
||||||
|
#endif
|
||||||
|
D3DFINDDEVICERESULT result = { 0 };
|
||||||
|
memset(&result, 0, sizeof(result));
|
||||||
|
result.dwSize = sizeof(result);
|
||||||
|
|
||||||
|
DXCheck(IDirect3D_FindDevice(dxc.pd3d, &search, &result));
|
||||||
|
DXCheck(IDirectDrawSurface_QueryInterface(dxc.pddsBack, &result.guid, (void**)&dxc.pd3dd));
|
||||||
|
gEngfuncs.Con_Printf("IDirectDrawSurface::QueryInterface(IDirect3DDevice) %p\n", dxc.pd3dd);
|
||||||
|
gEngfuncs.Con_Printf("DeviceZBufferBitDepth: 0x%X\n", result.ddHwDesc.dwDeviceZBufferBitDepth);
|
||||||
|
gEngfuncs.Con_Printf("SW DeviceZBufferBitDepth: 0x%X\n", result.ddSwDesc.dwDeviceZBufferBitDepth);
|
||||||
|
|
||||||
|
if( !dxc.pd3dd )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
D3DDEVICEDESC d3ddesc =
|
||||||
|
{
|
||||||
|
.dwSize = sizeof(d3ddesc)
|
||||||
|
};
|
||||||
|
D3DDEVICEDESC held3ddesc =
|
||||||
|
{
|
||||||
|
.dwSize = sizeof(held3ddesc)
|
||||||
|
};
|
||||||
|
DXCheck(IDirect3DDevice_GetCaps(dxc.pd3dd, &d3ddesc, &held3ddesc));
|
||||||
|
gEngfuncs.Con_Printf("IDirect3DDevice::GetCaps\n");
|
||||||
|
gEngfuncs.Con_Printf("DeviceZBufferBitDepth: 0x%X\n", result.ddHwDesc.dwDeviceZBufferBitDepth);
|
||||||
|
gEngfuncs.Con_Printf("HEL DeviceZBufferBitDepth: 0x%X\n", result.ddSwDesc.dwDeviceZBufferBitDepth);
|
||||||
|
|
||||||
|
D3DEXECUTEBUFFERDESC ebd = { 0 };
|
||||||
|
memset(&ebd, 0, sizeof(ebd));
|
||||||
|
ebd.dwSize = sizeof(ebd);
|
||||||
|
ebd.dwFlags = D3DDEB_BUFSIZE;
|
||||||
|
ebd.dwBufferSize = 1024 * 64;
|
||||||
|
DXCheck(IDirect3DDevice_CreateExecuteBuffer(dxc.pd3dd, &ebd, &dxc.pd3deb, NULL));
|
||||||
|
gEngfuncs.Con_Printf("IDirect3DDevice::CreateExecuteBuffer %p\n", dxc.pd3deb);
|
||||||
|
|
||||||
|
if (!dxc.pd3deb)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!dxc.viewport)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_AddViewport(dxc.pd3dd, dxc.viewport));
|
||||||
|
|
||||||
|
DDSURFACEDESC desc = { 0 };
|
||||||
|
memset(&desc, 0, sizeof(desc));
|
||||||
|
desc.dwSize = sizeof(desc);
|
||||||
|
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;
|
||||||
|
DXCheck(IDirectDrawSurface_GetSurfaceDesc(dxc.pddsBack, &desc));
|
||||||
|
|
||||||
|
D3DVIEWPORT viewport = { 0 };
|
||||||
|
viewport.dwSize = sizeof(viewport);
|
||||||
|
viewport.dwX = 0;
|
||||||
|
viewport.dwY = 0;
|
||||||
|
viewport.dwWidth = desc.dwWidth;
|
||||||
|
viewport.dwHeight = desc.dwHeight;
|
||||||
|
viewport.dvScaleX = 0.5 * viewport.dwWidth;
|
||||||
|
viewport.dvScaleY = 0.5f * viewport.dwHeight;
|
||||||
|
viewport.dvMaxX = 1;
|
||||||
|
viewport.dvMaxY = 1;
|
||||||
|
viewport.dvMinZ = 0;
|
||||||
|
viewport.dvMaxZ = 1;
|
||||||
|
DXCheck(IDirect3DViewport_SetViewport(dxc.viewport, &viewport));
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_CreateMatrix(dxc.pd3dd, &dxc.mtxWorld));
|
||||||
|
DXCheck(IDirect3DDevice_CreateMatrix(dxc.pd3dd, &dxc.mtxProjection));
|
||||||
|
|
||||||
|
//set transform state
|
||||||
|
{
|
||||||
|
d3dEBContext_t ebc = { 0 };
|
||||||
|
if (!D3D_StartExecuteBuffer(&ebc))
|
||||||
|
return false;
|
||||||
|
void *cur = ebc.data;
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATETRANSFORM, sizeof(D3DSTATE), 2);
|
||||||
|
D3D_PutTransformState(&cur, D3DTRANSFORMSTATE_WORLD, dxc.mtxWorld);
|
||||||
|
D3D_PutTransformState(&cur, D3DTRANSFORMSTATE_PROJECTION, dxc.mtxProjection);
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 2);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZWRITEENABLE, FALSE);
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_EXIT, 0, 0);
|
||||||
|
|
||||||
|
ebc.vertCount = 0;
|
||||||
|
ebc.insOffset = 0;
|
||||||
|
ebc.insLength = ((char *)cur - (char *)ebc.data);
|
||||||
|
|
||||||
|
if (!D3D_EndExecuteBuffer(&ebc))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_BeginScene(dxc.pd3dd));
|
||||||
|
DXCheck(IDirect3DDevice_Execute(dxc.pd3dd, ebc.pd3deb, dxc.viewport, D3DEXECUTE_CLIPPED));
|
||||||
|
DXCheck(IDirect3DDevice_EndScene(dxc.pd3dd));
|
||||||
|
|
||||||
|
D3D_ReleaseExecuteBuffer(&ebc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean R_Init( void )
|
||||||
|
{
|
||||||
|
memset(&dxc, 0, sizeof(dxc));
|
||||||
|
|
||||||
|
gEngfuncs.Cvar_RegisterVariable( &r_novis );
|
||||||
|
gEngfuncs.Cvar_RegisterVariable( &r_nocull );
|
||||||
|
gEngfuncs.Cvar_RegisterVariable( &r_lockpvs );
|
||||||
|
gEngfuncs.Cvar_RegisterVariable( &r_lockfrustum );
|
||||||
|
|
||||||
|
// init draw stack
|
||||||
|
tr.draw_list = &tr.draw_stack[0];
|
||||||
|
tr.draw_stack_pos = 0;
|
||||||
|
|
||||||
|
r_temppool = Mem_AllocPool("Render Zone");
|
||||||
|
|
||||||
|
// create the window
|
||||||
|
if (!gEngfuncs.R_Init_Video(REF_D3D))
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *handle = NULL;
|
||||||
|
ref_window_type_t wt = gEngfuncs.R_GetWindowHandle( &handle, REF_WINDOW_TYPE_WIN32 );
|
||||||
|
gEngfuncs.Con_Printf( "R_Init: WindowHandle %p type %d\n", handle, wt );
|
||||||
|
if (wt != REF_WINDOW_TYPE_WIN32 || !handle)
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dxc.window = (HWND)handle;
|
||||||
|
|
||||||
|
RECT dstRect = { 0 };
|
||||||
|
GetClientRect(dxc.window, &dstRect);
|
||||||
|
dxc.windowWidth = dstRect.right - dstRect.left;
|
||||||
|
dxc.windowHeight = dstRect.bottom - dstRect.top;
|
||||||
|
|
||||||
|
DXCheck(DirectDrawCreate(NULL, &dxc.pdd, NULL));
|
||||||
|
gEngfuncs.Con_Reportf("DirectDrawCreate %p\n", dxc.pdd);
|
||||||
|
if (!dxc.pdd)
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_SetCooperativeLevel(dxc.pdd, dxc.window, DDSCL_NORMAL));
|
||||||
|
|
||||||
|
if (!DD_CreatePrimarySurface())
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DD_CreateBackSurface())
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!D3D_Init())
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!D3D_InitDevice())
|
||||||
|
{
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see R_ProcessEntData for tr.entities initialization
|
||||||
|
tr.world = (struct world_static_s *)ENGINE_GET_PARM( PARM_GET_WORLD_PTR );
|
||||||
|
tr.viewent = (cl_entity_t *)ENGINE_GET_PARM( PARM_GET_VIEWENT_PTR );
|
||||||
|
|
||||||
|
dxc.renderMode = kRenderNormal;
|
||||||
|
Vector4Set(dxc.currentColor, 1, 1, 1, 1);
|
||||||
|
|
||||||
|
R_InitImages();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_Shutdown( void )
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf("ref: R_Shutdown()\n");
|
||||||
|
|
||||||
|
Mem_FreePool(&r_temppool);
|
||||||
|
|
||||||
|
gEngfuncs.R_Free_Video();
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_Resize( int width, int height )
|
||||||
|
{
|
||||||
|
dxc.windowWidth = width;
|
||||||
|
dxc.windowHeight = height;
|
||||||
|
|
||||||
|
if( dxc.pd3deb )
|
||||||
|
IDirect3DExecuteBuffer_Release(dxc.pd3deb);
|
||||||
|
dxc.pd3deb = NULL;
|
||||||
|
if( dxc.pd3dd )
|
||||||
|
IDirect3DDevice_Release(dxc.pd3dd);
|
||||||
|
dxc.pd3dd = NULL;
|
||||||
|
if( dxc.pddsBack )
|
||||||
|
IDirectDrawSurface_Release(dxc.pddsBack);
|
||||||
|
dxc.pddsBack = NULL;
|
||||||
|
if( dxc.pddsZBuffer )
|
||||||
|
IDirectDrawSurface_Release(dxc.pddsZBuffer);
|
||||||
|
dxc.pddsZBuffer = NULL;
|
||||||
|
if( dxc.pddBackClipper )
|
||||||
|
IDirectDrawClipper_Release(dxc.pddBackClipper);
|
||||||
|
dxc.pddBackClipper = NULL;
|
||||||
|
if( DD_CreateBackSurface() )
|
||||||
|
{
|
||||||
|
if( D3D_InitDevice() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_SetupAttributes( int safegl )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_InitExtensions( void )
|
||||||
|
{
|
||||||
|
tr.framecount = tr.visframecount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_ClearExtensions( void )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DX2_CREATE_EB 0
|
||||||
|
|
||||||
|
qboolean D3D_StartExecuteBuffer( d3dEBContext_t *ctx )
|
||||||
|
{
|
||||||
|
#if DX2_CREATE_EB
|
||||||
|
ctx->pd3deb = NULL;
|
||||||
|
D3DEXECUTEBUFFERDESC ebd = { 0 };
|
||||||
|
memset(&ebd, 0, sizeof(ebd));
|
||||||
|
ebd.dwSize = sizeof(ebd);
|
||||||
|
ebd.dwFlags = D3DDEB_BUFSIZE;
|
||||||
|
ebd.dwBufferSize = 512;
|
||||||
|
DXCheck(IDirect3DDevice_CreateExecuteBuffer(dxc.pd3dd, &ebd, &ctx->pd3deb, NULL));
|
||||||
|
#else
|
||||||
|
ctx->pd3deb = dxc.pd3deb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ctx->pd3deb)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
D3DEXECUTEBUFFERDESC ebDesc = { 0 };
|
||||||
|
memset(&ebDesc, 0, sizeof(ebDesc));
|
||||||
|
ebDesc.dwSize = sizeof(ebDesc);
|
||||||
|
ebDesc.dwFlags = D3DDEB_LPDATA;
|
||||||
|
HRESULT r = D3D_OK;
|
||||||
|
DXCheckRet(r, IDirect3DExecuteBuffer_Lock(ctx->pd3deb, &ebDesc));
|
||||||
|
if (r != D3D_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//gEngfuncs.Con_Printf("IDirect3DExecuteBuffer_Lock size %lu data %p\n", ebDesc.dwBufferSize, ebDesc.lpData);
|
||||||
|
|
||||||
|
ctx->bufferSize = ebDesc.dwBufferSize;
|
||||||
|
ctx->data = ebDesc.lpData;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean D3D_EndExecuteBuffer( d3dEBContext_t *ctx )
|
||||||
|
{
|
||||||
|
HRESULT r = D3D_OK;
|
||||||
|
DXCheckRet(r, IDirect3DExecuteBuffer_Unlock(ctx->pd3deb));
|
||||||
|
if( r != D3D_OK )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
D3DEXECUTEDATA exData = { 0 };
|
||||||
|
memset(&exData, 0, sizeof(exData));
|
||||||
|
exData.dwSize = sizeof(exData);
|
||||||
|
exData.dwVertexCount = ctx->vertCount;
|
||||||
|
exData.dwVertexOffset = 0;
|
||||||
|
exData.dwInstructionOffset = ctx->insOffset;
|
||||||
|
exData.dwInstructionLength = ctx->insLength;
|
||||||
|
DXCheckRet(r, IDirect3DExecuteBuffer_SetExecuteData(ctx->pd3deb, &exData));
|
||||||
|
|
||||||
|
return r == D3D_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_ReleaseExecuteBuffer( d3dEBContext_t *ctx )
|
||||||
|
{
|
||||||
|
#if DX2_CREATE_EB
|
||||||
|
if( ctx->pd3deb && ctx->pd3deb != dxc.pd3deb )
|
||||||
|
IDirect3DExecuteBuffer_Release( ctx->pd3deb );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_SetVertTL( D3DTLVERTEX* v, float x, float y, float z, float w, float tu, float tv )
|
||||||
|
{
|
||||||
|
v->sx = x;
|
||||||
|
v->sy = y;
|
||||||
|
v->sz = z;
|
||||||
|
v->rhw = w;
|
||||||
|
//v->color = RGBA_MAKE(250, 10, 10, 255);
|
||||||
|
v->tu = tu;
|
||||||
|
v->tv = tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_SetTri( D3DTRIANGLE *t, int v1, int v2, int v3 )
|
||||||
|
{
|
||||||
|
t->v1 = v1;
|
||||||
|
t->v2 = v2;
|
||||||
|
t->v3 = v3;
|
||||||
|
t->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_PutInstruction( void **dst, BYTE opcode, BYTE size, WORD count )
|
||||||
|
{
|
||||||
|
D3DINSTRUCTION* inst = (D3DINSTRUCTION*)*dst;
|
||||||
|
inst->bOpcode = opcode;
|
||||||
|
inst->bSize = size;
|
||||||
|
inst->wCount = count;
|
||||||
|
*dst = (void*)(((D3DINSTRUCTION*)*dst) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_PutProcessVertices( void** dst, DWORD flags, WORD start, WORD count )
|
||||||
|
{
|
||||||
|
D3D_PutInstruction(dst, D3DOP_PROCESSVERTICES, sizeof(D3DPROCESSVERTICES), 1);
|
||||||
|
|
||||||
|
D3DPROCESSVERTICES* pv = (D3DPROCESSVERTICES*)*dst;
|
||||||
|
pv->dwFlags = flags;
|
||||||
|
pv->wStart = start;
|
||||||
|
pv->wDest = start;
|
||||||
|
pv->dwCount = count;
|
||||||
|
pv->dwReserved = 0;
|
||||||
|
*dst = (void*)(((D3DPROCESSVERTICES*)*dst) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_PutRenderState( void** dst, D3DRENDERSTATETYPE type, DWORD arg )
|
||||||
|
{
|
||||||
|
D3DSTATE* s = (D3DSTATE*)*dst;
|
||||||
|
|
||||||
|
s->drstRenderStateType = type;
|
||||||
|
s->dwArg[0] = arg;
|
||||||
|
*dst = (void*)(((D3DSTATE*)*dst) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D_PutTransformState( void** dst, D3DTRANSFORMSTATETYPE type, DWORD arg )
|
||||||
|
{
|
||||||
|
D3DSTATE* s = (D3DSTATE*)*dst;
|
||||||
|
|
||||||
|
s->dtstTransformStateType = type;
|
||||||
|
s->dwArg[0] = arg;
|
||||||
|
*dst = (void*)(((D3DSTATE*)*dst) + 1);
|
||||||
|
}
|
||||||
204
ref/dx2/r_draw.c
Normal file
204
ref/dx2/r_draw.c
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
r_draw.c -- dx2 2D drawing
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
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 "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
|
||||||
|
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum )
|
||||||
|
{
|
||||||
|
if (texnum <= 0 || texnum >= MAX_TEXTURES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dx_texture_t *tex = R_GetTexture(texnum);
|
||||||
|
if (!tex->dds)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
RECT dstRect = { x, y, x + w, y + h };
|
||||||
|
RECT srcRect = { s1 * tex->width, t1 * tex->height, s2 * tex->width, t2 * tex->height};
|
||||||
|
|
||||||
|
if (dxc.renderMode == kRenderNormal)
|
||||||
|
{
|
||||||
|
DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, tex->dds, &srcRect, DDBLT_WAIT, NULL));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DDCOLORKEY ddck;
|
||||||
|
ddck.dwColorSpaceLowValue = 0;
|
||||||
|
ddck.dwColorSpaceHighValue = ddck.dwColorSpaceLowValue;
|
||||||
|
DXCheck(IDirectDrawSurface_SetColorKey(tex->dds, DDCKEY_SRCBLT, &ddck));
|
||||||
|
|
||||||
|
DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, tex->dds, &srcRect, DDBLT_WAIT | DDBLT_KEYSRC, NULL));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
if (!dxc.pd3dd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d3dEBContext_t ebc = { 0 };
|
||||||
|
{
|
||||||
|
if( !D3D_StartExecuteBuffer( &ebc ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
void *cur = ebc.data;
|
||||||
|
|
||||||
|
const int vertCount = 4;
|
||||||
|
D3DTLVERTEX* verts = (D3DTLVERTEX*)cur;
|
||||||
|
|
||||||
|
// fix for half coordinate pixel centers in d3d compared to gl
|
||||||
|
x -= 0.5;
|
||||||
|
y -= 0.5;
|
||||||
|
|
||||||
|
#define VecToD3DCOLOR(v) D3DRGBA(v[0], v[1], v[2], v[3])
|
||||||
|
|
||||||
|
verts[0].color = VecToD3DCOLOR(dxc.currentColor);
|
||||||
|
D3D_SetVertTL(verts, x, y, 0.5, 1, s1, t1);
|
||||||
|
verts[1].color = verts[0].color;
|
||||||
|
D3D_SetVertTL(verts + 1, x + w, y, 0.5, 1, s2, t1);
|
||||||
|
verts[2].color = verts[0].color;
|
||||||
|
D3D_SetVertTL(verts + 2, x + w, y + h, 0.5, 1, s2, t2);
|
||||||
|
verts[3].color = verts[0].color;
|
||||||
|
D3D_SetVertTL(verts + 3, x, y + h, 0.5, 1, s1, t2);
|
||||||
|
|
||||||
|
cur = (void*)(((D3DTLVERTEX*)cur) + vertCount);
|
||||||
|
|
||||||
|
void* insStart = cur;
|
||||||
|
|
||||||
|
D3D_PutProcessVertices(&cur, D3DPROCESSVERTICES_COPY | D3DPROCESSVERTICES_UPDATEEXTENTS, 0, vertCount);
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 3);
|
||||||
|
// TODO disable when == tr.whiteTexture
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_TEXTUREHANDLE, tex->d3dHandle);
|
||||||
|
//D3D_PutRenderState(&cur, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_DECAL);
|
||||||
|
|
||||||
|
if (dxc.renderMode == kRenderTransAlpha)
|
||||||
|
{
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_BLENDENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, TRUE);
|
||||||
|
}
|
||||||
|
else if (dxc.renderMode == kRenderGlow ||
|
||||||
|
dxc.renderMode == kRenderTransAdd)
|
||||||
|
{
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_BLENDENABLE, TRUE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 2);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
|
||||||
|
}
|
||||||
|
else if (dxc.renderMode == kRenderTransColor || dxc.renderMode == kRenderTransTexture)
|
||||||
|
{
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_BLENDENABLE, TRUE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 2);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||||
|
}
|
||||||
|
else// if (dxc.renderMode == kRenderNormal)
|
||||||
|
{
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_BLENDENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// align
|
||||||
|
if (((ULONG)cur) & 7) {
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), 2);
|
||||||
|
|
||||||
|
D3DTRIANGLE* tris = (D3DTRIANGLE*)cur;
|
||||||
|
D3D_SetTri(tris, 0, 1, 2);
|
||||||
|
D3D_SetTri(tris + 1, 0, 2, 3);
|
||||||
|
cur = (void*)(((D3DTRIANGLE*)cur) + 2);
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_EXIT, 0, 0);
|
||||||
|
|
||||||
|
ebc.vertCount = 4;
|
||||||
|
ebc.insOffset = ((char*)insStart - (char*)ebc.data);
|
||||||
|
ebc.insLength = ((char*)cur - (char*)insStart);
|
||||||
|
|
||||||
|
if( !D3D_EndExecuteBuffer( &ebc ))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_BeginScene(dxc.pd3dd));
|
||||||
|
DXCheck(IDirect3DDevice_Execute(dxc.pd3dd, ebc.pd3deb, dxc.viewport, D3DEXECUTE_CLIPPED));
|
||||||
|
DXCheck(IDirect3DDevice_EndScene(dxc.pd3dd));
|
||||||
|
|
||||||
|
D3D_ReleaseExecuteBuffer( &ebc );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillRGBA( int rendermode, float x, float y, float w, float h, byte r, byte g, byte b, byte a )
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
RECT dstRect = { x, y, x + w, y + h };
|
||||||
|
DDBLTFX bltFx = { 0 };
|
||||||
|
bltFx.dwSize = sizeof(bltFx);
|
||||||
|
bltFx.dwFillColor = RGBA_MAKE(r, g, b, a);
|
||||||
|
DXCheck(IDirectDrawSurface_Blt(dxc.pddsBack, &dstRect, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &bltFx));
|
||||||
|
#else
|
||||||
|
// TODO Restore after?
|
||||||
|
GL_SetRenderMode( (rendermode == kRenderTransAdd) ? rendermode : kRenderTransColor);
|
||||||
|
Vector4Set( dxc.currentColor, r / 255.f, g / 255.f, b / 255.f, a / 255.f );
|
||||||
|
R_DrawStretchPic( x, y, w, h, 0, 0, 0, 0, tr.whiteTexture );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_Set2DMode( qboolean enable )
|
||||||
|
{
|
||||||
|
if( enable && dxc.in2DMode )
|
||||||
|
return;
|
||||||
|
dxc.in2DMode = enable;
|
||||||
|
|
||||||
|
d3dEBContext_t ebc = { 0 };
|
||||||
|
if( !D3D_StartExecuteBuffer( &ebc ))
|
||||||
|
return;
|
||||||
|
void *cur = ebc.data;
|
||||||
|
|
||||||
|
if( enable )
|
||||||
|
{
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 4);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZWRITEENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, TRUE);
|
||||||
|
Vector4Set( dxc.currentColor, 1.0f, 1.0f, 1.0f, 1.0f );
|
||||||
|
|
||||||
|
RI.currententity = NULL;
|
||||||
|
RI.currentmodel = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 3);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZENABLE, TRUE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ZWRITEENABLE, TRUE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_CULLMODE, D3DCULL_CCW);
|
||||||
|
}
|
||||||
|
|
||||||
|
ebc.vertCount = 0;
|
||||||
|
ebc.insOffset = 0;
|
||||||
|
ebc.insLength = ((char *)cur - (char *)ebc.data);
|
||||||
|
|
||||||
|
if (!D3D_EndExecuteBuffer(&ebc))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_BeginScene(dxc.pd3dd));
|
||||||
|
DXCheck(IDirect3DDevice_Execute(dxc.pd3dd, ebc.pd3deb, dxc.viewport, D3DEXECUTE_CLIPPED));
|
||||||
|
DXCheck(IDirect3DDevice_EndScene(dxc.pd3dd));
|
||||||
|
|
||||||
|
D3D_ReleaseExecuteBuffer(&ebc);
|
||||||
|
|
||||||
|
}
|
||||||
185
ref/dx2/r_frustum.c
Normal file
185
ref/dx2/r_frustum.c
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
r_frustum.c - frustum test implementation
|
||||||
|
Copyright (C) 2016 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 "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
|
||||||
|
static void GL_FrustumSetPlane( gl_frustum_t *out, int side, const vec3_t vecNormal, float flDist )
|
||||||
|
{
|
||||||
|
Assert( side >= 0 && side < FRUSTUM_PLANES );
|
||||||
|
|
||||||
|
out->planes[side].type = PlaneTypeForNormal( vecNormal );
|
||||||
|
out->planes[side].signbits = SignbitsForPlane( vecNormal );
|
||||||
|
VectorCopy( vecNormal, out->planes[side].normal );
|
||||||
|
out->planes[side].dist = flDist;
|
||||||
|
|
||||||
|
SetBits( out->clipFlags, BIT( side ));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FrustumInitProj( gl_frustum_t *out, float flZNear, float flZFar, float flFovX, float flFovY )
|
||||||
|
{
|
||||||
|
float xs, xc;
|
||||||
|
vec3_t farpoint, nearpoint;
|
||||||
|
vec3_t normal, iforward;
|
||||||
|
|
||||||
|
// horizontal fov used for left and right planes
|
||||||
|
SinCos( DEG2RAD( flFovX ) * 0.5f, &xs, &xc );
|
||||||
|
|
||||||
|
// setup left plane
|
||||||
|
VectorMAM( xs, RI.cull_vforward, -xc, RI.cull_vright, normal );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_LEFT, normal, DotProduct( RI.cullorigin, normal ));
|
||||||
|
|
||||||
|
// setup right plane
|
||||||
|
VectorMAM( xs, RI.cull_vforward, xc, RI.cull_vright, normal );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_RIGHT, normal, DotProduct( RI.cullorigin, normal ));
|
||||||
|
|
||||||
|
// vertical fov used for top and bottom planes
|
||||||
|
SinCos( DEG2RAD( flFovY ) * 0.5f, &xs, &xc );
|
||||||
|
VectorNegate( RI.cull_vforward, iforward );
|
||||||
|
|
||||||
|
// setup bottom plane
|
||||||
|
VectorMAM( xs, RI.cull_vforward, -xc, RI.cull_vup, normal );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_BOTTOM, normal, DotProduct( RI.cullorigin, normal ));
|
||||||
|
|
||||||
|
// setup top plane
|
||||||
|
VectorMAM( xs, RI.cull_vforward, xc, RI.cull_vup, normal );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_TOP, normal, DotProduct( RI.cullorigin, normal ));
|
||||||
|
|
||||||
|
// setup far plane
|
||||||
|
VectorMA( RI.cullorigin, flZFar, RI.cull_vforward, farpoint );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_FAR, iforward, DotProduct( iforward, farpoint ));
|
||||||
|
|
||||||
|
// no need to setup backplane for general view.
|
||||||
|
if( flZNear == 0.0f ) return;
|
||||||
|
|
||||||
|
// setup near plane
|
||||||
|
VectorMA( RI.cullorigin, flZNear, RI.cull_vforward, nearpoint );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_NEAR, RI.cull_vforward, DotProduct( RI.cull_vforward, nearpoint ));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FrustumInitOrtho( gl_frustum_t *out, float xLeft, float xRight, float yTop, float yBottom, float flZNear, float flZFar )
|
||||||
|
{
|
||||||
|
vec3_t iforward, iright, iup;
|
||||||
|
|
||||||
|
// setup the near and far planes
|
||||||
|
float orgOffset = DotProduct( RI.cullorigin, RI.cull_vforward );
|
||||||
|
VectorNegate( RI.cull_vforward, iforward );
|
||||||
|
|
||||||
|
// because quake ortho is inverted and far and near should be swaped
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_FAR, iforward, -flZNear - orgOffset );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_NEAR, RI.cull_vforward, flZFar + orgOffset );
|
||||||
|
|
||||||
|
// setup left and right planes
|
||||||
|
orgOffset = DotProduct( RI.cullorigin, RI.cull_vright );
|
||||||
|
VectorNegate( RI.cull_vright, iright );
|
||||||
|
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_LEFT, RI.cull_vright, xLeft + orgOffset );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_RIGHT, iright, -xRight - orgOffset );
|
||||||
|
|
||||||
|
// setup top and buttom planes
|
||||||
|
orgOffset = DotProduct( RI.cullorigin, RI.cull_vup );
|
||||||
|
VectorNegate( RI.cull_vup, iup );
|
||||||
|
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_TOP, RI.cull_vup, yTop + orgOffset );
|
||||||
|
GL_FrustumSetPlane( out, FRUSTUM_BOTTOM, iup, -yBottom - orgOffset );
|
||||||
|
}
|
||||||
|
|
||||||
|
// cull methods
|
||||||
|
qboolean GL_FrustumCullBox( const gl_frustum_t *out, const vec3_t mins, const vec3_t maxs, int userClipFlags )
|
||||||
|
{
|
||||||
|
int iClipFlags;
|
||||||
|
int i, bit;
|
||||||
|
|
||||||
|
if( r_nocull.value )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( userClipFlags != 0 )
|
||||||
|
iClipFlags = userClipFlags;
|
||||||
|
else iClipFlags = out->clipFlags;
|
||||||
|
|
||||||
|
for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 )
|
||||||
|
{
|
||||||
|
const mplane_t *p = &out->planes[FRUSTUM_PLANES - i];
|
||||||
|
|
||||||
|
if( !FBitSet( iClipFlags, bit ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch( p->signbits )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * maxs[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * maxs[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if( p->normal[0] * maxs[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
if( p->normal[0] * mins[0] + p->normal[1] * maxs[1] + p->normal[2] * mins[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
if( p->normal[0] * maxs[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
if( p->normal[0] * mins[0] + p->normal[1] * mins[1] + p->normal[2] * mins[2] < p->dist )
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean GL_FrustumCullSphere( const gl_frustum_t *out, const vec3_t center, float radius, int userClipFlags )
|
||||||
|
{
|
||||||
|
int iClipFlags;
|
||||||
|
int i, bit;
|
||||||
|
|
||||||
|
if( r_nocull.value )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( userClipFlags != 0 )
|
||||||
|
iClipFlags = userClipFlags;
|
||||||
|
else iClipFlags = out->clipFlags;
|
||||||
|
|
||||||
|
for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 )
|
||||||
|
{
|
||||||
|
const mplane_t *p = &out->planes[FRUSTUM_PLANES - i];
|
||||||
|
|
||||||
|
if( !FBitSet( iClipFlags, bit ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( DotProduct( center, p->normal ) - p->dist <= -radius )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
41
ref/dx2/r_frustum.h
Normal file
41
ref/dx2/r_frustum.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
r_frustum.h - frustum test implementation
|
||||||
|
Copyright (C) 2016 Uncle Mike
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GL_FRUSTUM_H
|
||||||
|
#define GL_FRUSTUM_H
|
||||||
|
|
||||||
|
// don't change this order
|
||||||
|
#define FRUSTUM_LEFT 0
|
||||||
|
#define FRUSTUM_RIGHT 1
|
||||||
|
#define FRUSTUM_BOTTOM 2
|
||||||
|
#define FRUSTUM_TOP 3
|
||||||
|
#define FRUSTUM_FAR 4
|
||||||
|
#define FRUSTUM_NEAR 5
|
||||||
|
#define FRUSTUM_PLANES 6
|
||||||
|
|
||||||
|
typedef struct gl_frustum_s
|
||||||
|
{
|
||||||
|
mplane_t planes[FRUSTUM_PLANES];
|
||||||
|
unsigned int clipFlags;
|
||||||
|
} gl_frustum_t;
|
||||||
|
|
||||||
|
void GL_FrustumInitProj( gl_frustum_t *out, float flZNear, float flZFar, float flFovX, float flFovY );
|
||||||
|
void GL_FrustumInitOrtho( gl_frustum_t *out, float xLeft, float xRight, float yTop, float yBottom, float flZNear, float flZFar );
|
||||||
|
|
||||||
|
// cull methods
|
||||||
|
qboolean GL_FrustumCullBox( const gl_frustum_t *out, const vec3_t mins, const vec3_t maxs, int userClipFlags );
|
||||||
|
qboolean GL_FrustumCullSphere( const gl_frustum_t *out, const vec3_t centre, float radius, int userClipFlags );
|
||||||
|
|
||||||
|
#endif//GL_FRUSTUM_H
|
||||||
622
ref/dx2/r_image.c
Normal file
622
ref/dx2/r_image.c
Normal file
@@ -0,0 +1,622 @@
|
|||||||
|
/*
|
||||||
|
r_context.c -- dx2 renderer texture uploading and processing
|
||||||
|
Copyright (C) 2010 Uncle Mike
|
||||||
|
Copyright (C) 2025 lewa_j
|
||||||
|
|
||||||
|
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 "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
#include "crtlib.h"
|
||||||
|
#include "crclib.h"
|
||||||
|
|
||||||
|
static dx_texture_t dx_textures[MAX_TEXTURES];
|
||||||
|
static dx_texture_t *dx_texturesHashTable[TEXTURES_HASH_SIZE];
|
||||||
|
static uint dx_numTextures;
|
||||||
|
|
||||||
|
void R_InitImages( void )
|
||||||
|
{
|
||||||
|
memset(dx_textures, 0, sizeof(dx_textures));
|
||||||
|
memset(dx_texturesHashTable, 0, sizeof(dx_texturesHashTable));
|
||||||
|
dx_numTextures = 0;
|
||||||
|
|
||||||
|
// create unused 0-entry
|
||||||
|
Q_strncpy(dx_textures->name, "*unused*", sizeof(dx_textures->name));
|
||||||
|
dx_textures->hashValue = COM_HashKey(dx_textures->name, TEXTURES_HASH_SIZE);
|
||||||
|
dx_textures->nextHash = dx_texturesHashTable[dx_textures->hashValue];
|
||||||
|
dx_texturesHashTable[dx_textures->hashValue] = dx_textures;
|
||||||
|
dx_numTextures = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dx_texture_t *R_GetTexture( unsigned int texnum )
|
||||||
|
{
|
||||||
|
Assert(texnum < MAX_TEXTURES);
|
||||||
|
return &dx_textures[texnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_ShowTextures( void )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *R_GetTextureOriginalBuffer( unsigned int idx )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean GL_CheckTexName( const char *name )
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if( COM_StringEmptyOrNULL( name ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
len = Q_strlen( name );
|
||||||
|
|
||||||
|
// because multi-layered textures can exceed name string
|
||||||
|
if( len >= sizeof( dx_textures->name ))
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, len );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dx_texture_t *GL_TextureForName( const char *name )
|
||||||
|
{
|
||||||
|
dx_texture_t *tex;
|
||||||
|
uint hash;
|
||||||
|
|
||||||
|
// find the texture in array
|
||||||
|
hash = COM_HashKey(name, TEXTURES_HASH_SIZE);
|
||||||
|
|
||||||
|
for (tex = dx_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash)
|
||||||
|
{
|
||||||
|
if (!Q_stricmp(tex->name, name))
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dx_texture_t *GL_AllocTexture( const char *name, texFlags_t flags )
|
||||||
|
{
|
||||||
|
dx_texture_t *tex = NULL;
|
||||||
|
|
||||||
|
// find a free texture_t slot
|
||||||
|
uint i;
|
||||||
|
|
||||||
|
//skip 0
|
||||||
|
for (i = 1; i < MAX_TEXTURES; i++)
|
||||||
|
{
|
||||||
|
if (dx_textures[i].used)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tex = &dx_textures[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tex == NULL)
|
||||||
|
{
|
||||||
|
gEngfuncs.Host_Error("%s: MAX_TEXTURES limit exceeds\n", __func__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy initial params
|
||||||
|
Q_strncpy(tex->name, name, sizeof(tex->name));
|
||||||
|
tex->used = 1;
|
||||||
|
tex->flags = flags;
|
||||||
|
|
||||||
|
// increase counter
|
||||||
|
dx_numTextures = Q_max((tex - dx_textures) + 1, dx_numTextures);
|
||||||
|
|
||||||
|
// add to hash table
|
||||||
|
tex->hashValue = COM_HashKey(name, TEXTURES_HASH_SIZE);
|
||||||
|
tex->nextHash = dx_texturesHashTable[tex->hashValue];
|
||||||
|
dx_texturesHashTable[tex->hashValue] = tex;
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GL_ProcessImage( dx_texture_t *tex, rgbdata_t *pic )
|
||||||
|
{
|
||||||
|
uint img_flags = 0;
|
||||||
|
|
||||||
|
// force upload texture as RGB or RGBA (detail textures requires this)
|
||||||
|
if (tex->flags & TF_FORCE_COLOR) pic->flags |= IMAGE_HAS_COLOR;
|
||||||
|
if (pic->flags & IMAGE_HAS_ALPHA) tex->flags |= TF_HAS_ALPHA;
|
||||||
|
|
||||||
|
if (ImageCompressed(pic->type))
|
||||||
|
{
|
||||||
|
if (!pic->numMips)
|
||||||
|
tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request
|
||||||
|
|
||||||
|
// clear all the unsupported flags
|
||||||
|
tex->flags &= ~TF_KEEP_SOURCE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// copy flag about luma pixels
|
||||||
|
if (pic->flags & IMAGE_HAS_LUMA)
|
||||||
|
tex->flags |= TF_HAS_LUMA;
|
||||||
|
|
||||||
|
if (pic->flags & IMAGE_QUAKEPAL)
|
||||||
|
tex->flags |= TF_QUAKEPAL;
|
||||||
|
|
||||||
|
// create luma texture from quake texture
|
||||||
|
if (tex->flags & TF_MAKELUMA)
|
||||||
|
{
|
||||||
|
img_flags |= IMAGE_MAKE_LUMA;
|
||||||
|
tex->flags &= ~TF_MAKELUMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FBitSet(tex->flags, TF_IMG_UPLOADED) && FBitSet(tex->flags, TF_KEEP_SOURCE))
|
||||||
|
tex->original = gEngfuncs.FS_CopyImage(pic); // because current pic will be expanded to rgba
|
||||||
|
|
||||||
|
// we need to expand image into RGBA buffer
|
||||||
|
if (pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32)
|
||||||
|
img_flags |= IMAGE_FORCE_RGBA;
|
||||||
|
|
||||||
|
// processing image before uploading (force to rgba, make luma etc)
|
||||||
|
if (pic->buffer) gEngfuncs.Image_Process(&pic, 0, 0, img_flags, 0);
|
||||||
|
|
||||||
|
if (FBitSet(tex->flags, TF_LUMINANCE))
|
||||||
|
ClearBits(pic->flags, IMAGE_HAS_COLOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GL_CalcMipmapCount( dx_texture_t *tex, qboolean haveBuffer )
|
||||||
|
{
|
||||||
|
int width, height;
|
||||||
|
int mipcount;
|
||||||
|
|
||||||
|
Assert(tex != NULL);
|
||||||
|
|
||||||
|
//TEMP
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!haveBuffer)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// generate mip-levels by user request
|
||||||
|
if (FBitSet(tex->flags, TF_NOMIPMAP))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// mip-maps can't exceeds 16
|
||||||
|
for (mipcount = 0; mipcount < 16; mipcount++)
|
||||||
|
{
|
||||||
|
width = Q_max(1, (tex->width >> mipcount));
|
||||||
|
height = Q_max(1, (tex->height >> mipcount));
|
||||||
|
if (width == 1 && height == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mipcount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GL_TextureImageRAW( dx_texture_t *tex, int width, int height, int type, const char *data )
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DDSURFACEDESC ddsd = {};
|
||||||
|
ZeroMemory(&ddsd, sizeof(ddsd));
|
||||||
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
|
ddsd.dwFlags = DDSD_LPSURFACE;
|
||||||
|
DXCheck(IDirectDrawSurface_Lock(tex->dds, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL));
|
||||||
|
|
||||||
|
int pitch = ddsd.lPitch;
|
||||||
|
if (!pitch)
|
||||||
|
pitch = ddsd.dwWidth * 4;
|
||||||
|
char *dst = (char *)ddsd.lpSurface;
|
||||||
|
|
||||||
|
for (int j = 0; j < (int)ddsd.dwHeight; j++)
|
||||||
|
{
|
||||||
|
//RGBA to BGRA
|
||||||
|
if (type == PF_RGBA_32)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ddsd.dwWidth; i++)
|
||||||
|
{
|
||||||
|
dst[i * 4 + 0] = data[i * 4 + 2];
|
||||||
|
dst[i * 4 + 1] = data[i * 4 + 1];
|
||||||
|
dst[i * 4 + 2] = data[i * 4 + 0];
|
||||||
|
dst[i * 4 + 3] = data[i * 4 + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PF_BGRA_32)
|
||||||
|
{
|
||||||
|
memcpy(dst, data, ddsd.dwWidth * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst += pitch;
|
||||||
|
data += ddsd.dwWidth * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
DXCheck(IDirectDrawSurface_Unlock(tex->dds, ddsd.lpSurface));
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean GL_UploadTexture( dx_texture_t *tex, rgbdata_t *pic )
|
||||||
|
{
|
||||||
|
byte *buf, *data;
|
||||||
|
size_t size;
|
||||||
|
uint width, height;
|
||||||
|
uint j;
|
||||||
|
qboolean normalMap;
|
||||||
|
const byte *bufend;
|
||||||
|
|
||||||
|
// dedicated server
|
||||||
|
if (!dxc.window)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Assert(pic != NULL);
|
||||||
|
Assert(tex != NULL);
|
||||||
|
|
||||||
|
qboolean supported = true;
|
||||||
|
|
||||||
|
if (FBitSet(pic->flags, IMAGE_CUBEMAP))
|
||||||
|
supported = false;
|
||||||
|
else if (FBitSet(pic->flags, IMAGE_MULTILAYER) && pic->depth >= 1)
|
||||||
|
supported = false;
|
||||||
|
else if (pic->width > 1 && pic->height > 1 && pic->depth > 1)
|
||||||
|
supported = false;
|
||||||
|
else if (FBitSet(tex->flags, TF_RECTANGLE))
|
||||||
|
supported = false;
|
||||||
|
else if (FBitSet(tex->flags, TF_MULTISAMPLE))
|
||||||
|
supported = false;
|
||||||
|
|
||||||
|
// make sure what target is correct
|
||||||
|
if (!supported)
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_DPrintf(S_ERROR "%s: %s is not supported\n", __func__, tex->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (pic->type == PF_BC6H_SIGNED || pic->type == PF_BC6H_UNSIGNED || pic->type == PF_BC7_UNORM || pic->type == PF_BC7_SRGB)
|
||||||
|
if (ImageCompressed(pic->type))
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_DPrintf(S_ERROR "%s: compressed textures are not supported\n", __func__);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store original sizes
|
||||||
|
tex->srcWidth = pic->width;
|
||||||
|
tex->srcHeight = pic->height;
|
||||||
|
|
||||||
|
// set the texture dimensions
|
||||||
|
{
|
||||||
|
int step = 0;// (int)gl_round_down.value;
|
||||||
|
int scaled_width, scaled_height;
|
||||||
|
|
||||||
|
for (scaled_width = 1; scaled_width < pic->width; scaled_width <<= 1);
|
||||||
|
|
||||||
|
if (step > 0 && pic->width < scaled_width && (step == 1 || (scaled_width - pic->width) > (scaled_width >> step)))
|
||||||
|
scaled_width >>= 1;
|
||||||
|
|
||||||
|
for (scaled_height = 1; scaled_height < pic->height; scaled_height <<= 1);
|
||||||
|
|
||||||
|
if (step > 0 && pic->height < scaled_height && (step == 1 || (scaled_height - pic->height) > (scaled_height >> step)))
|
||||||
|
scaled_height >>= 1;
|
||||||
|
|
||||||
|
tex->width = Q_max(1, scaled_width);
|
||||||
|
tex->height = Q_max(1, scaled_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
DDSURFACEDESC ddsd = {};
|
||||||
|
ZeroMemory(&ddsd, sizeof(ddsd));
|
||||||
|
ddsd.dwSize = sizeof(ddsd);
|
||||||
|
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
|
||||||
|
ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
|
||||||
|
//if (pic->numMips > 1)
|
||||||
|
// ddsd.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
|
||||||
|
ddsd.dwWidth = tex->width;
|
||||||
|
ddsd.dwHeight = tex->height;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
//BGRA8
|
||||||
|
ddsd.dwFlags |= DDSD_PIXELFORMAT;
|
||||||
|
ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
|
||||||
|
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
|
||||||
|
if(FBitSet(pic->flags, IMAGE_HAS_ALPHA))
|
||||||
|
ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS;
|
||||||
|
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
|
||||||
|
ddsd.ddpfPixelFormat.dwBBitMask = 0xFF;
|
||||||
|
ddsd.ddpfPixelFormat.dwGBitMask = 0xFF00;
|
||||||
|
ddsd.ddpfPixelFormat.dwRBitMask = 0xFF0000;
|
||||||
|
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &tex->dds, NULL));
|
||||||
|
DXCheck(IDirectDrawSurface_QueryInterface(tex->dds, &IID_IDirect3DTexture, (void**)&tex->d3dTex));
|
||||||
|
DXCheck(IDirect3DTexture_GetHandle(tex->d3dTex, dxc.pd3dd, &tex->d3dHandle));
|
||||||
|
|
||||||
|
tex->fogParams[0] = pic->fogParams[0];
|
||||||
|
tex->fogParams[1] = pic->fogParams[1];
|
||||||
|
tex->fogParams[2] = pic->fogParams[2];
|
||||||
|
tex->fogParams[3] = pic->fogParams[3];
|
||||||
|
|
||||||
|
buf = pic->buffer;
|
||||||
|
bufend = pic->buffer + pic->size; // total image size include all the layers, cube sides, mipmaps
|
||||||
|
normalMap = FBitSet(tex->flags, TF_NORMALMAP) ? true : false;
|
||||||
|
|
||||||
|
if (Q_max(1, pic->numMips) > 1) // not-compressed DDS
|
||||||
|
{
|
||||||
|
//TEMP
|
||||||
|
j = 0;
|
||||||
|
//for (j = 0; j < Q_max(1, pic->numMips); j++)
|
||||||
|
{
|
||||||
|
// track the buffer bounds
|
||||||
|
if (buf != NULL && buf >= bufend)
|
||||||
|
gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name);
|
||||||
|
|
||||||
|
width = Q_max(1, (tex->width >> j));
|
||||||
|
height = Q_max(1, (tex->height >> j));
|
||||||
|
size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1);
|
||||||
|
GL_TextureImageRAW(tex, width, height, pic->type, buf);
|
||||||
|
buf += size; // move pointer
|
||||||
|
tex->numMips++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // RGBA32
|
||||||
|
{
|
||||||
|
// track the buffer bounds
|
||||||
|
if (buf != NULL && buf >= bufend)
|
||||||
|
gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name);
|
||||||
|
|
||||||
|
int mipCount = GL_CalcMipmapCount(tex, (buf != NULL));
|
||||||
|
|
||||||
|
// NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides
|
||||||
|
if (((pic->width != tex->width) || (pic->height != tex->height)))
|
||||||
|
data = GL_ResampleTexture(buf, pic->width, pic->height, tex->width, tex->height, normalMap);
|
||||||
|
else data = buf;
|
||||||
|
|
||||||
|
//if (!ImageCompressed(pic->type) && !FBitSet(tex->flags, TF_NOMIPMAP) && FBitSet(pic->flags, IMAGE_ONEBIT_ALPHA))
|
||||||
|
// data = GL_ApplyFilter(data, tex->width, tex->height);
|
||||||
|
|
||||||
|
// mips will be auto-generated if desired
|
||||||
|
for (j = 0; j < mipCount; j++)
|
||||||
|
{
|
||||||
|
width = Q_max(1, (tex->width >> j));
|
||||||
|
height = Q_max(1, (tex->height >> j));
|
||||||
|
size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1);
|
||||||
|
GL_TextureImageRAW(tex, width, height, pic->type, data);
|
||||||
|
//if (mipCount > 1)
|
||||||
|
// GL_BuildMipMap(data, width, height, tex->depth, tex->flags);
|
||||||
|
tex->numMips++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetBits(tex->flags, TF_IMG_UPLOADED); // done
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update )
|
||||||
|
{
|
||||||
|
dx_texture_t *tex;
|
||||||
|
|
||||||
|
if (!GL_CheckTexName(name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// see if already loaded
|
||||||
|
if ((tex = GL_TextureForName(name)) && !update)
|
||||||
|
return (tex - dx_textures);
|
||||||
|
|
||||||
|
// couldn't loading image
|
||||||
|
if (!pic) return 0;
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
{
|
||||||
|
if (tex == NULL)
|
||||||
|
gEngfuncs.Host_Error("%s: couldn't find texture %s for update\n", __func__, name);
|
||||||
|
SetBits(tex->flags, flags);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// allocate the new one
|
||||||
|
tex = GL_AllocTexture(name, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_ProcessImage(tex, pic);
|
||||||
|
|
||||||
|
if (!GL_UploadTexture(tex, pic))
|
||||||
|
{
|
||||||
|
memset(tex, 0, sizeof(dx_texture_t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tex - dx_textures);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags )
|
||||||
|
{
|
||||||
|
qboolean update = FBitSet( flags, TF_UPDATE ) ? true : false;
|
||||||
|
int datasize = 1;
|
||||||
|
rgbdata_t r_empty;
|
||||||
|
|
||||||
|
if( FBitSet( flags, TF_ARB_16BIT ))
|
||||||
|
datasize = 2;
|
||||||
|
else if( FBitSet( flags, TF_ARB_FLOAT ))
|
||||||
|
datasize = 4;
|
||||||
|
|
||||||
|
ClearBits( flags, TF_UPDATE );
|
||||||
|
memset( &r_empty, 0, sizeof( r_empty ));
|
||||||
|
r_empty.width = width;
|
||||||
|
r_empty.height = height;
|
||||||
|
r_empty.type = PF_RGBA_32;
|
||||||
|
r_empty.size = r_empty.width * r_empty.height * datasize * 4;
|
||||||
|
r_empty.buffer = (byte *)buffer;
|
||||||
|
|
||||||
|
// clear invalid combinations
|
||||||
|
ClearBits( flags, TF_TEXTURE_3D );
|
||||||
|
|
||||||
|
// if image not luminance and not alphacontrast it will have color
|
||||||
|
if( !FBitSet( flags, TF_LUMINANCE ) && !FBitSet( flags, TF_ALPHACONTRAST ))
|
||||||
|
SetBits( r_empty.flags, IMAGE_HAS_COLOR );
|
||||||
|
|
||||||
|
if( FBitSet( flags, TF_HAS_ALPHA ))
|
||||||
|
SetBits( r_empty.flags, IMAGE_HAS_ALPHA );
|
||||||
|
|
||||||
|
if( FBitSet( flags, TF_CUBEMAP ))
|
||||||
|
{
|
||||||
|
// no cubemaps in dx2
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int texnum = GL_LoadTextureFromBuffer( name, &r_empty, flags, update );
|
||||||
|
|
||||||
|
if( !Q_strcmp( name, REF_DEFAULT_TEXTURE ))
|
||||||
|
tr.defaultTexture = texnum;
|
||||||
|
else if( !Q_strcmp( name, REF_PARTICLE_TEXTURE ))
|
||||||
|
tr.particleTexture = texnum;
|
||||||
|
else if( !Q_strcmp( name, REF_WHITE_TEXTURE ))
|
||||||
|
tr.whiteTexture = texnum;
|
||||||
|
else if( !Q_strcmp( name, REF_GRAY_TEXTURE ))
|
||||||
|
tr.grayTexture = texnum;
|
||||||
|
else if( !Q_strcmp( name, REF_BLACK_TEXTURE ))
|
||||||
|
tr.blackTexture = texnum;
|
||||||
|
|
||||||
|
return texnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScale )
|
||||||
|
{
|
||||||
|
*xScale = *yScale = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_SetDetailScaleForTexture( int texture, float xScale, float yScale )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GL_FindTexture( const char *name )
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf("GL_FindTexture(%s)\n", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GL_TextureName( unsigned int texnum )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte *GL_TextureData( unsigned int texnum )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
|
||||||
|
{
|
||||||
|
dx_texture_t *tex;
|
||||||
|
rgbdata_t *pic;
|
||||||
|
uint picFlags = 0;
|
||||||
|
|
||||||
|
if (!GL_CheckTexName(name))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// see if already loaded
|
||||||
|
if ((tex = GL_TextureForName(name)))
|
||||||
|
return (tex - dx_textures);
|
||||||
|
|
||||||
|
gEngfuncs.Con_Printf("GL_LoadTexture(%s, %p, %zu, %X)\n", name, buf, size, flags);
|
||||||
|
|
||||||
|
if (FBitSet(flags, TF_NOFLIP_TGA))
|
||||||
|
SetBits(picFlags, IL_DONTFLIP_TGA);
|
||||||
|
|
||||||
|
if (FBitSet(flags, TF_KEEP_SOURCE) && !FBitSet(flags, TF_EXPAND_SOURCE))
|
||||||
|
SetBits(picFlags, IL_KEEP_8BIT);
|
||||||
|
|
||||||
|
// set some image flags
|
||||||
|
gEngfuncs.Image_SetForceFlags(picFlags);
|
||||||
|
|
||||||
|
pic = gEngfuncs.FS_LoadImage(name, buf, size);
|
||||||
|
if (!pic) return 0; // couldn't loading image
|
||||||
|
|
||||||
|
// allocate the new one
|
||||||
|
tex = GL_AllocTexture(name, flags);
|
||||||
|
GL_ProcessImage(tex, pic);
|
||||||
|
|
||||||
|
if (!GL_UploadTexture(tex, pic))
|
||||||
|
{
|
||||||
|
memset(tex, 0, sizeof(dx_texture_t));
|
||||||
|
gEngfuncs.FS_FreeImage(pic); // release source texture
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//GL_ApplyTextureParams(tex); // update texture filter, wrap etc
|
||||||
|
gEngfuncs.FS_FreeImage(pic); // release source texture
|
||||||
|
|
||||||
|
// NOTE: always return texnum as index in array or engine will stop work !!!
|
||||||
|
return tex - dx_textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_FreeTexture( unsigned int texnum )
|
||||||
|
{
|
||||||
|
if (texnum <= 0 || texnum >= MAX_TEXTURES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gEngfuncs.Con_Printf("GL_FreeTexture(%d) %s\n", texnum, dx_textures[texnum].name);
|
||||||
|
|
||||||
|
dx_texture_t* tex = R_GetTexture(texnum);
|
||||||
|
if (!tex->dds)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
if (!tex->name[0])
|
||||||
|
{
|
||||||
|
gEngfuncs.Con_Printf(S_ERROR "%s: trying to free unnamed texture with handle %X\n", __func__, tex->d3dHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove from hash table
|
||||||
|
dx_texture_t **prev = &dx_texturesHashTable[tex->hashValue];
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
dx_texture_t *cur = *prev;
|
||||||
|
if (!cur) break;
|
||||||
|
|
||||||
|
if (cur == tex)
|
||||||
|
{
|
||||||
|
*prev = cur->nextHash;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = &cur->nextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO invalidate texture state
|
||||||
|
|
||||||
|
// release source
|
||||||
|
if (tex->original)
|
||||||
|
gEngfuncs.FS_FreeImage(tex->original);
|
||||||
|
|
||||||
|
if (tex->d3dTex)
|
||||||
|
IDirect3DTexture_Release(tex->d3dTex);
|
||||||
|
if (tex->dds)
|
||||||
|
IDirectDrawSurface_Release(tex->dds);
|
||||||
|
memset(tex, 0, sizeof(*tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_OverrideTextureSourceSize( unsigned int textnum, unsigned int srcWidth, unsigned int srcHeight )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_UpdateTexture( int texnum, int cols, int rows, int width, int height, const byte *buffer, pixformat_t fmt )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
319
ref/dx2/r_local.h
Normal file
319
ref/dx2/r_local.h
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
r_local.h - renderer local declarations
|
||||||
|
Copyright (C) 2010 Uncle Mike
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ref_common.h"
|
||||||
|
#include "ref_api.h"
|
||||||
|
#include "r_frustum.h"
|
||||||
|
#include "ref_params.h"
|
||||||
|
#include "mod_local.h"
|
||||||
|
#include "com_strings.h"
|
||||||
|
#include "pmove.h"
|
||||||
|
|
||||||
|
#define DIRECTDRAW_VERSION 0x0200
|
||||||
|
#define DIRECT3D_VERSION 0x0500//D3DCOLORMODEL bug
|
||||||
|
#include <ddraw.h>
|
||||||
|
#include <d3d.h>
|
||||||
|
|
||||||
|
#define MAX_TEXTURES 8192
|
||||||
|
#define TEXTURES_HASH_SIZE (MAX_TEXTURES >> 2)
|
||||||
|
|
||||||
|
#define MAX_DRAW_STACK 2 // normal view and menu view
|
||||||
|
|
||||||
|
#define CULL_VISIBLE 0 // not culled
|
||||||
|
#define CULL_BACKSIDE 1 // backside of transparent wall
|
||||||
|
#define CULL_FRUSTUM 2 // culled by frustum
|
||||||
|
#define CULL_VISFRAME 3 // culled by PVS
|
||||||
|
#define CULL_OTHER 4 // culled by other reason
|
||||||
|
|
||||||
|
typedef struct dx_texture_s
|
||||||
|
{
|
||||||
|
char name[256]; // game path, including extension (can be store image programs)
|
||||||
|
word srcWidth; // keep unscaled sizes
|
||||||
|
word srcHeight;
|
||||||
|
word width; // upload width\height
|
||||||
|
word height;
|
||||||
|
|
||||||
|
byte numMips; // mipmap count
|
||||||
|
|
||||||
|
int used;
|
||||||
|
texFlags_t flags;
|
||||||
|
rgba_t fogParams; // some water textures
|
||||||
|
// contain info about underwater fog
|
||||||
|
rgbdata_t *original; // keep original image
|
||||||
|
#if 0
|
||||||
|
word depth; // texture depth or count of layers for 2D_ARRAY
|
||||||
|
|
||||||
|
GLuint target; // glTarget
|
||||||
|
GLuint texnum; // gl texture binding
|
||||||
|
GLint format; // uploaded format
|
||||||
|
|
||||||
|
// debug info
|
||||||
|
size_t size; // upload size for debug targets
|
||||||
|
|
||||||
|
// detail textures stuff
|
||||||
|
float xscale;
|
||||||
|
float yscale;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
IDirectDrawSurface *dds;
|
||||||
|
IDirect3DTexture *d3dTex;
|
||||||
|
D3DTEXTUREHANDLE d3dHandle;
|
||||||
|
uint hashValue;
|
||||||
|
struct dx_texture_s *nextHash;
|
||||||
|
} dx_texture_t;
|
||||||
|
|
||||||
|
typedef struct d3dEBContext_s
|
||||||
|
{
|
||||||
|
IDirect3DExecuteBuffer *pd3deb;
|
||||||
|
DWORD bufferSize;
|
||||||
|
void *data;
|
||||||
|
DWORD vertCount;
|
||||||
|
DWORD insOffset;
|
||||||
|
DWORD insLength;
|
||||||
|
} d3dEBContext_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ref_viewpass_t rvp;
|
||||||
|
|
||||||
|
cl_entity_t *currententity;
|
||||||
|
model_t *currentmodel;
|
||||||
|
cl_entity_t *currentbeam; // same as above but for beams
|
||||||
|
|
||||||
|
gl_frustum_t frustum;
|
||||||
|
|
||||||
|
mleaf_t *viewleaf;
|
||||||
|
mleaf_t *oldviewleaf;
|
||||||
|
vec3_t vforward;
|
||||||
|
vec3_t vright;
|
||||||
|
vec3_t vup;
|
||||||
|
|
||||||
|
vec3_t cullorigin;
|
||||||
|
vec3_t cull_vforward;
|
||||||
|
vec3_t cull_vright;
|
||||||
|
vec3_t cull_vup;
|
||||||
|
|
||||||
|
float farClip;
|
||||||
|
|
||||||
|
qboolean fogCustom;
|
||||||
|
qboolean fogEnabled;
|
||||||
|
qboolean fogSkybox;
|
||||||
|
vec4_t fogColor;
|
||||||
|
float fogDensity;
|
||||||
|
float fogStart;
|
||||||
|
float fogEnd;
|
||||||
|
int cached_contents; // in water
|
||||||
|
int cached_waterlevel; // was in water
|
||||||
|
|
||||||
|
float skyMins[2][SKYBOX_MAX_SIDES];
|
||||||
|
float skyMaxs[2][SKYBOX_MAX_SIDES];
|
||||||
|
|
||||||
|
matrix4x4 objectMatrix; // currententity matrix
|
||||||
|
matrix4x4 worldviewMatrix; // modelview for world
|
||||||
|
matrix4x4 modelviewMatrix; // worldviewMatrix * objectMatrix
|
||||||
|
|
||||||
|
matrix4x4 projectionMatrix;
|
||||||
|
matrix4x4 worldviewProjectionMatrix; // worldviewMatrix * projectionMatrix
|
||||||
|
byte visbytes[( MAX_MAP_LEAFS + 7 ) / 8];// actual PVS for current frame
|
||||||
|
|
||||||
|
float viewplanedist;
|
||||||
|
mplane_t clipPlane;
|
||||||
|
} ref_instance_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes
|
||||||
|
cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes
|
||||||
|
cl_entity_t *beam_entities[MAX_VISIBLE_PACKET];
|
||||||
|
uint num_solid_entities;
|
||||||
|
uint num_trans_entities;
|
||||||
|
uint num_beam_entities;
|
||||||
|
} draw_list_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int defaultTexture; // use for bad textures
|
||||||
|
int particleTexture;
|
||||||
|
int whiteTexture;
|
||||||
|
int grayTexture;
|
||||||
|
int blackTexture;
|
||||||
|
|
||||||
|
int skytexturenum; // index into model_t::textures
|
||||||
|
|
||||||
|
// entity lists
|
||||||
|
draw_list_t draw_stack[MAX_DRAW_STACK];
|
||||||
|
int draw_stack_pos;
|
||||||
|
draw_list_t *draw_list;
|
||||||
|
|
||||||
|
// matrix states
|
||||||
|
qboolean modelviewIdentity;
|
||||||
|
|
||||||
|
int visframecount; // PVS frame
|
||||||
|
int dlightframecount; // dynamic light frame
|
||||||
|
int realframecount; // not including viewpasses
|
||||||
|
int framecount;
|
||||||
|
|
||||||
|
qboolean fCustomRendering;
|
||||||
|
qboolean fResetVis;
|
||||||
|
|
||||||
|
double frametime; // special frametime for multipass rendering (will set to 0 on a nextview)
|
||||||
|
float blend; // global blend value
|
||||||
|
|
||||||
|
// cull info
|
||||||
|
vec3_t modelorg; // relative to viewpoint
|
||||||
|
|
||||||
|
// get from engine
|
||||||
|
model_t *worldmodel;
|
||||||
|
world_static_t *world;
|
||||||
|
cl_entity_t *entities;
|
||||||
|
cl_entity_t *viewent;
|
||||||
|
|
||||||
|
uint max_entities;
|
||||||
|
|
||||||
|
ref_screen_rotation_t rotation;
|
||||||
|
} dx_globals_t;
|
||||||
|
|
||||||
|
struct dx_context_s
|
||||||
|
{
|
||||||
|
HWND window;
|
||||||
|
int windowWidth;
|
||||||
|
int windowHeight;
|
||||||
|
IDirectDraw *pdd;
|
||||||
|
IDirectDrawSurface *pddsPrimary;
|
||||||
|
IDirectDrawSurface *pddsBack;
|
||||||
|
IDirectDrawSurface *pddsZBuffer;
|
||||||
|
IDirectDrawClipper *pddClipper;
|
||||||
|
IDirectDrawClipper *pddBackClipper;
|
||||||
|
IDirect3D *pd3d;
|
||||||
|
IDirect3DDevice *pd3dd;
|
||||||
|
IDirect3DExecuteBuffer *pd3deb;
|
||||||
|
IDirect3DViewport *viewport;
|
||||||
|
D3DMATRIXHANDLE mtxWorld;
|
||||||
|
D3DMATRIXHANDLE mtxProjection;
|
||||||
|
|
||||||
|
qboolean in2DMode;
|
||||||
|
int renderMode;
|
||||||
|
vec4_t currentColor;
|
||||||
|
D3DTEXTUREHANDLE currentTexture;
|
||||||
|
D3DTEXTUREHANDLE lastBoundTexture;
|
||||||
|
};
|
||||||
|
typedef struct dx_context_s dx_context_t;
|
||||||
|
|
||||||
|
extern ref_instance_t RI;
|
||||||
|
extern dx_globals_t tr;
|
||||||
|
extern dx_context_t dxc;
|
||||||
|
|
||||||
|
|
||||||
|
static inline cl_entity_t *CL_GetEntityByIndex( int index )
|
||||||
|
{
|
||||||
|
if( unlikely( index < 0 || index >= tr.max_entities || !tr.entities ))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &tr.entities[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline model_t *CL_ModelHandle( int index )
|
||||||
|
{
|
||||||
|
if( unlikely( index < 0 || index >= gp_cl->nummodels ))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return gp_cl->models[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *dxResultToStr( HRESULT r );
|
||||||
|
|
||||||
|
#define DXCheckRet(DST, CALL) \
|
||||||
|
(DST) = (CALL);\
|
||||||
|
if ((DST) != DD_OK)\
|
||||||
|
gEngfuncs.Con_Printf( S_ERROR "%s():%s:%d: DirectX error %X(%d 0x%X %d) %s at \"" #CALL "\"\n", __FUNCTION__, __FILE__, __LINE__, (DST), ( (DST) >> 31 ) & 1, ( (DST) >> 16 ) & 0x7FFF, (DST) & 0xFFFF, dxResultToStr( r ));
|
||||||
|
|
||||||
|
#define DXCheck(CALL) \
|
||||||
|
{\
|
||||||
|
HRESULT r = DD_OK;\
|
||||||
|
DXCheckRet(r, CALL)\
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_SetRenderMode( int mode );
|
||||||
|
void GL_Bind( int tmu, unsigned int texnum );
|
||||||
|
|
||||||
|
// r_d3d.c
|
||||||
|
qboolean R_Init( void );
|
||||||
|
void R_Shutdown( void );
|
||||||
|
void GL_SetupAttributes( int safegl );
|
||||||
|
void GL_InitExtensions( void );
|
||||||
|
void GL_ClearExtensions( void );
|
||||||
|
void D3D_Resize( int width, int height );
|
||||||
|
qboolean D3D_StartExecuteBuffer( d3dEBContext_t *ctx );
|
||||||
|
qboolean D3D_EndExecuteBuffer( d3dEBContext_t *ctx );
|
||||||
|
void D3D_ReleaseExecuteBuffer( d3dEBContext_t *ctx );
|
||||||
|
void D3D_SetVertTL( D3DTLVERTEX *v, float x, float y, float z, float w, float tu, float tv );
|
||||||
|
void D3D_SetTri( D3DTRIANGLE *t, int v1, int v2, int v3 );
|
||||||
|
void D3D_PutInstruction( void **dst, BYTE opcode, BYTE size, WORD count );
|
||||||
|
void D3D_PutProcessVertices( void **dst, DWORD flags, WORD start, WORD count );
|
||||||
|
void D3D_PutRenderState( void **dst, D3DRENDERSTATETYPE type, DWORD arg );
|
||||||
|
void D3D_PutTransformState( void **dst, D3DTRANSFORMSTATETYPE type, DWORD arg );
|
||||||
|
|
||||||
|
// r_main.c
|
||||||
|
void R_GammaChanged( qboolean do_reset_gamma );
|
||||||
|
void R_BeginFrame( qboolean clearScene );
|
||||||
|
void R_RenderScene( void );
|
||||||
|
void R_EndFrame( void );
|
||||||
|
void R_AllowFog( qboolean allowed );
|
||||||
|
void R_RenderFrame( const struct ref_viewpass_s *rvp );
|
||||||
|
qboolean R_AddEntity( struct cl_entity_s *clent, int type );
|
||||||
|
void R_ClearScene( void );
|
||||||
|
int WorldToScreen( const vec3_t world, vec3_t screen );
|
||||||
|
void R_LoadIdentity( void );
|
||||||
|
void R_SetupD3D( qboolean set_state );
|
||||||
|
|
||||||
|
// r_draw.c
|
||||||
|
void R_Set2DMode( qboolean enable );
|
||||||
|
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum );
|
||||||
|
void FillRGBA( int rendermode, float x, float y, float w, float h, byte r, byte g, byte b, byte a );
|
||||||
|
|
||||||
|
// r_image.c
|
||||||
|
void R_InitImages( void );
|
||||||
|
dx_texture_t *R_GetTexture( unsigned int texnum );
|
||||||
|
void R_ShowTextures( void );
|
||||||
|
const byte *R_GetTextureOriginalBuffer( unsigned int idx );
|
||||||
|
int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update );
|
||||||
|
void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor );
|
||||||
|
void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScale );
|
||||||
|
void R_SetDetailScaleForTexture( int texture, float xScale, float yScale );
|
||||||
|
int GL_FindTexture( const char *name );
|
||||||
|
const char *GL_TextureName( unsigned int texnum );
|
||||||
|
const byte *GL_TextureData( unsigned int texnum );
|
||||||
|
int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags );
|
||||||
|
int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags );
|
||||||
|
void GL_FreeTexture( unsigned int texnum );
|
||||||
|
void R_OverrideTextureSourceSize( unsigned int textnum, unsigned int srcWidth, unsigned int srcHeight );
|
||||||
|
void GL_UpdateTexture( int texnum, int cols, int rows, int width, int height, const byte *buffer, pixformat_t fmt );
|
||||||
|
|
||||||
|
// r_surf.c
|
||||||
|
void GL_SubdivideSurface( model_t *mod, msurface_t *fa );
|
||||||
|
void GL_OrthoBounds( const float *mins, const float *maxs );
|
||||||
|
byte *Mod_GetCurrentVis( void );
|
||||||
|
void R_DrawWorld( void );
|
||||||
|
void R_MarkLeaves( void );
|
||||||
|
void GL_BuildLightmaps( void );
|
||||||
|
|
||||||
|
extern convar_t r_novis;
|
||||||
|
extern convar_t r_nocull;
|
||||||
|
extern convar_t r_lockpvs;
|
||||||
|
extern convar_t r_lockfrustum;
|
||||||
|
|
||||||
380
ref/dx2/r_main.c
Normal file
380
ref/dx2/r_main.c
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
r_main.c -- dx2 renderer main loop
|
||||||
|
Copyright (C) 2010 Uncle Mike
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
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 "r_local.h"
|
||||||
|
#include "ref_params.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
|
||||||
|
ref_instance_t RI;
|
||||||
|
|
||||||
|
static void R_Clear( int bitMask )
|
||||||
|
{
|
||||||
|
int bits = D3DCLEAR_ZBUFFER;
|
||||||
|
//bits &= bitMask;
|
||||||
|
|
||||||
|
D3DRECT rect = {
|
||||||
|
.x1 = 0,
|
||||||
|
.y1 = 0,
|
||||||
|
.x2 = dxc.windowWidth,
|
||||||
|
.y2 = dxc.windowHeight,
|
||||||
|
};
|
||||||
|
DXCheck(IDirect3DViewport_Clear(dxc.viewport, 1, &rect, bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
static float R_GetFarClip( void )
|
||||||
|
{
|
||||||
|
if( tr.worldmodel && FBitSet( RI.rvp.flags, RF_DRAW_WORLD ) )
|
||||||
|
return gp_movevars->zmax * 1.73f;
|
||||||
|
|
||||||
|
return 2048.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_SetupFrustum( void )
|
||||||
|
{
|
||||||
|
// build the transformation matrix for the given view angles
|
||||||
|
AngleVectors( RI.rvp.viewangles, RI.vforward, RI.vright, RI.vup );
|
||||||
|
|
||||||
|
if( !r_lockfrustum.value )
|
||||||
|
{
|
||||||
|
VectorCopy( RI.rvp.vieworigin, RI.cullorigin );
|
||||||
|
VectorCopy( RI.vforward, RI.cull_vforward );
|
||||||
|
VectorCopy( RI.vright, RI.cull_vright );
|
||||||
|
VectorCopy( RI.vup, RI.cull_vup );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( FBitSet( RI.rvp.flags, RF_DRAW_OVERVIEW ))
|
||||||
|
{
|
||||||
|
const ref_overview_t *ov = gEngfuncs.GetOverviewParms();
|
||||||
|
GL_FrustumInitOrtho( &RI.frustum, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL_FrustumInitProj( &RI.frustum, 0.0f, R_GetFarClip(), RI.rvp.fov_x, RI.rvp.fov_y ); // NOTE: we ignore nearplane here (mirrors only)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_SetupProjectionMatrix( matrix4x4 m )
|
||||||
|
{
|
||||||
|
float xMin, xMax, yMin, yMax, zNear, zFar;
|
||||||
|
|
||||||
|
if( FBitSet( RI.rvp.flags, RF_DRAW_OVERVIEW ))
|
||||||
|
{
|
||||||
|
const ref_overview_t *ov = gEngfuncs.GetOverviewParms();
|
||||||
|
Matrix4x4_CreateOrtho( m, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RI.farClip = R_GetFarClip();
|
||||||
|
|
||||||
|
zNear = 4.0f;
|
||||||
|
zFar = Q_max( 256.0f, RI.farClip );
|
||||||
|
|
||||||
|
yMax = zNear * tan( RI.rvp.fov_y * M_PI_F / 360.0f );
|
||||||
|
yMin = -yMax;
|
||||||
|
|
||||||
|
xMax = zNear * tan( RI.rvp.fov_x * M_PI_F / 360.0f );
|
||||||
|
xMin = -xMax;
|
||||||
|
|
||||||
|
if( tr.rotation & 1 )
|
||||||
|
{
|
||||||
|
Matrix4x4_CreateProjection( m, yMax, yMin, xMax, xMin, zNear, zFar );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
R_SetupModelviewMatrix
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
static void R_SetupModelviewMatrix( matrix4x4 m )
|
||||||
|
{
|
||||||
|
Matrix4x4_CreateModelview( m );
|
||||||
|
if( tr.rotation & 1 )
|
||||||
|
{
|
||||||
|
Matrix4x4_ConcatRotate( m, anglemod( -RI.rvp.viewangles[2] + 90 ), 1, 0, 0 );
|
||||||
|
Matrix4x4_ConcatRotate( m, -RI.rvp.viewangles[0], 0, 1, 0 );
|
||||||
|
Matrix4x4_ConcatRotate( m, -RI.rvp.viewangles[1], 0, 0, 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Matrix4x4_ConcatRotate( m, -RI.rvp.viewangles[2], 1, 0, 0 );
|
||||||
|
Matrix4x4_ConcatRotate( m, -RI.rvp.viewangles[0], 0, 1, 0 );
|
||||||
|
Matrix4x4_ConcatRotate( m, -RI.rvp.viewangles[1], 0, 0, 1 );
|
||||||
|
}
|
||||||
|
Matrix4x4_ConcatTranslate( m, -RI.rvp.vieworigin[0], -RI.rvp.vieworigin[1], -RI.rvp.vieworigin[2] );
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_LoadIdentity( void )
|
||||||
|
{
|
||||||
|
if( tr.modelviewIdentity ) return;
|
||||||
|
|
||||||
|
Matrix4x4_LoadIdentity( RI.objectMatrix );
|
||||||
|
Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix );
|
||||||
|
|
||||||
|
//TODO GL_LoadMatrix( MODELVIEW, RI.modelviewMatrix );
|
||||||
|
tr.modelviewIdentity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_GammaChanged( qboolean do_reset_gamma )
|
||||||
|
{
|
||||||
|
if( do_reset_gamma )
|
||||||
|
{
|
||||||
|
// paranoia cubemap rendering
|
||||||
|
if( gEngfuncs.drawFuncs->GL_BuildLightmaps )
|
||||||
|
gEngfuncs.drawFuncs->GL_BuildLightmaps( );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//GL_RebuildLightmaps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_BeginFrame( qboolean clearScene )
|
||||||
|
{
|
||||||
|
if(( gl_clear->value || ENGINE_GET_PARM( PARM_DEV_OVERVIEW )) &&
|
||||||
|
clearScene && ENGINE_GET_PARM( PARM_CONNSTATE ) != ca_cinematic )
|
||||||
|
{
|
||||||
|
D3DRECT rect = {
|
||||||
|
.x1 = 0,
|
||||||
|
.y1 = 0,
|
||||||
|
.x2 = dxc.windowWidth,
|
||||||
|
.y2 = dxc.windowHeight,
|
||||||
|
};
|
||||||
|
DXCheck(IDirect3DViewport_Clear(dxc.viewport, 1, &rect, D3DCLEAR_TARGET));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_Set2DMode( true );
|
||||||
|
|
||||||
|
gEngfuncs.CL_ExtraUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_DrawEntitiesOnList( void )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_RenderScene( void )
|
||||||
|
{
|
||||||
|
if( !tr.worldmodel && FBitSet( RI.rvp.flags, RF_DRAW_WORLD ) )
|
||||||
|
gEngfuncs.Host_Error("%s: NULL worldmodel\n", __func__);
|
||||||
|
|
||||||
|
// frametime is valid only for normal pass
|
||||||
|
if( !FBitSet( RI.rvp.flags, RF_DRAW_CUBEMAP ))
|
||||||
|
tr.frametime = gp_cl->time - gp_cl->oldtime;
|
||||||
|
else tr.frametime = 0.0;
|
||||||
|
|
||||||
|
// begin a new frame
|
||||||
|
tr.framecount++;
|
||||||
|
|
||||||
|
tr.dlightframecount = R_PushDlights( tr.worldmodel, tr.framecount );
|
||||||
|
|
||||||
|
R_SetupFrustum();
|
||||||
|
#if 0
|
||||||
|
R_SetupFrame();
|
||||||
|
#endif
|
||||||
|
R_SetupD3D(true);
|
||||||
|
R_Clear(~0);
|
||||||
|
R_MarkLeaves();
|
||||||
|
#if 0
|
||||||
|
R_DrawFog();
|
||||||
|
if( FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||||
|
R_AnimateRipples();
|
||||||
|
|
||||||
|
R_CheckGLFog();
|
||||||
|
#endif
|
||||||
|
R_DrawWorld();
|
||||||
|
|
||||||
|
// R_CheckFog();
|
||||||
|
|
||||||
|
gEngfuncs.CL_ExtraUpdate(); // don't let sound get messed up if going slow
|
||||||
|
|
||||||
|
R_DrawEntitiesOnList();
|
||||||
|
|
||||||
|
// R_DrawWaterSurfaces();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_EndFrame( void )
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!dxc.window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
R_Set2DMode( false );
|
||||||
|
|
||||||
|
//swap
|
||||||
|
if (dxc.pddsBack)
|
||||||
|
{
|
||||||
|
RECT dstRect = { 0 };
|
||||||
|
POINT point = { 0,0 };
|
||||||
|
ClientToScreen(dxc.window, &point);
|
||||||
|
GetClientRect(dxc.window, &dstRect);
|
||||||
|
OffsetRect(&dstRect, point.x, point.y);
|
||||||
|
|
||||||
|
RECT srcRect = { 0,0,dxc.windowWidth,dxc.windowHeight };
|
||||||
|
DXCheck(IDirectDrawSurface_Blt(dxc.pddsPrimary, &dstRect, dxc.pddsBack, &srcRect, DDBLT_WAIT, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
//resize
|
||||||
|
RECT rect = { 0 };
|
||||||
|
GetClientRect(dxc.window, &rect);
|
||||||
|
int w = rect.right - rect.left;
|
||||||
|
int h = rect.bottom - rect.top;
|
||||||
|
|
||||||
|
if (w != dxc.windowWidth || h != dxc.windowHeight)
|
||||||
|
{
|
||||||
|
D3D_Resize(w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_AllowFog( qboolean allowed )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void R_SetupRefParams( const ref_viewpass_t *rvp )
|
||||||
|
{
|
||||||
|
RI.rvp = *rvp;
|
||||||
|
|
||||||
|
RI.farClip = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_RenderFrame( const ref_viewpass_t *rvp )
|
||||||
|
{
|
||||||
|
if( r_norefresh->value )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// setup the initial render params
|
||||||
|
R_SetupRefParams( rvp );
|
||||||
|
|
||||||
|
// completely override rendering
|
||||||
|
if( gEngfuncs.drawFuncs->GL_RenderFrame != NULL )
|
||||||
|
{
|
||||||
|
tr.fCustomRendering = true;
|
||||||
|
|
||||||
|
if( gEngfuncs.drawFuncs->GL_RenderFrame( rvp ))
|
||||||
|
{
|
||||||
|
R_GatherPlayerLight( tr.viewent );
|
||||||
|
tr.realframecount++;
|
||||||
|
tr.fResetVis = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.fCustomRendering = false;
|
||||||
|
#if 0
|
||||||
|
if( !FBitSet( RI.rvp.flags, RF_ONLY_CLIENTDRAW ))
|
||||||
|
R_RunViewmodelEvents();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tr.realframecount++; // right called after viewmodel events
|
||||||
|
R_RenderScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
qboolean R_AddEntity( struct cl_entity_s *clent, int type )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_ClearScene( void )
|
||||||
|
{
|
||||||
|
tr.draw_list->num_solid_entities = 0;
|
||||||
|
tr.draw_list->num_trans_entities = 0;
|
||||||
|
tr.draw_list->num_beam_entities = 0;
|
||||||
|
|
||||||
|
// clear the scene befor start new frame
|
||||||
|
if (gEngfuncs.drawFuncs->R_ClearScene != NULL)
|
||||||
|
gEngfuncs.drawFuncs->R_ClearScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int WorldToScreen( const vec3_t world, vec3_t screen )
|
||||||
|
{
|
||||||
|
VectorClear( screen );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void R_SetupD3D( qboolean set_state )
|
||||||
|
{
|
||||||
|
R_SetupModelviewMatrix( RI.worldviewMatrix );
|
||||||
|
R_SetupProjectionMatrix( RI.projectionMatrix );
|
||||||
|
|
||||||
|
Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix );
|
||||||
|
|
||||||
|
if( !set_state ) return;
|
||||||
|
|
||||||
|
D3DVIEWPORT viewport = { 0 };
|
||||||
|
viewport.dwSize = sizeof(viewport);
|
||||||
|
|
||||||
|
if( !FBitSet( RI.rvp.flags, RF_DRAW_CUBEMAP ))
|
||||||
|
{
|
||||||
|
int x, x2, y, y2;
|
||||||
|
|
||||||
|
// set up viewport (main, playersetup)
|
||||||
|
x = floor( RI.rvp.viewport[0] * gpGlobals->width / gpGlobals->width );
|
||||||
|
x2 = ceil( (RI.rvp.viewport[0] + RI.rvp.viewport[2]) * gpGlobals->width / gpGlobals->width );
|
||||||
|
y = floor( gpGlobals->height - RI.rvp.viewport[1] * gpGlobals->height / gpGlobals->height );
|
||||||
|
y2 = ceil( gpGlobals->height - (RI.rvp.viewport[1] + RI.rvp.viewport[3]) * gpGlobals->height / gpGlobals->height );
|
||||||
|
|
||||||
|
if( tr.rotation & 1 )
|
||||||
|
{
|
||||||
|
viewport.dwX = y2;
|
||||||
|
viewport.dwY = x;
|
||||||
|
viewport.dwWidth = y - y2;
|
||||||
|
viewport.dwHeight = x2 - x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viewport.dwX = x;
|
||||||
|
viewport.dwY = y2;
|
||||||
|
viewport.dwWidth = x2 - x;
|
||||||
|
viewport.dwHeight = y - y2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// envpass, mirrorpass
|
||||||
|
viewport.dwX = RI.rvp.viewport[0];
|
||||||
|
viewport.dwY = RI.rvp.viewport[1];
|
||||||
|
viewport.dwWidth = RI.rvp.viewport[2];
|
||||||
|
viewport.dwHeight = RI.rvp.viewport[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
viewport.dvScaleX = 0.5f * viewport.dwWidth;
|
||||||
|
viewport.dvScaleY = 0.5f * viewport.dwHeight;
|
||||||
|
viewport.dvMaxX = 1;//calc for partial views
|
||||||
|
viewport.dvMaxY = 1;
|
||||||
|
viewport.dvMinZ = 0;
|
||||||
|
viewport.dvMaxZ = 1;
|
||||||
|
DXCheck(IDirect3DViewport_SetViewport(dxc.viewport, &viewport));
|
||||||
|
|
||||||
|
{
|
||||||
|
float dest[16];
|
||||||
|
Matrix4x4_ToArrayFloatGL(RI.projectionMatrix, dest);
|
||||||
|
DXCheck(IDirect3DDevice_SetMatrix(dxc.pd3dd, dxc.mtxProjection, (D3DMATRIX *)&dest));
|
||||||
|
Matrix4x4_ToArrayFloatGL(RI.worldviewMatrix, dest);
|
||||||
|
DXCheck(IDirect3DDevice_SetMatrix(dxc.pd3dd, dxc.mtxWorld, (D3DMATRIX *)&dest));
|
||||||
|
}
|
||||||
|
}
|
||||||
625
ref/dx2/r_surf.c
Normal file
625
ref/dx2/r_surf.c
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
/*
|
||||||
|
r_surf.c - surface-related refresh code
|
||||||
|
Copyright (C) 2025-2026 lewa_j
|
||||||
|
|
||||||
|
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 "r_local.h"
|
||||||
|
#include "xash3d_mathlib.h"
|
||||||
|
|
||||||
|
|
||||||
|
void GL_OrthoBounds( const float *mins, const float *maxs )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte *Mod_GetCurrentVis( void )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_LightmapCoord( const vec3_t v, const msurface_t *surf, const float sample_size, vec2_t coords )
|
||||||
|
{
|
||||||
|
const mextrasurf_t *info = surf->info;
|
||||||
|
float s, t;
|
||||||
|
|
||||||
|
#define BLOCK_SIZE 256
|
||||||
|
|
||||||
|
s = DotProduct( v, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0];
|
||||||
|
s += surf->light_s * sample_size;
|
||||||
|
s += sample_size * 0.5f;
|
||||||
|
s /= BLOCK_SIZE * sample_size;
|
||||||
|
|
||||||
|
t = DotProduct( v, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1];
|
||||||
|
t += surf->light_t * sample_size;
|
||||||
|
t += sample_size * 0.5f;
|
||||||
|
t /= BLOCK_SIZE * sample_size;
|
||||||
|
|
||||||
|
#undef BLOCK_SIZE
|
||||||
|
|
||||||
|
Vector2Set( coords, s, t );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_TextureCoord( const vec3_t v, const msurface_t *surf, vec2_t coords )
|
||||||
|
{
|
||||||
|
const mtexinfo_t *info = surf->texinfo;
|
||||||
|
float s, t;
|
||||||
|
|
||||||
|
s = DotProduct( v, info->vecs[0] );
|
||||||
|
t = DotProduct( v, info->vecs[1] );
|
||||||
|
|
||||||
|
if( !FBitSet( surf->flags, SURF_DRAWTURB ))
|
||||||
|
{
|
||||||
|
s = ( s + info->vecs[0][3] ) / info->texture->width;
|
||||||
|
t = ( t + info->vecs[1][3] ) / info->texture->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2Set( coords, s, t );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_GetEdgePosition( const model_t *mod, const msurface_t *fa, int i, vec3_t vec )
|
||||||
|
{
|
||||||
|
const int lindex = mod->surfedges[fa->firstedge + i];
|
||||||
|
|
||||||
|
if( FBitSet( mod->flags, MODEL_QBSP2 ))
|
||||||
|
{
|
||||||
|
const medge32_t *pedges = mod->edges32;
|
||||||
|
|
||||||
|
if( lindex > 0 )
|
||||||
|
VectorCopy( mod->vertexes[pedges[lindex].v[0]].position, vec );
|
||||||
|
else
|
||||||
|
VectorCopy( mod->vertexes[pedges[-lindex].v[1]].position, vec );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const medge16_t *pedges = mod->edges16;
|
||||||
|
|
||||||
|
if( lindex > 0 )
|
||||||
|
VectorCopy( mod->vertexes[pedges[lindex].v[0]].position, vec );
|
||||||
|
else
|
||||||
|
VectorCopy( mod->vertexes[pedges[-lindex].v[1]].position, vec );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL_SubdivideSurface( model_t *mod, msurface_t *fa )
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
GL_BuildPolygonFromSurface
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
static int GL_BuildPolygonFromSurface( model_t *mod, msurface_t *fa )
|
||||||
|
{
|
||||||
|
int i, lnumverts, nColinElim = 0;
|
||||||
|
float sample_size;
|
||||||
|
texture_t *tex;
|
||||||
|
dx_texture_t *dxt;
|
||||||
|
glpoly2_t *poly;
|
||||||
|
|
||||||
|
if( !mod || !fa->texinfo || !fa->texinfo->texture )
|
||||||
|
return nColinElim; // bad polygon ?
|
||||||
|
|
||||||
|
if( FBitSet( fa->flags, SURF_CONVEYOR ) && fa->texinfo->texture->gl_texturenum != 0 )
|
||||||
|
{
|
||||||
|
dxt = R_GetTexture( fa->texinfo->texture->gl_texturenum );
|
||||||
|
tex = fa->texinfo->texture;
|
||||||
|
Assert( dxt != NULL && tex != NULL );
|
||||||
|
|
||||||
|
// update conveyor widths for keep properly speed of scrolling
|
||||||
|
dxt->srcWidth = tex->width;
|
||||||
|
dxt->srcHeight = tex->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
sample_size = gEngfuncs.Mod_SampleSizeForFace( fa );
|
||||||
|
|
||||||
|
// reconstruct the polygon
|
||||||
|
lnumverts = fa->numedges;
|
||||||
|
|
||||||
|
// detach if already created, reconstruct again
|
||||||
|
poly = fa->polys;
|
||||||
|
fa->polys = NULL;
|
||||||
|
|
||||||
|
// quake simple models (healthkits etc) need to be reconstructed their polys because LM coords has changed after the map change
|
||||||
|
poly = Mem_Realloc( mod->mempool, poly, sizeof( glpoly2_t ) + lnumverts * VERTEXSIZE * sizeof( float ));
|
||||||
|
poly->next = fa->polys;
|
||||||
|
poly->flags = fa->flags;
|
||||||
|
fa->polys = poly;
|
||||||
|
poly->numverts = lnumverts;
|
||||||
|
|
||||||
|
for( i = 0; i < lnumverts; i++ )
|
||||||
|
{
|
||||||
|
R_GetEdgePosition( mod, fa, i, poly->verts[i] );
|
||||||
|
R_TextureCoord( poly->verts[i], fa, &poly->verts[i][3] );
|
||||||
|
R_LightmapCoord( poly->verts[i], fa, sample_size, &poly->verts[i][5] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO or not TODO gl_keeptjunctions?
|
||||||
|
|
||||||
|
poly->numverts = lnumverts;
|
||||||
|
return nColinElim;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
R_TextureAnimation
|
||||||
|
|
||||||
|
Returns the proper texture for a given time and surface
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
static texture_t *R_TextureAnimation( msurface_t *s )
|
||||||
|
{
|
||||||
|
texture_t *base = s->texinfo->texture;
|
||||||
|
// TODO
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawGLPoly( glpoly2_t *p, float xScale, float yScale )
|
||||||
|
{
|
||||||
|
float *v;
|
||||||
|
float sOffset, sy;
|
||||||
|
float tOffset, cy;
|
||||||
|
cl_entity_t *e = RI.currententity;
|
||||||
|
int i, hasScale = false;
|
||||||
|
|
||||||
|
if( !p ) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
sOffset = tOffset = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( xScale != 0.0f && yScale != 0.0f )
|
||||||
|
hasScale = true;
|
||||||
|
|
||||||
|
d3dEBContext_t ebc = { 0 };
|
||||||
|
if( !D3D_StartExecuteBuffer( &ebc ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
void *cur = ebc.data;
|
||||||
|
|
||||||
|
D3DLVERTEX* verts = (D3DLVERTEX*)cur;
|
||||||
|
|
||||||
|
for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
|
||||||
|
{
|
||||||
|
D3DLVERTEX *dv = verts + i;
|
||||||
|
if( hasScale )
|
||||||
|
{
|
||||||
|
dv->tu = ( v[3] + sOffset ) * xScale;
|
||||||
|
dv->tv = ( v[4] + tOffset ) * yScale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dv->tu = v[3] + sOffset;
|
||||||
|
dv->tv = v[4] + tOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
dv->x = v[0];
|
||||||
|
dv->y = v[1];
|
||||||
|
dv->z = v[2];
|
||||||
|
dv->color = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = (void*)(((D3DTLVERTEX*)cur) + p->numverts);
|
||||||
|
|
||||||
|
void* insStart = cur;
|
||||||
|
|
||||||
|
D3D_PutProcessVertices(&cur, D3DPROCESSVERTICES_TRANSFORM | D3DPROCESSVERTICES_UPDATEEXTENTS, 0, p->numverts);
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_STATERENDER, sizeof(D3DSTATE), 4);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_TEXTUREHANDLE, dxc.currentTexture);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_DECAL);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_BLENDENABLE, FALSE);
|
||||||
|
D3D_PutRenderState(&cur, D3DRENDERSTATE_ALPHATESTENABLE, FALSE);
|
||||||
|
|
||||||
|
// align
|
||||||
|
if (((ULONG)cur) & 7) {
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), 0);
|
||||||
|
}
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), p->numverts - 2);
|
||||||
|
|
||||||
|
for (i = 0; i < p->numverts; i++)
|
||||||
|
{
|
||||||
|
D3D_SetTri(cur, 0, i + 1, i + 2);
|
||||||
|
cur = (void*)(((D3DTRIANGLE*)cur) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D_PutInstruction(&cur, D3DOP_EXIT, 0, 0);
|
||||||
|
|
||||||
|
ebc.vertCount = p->numverts;
|
||||||
|
ebc.insOffset = ((char*)insStart - (char*)ebc.data);
|
||||||
|
ebc.insLength = ((char*)cur - (char*)insStart);
|
||||||
|
|
||||||
|
if( !D3D_EndExecuteBuffer( &ebc ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DXCheck(IDirect3DDevice_BeginScene(dxc.pd3dd));
|
||||||
|
DXCheck(IDirect3DDevice_Execute(dxc.pd3dd, ebc.pd3deb, dxc.viewport, D3DEXECUTE_CLIPPED));
|
||||||
|
DXCheck(IDirect3DDevice_EndScene(dxc.pd3dd));
|
||||||
|
|
||||||
|
D3D_ReleaseExecuteBuffer( &ebc );
|
||||||
|
//
|
||||||
|
|
||||||
|
//if( FBitSet( p->flags, SURF_DRAWTILED ))
|
||||||
|
// GL_SetupFogColorForSurfaces();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
|
||||||
|
{
|
||||||
|
texture_t *t;
|
||||||
|
|
||||||
|
//r_stats.c_world_polys++;
|
||||||
|
|
||||||
|
if( fa->flags & SURF_DRAWSKY )
|
||||||
|
return; // already handled
|
||||||
|
|
||||||
|
t = R_TextureAnimation( fa );
|
||||||
|
|
||||||
|
GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
|
||||||
|
|
||||||
|
DrawGLPoly( fa->polys, 0.0f, 0.0f );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_DrawTextureChains( void )
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
msurface_t *s;
|
||||||
|
texture_t *t;
|
||||||
|
|
||||||
|
// make sure what color is reset
|
||||||
|
Vector4Set( dxc.currentColor, 1, 1, 1, 1 );
|
||||||
|
R_LoadIdentity(); // set identity matrix
|
||||||
|
|
||||||
|
//GL_SetupFogColorForSurfaces();
|
||||||
|
|
||||||
|
// restore worldmodel
|
||||||
|
RI.currententity = CL_GetEntityByIndex( 0 );
|
||||||
|
RI.currentmodel = RI.currententity->model;
|
||||||
|
|
||||||
|
for( i = 0; i < tr.worldmodel->numtextures; i++ )
|
||||||
|
{
|
||||||
|
t = tr.worldmodel->textures[i];
|
||||||
|
if( !t ) continue;
|
||||||
|
|
||||||
|
s = t->texturechain;
|
||||||
|
|
||||||
|
if( !s || ( i == tr.skytexturenum ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for( ; s != NULL; s = s->texturechain )
|
||||||
|
R_RenderBrushPoly( s, CULL_VISIBLE );
|
||||||
|
t->texturechain = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
R_CullSurface
|
||||||
|
|
||||||
|
cull invisible surfaces
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
static int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags )
|
||||||
|
{
|
||||||
|
cl_entity_t *e = RI.currententity;
|
||||||
|
|
||||||
|
if( !surf || !surf->texinfo || !surf->texinfo->texture )
|
||||||
|
return CULL_OTHER;
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
return CULL_VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void R_RecursiveWorldNode( mnode_t *node, uint clipflags )
|
||||||
|
{
|
||||||
|
int i, clipped;
|
||||||
|
msurface_t *surf, **mark;
|
||||||
|
mleaf_t *pleaf;
|
||||||
|
int c, side;
|
||||||
|
float dot;
|
||||||
|
mnode_t *children[2];
|
||||||
|
int numsurfaces, firstsurface;
|
||||||
|
|
||||||
|
loc0:
|
||||||
|
if( node->contents == CONTENTS_SOLID )
|
||||||
|
return; // hit a solid leaf
|
||||||
|
|
||||||
|
if( node->visframe != tr.visframecount )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( clipflags && !r_nocull.value )
|
||||||
|
{
|
||||||
|
for( i = 0; i < 6; i++ )
|
||||||
|
{
|
||||||
|
const mplane_t *p = &RI.frustum.planes[i];
|
||||||
|
|
||||||
|
if( !FBitSet( clipflags, BIT( i )))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, p );
|
||||||
|
if( clipped == 2 ) return;
|
||||||
|
if( clipped == 1 ) ClearBits( clipflags, BIT( i ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a leaf node, draw stuff
|
||||||
|
if( node->contents < 0 )
|
||||||
|
{
|
||||||
|
pleaf = (mleaf_t *)node;
|
||||||
|
|
||||||
|
mark = pleaf->firstmarksurface;
|
||||||
|
c = pleaf->nummarksurfaces;
|
||||||
|
|
||||||
|
if( c )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
(*mark)->visframe = tr.framecount;
|
||||||
|
mark++;
|
||||||
|
} while( --c );
|
||||||
|
}
|
||||||
|
|
||||||
|
// deal with model fragments in this leaf
|
||||||
|
if( pleaf->efrags )
|
||||||
|
gEngfuncs.R_StoreEfrags( &pleaf->efrags, tr.realframecount );
|
||||||
|
|
||||||
|
// r_stats.c_world_leafs++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is just a decision point, so go down the apropriate sides
|
||||||
|
|
||||||
|
// find which side of the node we are on
|
||||||
|
dot = PlaneDiff( tr.modelorg, node->plane );
|
||||||
|
side = (dot >= 0.0f) ? 0 : 1;
|
||||||
|
|
||||||
|
// recurse down the children, front side first
|
||||||
|
node_children( children, node, tr.worldmodel );
|
||||||
|
R_RecursiveWorldNode( children[side], clipflags );
|
||||||
|
|
||||||
|
firstsurface = node_firstsurface( node, tr.worldmodel );
|
||||||
|
numsurfaces = node_numsurfaces( node, tr.worldmodel );
|
||||||
|
|
||||||
|
// draw stuff
|
||||||
|
for( c = numsurfaces, surf = tr.worldmodel->surfaces + firstsurface; c; c--, surf++ )
|
||||||
|
{
|
||||||
|
if( R_CullSurface( surf, &RI.frustum, clipflags ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( surf->flags & SURF_DRAWSKY )
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// make sky chain to right clip the skybox
|
||||||
|
surf->texturechain = skychain;
|
||||||
|
skychain = surf;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
surf->texturechain = surf->texinfo->texture->texturechain;
|
||||||
|
surf->texinfo->texture->texturechain = surf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse down the back side
|
||||||
|
node = children[!side];
|
||||||
|
goto loc0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void R_DrawWorld( void )
|
||||||
|
{
|
||||||
|
double start, end;
|
||||||
|
|
||||||
|
// paranoia issues: when gl_renderer is "0" we need have something valid for currententity
|
||||||
|
// to prevent crashing until HeadShield drawing.
|
||||||
|
RI.currententity = CL_GetEntityByIndex( 0 );
|
||||||
|
if( !RI.currententity )
|
||||||
|
return;
|
||||||
|
|
||||||
|
RI.currentmodel = RI.currententity->model;
|
||||||
|
if( !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ) || FBitSet( RI.rvp.flags, RF_ONLY_CLIENTDRAW ))
|
||||||
|
return;
|
||||||
|
|
||||||
|
VectorCopy( RI.cullorigin, tr.modelorg );
|
||||||
|
//memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces ));
|
||||||
|
//memset( fullbright_surfaces, 0, sizeof( fullbright_surfaces ));
|
||||||
|
//memset( detail_surfaces, 0, sizeof( detail_surfaces ));
|
||||||
|
|
||||||
|
//gl_lms.dynamic_surfaces = NULL;
|
||||||
|
tr.blend = 1.0f;
|
||||||
|
|
||||||
|
//R_ClearSkyBox ();
|
||||||
|
|
||||||
|
start = gEngfuncs.pfnTime();
|
||||||
|
if( FBitSet( RI.rvp.flags, RF_DRAW_OVERVIEW ))
|
||||||
|
{
|
||||||
|
//R_DrawWorldTopView( tr.worldmodel->nodes, RI.frustum.clipFlags );
|
||||||
|
}
|
||||||
|
else R_RecursiveWorldNode( tr.worldmodel->nodes, RI.frustum.clipFlags );
|
||||||
|
end = gEngfuncs.pfnTime();
|
||||||
|
|
||||||
|
//r_stats.t_world_node = end - start;
|
||||||
|
|
||||||
|
start = gEngfuncs.pfnTime();
|
||||||
|
|
||||||
|
R_DrawTextureChains();
|
||||||
|
|
||||||
|
end = gEngfuncs.pfnTime();
|
||||||
|
|
||||||
|
//r_stats.t_world_draw = end - start;
|
||||||
|
//tr.num_draw_decals = 0;
|
||||||
|
//skychain = NULL;
|
||||||
|
|
||||||
|
gEngfuncs.R_DrawWorldHull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
R_MarkLeaves
|
||||||
|
|
||||||
|
Mark the leaves and nodes that are in the PVS for the current leaf
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
void R_MarkLeaves( void )
|
||||||
|
{
|
||||||
|
qboolean novis = false;
|
||||||
|
qboolean force = false;
|
||||||
|
mleaf_t *leaf = NULL;
|
||||||
|
mnode_t *node;
|
||||||
|
vec3_t test;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if( !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ) ) return;
|
||||||
|
|
||||||
|
if( FBitSet( r_novis.flags, FCVAR_CHANGED ) || tr.fResetVis )
|
||||||
|
{
|
||||||
|
// force recalc viewleaf
|
||||||
|
ClearBits( r_novis.flags, FCVAR_CHANGED );
|
||||||
|
tr.fResetVis = false;
|
||||||
|
RI.viewleaf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy( RI.rvp.vieworigin, test );
|
||||||
|
|
||||||
|
if( RI.viewleaf != NULL )
|
||||||
|
{
|
||||||
|
// merge two leafs that can be a crossed-line contents
|
||||||
|
if( RI.viewleaf->contents == CONTENTS_EMPTY )
|
||||||
|
VectorSet( test, RI.rvp.vieworigin[0], RI.rvp.vieworigin[1], RI.rvp.vieworigin[2] - 16.0f );
|
||||||
|
else
|
||||||
|
VectorSet( test, RI.rvp.vieworigin[0], RI.rvp.vieworigin[1], RI.rvp.vieworigin[2] + 16.0f );
|
||||||
|
|
||||||
|
leaf = gEngfuncs.Mod_PointInLeaf( test, tr.worldmodel->nodes, tr.worldmodel );
|
||||||
|
|
||||||
|
if(( leaf->contents != CONTENTS_SOLID ) && ( RI.viewleaf != leaf ))
|
||||||
|
force = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( RI.viewleaf == RI.oldviewleaf && RI.viewleaf != NULL && !force )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// development aid to let you run around
|
||||||
|
// and see exactly where the pvs ends
|
||||||
|
if( r_lockpvs.value ) return;
|
||||||
|
|
||||||
|
RI.oldviewleaf = RI.viewleaf;
|
||||||
|
tr.visframecount++;
|
||||||
|
|
||||||
|
if( r_novis.value || FBitSet( RI.rvp.flags, RF_DRAW_OVERVIEW ) || !RI.viewleaf || !tr.worldmodel->visdata )
|
||||||
|
novis = true;
|
||||||
|
|
||||||
|
gEngfuncs.R_FatPVS( RI.rvp.vieworigin, r_pvs_radius->value, RI.visbytes, false, novis );
|
||||||
|
if( force && !novis )
|
||||||
|
gEngfuncs.R_FatPVS( test, r_pvs_radius->value, RI.visbytes, true, novis );
|
||||||
|
|
||||||
|
for( i = 0; i < tr.worldmodel->numleafs; i++ )
|
||||||
|
{
|
||||||
|
if( CHECKVISBIT( RI.visbytes, i ))
|
||||||
|
{
|
||||||
|
node = (mnode_t *)&tr.worldmodel->leafs[i+1];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( node->visframe == tr.visframecount )
|
||||||
|
break;
|
||||||
|
node->visframe = tr.visframecount;
|
||||||
|
node = node->parent;
|
||||||
|
} while( node );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
GL_BuildLightmaps
|
||||||
|
|
||||||
|
Builds the lightmap texture
|
||||||
|
with all the surfaces from all brush models
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void GL_BuildLightmaps( void )
|
||||||
|
{
|
||||||
|
int i, j, nColinElim = 0;
|
||||||
|
model_t *m;
|
||||||
|
#if 0
|
||||||
|
// release old lightmaps
|
||||||
|
for( i = 0; i < MAX_LIGHTMAPS; i++ )
|
||||||
|
{
|
||||||
|
if( !tr.lightmapTextures[i] ) break;
|
||||||
|
GL_FreeTexture( tr.lightmapTextures[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
|
||||||
|
#endif
|
||||||
|
memset( &RI, 0, sizeof( RI ));
|
||||||
|
#if 0
|
||||||
|
// update the lightmap blocksize
|
||||||
|
if( FBitSet( gp_host->features, ENGINE_LARGE_LIGHTMAPS ) || tr.world->version == QBSP2_VERSION || r_large_lightmaps.value )
|
||||||
|
tr.block_size = BLOCK_SIZE_MAX;
|
||||||
|
else tr.block_size = BLOCK_SIZE_DEFAULT;
|
||||||
|
|
||||||
|
skychain = NULL;
|
||||||
|
#endif
|
||||||
|
tr.framecount = tr.visframecount = 1; // no dlight cache
|
||||||
|
//gl_lms.current_lightmap_texture = 0;
|
||||||
|
tr.modelviewIdentity = false;
|
||||||
|
tr.realframecount = 1;
|
||||||
|
#if 0
|
||||||
|
// setup the texture for dlights
|
||||||
|
R_InitDlightTexture();
|
||||||
|
|
||||||
|
// setup all the lightstyles
|
||||||
|
CL_RunLightStyles((lightstyle_t *)ENGINE_GET_PARM( PARM_GET_LIGHTSTYLES_PTR ));
|
||||||
|
|
||||||
|
LM_InitBlock();
|
||||||
|
#endif
|
||||||
|
for( i = 0; i < gp_cl->nummodels; i++ )
|
||||||
|
{
|
||||||
|
if(( m = CL_ModelHandle( i + 1 )) == NULL )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( m->name[0] == '*' || m->type != mod_brush )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for( j = 0; j < m->numsurfaces; j++ )
|
||||||
|
{
|
||||||
|
// clearing all decal chains
|
||||||
|
m->surfaces[j].pdecals = NULL;
|
||||||
|
m->surfaces[j].visframe = 0;
|
||||||
|
|
||||||
|
//GL_CreateSurfaceLightmap( m->surfaces + j, m );
|
||||||
|
|
||||||
|
if( m->surfaces[j].flags & SURF_DRAWTURB )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nColinElim += GL_BuildPolygonFromSurface( m, m->surfaces + j );
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearing visframe
|
||||||
|
for( j = 0; j < m->numleafs; j++ )
|
||||||
|
m->leafs[j+1].visframe = 0;
|
||||||
|
for( j = 0; j < m->numnodes; j++ )
|
||||||
|
m->nodes[j].visframe = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LM_UploadBlock( false );
|
||||||
|
|
||||||
|
if( gEngfuncs.drawFuncs->GL_BuildLightmaps )
|
||||||
|
{
|
||||||
|
// build lightmaps on the client-side
|
||||||
|
gEngfuncs.drawFuncs->GL_BuildLightmaps( );
|
||||||
|
}
|
||||||
|
}
|
||||||
18
ref/dx2/wscript
Normal file
18
ref/dx2/wscript
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# mittorn, 2018
|
||||||
|
|
||||||
|
def options(opt):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
bld.shlib(
|
||||||
|
source = ['r_context.c', 'r_image.c', 'r_d3d.c', 'r_main.c', 'r_draw.c', 'r_surf.c', 'r_frustum.c'],
|
||||||
|
target = 'ref_dx2',
|
||||||
|
defines = 'REF_DLL',
|
||||||
|
use = 'ref_common engine_includes sdk_includes werror public USER32 DDRAW',
|
||||||
|
install_path = bld.env.LIBDIR
|
||||||
|
)
|
||||||
@@ -721,7 +721,7 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos
|
|||||||
|
|
||||||
if( model->type != mod_brush )
|
if( model->type != mod_brush )
|
||||||
{
|
{
|
||||||
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
|
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ static texture_t *R_TextureAnimation( msurface_t *s )
|
|||||||
R_AddDynamicLights
|
R_AddDynamicLights
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
static void R_AddDynamicLights( const msurface_t *surf )
|
static void R_AddDynamicLights( const msurface_t *surf, float sample_size, int smax, int tmax )
|
||||||
{
|
{
|
||||||
const mextrasurf_t *info = surf->info;
|
const mextrasurf_t *info = surf->info;
|
||||||
int sample_frac = 1.0;
|
int sample_frac = 1.0;
|
||||||
@@ -575,9 +575,6 @@ static void R_AddDynamicLights( const msurface_t *surf )
|
|||||||
if( !surf->dlightbits )
|
if( !surf->dlightbits )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
|
|
||||||
int smax = (info->lightextents[0] / sample_size) + 1;
|
|
||||||
int tmax = (info->lightextents[1] / sample_size) + 1;
|
|
||||||
mtexinfo_t *tex = surf->texinfo;
|
mtexinfo_t *tex = surf->texinfo;
|
||||||
|
|
||||||
if( FBitSet( tex->flags, TEX_WORLD_LUXELS ))
|
if( FBitSet( tex->flags, TEX_WORLD_LUXELS ))
|
||||||
@@ -624,14 +621,22 @@ static void R_AddDynamicLights( const msurface_t *surf )
|
|||||||
float sl = DotProduct( impact, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0];
|
float sl = DotProduct( impact, info->lmvecs[0] ) + info->lmvecs[0][3] - info->lightmapmins[0];
|
||||||
float tl = DotProduct( impact, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1];
|
float tl = DotProduct( impact, info->lmvecs[1] ) + info->lmvecs[1][3] - info->lightmapmins[1];
|
||||||
|
|
||||||
for( int t = 0; t < tmax; t++ )
|
// dist >= max( sd, td ), so a luxel can only pass the test below when both
|
||||||
|
// sd and td are under minlight
|
||||||
|
float half = ( minlight + 1.0f ) / ( sample_size * sample_frac );
|
||||||
|
int s0 = Q_max( 0, (int)( sl / sample_size - half ));
|
||||||
|
int s1 = Q_min( smax - 1, (int)( sl / sample_size + half ));
|
||||||
|
int t0 = Q_max( 0, (int)( tl / sample_size - half ));
|
||||||
|
int t1 = Q_min( tmax - 1, (int)( tl / sample_size + half ));
|
||||||
|
|
||||||
|
for( int t = t0; t <= t1; t++ )
|
||||||
{
|
{
|
||||||
int td = (tl - sample_size * t) * sample_frac;
|
int td = (tl - sample_size * t) * sample_frac;
|
||||||
|
|
||||||
if( td < 0 )
|
if( td < 0 )
|
||||||
td = -td;
|
td = -td;
|
||||||
|
|
||||||
for( int s = 0; s < smax; s++ )
|
for( int s = s0; s <= s1; s++ )
|
||||||
{
|
{
|
||||||
int sd = (sl - sample_size * s) * sample_frac;
|
int sd = (sl - sample_size * s) * sample_frac;
|
||||||
float dist;
|
float dist;
|
||||||
@@ -755,7 +760,7 @@ static void R_BuildLightMap( const msurface_t *surf, byte *dest, int stride, qbo
|
|||||||
|
|
||||||
// add all the dynamic lights
|
// add all the dynamic lights
|
||||||
if( surf->dlightframe == tr.framecount && dynamic )
|
if( surf->dlightframe == tr.framecount && dynamic )
|
||||||
R_AddDynamicLights( surf );
|
R_AddDynamicLights( surf, sample_size, smax, tmax );
|
||||||
|
|
||||||
for( int t = 0; t < tmax; t++ )
|
for( int t = 0; t < tmax; t++ )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ static float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **old
|
|||||||
// are positive, so we don't have to worry about division by zero
|
// are positive, so we don't have to worry about division by zero
|
||||||
float targettime = time - ((int)(time / fullinterval)) * fullinterval;
|
float targettime = time - ((int)(time / fullinterval)) * fullinterval;
|
||||||
|
|
||||||
// LordHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0,
|
// LadyHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0,
|
||||||
// i instead measure the time of the first frame, hoping it is consistent
|
// i instead measure the time of the first frame, hoping it is consistent
|
||||||
int i, j;
|
int i, j;
|
||||||
for( i = 0, j = numframes - 1; i < (numframes - 1); i++ )
|
for( i = 0, j = numframes - 1; i < (numframes - 1); i++ )
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def configure(conf):
|
|||||||
conf.check_cc(lib='log')
|
conf.check_cc(lib='log')
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
libs = [ 'ref_common', 'engine_includes', 'werror' ]
|
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
|
||||||
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
||||||
if bld.env.DEST_OS == 'psvita':
|
if bld.env.DEST_OS == 'psvita':
|
||||||
libs += [ 'sdk_includes', 'vgl_shim' ]
|
libs += [ 'sdk_includes', 'vgl_shim' ]
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ def build(bld):
|
|||||||
source = 'r_context.c',
|
source = 'r_context.c',
|
||||||
target = 'ref_null',
|
target = 'ref_null',
|
||||||
defines = 'REF_DLL',
|
defines = 'REF_DLL',
|
||||||
use = 'engine_includes sdk_includes werror',
|
use = 'engine_includes sdk_includes werror yy_thunks',
|
||||||
install_path = bld.env.LIBDIR
|
install_path = bld.env.LIBDIR
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -470,6 +470,8 @@ static decal_t *R_DecalIntersect( decalinfo_t *decalinfo, msurface_t *surf, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( pDecal == pDecal->pnext ) break;
|
||||||
pDecal = pDecal->pnext;
|
pDecal = pDecal->pnext;
|
||||||
}
|
}
|
||||||
return plast;
|
return plast;
|
||||||
@@ -523,7 +525,10 @@ static void R_AddDecalToSurface( decal_t *pdecal, msurface_t *surf, decalinfo_t
|
|||||||
if( pold )
|
if( pold )
|
||||||
{
|
{
|
||||||
while( pold->pnext )
|
while( pold->pnext )
|
||||||
|
{
|
||||||
|
if( pold == pold->pnext ) break;
|
||||||
pold = pold->pnext;
|
pold = pold->pnext;
|
||||||
|
}
|
||||||
pold->pnext = pdecal;
|
pold->pnext = pdecal;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -599,8 +604,10 @@ static void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo )
|
|||||||
{
|
{
|
||||||
// NOTE: we may have the decal on this surface that come from another level.
|
// NOTE: we may have the decal on this surface that come from another level.
|
||||||
// check duplicate with same position and texture
|
// check duplicate with same position and texture
|
||||||
while( decal != NULL && decal != decal->pnext )
|
while( decal != NULL )
|
||||||
{
|
{
|
||||||
|
if( decal == decal->pnext ) break;
|
||||||
|
|
||||||
if( VectorCompare( decal->position, decalinfo->m_Position ) && decal->texture == decalinfo->m_iTexture )
|
if( VectorCompare( decal->position, decalinfo->m_Position ) && decal->texture == decalinfo->m_iTexture )
|
||||||
return; // decal already exists, don't place it again
|
return; // decal already exists, don't place it again
|
||||||
decal = decal->pnext;
|
decal = decal->pnext;
|
||||||
@@ -743,7 +750,7 @@ void GAME_EXPORT R_DecalShoot( int textureIndex, int entityIndex, int modelIndex
|
|||||||
|
|
||||||
if( model->type != mod_brush )
|
if( model->type != mod_brush )
|
||||||
{
|
{
|
||||||
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
|
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def configure(conf):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
libs = [ 'ref_common', 'engine_includes', 'werror' ]
|
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
|
||||||
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
|
||||||
if bld.env.DEST_OS == 'psvita':
|
if bld.env.DEST_OS == 'psvita':
|
||||||
libs += [ 'sdk_includes' ]
|
libs += [ 'sdk_includes' ]
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ mv "SDL2-$SDL_VERSION" SDL2_VC
|
|||||||
|
|
||||||
if [ "$GH_CPU_ARCH" = "i386" ]; then
|
if [ "$GH_CPU_ARCH" = "i386" ]; then
|
||||||
rustup target add i686-pc-windows-msvc
|
rustup target add i686-pc-windows-msvc
|
||||||
|
|
||||||
|
# YY-Thunks obj for Windows XP support
|
||||||
|
curl -L "https://github.com/Chuyu-Team/YY-Thunks/releases/download/$YY_THUNKS_VERSION/YY-Thunks-Objs.zip" -o yy-thunks.zip
|
||||||
|
unzip -q -j -o yy-thunks.zip 'objs/x86/YY_Thunks_for_WinXP.obj' -d 3rdparty/yy-thunks
|
||||||
|
rm yy-thunks.zip
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl -L https://github.com/FWGS/potential-meme/releases/download/prebuilts/mingw-w64-x86_64-pkgconf-1.2.3.0-1-any.pkg.tar.zst -o pkgconf.tar.zst
|
curl -L https://github.com/FWGS/potential-meme/releases/download/prebuilts/mingw-w64-x86_64-pkgconf-1.2.3.0-1-any.pkg.tar.zst -o pkgconf.tar.zst
|
||||||
|
|||||||
121
scripts/waifulib/clang_format.py
Normal file
121
scripts/waifulib/clang_format.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# clang_format - `./waf format` runs clang-format -i in parallel on opted-in source files.
|
||||||
|
# Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
#
|
||||||
|
# To override clang-format binary:
|
||||||
|
# CLANG_FORMAT=xxx ./waf configure
|
||||||
|
# CLANG_FORMAT=xxx ./waf format # for post-configure override, won't check for minimum version
|
||||||
|
#
|
||||||
|
# Opt task gens in:
|
||||||
|
# bld.stlib(features='format', source='...', target='...', ...)
|
||||||
|
|
||||||
|
from waflib import Build, Logs, Task, TaskGen, Utils
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Enforce minimum major version
|
||||||
|
CLANG_FORMAT_MIN_MAJOR = None
|
||||||
|
|
||||||
|
# File extensions to format
|
||||||
|
CLANG_FORMAT_EXTS = ['.c', '.cpp', '.h']
|
||||||
|
|
||||||
|
def probe_major(binary):
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output([binary, '--version'], stderr=subprocess.DEVNULL).decode('utf-8', 'replace')
|
||||||
|
except (OSError, subprocess.CalledProcessError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
m = re.search(r'version\s+(\d+)', out)
|
||||||
|
return int(m.group(1)) if m else None
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
conf.find_program('clang-format', var='CLANG_FORMAT', mandatory=False)
|
||||||
|
|
||||||
|
if conf.env.CLANG_FORMAT and CLANG_FORMAT_MIN_MAJOR is not None:
|
||||||
|
major = probe_major(conf.env.CLANG_FORMAT[0])
|
||||||
|
|
||||||
|
if major is None:
|
||||||
|
Logs.warn('clang-format is not an executable or invalid')
|
||||||
|
return
|
||||||
|
|
||||||
|
if major < CLANG_FORMAT_MIN_MAJOR:
|
||||||
|
Logs.warn('clang-format %d found, need >= %d, format command won\'t be available' % (major, CLANG_FORMAT_MIN_MAJOR))
|
||||||
|
conf.env.CLANG_FORMAT = []
|
||||||
|
return
|
||||||
|
|
||||||
|
conf.msg('Checking clang-format version', major)
|
||||||
|
|
||||||
|
class ClangFormatTask(Task.Task):
|
||||||
|
color = 'PINK'
|
||||||
|
always_run = True
|
||||||
|
binary = 'clang-format' # just a default, will be overriden
|
||||||
|
|
||||||
|
def keyword(self):
|
||||||
|
return 'Formatting'
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
return self.exec_command([self.binary, '-i', self.inputs[0].abspath()], env=self.env.env or None)
|
||||||
|
|
||||||
|
# need this because if we add headers to to `source`
|
||||||
|
# we can catch it in `format` feature, as nothing else
|
||||||
|
# catches .h
|
||||||
|
@TaskGen.extension('.h')
|
||||||
|
def format_stub(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@TaskGen.feature('format')
|
||||||
|
def make_format_tasks(self):
|
||||||
|
if self.bld.cmd != 'format':
|
||||||
|
return
|
||||||
|
|
||||||
|
for n in self.to_nodes(Utils.to_list(getattr(self, 'source', []))):
|
||||||
|
if n.suffix() in CLANG_FORMAT_EXTS:
|
||||||
|
self.create_task('ClangFormatTask', n)
|
||||||
|
|
||||||
|
self.source = [] # remove anything build related
|
||||||
|
|
||||||
|
class FormatContext(Build.BuildContext):
|
||||||
|
cmd = 'format'
|
||||||
|
fun = 'build'
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.restore()
|
||||||
|
if not self.all_envs:
|
||||||
|
self.load_envs()
|
||||||
|
self.recurse([self.run_dir])
|
||||||
|
|
||||||
|
binary = os.environ.get('CLANG_FORMAT') or (self.env.CLANG_FORMAT and self.env.CLANG_FORMAT[0])
|
||||||
|
if not binary:
|
||||||
|
self.fatal('format: clang-format not configured')
|
||||||
|
ClangFormatTask.binary = binary
|
||||||
|
|
||||||
|
# loops below filter taskgens by target name, format feature and by c-f task class
|
||||||
|
wanted = set(self.targets.split(',')) if self.targets else None
|
||||||
|
tasks = []
|
||||||
|
for group in self.groups:
|
||||||
|
for tg in group:
|
||||||
|
if not isinstance(tg, TaskGen.task_gen):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 'format' not in Utils.to_list(tg.features):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if wanted and getattr(tg, 'name', None) not in wanted:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tg.post()
|
||||||
|
for t in tg.tasks:
|
||||||
|
if isinstance(t, ClangFormatTask):
|
||||||
|
tasks.append(t)
|
||||||
|
|
||||||
|
if not tasks:
|
||||||
|
self.fatal("format: no sources matched (need features='format' on a task gen)")
|
||||||
|
|
||||||
|
Logs.info('format: %s on %d file(s)' % (binary, len(tasks)))
|
||||||
|
self.progress_bar = 0 # FIXME: doesn't work with progress bar for some reason
|
||||||
|
self.groups = [tasks]
|
||||||
|
self.cur_group = 0
|
||||||
|
self.targets = ''
|
||||||
|
self.compile()
|
||||||
@@ -19,7 +19,7 @@ def build(bld):
|
|||||||
bld.program(source = bld.path.ant_glob('*.c'),
|
bld.program(source = bld.path.ant_glob('*.c'),
|
||||||
target = 'mdldec',
|
target = 'mdldec',
|
||||||
includes = '.',
|
includes = '.',
|
||||||
use = 'engine_includes public M werror',
|
use = 'engine_includes public M werror yy_thunks',
|
||||||
install_path = bld.env.BINDIR,
|
install_path = bld.env.BINDIR,
|
||||||
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -6,12 +7,13 @@
|
|||||||
#error
|
#error
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef int (*FuzzFunc)(const char *Data, size_t Size);
|
typedef int (*FuzzFunc)(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
void *handle = NULL;
|
static void *handle = NULL;
|
||||||
FuzzFunc f = NULL;
|
static FuzzFunc f = NULL;
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput( const char *Data, size_t Size )
|
int LLVMFuzzerTestOneInput( const uint8_t *Data, size_t Size );
|
||||||
|
int LLVMFuzzerTestOneInput( const uint8_t *Data, size_t Size )
|
||||||
{
|
{
|
||||||
if( !handle )
|
if( !handle )
|
||||||
handle = dlopen( LIB, RTLD_NOW );
|
handle = dlopen( LIB, RTLD_NOW );
|
||||||
|
|||||||
@@ -37,4 +37,6 @@ def build(bld):
|
|||||||
add_runner_target(bld, 'libxash.so', 'Image_LoadPNG')
|
add_runner_target(bld, 'libxash.so', 'Image_LoadPNG')
|
||||||
add_runner_target(bld, 'libxash.so', 'Image_LoadDDS')
|
add_runner_target(bld, 'libxash.so', 'Image_LoadDDS')
|
||||||
add_runner_target(bld, 'libxash.so', 'Image_LoadTGA')
|
add_runner_target(bld, 'libxash.so', 'Image_LoadTGA')
|
||||||
|
add_runner_target(bld, 'libxash.so', 'Image_LoadSPR')
|
||||||
add_runner_target(bld, 'libxash.so', 'Mod_LoadModel')
|
add_runner_target(bld, 'libxash.so', 'Mod_LoadModel')
|
||||||
|
add_runner_target(bld, 'libxash.so', 'Mod_LoadSpriteModel')
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ def build(bld):
|
|||||||
bld.program(source = bld.path.ant_glob('*.c'),
|
bld.program(source = bld.path.ant_glob('*.c'),
|
||||||
target = 'xar',
|
target = 'xar',
|
||||||
includes = '.',
|
includes = '.',
|
||||||
use = 'public filesystem_includes werror',
|
use = 'public filesystem_includes werror yy_thunks',
|
||||||
rpath = bld.env.DEFAULT_RPATH,
|
rpath = bld.env.DEFAULT_RPATH,
|
||||||
install_path = bld.env.BINDIR,
|
install_path = bld.env.BINDIR,
|
||||||
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
||||||
|
|||||||
91
utils/xash-clang-format/config.c
Normal file
91
utils/xash-clang-format/config.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
config.c - .clang-format extension parser
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
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 <string.h>
|
||||||
|
#include "crtlib.h"
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
#define XCF_KEY "XashCollapseParens"
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ConfigDefault
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static void ConfigDefault( xcf_config_t *c )
|
||||||
|
{
|
||||||
|
c->collapse_parens = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
SkipWhitespace
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static const char *SkipWhitespace( const char *p, const char *end )
|
||||||
|
{
|
||||||
|
for( ; p < end && ( *p == ' ' || *p == '\t' ); p++ )
|
||||||
|
;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ParseConfigText
|
||||||
|
|
||||||
|
Parse extension keys out of a YAML text body.
|
||||||
|
We only look at lines that begin (after optional whitespace) with `#`.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean ParseConfigText( const char *text, xcf_config_t *cfg )
|
||||||
|
{
|
||||||
|
ConfigDefault( cfg );
|
||||||
|
if( !text )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for( const char *p = text; *p; )
|
||||||
|
{
|
||||||
|
const char *end = Q_strchrnul( p, '\n' );
|
||||||
|
const char *q;
|
||||||
|
const size_t klen = sizeof( XCF_KEY ) - 1;
|
||||||
|
|
||||||
|
q = SkipWhitespace( p, end );
|
||||||
|
|
||||||
|
if( q < end && *q == '#' )
|
||||||
|
{
|
||||||
|
q = SkipWhitespace( q + 1, end );
|
||||||
|
|
||||||
|
if( end - q >= klen && memcmp( q, XCF_KEY, klen ) == 0 )
|
||||||
|
{
|
||||||
|
q = SkipWhitespace( q + klen, end );
|
||||||
|
|
||||||
|
if( q < end && *q == ':' )
|
||||||
|
{
|
||||||
|
q = SkipWhitespace( q + 1, end );
|
||||||
|
|
||||||
|
if( end - q >= 4 && memcmp( q, "true", 4 ) == 0 )
|
||||||
|
cfg->collapse_parens = true;
|
||||||
|
else if( end - q >= 5 && memcmp( q, "false", 5 ) == 0 )
|
||||||
|
cfg->collapse_parens = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !*end )
|
||||||
|
break;
|
||||||
|
p = end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
402
utils/xash-clang-format/main.c
Normal file
402
utils/xash-clang-format/main.c
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
main.c - xash-clang-format wrapper entry point
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "crtlib.h"
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
#ifndef XCF_CLANG_FORMAT_DEFAULT
|
||||||
|
#define XCF_CLANG_FORMAT_DEFAULT "clang-format"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: move to custom config keys
|
||||||
|
#define XCF_MIN_CLANG_FORMAT_MAJOR 17
|
||||||
|
|
||||||
|
static const char *g_clang_format = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ResolveClangFormat
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static const char *ResolveClangFormat( void )
|
||||||
|
{
|
||||||
|
const char *e = getenv( "XASH_CLANG_FORMAT_BIN" );
|
||||||
|
|
||||||
|
if( !COM_StringEmptyOrNULL( e ))
|
||||||
|
return e;
|
||||||
|
|
||||||
|
return XCF_CLANG_FORMAT_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
IsFileArg
|
||||||
|
|
||||||
|
Attempt to distinguish files from other args.
|
||||||
|
But to keep parsing args simple, it will parse value as --key value as
|
||||||
|
filename. :(
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean IsFileArg( const char *a )
|
||||||
|
{
|
||||||
|
if( COM_StringEmptyOrNULL( a ))
|
||||||
|
return false;
|
||||||
|
if( a[0] == '-' )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ReadTheFile
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean ReadTheFile( const char *path, xcf_buf_t *out )
|
||||||
|
{
|
||||||
|
FILE *f = fopen( path, "rb" );
|
||||||
|
|
||||||
|
if( !f )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char buf[8192];
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
while(( n = fread( buf, 1, sizeof( buf ), f )) > 0 )
|
||||||
|
{
|
||||||
|
if( !BufPutMem( out, buf, n ))
|
||||||
|
{
|
||||||
|
fclose( f );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose( f );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
WriteTheFile
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean WriteTheFile( const char *path, const char *data, size_t len )
|
||||||
|
{
|
||||||
|
FILE *f = fopen( path, "wb" );
|
||||||
|
|
||||||
|
if( !f )
|
||||||
|
return false;
|
||||||
|
if( fwrite( data, 1, len, f ) != len )
|
||||||
|
{
|
||||||
|
fclose( f );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if( fclose( f ) != 0 )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
FindConfig
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static char *FindConfig( const char *startdir )
|
||||||
|
{
|
||||||
|
char dir[MAX_SYSPATH];
|
||||||
|
|
||||||
|
if( startdir )
|
||||||
|
{
|
||||||
|
Q_strncpy( dir, startdir, sizeof( dir ));
|
||||||
|
}
|
||||||
|
else if( !getcwd( dir, sizeof( dir )))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
char path[MAX_SYSPATH];
|
||||||
|
struct stat st;
|
||||||
|
char *slash;
|
||||||
|
|
||||||
|
Q_snprintf( path, sizeof( path ), "%s/.clang-format", dir );
|
||||||
|
|
||||||
|
if( stat( path, &st ) == 0 && S_ISREG( st.st_mode ))
|
||||||
|
{
|
||||||
|
size_t len = Q_strlen( path ) + 1;
|
||||||
|
char *r = malloc( len );
|
||||||
|
|
||||||
|
if( r )
|
||||||
|
Q_strncpy( r, path, len );
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
slash = Q_strrchr( dir, '/' );
|
||||||
|
if( !slash || slash == dir )
|
||||||
|
break;
|
||||||
|
*slash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
LoadConfig
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean LoadConfig( const char *path, xcf_config_t *cfg )
|
||||||
|
{
|
||||||
|
xcf_buf_t buf = { 0 };
|
||||||
|
qboolean ok = false;
|
||||||
|
|
||||||
|
if( path && ReadTheFile( path, &buf ) && BufPutChar( &buf, 0 ))
|
||||||
|
ok = ParseConfigText( buf.data, cfg );
|
||||||
|
else
|
||||||
|
ParseConfigText( "", cfg );
|
||||||
|
|
||||||
|
BufFree( &buf );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
Emit
|
||||||
|
|
||||||
|
Transform pass on buffer and output to file
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean Emit( const xcf_config_t *cfg, const xcf_buf_t *raw, FILE *dst, const char *write_path )
|
||||||
|
{
|
||||||
|
xcf_buf_t out = { 0 };
|
||||||
|
const char *data;
|
||||||
|
size_t len;
|
||||||
|
if( cfg->collapse_parens && Transform( raw->data, raw->len, &out ))
|
||||||
|
{
|
||||||
|
data = out.data;
|
||||||
|
len = out.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = raw->data;
|
||||||
|
len = raw->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean ok = true;
|
||||||
|
if( write_path )
|
||||||
|
{
|
||||||
|
if( !WriteTheFile( write_path, data, len ))
|
||||||
|
{
|
||||||
|
fprintf( stderr, "xash-clang-format: cannot write %s: %s\n", write_path, strerror( errno ));
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( len )
|
||||||
|
{
|
||||||
|
if( fwrite( data, 1, len, dst ) != len )
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufFree( &out );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
PipeMode
|
||||||
|
|
||||||
|
Process stdout (when clang-format called without -i)
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static int PipeMode( int argc, char **argv, const xcf_config_t *cfg )
|
||||||
|
{
|
||||||
|
char **fwd = malloc( sizeof( char * ) * ( argc + 1 ));
|
||||||
|
if( !fwd )
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fwd[0] = (char *)g_clang_format;
|
||||||
|
for( int i = 1; i < argc; i++ )
|
||||||
|
fwd[i] = argv[i];
|
||||||
|
fwd[argc] = NULL;
|
||||||
|
|
||||||
|
xcf_buf_t raw = { 0 };
|
||||||
|
int rc = RunCapture( fwd, &raw, false );
|
||||||
|
free( fwd );
|
||||||
|
|
||||||
|
if( rc != 0 )
|
||||||
|
{
|
||||||
|
// pass through whatever clang-format produced
|
||||||
|
if( raw.len )
|
||||||
|
fwrite( raw.data, 1, raw.len, stdout );
|
||||||
|
BufFree( &raw );
|
||||||
|
return rc < 0 ? 1 : rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = Emit( cfg, &raw, stdout, NULL ) ? 0 : 1;
|
||||||
|
BufFree( &raw );
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
PostProcessFile
|
||||||
|
|
||||||
|
Read `path`, run the paren-collapse pass, write result back.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean PostProcessFile( const char *path )
|
||||||
|
{
|
||||||
|
xcf_buf_t in = { 0 };
|
||||||
|
xcf_buf_t out = { 0 };
|
||||||
|
qboolean ok = false;
|
||||||
|
|
||||||
|
if( ReadTheFile( path, &in ) && Transform( in.data, in.len, &out ))
|
||||||
|
ok = WriteTheFile( path, out.data, out.len );
|
||||||
|
|
||||||
|
BufFree( &in );
|
||||||
|
BufFree( &out );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
InPlaceMode
|
||||||
|
|
||||||
|
Post-process mode, when clang-format is called with -i
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static int InPlaceMode( int argc, char **argv, const xcf_config_t *cfg )
|
||||||
|
{
|
||||||
|
char **fwd = malloc( sizeof( char * ) * ( argc + 1 ));
|
||||||
|
|
||||||
|
if( !fwd )
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fwd[0] = (char *)g_clang_format;
|
||||||
|
for( int i = 1; i < argc; i++ )
|
||||||
|
fwd[i] = argv[i];
|
||||||
|
fwd[argc] = NULL;
|
||||||
|
|
||||||
|
xcf_buf_t sink = { 0 };
|
||||||
|
int rc = RunCapture( fwd, &sink, false );
|
||||||
|
|
||||||
|
BufFree( &sink );
|
||||||
|
free( fwd );
|
||||||
|
|
||||||
|
if( rc != 0 )
|
||||||
|
return rc < 0 ? 1 : rc;
|
||||||
|
|
||||||
|
if( !cfg->collapse_parens )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for( int i = 1; i < argc; i++ )
|
||||||
|
{
|
||||||
|
if( !IsFileArg( argv[i] ))
|
||||||
|
continue;
|
||||||
|
if( !PostProcessFile( argv[i] ))
|
||||||
|
{
|
||||||
|
fprintf( stderr, "xash-clang-format: post-process failed for %s\n", argv[i] );
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Usage( FILE *dst )
|
||||||
|
{
|
||||||
|
fprintf( dst,
|
||||||
|
"xash-clang-format: clang-format wrapper with xash3d-fwgs extensions\n"
|
||||||
|
"\n"
|
||||||
|
"Usage: xash-clang-format [clang-format options] [<file> ...]\n"
|
||||||
|
" xash-clang-format -i <file> ...\n"
|
||||||
|
"\n"
|
||||||
|
"Most clang-format flags are forwarded as-is but there are limitations:\n"
|
||||||
|
"* --key value must be passed as --key=value\n"
|
||||||
|
"\n"
|
||||||
|
"Use XASH_CLANG_FORMAT_BIN envvar to override the clang-format binary;\n"
|
||||||
|
" setting it also skips the version probe.\n"
|
||||||
|
"\n"
|
||||||
|
"Wrapper's config is shared with clang-format config, but extended keys\n"
|
||||||
|
" start with comment hash.\n"
|
||||||
|
"New keys:\n"
|
||||||
|
" XashCollapseParens, accepts 'true' or 'false'\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( int argc, char **argv )
|
||||||
|
{
|
||||||
|
g_clang_format = ResolveClangFormat();
|
||||||
|
|
||||||
|
qboolean has_inplace = false;
|
||||||
|
qboolean has_version = false;
|
||||||
|
qboolean has_help = false;
|
||||||
|
|
||||||
|
for( int i = 1; i < argc; i++ )
|
||||||
|
{
|
||||||
|
if( Q_strcmp( argv[i], "-i" ) == 0 )
|
||||||
|
has_inplace = true;
|
||||||
|
else if( Q_strcmp( argv[i], "--version" ) == 0 || Q_strcmp( argv[i], "-version" ) == 0 )
|
||||||
|
has_version = true;
|
||||||
|
else if( Q_strcmp( argv[i], "--help" ) == 0 || Q_strcmp( argv[i], "-h" ) == 0 || Q_strcmp( argv[i], "-help" ) == 0 )
|
||||||
|
has_help = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( has_help )
|
||||||
|
{
|
||||||
|
Usage( stdout );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( has_version )
|
||||||
|
{
|
||||||
|
// transparent passthrough so tools that scrape `clang-format --version`
|
||||||
|
// (e.g. git clang-format auto-detection) keep working
|
||||||
|
execlp( g_clang_format, g_clang_format, "--version", (char *)NULL );
|
||||||
|
fprintf( stderr, "xash-clang-format: cannot exec %s: %s\n", g_clang_format, strerror( errno ));
|
||||||
|
return 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if caller sets XASH_CLANG_FORMAT_BIN — they own version compatibility
|
||||||
|
// so skip the probe (lets ./waf format do the check once for parallel invocation)
|
||||||
|
if( COM_StringEmptyOrNULL( getenv( "XASH_CLANG_FORMAT_BIN" )))
|
||||||
|
{
|
||||||
|
int major = 0;
|
||||||
|
|
||||||
|
if( !ClangFormatVersion( g_clang_format, &major ))
|
||||||
|
{
|
||||||
|
fprintf( stderr, "xash-clang-format: cannot probe clang-format version (binary: %s)\n", g_clang_format );
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if( major < XCF_MIN_CLANG_FORMAT_MAJOR )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "xash-clang-format: clang-format %d found, need >= %d\n",
|
||||||
|
major, XCF_MIN_CLANG_FORMAT_MAJOR );
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xcf_config_t cfg;
|
||||||
|
char *cfgpath = FindConfig( NULL );
|
||||||
|
LoadConfig( cfgpath, &cfg );
|
||||||
|
free( cfgpath );
|
||||||
|
|
||||||
|
return has_inplace ? InPlaceMode( argc, argv, &cfg ) : PipeMode( argc, argv, &cfg );
|
||||||
|
}
|
||||||
99
utils/xash-clang-format/proc.c
Normal file
99
utils/xash-clang-format/proc.c
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
proc.c - subprocess utilities
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
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 <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
RunCapture
|
||||||
|
|
||||||
|
Spawn argv as a child, capture stdout into `out`.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
int RunCapture( char *const argv[], xcf_buf_t *out, qboolean silent_stderr )
|
||||||
|
{
|
||||||
|
int p[2];
|
||||||
|
pid_t pid;
|
||||||
|
char buf[8192];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if( pipe( p ) < 0 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if( pid < 0 )
|
||||||
|
{
|
||||||
|
close( p[0] );
|
||||||
|
close( p[1] );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pid == 0 )
|
||||||
|
{
|
||||||
|
close( p[0] );
|
||||||
|
dup2( p[1], STDOUT_FILENO );
|
||||||
|
close( p[1] );
|
||||||
|
if( silent_stderr )
|
||||||
|
{
|
||||||
|
int devnull = open( "/dev/null", O_WRONLY );
|
||||||
|
|
||||||
|
if( devnull >= 0 )
|
||||||
|
{
|
||||||
|
dup2( devnull, STDERR_FILENO );
|
||||||
|
close( devnull );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execvp( argv[0], argv );
|
||||||
|
_exit( 127 );
|
||||||
|
}
|
||||||
|
|
||||||
|
close( p[1] );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
ssize_t r = read( p[0], buf, sizeof( buf ));
|
||||||
|
|
||||||
|
if( r < 0 )
|
||||||
|
{
|
||||||
|
if( errno == EINTR )
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( r == 0 )
|
||||||
|
break;
|
||||||
|
if( !BufPutMem( out, buf, r ))
|
||||||
|
{
|
||||||
|
close( p[0] );
|
||||||
|
kill( pid, SIGKILL );
|
||||||
|
waitpid( pid, NULL, 0 );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close( p[0] );
|
||||||
|
|
||||||
|
if( waitpid( pid, &status, 0 ) < 0 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// heh wife excited
|
||||||
|
if( WIFEXITED( status ))
|
||||||
|
return WEXITSTATUS( status );
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
77
utils/xash-clang-format/tests/test_config.c
Normal file
77
utils/xash-clang-format/tests/test_config.c
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
static int run_case( const char *label, const char *yaml, qboolean expect )
|
||||||
|
{
|
||||||
|
xcf_config_t cfg;
|
||||||
|
|
||||||
|
ParseConfigText( yaml, &cfg );
|
||||||
|
|
||||||
|
if( cfg.collapse_parens != expect )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "[%s] collapse_parens=%d, expected %d\n yaml:\n%s\n",
|
||||||
|
label, cfg.collapse_parens, expect, yaml );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if( !run_case( "empty", "", false ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
xcf_config_t cfg2;
|
||||||
|
|
||||||
|
ParseConfigText( NULL, &cfg2 );
|
||||||
|
if( cfg2.collapse_parens )
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "explicit true",
|
||||||
|
"# XashCollapseParens: true\n", true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "explicit false",
|
||||||
|
"# XashCollapseParens: false\n", false ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "leading ws",
|
||||||
|
" # XashCollapseParens: true\n", true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "tabs",
|
||||||
|
"#\tXashCollapseParens\t:\ttrue\n", true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "unrelated key",
|
||||||
|
"# SomeOtherKey: true\nIndentWidth: 4\n", false ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "in larger config",
|
||||||
|
"---\n"
|
||||||
|
"Language: Cpp\n"
|
||||||
|
"# XashCollapseParens: true\n"
|
||||||
|
"IndentWidth: 4\n",
|
||||||
|
true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "junk value",
|
||||||
|
"# XashCollapseParens: maybe\n", false ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "no colon",
|
||||||
|
"# XashCollapseParens true\n", false ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "last wins",
|
||||||
|
"# XashCollapseParens: false\n# XashCollapseParens: true\n", true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "no trailing nl",
|
||||||
|
"# XashCollapseParens: true", true ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
171
utils/xash-clang-format/tests/test_transform.c
Normal file
171
utils/xash-clang-format/tests/test_transform.c
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
static int run_case( const char *label, const char *in, const char *expect )
|
||||||
|
{
|
||||||
|
xcf_buf_t out = { 0 };
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
if( !Transform( in, strlen( in ), &out ))
|
||||||
|
{
|
||||||
|
fprintf( stderr, "[%s] Transform returned error\n", label );
|
||||||
|
BufFree( &out );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = ( out.len == strlen( expect ) && memcmp( out.data, expect, out.len ) == 0 );
|
||||||
|
|
||||||
|
if( !ok )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "[%s] mismatch\n in: %s\n out: %.*s\n expect: %s\n",
|
||||||
|
label, in, (int)out.len, out.data, expect );
|
||||||
|
}
|
||||||
|
|
||||||
|
BufFree( &out );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if( !run_case( "outer parens",
|
||||||
|
"if( ( a + b ) )\n",
|
||||||
|
"if(( a + b ))\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "triple nesting",
|
||||||
|
"x = ( ( ( a ) ) );\n",
|
||||||
|
"x = ((( a )));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "mixed [ )",
|
||||||
|
"arr[i] )\n",
|
||||||
|
"arr[i] )\n" ))
|
||||||
|
return ++n;
|
||||||
|
if( !run_case( "mixed ) ]",
|
||||||
|
"f( a ) ];\n",
|
||||||
|
"f( a ) ];\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "newline boundary",
|
||||||
|
"(\n(\n",
|
||||||
|
"(\n(\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "string literal",
|
||||||
|
"x = \"( ( hello ) )\";\n",
|
||||||
|
"x = \"( ( hello ) )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "escaped quote in string",
|
||||||
|
"x = \"a \\\" ( ( b ) ) \";\n",
|
||||||
|
"x = \"a \\\" ( ( b ) ) \";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "line comment",
|
||||||
|
"a; // ( ( comment ) )\nb;\n",
|
||||||
|
"a; // ( ( comment ) )\nb;\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "block comment",
|
||||||
|
"/* ( ( leave\nthis ) ) alone */ x;\n",
|
||||||
|
"/* ( ( leave\nthis ) ) alone */ x;\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "char literal '('",
|
||||||
|
"if( c == '(' )\n",
|
||||||
|
"if( c == '(' )\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "raw string",
|
||||||
|
"const char *s = R\"x(( (\nstays )x\";\n",
|
||||||
|
"const char *s = R\"x(( (\nstays )x\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "raw string empty delim",
|
||||||
|
"R\"(( (\n))\"",
|
||||||
|
"R\"(( (\n))\"" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "ident Rabbit not raw",
|
||||||
|
"int Rabbit = ( ( 1 ) );\n",
|
||||||
|
"int Rabbit = (( 1 ));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "u8R raw",
|
||||||
|
"const auto s = u8R\"(( ( )\";\n",
|
||||||
|
"const auto s = u8R\"(( ( )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "include angle",
|
||||||
|
"#include <foo.h>\nf( ( x ) );\n",
|
||||||
|
"#include <foo.h>\nf(( x ));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "tabs between parens",
|
||||||
|
"f(\t(\tx\t)\t);\n",
|
||||||
|
"f((\tx\t));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "empty", "", "" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "idempotent",
|
||||||
|
"f((( x )));\n",
|
||||||
|
"f((( x )));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "already touching",
|
||||||
|
"((a))",
|
||||||
|
"((a))" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "single paren",
|
||||||
|
"f( a, b )",
|
||||||
|
"f( a, b )" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "multiple pairs",
|
||||||
|
"f( ( a ) ) + g( ( b ) );\n",
|
||||||
|
"f(( a )) + g(( b ));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "L wide string",
|
||||||
|
"wchar_t *s = L\"( ( x ) )\";\n",
|
||||||
|
"wchar_t *s = L\"( ( x ) )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "u utf16 string",
|
||||||
|
"auto s = u\"( ( x ) )\";\n",
|
||||||
|
"auto s = u\"( ( x ) )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "U utf32 string",
|
||||||
|
"auto s = U\"( ( x ) )\";\n",
|
||||||
|
"auto s = U\"( ( x ) )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "u8 utf8 string",
|
||||||
|
"auto s = u8\"( ( x ) )\";\n",
|
||||||
|
"auto s = u8\"( ( x ) )\";\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "L wide char",
|
||||||
|
"if( c == L'(' )\n",
|
||||||
|
"if( c == L'(' )\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "u8 char",
|
||||||
|
"if( c == u8'(' )\n",
|
||||||
|
"if( c == u8'(' )\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "ident L not prefix",
|
||||||
|
"int callL = ( ( 1 ) );\n",
|
||||||
|
"int callL = (( 1 ));\n" ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
58
utils/xash-clang-format/tests/test_version.c
Normal file
58
utils/xash-clang-format/tests/test_version.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
static int run_case( const char *label, const char *s, qboolean expect_ok, int expect_major )
|
||||||
|
{
|
||||||
|
int major = -1;
|
||||||
|
qboolean ok = ParseVersion( s, &major );
|
||||||
|
|
||||||
|
if( ok != expect_ok )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "[%s] ok=%d expected %d (input: %s)\n", label, ok, expect_ok, s ? s : "(null)" );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if( ok && major != expect_major )
|
||||||
|
{
|
||||||
|
fprintf( stderr, "[%s] major=%d expected %d (input: %s)\n", label, major, expect_major, s );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if( !run_case( "vanilla 17",
|
||||||
|
"clang-format version 17.0.6 (Fedora 17.0.6-1.fc40)\n", true, 17 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "ubuntu 18",
|
||||||
|
"Ubuntu clang-format version 18.1.3 (1ubuntu1)\n", true, 18 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "large major",
|
||||||
|
"clang-format version 100.0.0\n", true, 100 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "garbage",
|
||||||
|
"this has no version here\n", false, 0 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "null", NULL, false, 0 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "distro tag with digits",
|
||||||
|
"Ubuntu99 clang-format version 17.0.6\n", true, 17 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "bare digits",
|
||||||
|
"foo 42 bar\n", false, 0 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
if( !run_case( "no version word",
|
||||||
|
"fake clang-format 19.1.0\n", false, 0 ))
|
||||||
|
return ++n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
325
utils/xash-clang-format/transform.c
Normal file
325
utils/xash-clang-format/transform.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
transform.c - source post-processing pass
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
#define XCF_RAW_DELIM_MAX 16 // C++11: 16 chars max in raw-string delimiter
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
BufFree
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void BufFree( xcf_buf_t *b )
|
||||||
|
{
|
||||||
|
free( b->data );
|
||||||
|
b->data = NULL;
|
||||||
|
b->len = b->cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
BufGrow
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean BufGrow( xcf_buf_t *b, size_t need )
|
||||||
|
{
|
||||||
|
size_t cap;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if( b->len + need <= b->cap )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
cap = b->cap ? b->cap : 256;
|
||||||
|
while( cap < b->len + need )
|
||||||
|
cap *= 2;
|
||||||
|
|
||||||
|
p = realloc( b->data, cap );
|
||||||
|
if( !p )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
b->data = p;
|
||||||
|
b->cap = cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
BufPutChar
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean BufPutChar( xcf_buf_t *b, char c )
|
||||||
|
{
|
||||||
|
if( !BufGrow( b, 1 ))
|
||||||
|
return false;
|
||||||
|
b->data[b->len++] = c;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
BufPutMem
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean BufPutMem( xcf_buf_t *b, const char *src, size_t n )
|
||||||
|
{
|
||||||
|
if( !n )
|
||||||
|
return true;
|
||||||
|
if( !BufGrow( b, n ))
|
||||||
|
return false;
|
||||||
|
memcpy( b->data + b->len, src, n );
|
||||||
|
b->len += n;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
TryCollapse
|
||||||
|
|
||||||
|
Collapse spaces between the same type parentheses
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static void TryCollapse( xcf_buf_t *out, char paren )
|
||||||
|
{
|
||||||
|
size_t i = out->len;
|
||||||
|
|
||||||
|
while( i > 0 && ( out->data[i - 1] == ' ' || out->data[i - 1] == '\t' ))
|
||||||
|
i--;
|
||||||
|
|
||||||
|
if( i == 0 || i == out->len )
|
||||||
|
return; // nothing to collapse, or no whitespace between
|
||||||
|
|
||||||
|
if( out->data[i - 1] != paren )
|
||||||
|
return; // different bracket types or different char entirely
|
||||||
|
|
||||||
|
out->len = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
DetectRaw
|
||||||
|
|
||||||
|
Detect C++11 raw string because why not
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static qboolean DetectRaw( const char *s, size_t len, size_t i, size_t *prefix_end, size_t *paren )
|
||||||
|
{
|
||||||
|
size_t j = i, k;
|
||||||
|
|
||||||
|
// must not be in the middle of an identifier
|
||||||
|
if( i > 0 && ( isalnum( s[i - 1] ) || s[i - 1] == '_' ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( j + 2 < len && s[j] == 'u' && s[j + 1] == '8' && s[j + 2] == 'R' )
|
||||||
|
j += 3;
|
||||||
|
else if( j + 1 < len && ( s[j] == 'u' || s[j] == 'U' || s[j] == 'L' ) && s[j + 1] == 'R' )
|
||||||
|
j += 2;
|
||||||
|
else if( j < len && s[j] == 'R' )
|
||||||
|
j += 1;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( j >= len || s[j] != '"' )
|
||||||
|
return false;
|
||||||
|
j++; // past the opening quote
|
||||||
|
*prefix_end = j;
|
||||||
|
|
||||||
|
k = j;
|
||||||
|
while( k < len && s[k] != '(' && k - j < XCF_RAW_DELIM_MAX )
|
||||||
|
k++;
|
||||||
|
|
||||||
|
if( k >= len || s[k] != '(' )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*paren = k;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
S_NORMAL,
|
||||||
|
S_LINE_CMT,
|
||||||
|
S_BLOCK_CMT,
|
||||||
|
S_STRING,
|
||||||
|
S_CHAR,
|
||||||
|
S_RAW,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
Transform
|
||||||
|
|
||||||
|
Parse source, find parens and try collapse them
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean Transform( const char *in, size_t in_len, xcf_buf_t *out )
|
||||||
|
{
|
||||||
|
size_t raw_dlen = 0;
|
||||||
|
char raw_delim[XCF_RAW_DELIM_MAX + 1];
|
||||||
|
int state = S_NORMAL;
|
||||||
|
|
||||||
|
for( size_t i = 0; i < in_len; i++ )
|
||||||
|
{
|
||||||
|
char c1 = in[i];
|
||||||
|
char c2 = ( i + 1 < in_len ) ? in[i + 1] : 0;
|
||||||
|
|
||||||
|
switch( state )
|
||||||
|
{
|
||||||
|
case S_NORMAL:
|
||||||
|
{
|
||||||
|
size_t pe, pp;
|
||||||
|
|
||||||
|
if( c1 == '/' && c2 == '/' )
|
||||||
|
{
|
||||||
|
if( !BufPutMem( out, in + i, 2 ))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
state = S_LINE_CMT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( c1 == '/' && c2 == '*' )
|
||||||
|
{
|
||||||
|
if( !BufPutMem( out, in + i, 2 ))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
state = S_BLOCK_CMT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( DetectRaw( in, in_len, i, &pe, &pp ))
|
||||||
|
{
|
||||||
|
size_t dl = pp - pe;
|
||||||
|
|
||||||
|
if( dl > XCF_RAW_DELIM_MAX )
|
||||||
|
dl = XCF_RAW_DELIM_MAX;
|
||||||
|
|
||||||
|
if( !BufPutMem( out, in + i, pp - i + 1 ))
|
||||||
|
return false;
|
||||||
|
memcpy( raw_delim, in + pe, dl );
|
||||||
|
raw_delim[dl] = 0;
|
||||||
|
raw_dlen = dl;
|
||||||
|
i = pp; // sit on the '('
|
||||||
|
state = S_RAW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( c1 == '"' )
|
||||||
|
{
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
state = S_STRING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( c1 == '\'' )
|
||||||
|
{
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
state = S_CHAR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( c1 == '(' || c1 == ')' )
|
||||||
|
TryCollapse( out, c1 );
|
||||||
|
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case S_LINE_CMT:
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
if( c1 == '\n' )
|
||||||
|
state = S_NORMAL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_BLOCK_CMT:
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
if( c1 == '*' && c2 == '/' )
|
||||||
|
{
|
||||||
|
if( !BufPutChar( out, c2 ))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_STRING:
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
if( c1 == '\\' && c2 )
|
||||||
|
{
|
||||||
|
if( !BufPutChar( out, c2 ))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if( c1 == '"' )
|
||||||
|
{
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
else if( c1 == '\n' )
|
||||||
|
{
|
||||||
|
// unterminated; recover at next line
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_CHAR:
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
if( c1 == '\\' && c2 )
|
||||||
|
{
|
||||||
|
if( !BufPutChar( out, c2 ))
|
||||||
|
return false;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if( c1 == '\'' )
|
||||||
|
{
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
else if( c1 == '\n' )
|
||||||
|
{
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_RAW:
|
||||||
|
if( !BufPutChar( out, c1 ))
|
||||||
|
return false;
|
||||||
|
if( c1 == ')'
|
||||||
|
&& i + 1 + raw_dlen + 1 <= in_len
|
||||||
|
&& memcmp( in + i + 1, raw_delim, raw_dlen ) == 0
|
||||||
|
&& in[i + 1 + raw_dlen] == '"' )
|
||||||
|
{
|
||||||
|
if( !BufPutMem( out, in + i + 1, raw_dlen + 1 ))
|
||||||
|
return false;
|
||||||
|
i += raw_dlen + 1;
|
||||||
|
state = S_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
67
utils/xash-clang-format/version.c
Normal file
67
utils/xash-clang-format/version.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
version.c - clang-format version probe
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
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 "crtlib.h"
|
||||||
|
#include "xcf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ParseVersion
|
||||||
|
|
||||||
|
Parse the major version number out of a `clang-format --version`
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean ParseVersion( const char *s, int *major )
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
if( COM_StringEmptyOrNULL( s ) || !major )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// anchor on "version "
|
||||||
|
p = Q_strstr( s, "version " );
|
||||||
|
if( !p )
|
||||||
|
return false;
|
||||||
|
p += sizeof( "version " ) - 1;
|
||||||
|
|
||||||
|
while( *p == ' ' || *p == '\t' )
|
||||||
|
p++;
|
||||||
|
if( *p < '0' || *p > '9' )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*major = Q_atoi( p );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
ClangFormatVersion
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean ClangFormatVersion( const char *exe, int *major )
|
||||||
|
{
|
||||||
|
char *argv[] = { (char *)exe, (char *)"--version", NULL };
|
||||||
|
xcf_buf_t out = { 0 };
|
||||||
|
qboolean ok = false;
|
||||||
|
|
||||||
|
if( !exe || !major )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( RunCapture( argv, &out, true ) == 0 && BufPutChar( &out, 0 ))
|
||||||
|
ok = ParseVersion( out.data, major );
|
||||||
|
|
||||||
|
BufFree( &out );
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
42
utils/xash-clang-format/wscript
Normal file
42
utils/xash-clang-format/wscript
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
def options(opt):
|
||||||
|
grp = opt.get_option_group('Utilities options')
|
||||||
|
|
||||||
|
grp.add_option('--disable-utils-xcf', action='store_true',
|
||||||
|
dest='DISABLE_UTILS_XCF', default = False,
|
||||||
|
help='disable xash-clang-format wrapper utility [default: %(default)s]')
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
# wrapper relies on POSIX, disable on Windows for now
|
||||||
|
conf.env.DISABLE_UTILS_XCF = conf.options.DISABLE_UTILS_XCF or conf.env.DEST_OS == 'win32'
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
if bld.env.DISABLE_UTILS_XCF:
|
||||||
|
return
|
||||||
|
|
||||||
|
bld.stlib(source = bld.path.ant_glob(['*.c', '*.h'], excl='main.c'),
|
||||||
|
target = 'xcflib',
|
||||||
|
features = 'format',
|
||||||
|
includes = '.',
|
||||||
|
use = 'public werror',
|
||||||
|
install_path = None)
|
||||||
|
|
||||||
|
bld.program(source = 'main.c',
|
||||||
|
target = 'xash-clang-format',
|
||||||
|
features = 'format',
|
||||||
|
use = 'xcflib werror',
|
||||||
|
install_path = bld.env.BINDIR,
|
||||||
|
subsystem = bld.env.CONSOLE_SUBSYSTEM)
|
||||||
|
|
||||||
|
if bld.env.TESTS:
|
||||||
|
tests = ['transform', 'config', 'version']
|
||||||
|
|
||||||
|
for name in tests:
|
||||||
|
bld.program(features = 'test format',
|
||||||
|
source = 'tests/test_%s.c' % name,
|
||||||
|
target = 'test_xcflib_%s' % name,
|
||||||
|
includes = '.',
|
||||||
|
use = 'xcflib werror',
|
||||||
|
install_path = None)
|
||||||
57
utils/xash-clang-format/xcf.h
Normal file
57
utils/xash-clang-format/xcf.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
xcf.h - clang-format wrapper internals
|
||||||
|
Copyright (C) 2026 Xash3D FWGS contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
#ifndef XCF_H
|
||||||
|
#define XCF_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "xash3d_types.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// transform.c
|
||||||
|
//
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
size_t cap;
|
||||||
|
} xcf_buf_t;
|
||||||
|
|
||||||
|
void BufFree( xcf_buf_t *b );
|
||||||
|
qboolean BufPutChar( xcf_buf_t *b, char c );
|
||||||
|
qboolean BufPutMem( xcf_buf_t *b, const char *src, size_t n );
|
||||||
|
qboolean Transform( const char *in, size_t in_len, xcf_buf_t *out );
|
||||||
|
|
||||||
|
//
|
||||||
|
// config.c
|
||||||
|
//
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
qboolean collapse_parens; // default: false
|
||||||
|
} xcf_config_t;
|
||||||
|
|
||||||
|
qboolean ParseConfigText( const char *text, xcf_config_t *cfg );
|
||||||
|
|
||||||
|
//
|
||||||
|
// version.c
|
||||||
|
//
|
||||||
|
qboolean ParseVersion( const char *s, int *major );
|
||||||
|
qboolean ClangFormatVersion( const char *exe, int *major );
|
||||||
|
|
||||||
|
//
|
||||||
|
// proc.c
|
||||||
|
//
|
||||||
|
int RunCapture( char *const argv[], xcf_buf_t *out, qboolean silent_stderr );
|
||||||
|
|
||||||
|
#endif // XCF_H
|
||||||
36
wscript
36
wscript
@@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
from waflib import Build, Context, Logs, TaskGen
|
from waflib import Build, Context, Logs, TaskGen
|
||||||
from waflib.Tools import waf_unit_test, c_tests
|
from waflib.Tools import waf_unit_test, c_tests
|
||||||
|
import clang_format
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
# we need version 17 minimum
|
||||||
|
clang_format.CLANG_FORMAT_MIN_MAJOR = 17
|
||||||
|
|
||||||
VERSION = '0.99'
|
VERSION = '0.99'
|
||||||
APPNAME = 'xash3d-fwgs'
|
APPNAME = 'xash3d-fwgs'
|
||||||
top = '.'
|
top = '.'
|
||||||
@@ -32,6 +36,24 @@ def apply_subsystem_msvc(self):
|
|||||||
if 'test' in self.features:
|
if 'test' in self.features:
|
||||||
self.subsystem = self.env.CONSOLE_SUBSYSTEM
|
self.subsystem = self.env.CONSOLE_SUBSYSTEM
|
||||||
|
|
||||||
|
@TaskGen.feature('format')
|
||||||
|
@TaskGen.after_method('make_format_tasks')
|
||||||
|
def use_xash_clang_format(self):
|
||||||
|
if self.bld.cmd != 'format':
|
||||||
|
return
|
||||||
|
|
||||||
|
wrapper = self.bld.bldnode.find_node('utils/xash-clang-format/xash-clang-format')
|
||||||
|
if not wrapper:
|
||||||
|
self.bld.fatal('no xash-clang-format found in build directory, reconfigure and rebuild with --enable-utils')
|
||||||
|
return
|
||||||
|
|
||||||
|
real_cf = clang_format.ClangFormatTask.binary
|
||||||
|
for t in self.tasks:
|
||||||
|
if isinstance(t, clang_format.ClangFormatTask):
|
||||||
|
t.binary = wrapper.abspath()
|
||||||
|
t.env.env = dict(os.environ, XASH_CLANG_FORMAT_BIN=real_cf)
|
||||||
|
|
||||||
|
|
||||||
class Subproject:
|
class Subproject:
|
||||||
def __init__(self, name, fnFilter = None):
|
def __init__(self, name, fnFilter = None):
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -85,6 +107,8 @@ SUBDIRS = [
|
|||||||
Subproject('stub/server'),
|
Subproject('stub/server'),
|
||||||
Subproject('3rdparty/libbacktrace'),
|
Subproject('3rdparty/libbacktrace'),
|
||||||
Subproject('3rdparty/library_suffix'),
|
Subproject('3rdparty/library_suffix'),
|
||||||
|
Subproject('3rdparty/yy-thunks'),
|
||||||
|
Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER),
|
||||||
|
|
||||||
# disable only by engine feature, makes no sense to even parse subprojects in dedicated mode
|
# disable only by engine feature, makes no sense to even parse subprojects in dedicated mode
|
||||||
Subproject('3rdparty/extras', lambda x: x.env.CLIENT and x.env.DEST_OS != 'android'),
|
Subproject('3rdparty/extras', lambda x: x.env.CLIENT and x.env.DEST_OS != 'android'),
|
||||||
@@ -94,6 +118,7 @@ SUBDIRS = [
|
|||||||
Subproject('ref/common', lambda x: x.env.CLIENT),
|
Subproject('ref/common', lambda x: x.env.CLIENT),
|
||||||
Subproject('ref/gl', lambda x: x.env.CLIENT and (x.env.GL or x.env.NANOGL or x.env.GLWES or x.env.GL4ES or x.env.GLES3COMPAT)),
|
Subproject('ref/gl', lambda x: x.env.CLIENT and (x.env.GL or x.env.NANOGL or x.env.GLWES or x.env.GL4ES or x.env.GLES3COMPAT)),
|
||||||
Subproject('ref/soft', lambda x: x.env.CLIENT and x.env.SOFT),
|
Subproject('ref/soft', lambda x: x.env.CLIENT and x.env.SOFT),
|
||||||
|
Subproject('ref/dx2', lambda x: x.env.CLIENT and x.env.DX2),
|
||||||
Subproject('ref/null', lambda x: x.env.CLIENT and x.env.NULL),
|
Subproject('ref/null', lambda x: x.env.CLIENT and x.env.NULL),
|
||||||
Subproject('3rdparty/bzip2', lambda x: x.env.CLIENT and not x.env.HAVE_SYSTEM_BZ2),
|
Subproject('3rdparty/bzip2', lambda x: x.env.CLIENT and not x.env.HAVE_SYSTEM_BZ2),
|
||||||
Subproject('3rdparty/mbedtls'),
|
Subproject('3rdparty/mbedtls'),
|
||||||
@@ -111,9 +136,9 @@ SUBDIRS = [
|
|||||||
Subproject('engine'), # keep latest for static linking
|
Subproject('engine'), # keep latest for static linking
|
||||||
|
|
||||||
# enabled optionally
|
# enabled optionally
|
||||||
Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS),
|
Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS),
|
||||||
Subproject('utils/xar', lambda x: x.env.ENABLE_UTILS and x.env.ENABLE_XAR),
|
Subproject('utils/xar', lambda x: x.env.ENABLE_UTILS and x.env.ENABLE_XAR),
|
||||||
Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER),
|
Subproject('utils/xash-clang-format', lambda x: x.env.ENABLE_UTILS),
|
||||||
|
|
||||||
# enabled on PSVita only
|
# enabled on PSVita only
|
||||||
Subproject('ref/gl/vgl_shim', lambda x: x.env.DEST_OS == 'psvita'),
|
Subproject('ref/gl/vgl_shim', lambda x: x.env.DEST_OS == 'psvita'),
|
||||||
@@ -127,6 +152,7 @@ REFDLLS = [
|
|||||||
RefDll('gl4es', False),
|
RefDll('gl4es', False),
|
||||||
RefDll('gles3compat', False, 'GLES3COMPAT'),
|
RefDll('gles3compat', False, 'GLES3COMPAT'),
|
||||||
RefDll('null', False),
|
RefDll('null', False),
|
||||||
|
RefDll('dx2', False),
|
||||||
]
|
]
|
||||||
|
|
||||||
def options(opt):
|
def options(opt):
|
||||||
@@ -235,7 +261,7 @@ def configure(conf):
|
|||||||
if conf.env.COMPILER_CC == 'msvc':
|
if conf.env.COMPILER_CC == 'msvc':
|
||||||
conf.load('msvc_pdb')
|
conf.load('msvc_pdb')
|
||||||
|
|
||||||
conf.load('msvs subproject clang_compilation_database strip_on_install waf_unit_test enforce_pic force_32bit ninja')
|
conf.load('msvs subproject clang_compilation_database strip_on_install waf_unit_test enforce_pic force_32bit ninja clang_format')
|
||||||
|
|
||||||
conf.env.MSVC_SUBSYSTEM = 'WINDOWS'
|
conf.env.MSVC_SUBSYSTEM = 'WINDOWS'
|
||||||
conf.env.CONSOLE_SUBSYSTEM = 'CONSOLE'
|
conf.env.CONSOLE_SUBSYSTEM = 'CONSOLE'
|
||||||
@@ -471,7 +497,7 @@ def configure(conf):
|
|||||||
# Don't check them more than once, to save time
|
# Don't check them more than once, to save time
|
||||||
# Usually, they are always available
|
# Usually, they are always available
|
||||||
# but we need them in uselib
|
# but we need them in uselib
|
||||||
a = [ 'user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp', 'psapi', 'ws2_32', 'bcrypt' ]
|
a = [ 'user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp', 'psapi', 'ws2_32', 'bcrypt', 'ddraw' ]
|
||||||
if conf.env.COMPILER_CC == 'msvc':
|
if conf.env.COMPILER_CC == 'msvc':
|
||||||
for i in a:
|
for i in a:
|
||||||
conf.start_msg('Checking for MSVC library')
|
conf.start_msg('Checking for MSVC library')
|
||||||
|
|||||||
Reference in New Issue
Block a user