From 0daf12924dced6614ec2634e611bb66147ce27e0 Mon Sep 17 00:00:00 2001 From: s1lent Date: Tue, 5 Dec 2017 22:55:45 +0700 Subject: [PATCH 1/4] Add project FileSystem_Stdio --- msvc/ReHLDS.sln | 24 + publish.gradle | 4 + .../filesystem/FileSystem_Stdio/build.gradle | 158 ++ .../FileSystem_Stdio/msvc/PostBuild.bat | 39 + .../msvc/filesystem_stdio.sln | 22 + .../msvc/filesystem_stdio.vcxproj | 174 ++ .../msvc/filesystem_stdio.vcxproj.filters | 60 + .../FileSystem_Stdio/src/BaseFileSystem.cpp | 1676 ++++++++++++++ .../FileSystem_Stdio/src/BaseFileSystem.h | 378 +++ .../FileSystem_Stdio/src/FileSystem_Stdio.cpp | 232 ++ .../FileSystem_Stdio/src/FileSystem_Stdio.h | 91 + .../src/filesystem_helpers.cpp | 178 ++ .../FileSystem_Stdio/src/filesystem_helpers.h | 36 + .../FileSystem_Stdio/src/linux_support.cpp | 258 +++ .../FileSystem_Stdio/src/linux_support.h | 62 + .../FileSystem_Stdio/src/pathmatch.cpp | 898 ++++++++ .../src/pathmatch_casefolding.h | 2031 +++++++++++++++++ .../FileSystem_Stdio/src/precompiled.cpp | 29 + .../FileSystem_Stdio/src/precompiled.h | 40 + .../src/public_amalgamation.cpp | 7 + rehlds/filesystem/build.gradle | 5 + rehlds/hookers/filesystem/hooklist.cpp | 182 ++ rehlds/hookers/filesystem/hooklist.h | 42 + rehlds/hookers/filesystem/main.cpp | 86 + rehlds/hookers/helper.h | 18 +- rehlds/public/FileSystem.h | 14 +- rehlds/public/interface.h | 2 +- rehlds/public/tier0/characterset.cpp | 50 + rehlds/public/tier0/characterset.h | 46 + rehlds/public/tier0/dbg.h | 40 +- rehlds/public/utlmemory.h | 27 +- rehlds/public/utlrbtree.h | 1223 +++++----- rehlds/public/utlsymbol.cpp | 152 ++ rehlds/public/utlsymbol.h | 113 + rehlds/public/utlvector.h | 452 ++-- settings.gradle | 1 + shared_gcc.gradle | 2 +- shared_icc.gradle | 2 +- 38 files changed, 7896 insertions(+), 958 deletions(-) create mode 100644 rehlds/filesystem/FileSystem_Stdio/build.gradle create mode 100644 rehlds/filesystem/FileSystem_Stdio/msvc/PostBuild.bat create mode 100644 rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.sln create mode 100644 rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj create mode 100644 rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj.filters create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/linux_support.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/linux_support.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/pathmatch.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/pathmatch_casefolding.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/precompiled.cpp create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/precompiled.h create mode 100644 rehlds/filesystem/FileSystem_Stdio/src/public_amalgamation.cpp create mode 100644 rehlds/filesystem/build.gradle create mode 100644 rehlds/hookers/filesystem/hooklist.cpp create mode 100644 rehlds/hookers/filesystem/hooklist.h create mode 100644 rehlds/hookers/filesystem/main.cpp create mode 100644 rehlds/public/tier0/characterset.cpp create mode 100644 rehlds/public/tier0/characterset.h create mode 100644 rehlds/public/utlsymbol.cpp create mode 100644 rehlds/public/utlsymbol.h diff --git a/msvc/ReHLDS.sln b/msvc/ReHLDS.sln index e5ede79..c8e3daa 100644 --- a/msvc/ReHLDS.sln +++ b/msvc/ReHLDS.sln @@ -38,6 +38,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Director", "..\rehlds\HLTV\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proxy", "..\rehlds\HLTV\Proxy\msvc\Proxy.vcxproj", "{ADDFF069-D39D-4A1B-87C9-85A62B980547}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filesystem_stdio", "..\rehlds\filesystem\FileSystem_Stdio\msvc\filesystem_stdio.vcxproj", "{A428392F-52FB-489E-87D8-623528C7F4F9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Play|Win32 = Debug Play|Win32 @@ -251,6 +253,28 @@ Global {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Test Fixes|Win32.Build.0 = Release|Win32 {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Tests|Win32.ActiveCfg = Release|Win32 {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Tests|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Play|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Play|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Record|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Record|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Swds Play|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Swds Play|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Swds|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug Swds|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Play|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Play|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Swds Play|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Swds Play|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Swds|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release Swds|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Test Fixes|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Test Fixes|Win32.Build.0 = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Tests|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Tests|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/publish.gradle b/publish.gradle index e46f830..6e94cb5 100644 --- a/publish.gradle +++ b/publish.gradle @@ -62,6 +62,10 @@ task publishPrepareFiles { _copyFile('publish/director.dll', 'publish/publishRoot/bin/win32/valve/dlls/director.dll') _copyFile('publish/director.so', 'publish/publishRoot/bin/linux32/valve/dlls/director.so') + // FileSystem binaries + _copyFile('publish/filesystem_stdio.dll', 'publish/publishRoot/bin/win32/filesystem_stdio.dll') + _copyFile('publish/filesystem_stdio.so', 'publish/publishRoot/bin/linux32/filesystem_stdio.so') + // hlsdk project.file('publish/publishRoot/hlsdk').mkdirs() copy { diff --git a/rehlds/filesystem/FileSystem_Stdio/build.gradle b/rehlds/filesystem/FileSystem_Stdio/build.gradle new file mode 100644 index 0000000..93910cc --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/build.gradle @@ -0,0 +1,158 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.doomedsociety.gradlecpp.GradleCppUtils +import org.gradle.nativeplatform.NativeBinarySpec +import org.gradle.nativeplatform.NativeLibrarySpec +import org.gradle.nativeplatform.toolchain.VisualCpp + +apply plugin: 'cpp' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +void setupToolchain(NativeBinarySpec b) { + boolean useGcc = project.hasProperty("useGcc") + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/../..', '/src', '/../../common', '/../../public', '/../../public/rehlds'); + cfg.singleDefines 'USE_BREAKPAD_HANDLER' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'filesystem_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF' + } + else if (cfg instanceof GccToolchainConfig) { + if (!useGcc) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'filesystem_pch' + ); + } + + cfg.compilerOptions.languageStandard = 'c++11' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + '_unlink': 'unlink', + ]); + + if (useGcc) { + // Produce code optimized for the most common IA32/AMD64/EM64T processors. + // As new processors are deployed in the marketplace, the behavior of this option will change. + cfg.compilerOptions.args '-mtune=generic', '-Wno-write-strings', '-msse3', '-flto' + } else { + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp' + } + + cfg.compilerOptions.args '-fno-exceptions' + cfg.compilerOptions.interProceduralOptimizations = false; + cfg.linkerOptions.interProceduralOptimizations = false; + + def funcToWrap = [ + "freopen", "fopen", "fopen64", "open", "open64", "creat", "access", + "stat", "lstat", "scandir", "opendir", "__xstat", "__lxstat", "__xstat64", "__lxstat64", + "chmod", "chown", "lchown", "symlink", "link", "mknod", "mount", "unlink", + "mkfifo", "rename", "utime", "utimes", "mkdir", "rmdir" + ]; + + funcToWrap.each { + cfg.linkerOptions.args "-Wl,-wrap," + it + } + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + if (project.hasProperty("useGcc")) { + gcc(Gcc) + } else { + icc(Icc) + } + } + + components { + filesystem(NativeLibrarySpec) { + targetPlatform 'x86' + baseName 'filesystem_stdio' + + sources { + filesystem_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + } + + filesystem_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + } + } + } + + binaries.all { + NativeBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFinalize << { + if (GradleCppUtils.windows) { + return; + } + + binaries.withType(SharedLibraryBinarySpec) { + def sharedBinary = it.getSharedLibraryFile(); + if (sharedBinary.exists()) { + sharedBinary.renameTo(new File(sharedBinary.getParent() + "/" + sharedBinary.getName().replaceFirst("^lib", ""))); + } + } +} + +task buildFixes { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +build.finalizedBy(buildFinalize); +buildFixes.finalizedBy(buildFinalize); +buildRelease.finalizedBy(buildFinalize); + +// prevent static lib building +binaries.withType(StaticLibraryBinarySpec) { binary -> + buildable = false +} diff --git a/rehlds/filesystem/FileSystem_Stdio/msvc/PostBuild.bat b/rehlds/filesystem/FileSystem_Stdio/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.sln b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.sln new file mode 100644 index 0000000..8652651 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filesystem_stdio", "filesystem_stdio.vcxproj", "{A428392F-52FB-489E-87D8-623528C7F4F9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug|Win32.ActiveCfg = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Debug|Win32.Build.0 = Debug|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release|Win32.ActiveCfg = Release|Win32 + {A428392F-52FB-489E-87D8-623528C7F4F9}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj new file mode 100644 index 0000000..fbf39c2 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A428392F-52FB-489E-87D8-623528C7F4F9} + Win32Proj + filesystem_stdio + 8.1 + + + + DynamicLibrary + true + v120_xp + v140_xp + v141_xp + MultiByte + + + DynamicLibrary + false + v120_xp + v140_xp + v141_xp + true + MultiByte + + + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_WINDOWS;USE_BREAKPAD_HANDLER;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) + precompiled.h + $(ProjectDir)..\src;$(ProjectDir)..\..\..\;$(ProjectDir)..\..\..\common;$(ProjectDir)..\..\..\public;$(ProjectDir)..\..\..\public\rehlds;%(AdditionalIncludeDirectories) + MultiThreadedDebug + EnableFastChecks + false + EditAndContinue + Default + false + + + + + Windows + true + psapi.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + build.always.run + + + build.always.run + + + + + Level3 + Use + MinSpace + true + false + WIN32;_WINDOWS;USE_BREAKPAD_HANDLER;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) + precompiled.h + $(ProjectDir)..\src;$(ProjectDir)..\..\..\;$(ProjectDir)..\..\..\common;$(ProjectDir)..\..\..\public;$(ProjectDir)..\..\..\public\rehlds;%(AdditionalIncludeDirectories) + MultiThreaded + Disabled + + + Windows + true + true + true + psapi.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + build.always.run + + + build.always.run + + + + + true + true + + + true + true + + + + + + true + true + + + true + true + + + Create + Create + + + + + + true + true + + + + + + true + true + + + + + + + \ No newline at end of file diff --git a/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj.filters b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj.filters new file mode 100644 index 0000000..5f4abdf --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/msvc/filesystem_stdio.vcxproj.filters @@ -0,0 +1,60 @@ + + + + + {3f7611e3-cf43-4956-a8a1-b6efbcf1a63d} + + + {cacc7b07-9ac2-42d5-bba3-14c061a19d11} + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + hookers + + + hookers + + + + + src + + + src + + + src + + + src + + + hookers + + + src + + + \ No newline at end of file diff --git a/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.cpp b/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.cpp new file mode 100644 index 0000000..cc94e93 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.cpp @@ -0,0 +1,1676 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +float g_flDummyFloat = 1.0f; +CBaseFileSystem *CBaseFileSystem::s_pFileSystem; + +CBaseFileSystem *BaseFileSystem() +{ + return CBaseFileSystem::s_pFileSystem; +} + +CBaseFileSystem::CBaseFileSystem() +{ + s_pFileSystem = this; + + m_fwLevel = FILESYSTEM_WARNING_REPORTUNCLOSED; + m_pfnWarning = nullptr; + m_nOpenCount = 0; +} + +void CBaseFileSystem::Mount() +{ + g_flDummyFloat += g_flDummyFloat; +} + +void CBaseFileSystem::Unmount() +{ + ; +} + +void CBaseFileSystem::CreateDirHierarchy(const char *path, const char *pathID) +{ + CSearchPath *searchPath = GetWritePath(pathID); + int len = Q_strlen(searchPath->GetPath().String()) + Q_strlen(path); + char *buf = (char *)alloca((len + 1) * sizeof(char)); + + Q_strcpy(buf, searchPath->GetPath().String()); + Q_strcat(buf, path); + + FixSlashes(buf); + + char *s = buf; + char *end = &buf[len]; + while (s < end) + { + if (*s == CORRECT_PATH_SEPARATOR) { + *s = '\0'; +#ifdef _WIN32 + _mkdir(buf); +#else + mkdir(buf, S_IRWXU | S_IRGRP | S_IROTH); // owner has rwx, rest have r +#endif // _WIN32 + + *s = CORRECT_PATH_SEPARATOR; + } + + s++; + } + +#ifdef _WIN32 + _mkdir(buf); +#else + mkdir(buf, S_IRWXU | S_IRGRP | S_IROTH); // owner has rwx, rest have r +#endif // _WIN32 +} + +void CBaseFileSystem::PrintOpenedFiles() +{ + Trace_DumpUnclosedFiles(); +} + +bool CBaseFileSystem::GetCurrentDirectory(char *pDirectory, int maxlen) +{ +#ifdef _WIN32 + if (!::GetCurrentDirectoryA(maxlen, pDirectory)) +#else + if (!getcwd(pDirectory, maxlen)) +#endif // _WIN32 + return false; + + FixSlashes(pDirectory); + + // Strip the last slash + int len = Q_strlen(pDirectory); + if (pDirectory[len - 1] == CORRECT_PATH_SEPARATOR) { + pDirectory[len - 1] = '\0'; + } + + return true; +} + +void CBaseFileSystem::AddSearchPathNoWrite(const char *pPath, const char *pathID) +{ + AddSearchPathInternal(pPath, pathID, false); +} + +// Create the search path +void CBaseFileSystem::AddSearchPath(const char *pPath, const char *pathID) +{ + AddSearchPathInternal(pPath, pathID, true); +} + +// This is where search paths are created. Map files are created at head of list +// (they occur after file system paths have already been set) so they get highest priority. +// Otherwise, we add the disk (non-packfile) path and then the paks if they exist for the path. +void CBaseFileSystem::AddSearchPathInternal(const char *pPath, const char *pathID, bool bAllowWrite) +{ + if (Q_strstr(pPath, ".bsp")) { + return; + } + + // Clean up the name + char *newPath = (char *)alloca((MAX_PATH + 1) * sizeof(char)); + +#ifdef _WIN32 + if (Q_strchr(pPath, ':')) +#else + if (pPath && *pPath == '/') +#endif // _WIN32 + { + Q_strcpy(newPath, pPath); + } + else + { + GetCurrentDirectory(newPath, MAX_PATH); + FixPath(newPath); + + if (Q_strcmp(pPath, ".") != 0) { + Q_strcat(newPath, pPath); + } + } + +#ifdef _WIN32 // don't do this on linux! + Q_strlwr(newPath); +#endif // _WIN32 + + FixPath(newPath); + + // Make sure that it doesn't already exist + CUtlSymbol pathSymbol(newPath); + CUtlSymbol pathIDSymbol(pathID); + + int c = m_SearchPaths.Count(); + for (int i = 0; i < c; i++) + { + CSearchPath *pSearchPath = &m_SearchPaths[i]; + if (pSearchPath->GetPath() == pathSymbol && pSearchPath->GetPathID() == pathIDSymbol) { + // this entry is already at the head + return; + } + } + + // Add to list + int newIndex = m_SearchPaths.AddToTail(); + + CSearchPath *sp = &m_SearchPaths[newIndex]; + sp->SetPath(pathSymbol); + sp->SetPathID(pathIDSymbol); + sp->SetWritePath(bAllowWrite); + + // Add pack files for this path next + AddPackFiles(newPath); +} + +// Returns true on success, false on failure +bool CBaseFileSystem::RemoveSearchPath(const char *pPath) +{ + char *newPath = (char *)alloca((Q_strlen(pPath) + 1) * sizeof(char)); + +#ifdef _WIN32 + if (Q_strchr(pPath, ':')) +#else + if (pPath && *pPath == '/') +#endif // _WIN32 + { + Q_strcpy(newPath, pPath); + } + else + { + GetCurrentDirectory(newPath, MAX_PATH); + FixPath(newPath); + if (Q_strcmp(pPath, ".")) { + Q_strcat(newPath, pPath); + } + } + +#ifdef _WIN32 // don't do this on linux! + Q_strlwr(newPath); +#endif // _WIN32 + + FixPath(newPath); + + CUtlSymbol lookup(newPath); + + bool bret = false; + + // Count backward since we're possibly deleting one or more pack files, too + int i; + int c = m_SearchPaths.Count(); + for (i = c - 1; i >= 0; i--) + { + if (m_SearchPaths[i].GetPath() != lookup) + continue; + + m_SearchPaths.Remove(i); + bret = true; + } + + return bret; +} + +// Finds a search path that should be used for writing to, given a pathID +CBaseFileSystem::CSearchPath *CBaseFileSystem::GetWritePath(const char *pathID) +{ + int iPath = 0; + CSearchPath *searchPath = m_SearchPaths.Base(); + while (searchPath && iPath < m_SearchPaths.Count()) + { + if (searchPath->IsAllowWrite()) { + break; + } + + searchPath = &m_SearchPaths[iPath++]; + } + + if (!pathID || m_SearchPaths.Count() <= 0) { + return searchPath; + } + + CUtlSymbol lookup(pathID); + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + if (m_SearchPaths[i].GetPathID() == lookup) { + return &m_SearchPaths[i]; + } + } + + return searchPath; +} + +// Returns true on success, false on failure +bool CBaseFileSystem::IsDirectory(const char *pFileName) +{ + // Allow for UNC-type syntax to specify the path ID. + struct _stat buf; + + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + CSearchPath *sp = &m_SearchPaths[i]; + if (!sp->IsPackFile()) + { + int len = Q_strlen(sp->GetPath().String()) + Q_strlen(pFileName); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, sp->GetPath().String()); + Q_strcat(pTmpFileName, pFileName); + + FixSlashes(pTmpFileName); + + if (FS_stat(pTmpFileName, &buf) != -1) + { + if (buf.st_mode & _S_IFDIR) + return true; + } + } + } + + return false; +} + +// The base file search goes through here +FileHandle_t CBaseFileSystem::FindFile(CSearchPath *path, const char *pFileName, const char *pOptions, bool bFromCache) +{ + CFileHandle *fh = nullptr; + if (path->IsPackFile()) + { + char *temp = (char *)alloca((Q_strlen(pFileName) + 1) * sizeof(char)); + + Q_strcpy(temp, pFileName); + Q_strlwr(temp); + FixSlashes(temp); + + CPackFileEntry search; + search.m_Name = temp; + search.m_nPosition = 0; + search.m_nLength = 0; + + int searchresult = path->m_PackFiles.Find(search); + if (searchresult == path->m_PackFiles.InvalidIndex()) { + return FILESYSTEM_INVALID_HANDLE; + } + + CPackFileEntry result = path->m_PackFiles[searchresult]; + FS_fseek(path->m_hPackFile->m_pFile, path->m_hPackFile->m_nStartOffset + result.m_nPosition, FILESYSTEM_SEEK_HEAD); + + fh = new CFileHandle; + fh->m_pFile = path->m_hPackFile->m_pFile; + fh->m_nStartOffset = result.m_nPosition; + fh->m_nLength = result.m_nLength; + fh->m_nFileTime = path->m_lPackFileTime; + fh->m_bPack = true; + fh->m_bErrorFlagged = false; + } + else if ((!Q_strchr(pOptions, 'w') && !Q_strchr(pOptions, '+')) || path->IsAllowWrite()) + { + int len = Q_strlen(path->GetPath().String()) + Q_strlen(pFileName); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, path->GetPath().String()); + Q_strcat(pTmpFileName, pFileName); + + FixSlashes(pTmpFileName); + + FILE *fp = Trace_FOpen(pTmpFileName, pOptions, bFromCache); + if (!fp) { + return FILESYSTEM_INVALID_HANDLE; + } + + struct _stat buf; + if (FS_stat(pTmpFileName, &buf) == -1) { + Warning(FILESYSTEM_WARNING, "_stat on file %s which appeared to exist failed!!!\n", pTmpFileName); + } + + fh = new CFileHandle; + fh->m_pFile = fp; + fh->m_nStartOffset = 0; + fh->m_nLength = buf.st_size; + fh->m_nFileTime = Q_max(buf.st_ctime, buf.st_mtime); + fh->m_bPack = false; + fh->m_bErrorFlagged = false; + } + + return reinterpret_cast(fh); +} + +// Returns true on success, false on failure. +bool CBaseFileSystem::FileExists(const char *pFileName) +{ + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + int size = FastFindFileSize(&m_SearchPaths[i], pFileName); + if (size != -1) { + return true; + } + } + + return false; +} + +FileHandle_t CBaseFileSystem::Open(const char *pFileName, const char *pOptions, const char *pathID) +{ + // Try each of the search paths in succession + // FIXME: call createdirhierarchy upon opening for write. + if (Q_strstr(pOptions, "r") && !Q_strstr(pOptions, "+")) + { + CUtlSymbol lookup(pathID); + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + if (pathID && m_SearchPaths[i].GetPathID() != lookup) { + continue; + } + + FileHandle_t filehandle = FindFile(&m_SearchPaths[i], pFileName, pOptions); + if (filehandle != FILESYSTEM_INVALID_HANDLE) { + return filehandle; + } + } + + return FILESYSTEM_INVALID_HANDLE; + } + + CSearchPath *searchPath = GetWritePath(pathID); + int len = Q_strlen(searchPath->GetPath().String()) + Q_strlen(pFileName); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + + Q_strcpy(pTmpFileName, searchPath->GetPath().String()); + Q_strcat(pTmpFileName, pFileName); + + FixSlashes(pTmpFileName); + + FILE *fp = Trace_FOpen(pTmpFileName, pOptions); + if (!fp) { + return FILESYSTEM_INVALID_HANDLE; + } + + struct _stat buf; + if (FS_stat(pTmpFileName, &buf) == -1) { + Warning(FILESYSTEM_WARNING, "_stat on file %s which appeared to exist failed!!!\n", pTmpFileName); + } + + CFileHandle *fh; + + fh = new CFileHandle; + fh->m_pFile = fp; + fh->m_nStartOffset = 0; + fh->m_nLength = buf.st_size; + fh->m_nFileTime = buf.st_mtime; + fh->m_bPack = false; + fh->m_bErrorFlagged = false; + + return reinterpret_cast(fh); +} + +FileHandle_t CBaseFileSystem::OpenFromCacheForRead(const char *pFileName, const char *pOptions, const char *pathID) +{ + CUtlSymbol lookup(pathID); + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + if (pathID && m_SearchPaths[i].GetPathID() != lookup) { + continue; + } + + FileHandle_t filehandle = FindFile(&m_SearchPaths[i], pFileName, pOptions, true); + if (filehandle != FILESYSTEM_INVALID_HANDLE) { + return filehandle; + } + } + + return FILESYSTEM_INVALID_HANDLE; +} + +void CBaseFileSystem::Close(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Close NULL file handle!\n"); + return; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Close NULL file pointer inside valid file handle!\n"); + return; + } + + // found handle in packfiles, we shouldn't release handle + if (m_PackFileHandles.Find(fh->m_pFile) != m_PackFileHandles.InvalidIndex()) { + return; + } + + Trace_FClose(fh->m_pFile); + fh->m_pFile = nullptr; + delete fh; +} + +void CBaseFileSystem::Seek(FileHandle_t file, int pos, FileSystemSeek_t whence) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "Tried to Seek NULL file handle!\n"); + return; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Seek NULL file pointer inside valid file handle!\n"); + return; + } + + // check bounds + FileSystemSeek_t seekType = whence; + if (seekType < FILESYSTEM_SEEK_HEAD || seekType > FILESYSTEM_SEEK_TAIL) + seekType = FILESYSTEM_SEEK_HEAD; + + if (!fh->m_bPack) + { + FS_fseek(fh->m_pFile, pos, seekType); + return; + } + + if (whence == FILESYSTEM_SEEK_CURRENT) + { + FS_fseek(fh->m_pFile, pos, seekType); + return; + } + + if (whence == FILESYSTEM_SEEK_HEAD) + { + FS_fseek(fh->m_pFile, pos + fh->m_nStartOffset, seekType); + return; + } + + FS_fseek(fh->m_pFile, pos + fh->m_nStartOffset + fh->m_nLength, seekType); +} + +size_t CBaseFileSystem::Tell(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Tell NULL file handle!\n"); + return 0; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Tell NULL file pointer inside valid file handle!\n"); + return 0; + } + + // Pack files are relative + return FS_ftell(fh->m_pFile) - (size_t)fh->m_nStartOffset; +} + +size_t CBaseFileSystem::Size(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Size NULL file handle!\n"); + return 0; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Size NULL file pointer inside valid file handle!\n"); + return 0; + } + + return fh->m_nLength; +} + +size_t CBaseFileSystem::Size(const char *pFileName) +{ + int i = 0; + while (i < m_SearchPaths.Count()) + { + int size = FastFindFileSize(&m_SearchPaths[i], pFileName); + if (size != -1) { + return size; + } + + i++; + } + + return -1; +} + +int CBaseFileSystem::FastFindFileSize(CSearchPath *path, const char *pFileName) +{ + if (path->IsPackFile()) + { + char *temp = (char *)alloca((Q_strlen(pFileName) + 1) * sizeof(char)); + + Q_strcpy(temp, pFileName); + Q_strlwr(temp); + FixSlashes(temp); + + CPackFileEntry search; + search.m_Name = temp; + search.m_nPosition = 0; + search.m_nLength = 0; + + int searchresult = path->m_PackFiles.Find(search); + if (searchresult == path->m_PackFiles.InvalidIndex()) { + return -1; + } + + return path->m_PackFiles[searchresult].m_nLength; + } + + // Is it an absolute path? + char pTmpFileName[MAX_PATH]; + Q_strlcpy(pTmpFileName, path->GetPath().String()); + Q_strlcat(pTmpFileName, pFileName); + FixSlashes(pTmpFileName); + + struct _stat buf; + if (FS_stat(pTmpFileName, &buf) == -1) { + return -1; + } + + return buf.st_size; +} + +bool CBaseFileSystem::EndOfFile(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to EndOfFile NULL file handle!\n"); + return true; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to EndOfFile NULL file pointer inside valid file handle!\n"); + return true; + } + + if (fh->m_bPack) + { + if (FS_ftell(fh->m_pFile) >= (fh->m_nStartOffset + fh->m_nLength)) { + return true; + } + + return false; + } + + if (FS_feof(fh->m_pFile)) { + return true; + } + + return false; +} + +int CBaseFileSystem::Read(void *pOutput, int size, FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Read NULL file handle!\n"); + return 0; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Read NULL file pointer inside valid file handle!\n"); + return 0; + } + + int result = FS_fread(pOutput, 1, size, fh->m_pFile); + if (result != size) { + fh->m_bErrorFlagged = true; + } + + return result; +} + +int CBaseFileSystem::Write(const void *pInput, int size, FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Write NULL file handle!\n"); + return 0; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Write NULL file pointer inside valid file handle!\n"); + return 0; + } + + return FS_fwrite(pInput, 1, size, fh->m_pFile); +} + +int CBaseFileSystem::FPrintf(FileHandle_t file, const char *fmt, ...) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to FS: fprintf NULL file handle!\n"); + return 0; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to FS: fprintf NULL file pointer inside valid file handle!\n"); + return 0; + } + + va_list argptr; + va_start(argptr, fmt); + int len = FS_vfprintf(fh->m_pFile, fmt, argptr); + va_end(argptr); + + return len; +} + +void *CBaseFileSystem::GetReadBuffer(FileHandle_t file, int *outBufferSize, bool failIfNotInCache) +{ + *outBufferSize = 0; + return nullptr; +} + +void CBaseFileSystem::ReleaseReadBuffer(FileHandle_t file, void *readBuffer) +{ + ; +} + +bool CBaseFileSystem::IsOk(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to IsOk NULL file handle!\n"); + return false; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to IsOk NULL file pointer inside valid file handle!\n"); + return false; + } + + if (fh->m_bErrorFlagged) { + return false; + } + + return FS_ferror(fh->m_pFile) == 0 ? true : false; +} + +void CBaseFileSystem::Flush(FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Flush NULL file handle!\n"); + return; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to Flush NULL file pointer inside valid file handle!\n"); + return; + } + + FS_fflush(fh->m_pFile); +} + +char *CBaseFileSystem::ReadLine(char *pOutput, int maxChars, FileHandle_t file) +{ + CFileHandle *fh = reinterpret_cast(file); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to ReadLine NULL file handle!\n"); + return ""; + } + + if (!fh->m_pFile) { + Warning(FILESYSTEM_WARNING, "FS: Tried to ReadLine NULL file pointer inside valid file handle!\n"); + return ""; + } + + return FS_fgets(pOutput, maxChars, fh->m_pFile); +} + +// Deletes a file +void CBaseFileSystem::RemoveFile(const char *pRelativePath, const char *pathID) +{ + CSearchPath *searchPath = GetWritePath(pathID); + int len = Q_strlen(searchPath->GetPath().String()) + Q_strlen(pRelativePath); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + + Q_strcpy(pTmpFileName, searchPath->GetPath().String()); + Q_strcat(pTmpFileName, pRelativePath); + + FixSlashes(pTmpFileName); + _unlink(pTmpFileName); +} + +// Converts a partial path into a full path +// Relative paths that are pack based are returned as an absolute path .../zip?.zip/foo +// A pack absolute path can be sent back in for opening, and the file will be properly +// detected as pack based and mounted inside the pack. +const char *CBaseFileSystem::GetLocalPath(const char *pFileName, char *pLocalPath, int localPathBufferSize) +{ +#ifdef _WIN32 + if (Q_strchr(pFileName, ':')) +#else + if (pFileName && *pFileName == '/') +#endif // _WIN32 + { + Q_strncpy(pLocalPath, pFileName, localPathBufferSize); + pLocalPath[localPathBufferSize - 1] = '\0'; + FixSlashes(pLocalPath); + return pLocalPath; + } + + struct _stat buf; + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + CSearchPath *pSearchPath = &m_SearchPaths[i]; + if (pSearchPath->IsPackFile()) + { + char *temp = (char *)alloca((Q_strlen(pFileName) + 1) * sizeof(char)); + + Q_strcpy(temp, pFileName); + Q_strlwr(temp); + FixSlashes(temp); + + CPackFileEntry search; + search.m_Name = temp; + search.m_nPosition = 0; + search.m_nLength = 0; + + int searchresult = pSearchPath->m_PackFiles.Find(search); + if (searchresult != pSearchPath->m_PackFiles.InvalidIndex()) + { + int len = Q_strlen(pFileName); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, pFileName); + + Q_snprintf(pLocalPath, localPathBufferSize - 1, "%s%s", pSearchPath->GetPath().String(), pTmpFileName); + pLocalPath[localPathBufferSize - 1] = '\0'; + FixSlashes(pLocalPath); + return pLocalPath; + } + } + else + { + int len = Q_strlen(pSearchPath->GetPath().String()) + Q_strlen(pFileName); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, pSearchPath->GetPath().String()); + Q_strcat(pTmpFileName, pFileName); + FixSlashes(pTmpFileName); + + if (FS_stat(pTmpFileName, &buf) != -1) { + Q_strncpy(pLocalPath, pTmpFileName, localPathBufferSize); + pLocalPath[localPathBufferSize - 1] = '\0'; + return pLocalPath; + } + } + } + + return nullptr; +} + +FILE *CBaseFileSystem::Trace_FOpen(const char *filename, const char *options, bool bFromCache) +{ + FILE *fp = FS_fopen(filename, options, bFromCache); + if (!fp) { + return nullptr; + } + + COpenedFile file; + file.SetName(filename); + file.m_pFile = fp; + + m_OpenedFiles.AddToTail(file); + m_nOpenCount++; + + if (m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES) { + Warning(FILESYSTEM_WARNING_REPORTALLACCESSES, "---FS: open %s %p %i\n", filename, fp, m_nOpenCount); + } + + return fp; +} + +void CBaseFileSystem::Trace_FClose(FILE *fp) +{ + if (!fp) { + return; + } + + COpenedFile file; + file.m_pFile = fp; + + int result = m_OpenedFiles.Find(file); + if (result != m_OpenedFiles.InvalidIndex()) + { + m_nOpenCount--; + + COpenedFile found = m_OpenedFiles[result]; + if (m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES) { + Warning(FILESYSTEM_WARNING_REPORTALLACCESSES, "---FS: close %s %p %i\n", found.GetName(), fp, m_nOpenCount); + } + + m_OpenedFiles.FindAndRemove(found); + } + else + { + Assert(0); + + if (m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES) + { + Warning(FILESYSTEM_WARNING_REPORTALLACCESSES, "Tried to close unknown file pointer %p\n", fp); + } + } + + FS_fclose(fp); +} + +void CBaseFileSystem::Trace_DumpUnclosedFiles() +{ + for (int i = 0; i < m_OpenedFiles.Count(); i++) + { + if (m_fwLevel <= FILESYSTEM_WARNING_QUIET) { + continue; + } + + COpenedFile *found = &m_OpenedFiles[i]; + Warning(FILESYSTEM_WARNING_REPORTUNCLOSED, "File %s was never closed\n", found ? found->GetName() : "???"); + } +} + +bool CBaseFileSystem::AddPackFile(const char *pFileName, const char *pathID) +{ + return AddPackFileFromPath("", pFileName, true, pathID); +} + +// Adds a pack file from the specified path +// Returns true on success, false on failure +bool CBaseFileSystem::AddPackFileFromPath(const char *pPath, const char *pakfile, bool bCheckForAppendedPack, const char *pathID) +{ + char fullpath[MAX_PATH]; + Q_snprintf(fullpath, sizeof(fullpath), "%s%s", pPath, pakfile); + FixSlashes(fullpath); + + struct _stat buf; + if (FS_stat(fullpath, &buf) == -1) { + return false; + } + + CFileHandle *fh = new CFileHandle; + fh->m_nStartOffset = 0; + fh->m_nLength = 0; + fh->m_nFileTime = 0; + fh->m_bPack = false; + fh->m_bErrorFlagged = false; + fh->m_pFile = Trace_FOpen(fullpath, "rb"); + + // Add this pack file to the search path + int newIndex = m_SearchPaths.AddToTail(); + CSearchPath *sp = &m_SearchPaths[newIndex]; + sp->m_hPackFile = fh; + sp->m_Path = pPath; + sp->m_PathID = pathID; + sp->m_bIsPackFile = true; + sp->m_lPackFileTime = GetFileTime(pakfile); + + int64_t headeroffset = 0LL; + + // Get the length of the pack file + if (bCheckForAppendedPack) + { + // Get the length of the pack file: + packappenededheader_t appended; + FS_fseek(fh->m_pFile, -signed(sizeof(appended)), FILESYSTEM_SEEK_TAIL); + FS_fread(&appended, 1, sizeof(appended), fh->m_pFile); + + if (Q_strcmp(appended.id, "PACKAPPE") != 0) + { + m_SearchPaths.Remove(newIndex); + return false; + } + + headeroffset = appended.packheaderpos; + } + + if (!PreparePackFile(*sp, headeroffset)) + { + // Failed for some reason, ignore it + Trace_FClose(fh->m_pFile); + m_SearchPaths.Remove(newIndex); + return false; + } + + // Add handle to list + m_PackFileHandles.AddToTail(fh->m_pFile); + return true; +} + +const size_t MAX_DIR_PACK_FILES = 0x8000u; + +// Parse the pack file to build the file directory and preload section +bool CBaseFileSystem::PreparePackFile(CSearchPath &packfile, int64_t offsetofpackinmetafile) +{ + packheader_t header; + FS_fseek(packfile.m_hPackFile->m_pFile, packfile.m_hPackFile->m_nStartOffset + offsetofpackinmetafile, FILESYSTEM_SEEK_HEAD); + FS_fread(&header, 1, sizeof(header), packfile.m_hPackFile->m_pFile); + + if (*(uint32_t *)header.id == MAKEID('P', 'K', '6', '4')) { + return Prepare64BitPackFile(packfile, offsetofpackinmetafile); + } + + if (*(uint32_t *)header.id != MAKEID('P', 'A', 'C', 'K')) + { + Warning(FILESYSTEM_WARNING, "%s is not a packfile\n", packfile.GetPath().String()); + return false; + } + + int numpackfiles = header.dirlen / sizeof(packfile_t); + if (numpackfiles > MAX_DIR_PACK_FILES) { + Warning(FILESYSTEM_WARNING, "%s has %i files\n", packfile.GetPath().String(), numpackfiles); + return false; + } + + if (numpackfiles > 0) + { + packfile_t *newfiles = new packfile_t[numpackfiles]; + if (!newfiles) { + Warning(FILESYSTEM_WARNING, "%s out of memory allocating directory for %i files\n", packfile.GetPath().String(), numpackfiles); + return false; + } + + FS_fseek(packfile.m_hPackFile->m_pFile, packfile.m_hPackFile->m_nStartOffset + header.dirofs + offsetofpackinmetafile, FILESYSTEM_SEEK_HEAD); + Read(newfiles, header.dirlen, packfile.m_hPackFile->m_pFile); + + for (int i = 0; i < numpackfiles; i++) + { + Q_strlwr(newfiles[i].name); + FixSlashes(newfiles[i].name); + + CPackFileEntry lookup; + lookup.m_Name = newfiles[i].name; + lookup.m_nPosition = newfiles[i].filepos + offsetofpackinmetafile; + lookup.m_nLength = newfiles[i].filelen; + + packfile.m_PackFiles.Insert(lookup); + } + + packfile.m_nNumPackFiles = numpackfiles; + delete [] newfiles; + return true; + } + + return false; +} + +bool CBaseFileSystem::Prepare64BitPackFile(CSearchPath &packfile, int64_t offsetofpackinmetafile) +{ + packheader64_t header; + FS_fseek(packfile.m_hPackFile->m_pFile, packfile.m_hPackFile->m_nStartOffset + offsetofpackinmetafile, FILESYSTEM_SEEK_HEAD); + FS_fread(&header, 1, sizeof(header), packfile.m_hPackFile->m_pFile); + + if (*(uint32_t *)header.id != MAKEID('P', 'K', '6', '4')) + { + Warning(FILESYSTEM_WARNING, "%s is not a packfile\n", packfile.GetPath().String()); + return false; + } + + int numpackfiles = header.dirlen / sizeof(packfile64_t); + if (numpackfiles > MAX_DIR_PACK_FILES) { + Warning(FILESYSTEM_WARNING, "%s has %i files\n", packfile.GetPath().String(), numpackfiles); + return false; + } + + if (numpackfiles > 0) + { + packfile64_t *newfiles = new packfile64_t[numpackfiles]; + if (!newfiles) { + Warning(FILESYSTEM_WARNING, "%s out of memory allocating directory for %i files\n", packfile.GetPath().String(), numpackfiles); + return false; + } + + FS_fseek(packfile.m_hPackFile->m_pFile, packfile.m_hPackFile->m_nStartOffset + header.dirofs + offsetofpackinmetafile, FILESYSTEM_SEEK_HEAD); + Read(newfiles, header.dirlen, packfile.m_hPackFile->m_pFile); + + for (int i = 0; i < numpackfiles; i++) + { + Q_strlwr(newfiles[i].name); + FixSlashes(newfiles[i].name); + + CPackFileEntry lookup; + lookup.m_Name = newfiles[i].name; + lookup.m_nPosition = newfiles[i].filepos + offsetofpackinmetafile; + lookup.m_nLength = newfiles[i].filelen; + + packfile.m_PackFiles.Insert(lookup); + } + + packfile.m_nNumPackFiles = numpackfiles; + delete [] newfiles; + return true; + } + + return false; +} + +// Search pPath for pak?.pak files and add to search path if found +void CBaseFileSystem::AddPackFiles(const char *pPath) +{ + int pakcount; + // determine pak files, [pak0..pakN] + for (pakcount = 0; ; pakcount++) + { + char pakfile[MAX_PATH], fullpath[MAX_PATH]; + Q_sprintf(pakfile, "pak%i.pak", pakcount); + Q_sprintf(fullpath, "%s%s", pPath, pakfile); + FixSlashes(fullpath); + + struct _stat buf; + if (FS_stat(fullpath, &buf) == -1) + break; + } + + // Add any pack files in the format pak1.pak ... pak0.pak + // Add them backwards so pak(N) is higher priority than pak(N-1), etc. + for (int i = pakcount - 1; i >= 0; i--) + { + char pakfile[512]; + Q_sprintf(pakfile, "pak%i.pak", i); + AddPackFileFromPath(pPath, pakfile, false, ""); + } +} + +// Wipe all map (.bsp) pak file search paths +void CBaseFileSystem::RemoveAllMapSearchPaths() +{ + int c = m_SearchPaths.Count(); + for (int i = c - 1; i >= 0; i--) + { + if (!m_SearchPaths[i].IsMapPath()) + continue; + + m_SearchPaths.Remove(i); + } +} + +// Performs a simple case-insensitive string comparison, honoring trailing * wildcards +bool IsWildCardMatch(const char *wildcardString, const char *stringToCheck) +{ + char wcChar; + char strChar; + + if (!Q_strcmp(stringToCheck, ".") || !Q_strcmp(stringToCheck, "..")) + { + // ignore containing the levels directory + return false; + } + + if (!Q_strcmp(wildcardString, "*.*") || !Q_strcmp(wildcardString, "*")) + { + // matches everything + return true; + } + + // use the starMatchesZero variable to determine whether an asterisk + // matches zero or more characters (TRUE) or one or more characters (FALSE) + bool starMatchesZero = true; + + while ((strChar = *stringToCheck) && (wcChar = *wildcardString)) + { + // we only want to advance the pointers if we successfully assigned + // both of our char variables, so we'll do it here rather than in the + // loop condition itself + *stringToCheck++; + *wildcardString++; + + // if this isn't a case-sensitive match, make both chars uppercase + wcChar = toupper(wcChar); + strChar = toupper(strChar); + + // check the wcChar against our wildcard list + switch (wcChar) + { + // an asterisk matches zero or more characters + case '*' : + // do a recursive call against the rest of the string, + // until we've either found a match or the string has + // ended + if (starMatchesZero) + *stringToCheck--; + + while (*stringToCheck) + { + if (IsWildCardMatch(wildcardString, stringToCheck++)) + return true; + } + + break; + + // a question mark matches any single character + case '?' : + break; + + // if we fell through, we want an exact match + default : + if (wcChar != strChar) + return false; + break; + } + } + + // if we have any asterisks left at the end of the wildcard string, we can + // advance past them if starMatchesZero is TRUE (so "blah*" will match "blah") + while (*wildcardString && starMatchesZero) + { + if (*wildcardString == '*') + wildcardString++; + else + break; + } + + // if we got to the end but there's still stuff left in either of our strings, + // return false; otherwise, we have a match + if (*stringToCheck || *wildcardString) + return false; + else + return true; +} + +const char *CBaseFileSystem::SearchPakFile(const char *pWildCard, int currentSearchPathID, bool first) +{ + auto &curSearch = m_SearchPaths[currentSearchPathID]; + + if (first) { + curSearch.m_iCurSearchFile = 0; + } + + while (curSearch.m_iCurSearchFile < curSearch.m_PackFiles.Count()) + { + const char *file = curSearch.m_PackFiles[curSearch.m_iCurSearchFile++].m_Name.String(); + if (IsWildCardMatch(pWildCard, file)) { + return file; + } + } + + return nullptr; +} + +const char *CBaseFileSystem::FindFirst(const char *pWildCard, FileFindHandle_t *pHandle, const char *pathID) +{ + Assert(pWildCard); + Assert(pHandle); + + FileFindHandle_t hTmpHandle = m_FindData.AddToTail(); + FindData_t *pFindData = &m_FindData[hTmpHandle]; + Assert(pFindData); + + int maxlen = Q_strlen(pWildCard) + 1; + pFindData->m_WildCardString.AddMultipleToTail(maxlen); + Q_strcpy(pFindData->m_WildCardString.Base(), pWildCard); + FixSlashes(pFindData->m_WildCardString.Base()); + + CUtlSymbol lookup(pathID); + int c = m_SearchPaths.Count(); + for (pFindData->m_CurrentSearchPathID = 0; pFindData->m_CurrentSearchPathID < c; pFindData->m_CurrentSearchPathID++) + { + if (pathID && m_SearchPaths[pFindData->m_CurrentSearchPathID].GetPathID() != lookup) { + continue; + } + + pFindData->m_LimitedPathID = pathID ? (UtlSymId_t)lookup : -1; + + const char *fileName = FindFirstHelper(pWildCard, pHandle, pFindData->m_CurrentSearchPathID, pFindData); + if (fileName) { + return fileName; + } + } + + // Handle failure here + pFindData = nullptr; + m_FindData.Remove(hTmpHandle); + return nullptr; +} + +const char *CBaseFileSystem::FindFirstHelper(const char *pWildCard, FileFindHandle_t *pHandle, int searchPath, FindData_t *pFindData) +{ + CSearchPath *pSearchPath = &m_SearchPaths[searchPath]; + if (pSearchPath->IsPackFile()) + { + const char *fname = SearchPakFile(pFindData->m_WildCardString.Base(), searchPath); + if (fname) { + pFindData->m_FindHandle = INVALID_HANDLE_VALUE; + *pHandle = m_FindData.Count() - 1; + return fname; + } + } + else + { + int len = Q_strlen(pSearchPath->GetPath().String()) + pFindData->m_WildCardString.Size(); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, pSearchPath->GetPath().String()); + Q_strcat(pTmpFileName, pFindData->m_WildCardString.Base()); + FixSlashes(pTmpFileName); + + if ((pFindData->m_FindHandle = FS_FindFirstFile(pTmpFileName, &pFindData->m_FindData)) != INVALID_HANDLE_VALUE) + { + *pHandle = m_FindData.Count() - 1; + return pFindData->m_FindData.cFileName; + } + } + + return nullptr; +} + +bool CBaseFileSystem::FileInSearchPaths(const char *pSearchWildcard, const char *pFileName, int minSearchPathID, int maxSearchPathID) +{ + if (minSearchPathID > maxSearchPathID) + return false; + + const char *tmp = &pSearchWildcard[Q_strlen(pSearchWildcard) - 1]; + for (; *tmp != CORRECT_PATH_SEPARATOR && pSearchWildcard < tmp; tmp--) {} + + if (++tmp <= pSearchWildcard) + return false; + + int pathStrLen = tmp - pSearchWildcard; + int fileNameStrLen = Q_strlen(pFileName); + char *pFileNameWithPath = (char *)alloca((pathStrLen + fileNameStrLen + 1) * sizeof(char)); + Q_strncpy(pFileNameWithPath, pSearchWildcard, pathStrLen); + pFileNameWithPath[pathStrLen] = '\0'; + Q_strcat(pFileNameWithPath, pFileName); + + for (int i = minSearchPathID; i <= maxSearchPathID; i++) + { + CSearchPath *pSearchPath = &m_SearchPaths[i]; + + if (!pSearchPath->IsPackFile()) + { + int len = Q_strlen(pFileNameWithPath) + Q_strlen(pSearchPath->GetPath().String()); + char *fullFilePath = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(fullFilePath, pSearchPath->GetPath().String()); + Q_strcat(fullFilePath, pFileNameWithPath); + + struct _stat buf; + if (FS_stat(fullFilePath, &buf) != -1) { + return true; + } + } + else + { + int curSearch = pSearchPath->m_iCurSearchFile; + bool ret = SearchPakFile(pSearchWildcard, i) ? true : false; + pSearchPath->m_iCurSearchFile = curSearch; + + if (ret) { + return ret; + } + } + } + + return false; +} + +// Get the next file, trucking through the path. Don't check for duplicates. +bool CBaseFileSystem::FindNextFileHelper(FindData_t *pFindData) +{ + if (m_SearchPaths[pFindData->m_CurrentSearchPathID].IsPackFile()) + { + const char *file = SearchPakFile(pFindData->m_WildCardString.Base(), pFindData->m_CurrentSearchPathID, false); + if (file) + { + char *pFileNameNoPath = const_cast(Q_strrchr(file, CORRECT_PATH_SEPARATOR)); + if (pFileNameNoPath) + { + file = pFileNameNoPath + 1; + } + + Q_strlcpy(pFindData->m_FindData.cFileName, file); + FixSlashes(pFindData->m_FindData.cFileName); + return true; + } + } + // Try the same search path that we were already searching on + else if (FS_FindNextFile(pFindData->m_FindHandle, &pFindData->m_FindData)) + { + return true; + } + + // This happens when we searched a full path + // if (pFindData->m_CurrentSearchPathID < 0) + // return false; + + pFindData->m_CurrentSearchPathID++; + + if (pFindData->m_FindHandle != INVALID_HANDLE_VALUE) + { + FS_FindClose(pFindData->m_FindHandle); + } + + pFindData->m_FindHandle = INVALID_HANDLE_VALUE; + + int c = m_SearchPaths.Count(); + for(; pFindData->m_CurrentSearchPathID < c; pFindData->m_CurrentSearchPathID++) + { + CSearchPath *pSearchPath = &m_SearchPaths[pFindData->m_CurrentSearchPathID]; + + if (pFindData->m_LimitedPathID != -1) + { + if (pSearchPath->GetPath() != pFindData->m_LimitedPathID) { + return false; + } + } + + if (pSearchPath->IsPackFile()) + { + const char *file = SearchPakFile(pFindData->m_WildCardString.Base(), pFindData->m_CurrentSearchPathID); + if (file) + { + Q_strlcpy(pFindData->m_FindData.cFileName, Q_strrchr(file, CORRECT_PATH_SEPARATOR) + 1); + FixSlashes(pFindData->m_FindData.cFileName); + return true; + } + } + else + { + int len = Q_strlen(pSearchPath->GetPath().String() + pFindData->m_WildCardString.Size()); + char *pTmpFileName = (char *)alloca((len + 1) * sizeof(char)); + Q_strcpy(pTmpFileName, pSearchPath->GetPath().String()); + Q_strcat(pTmpFileName, pFindData->m_WildCardString.Base()); + FixSlashes(pTmpFileName); + + if ((pFindData->m_FindHandle = FS_FindFirstFile(pTmpFileName, &pFindData->m_FindData)) != INVALID_HANDLE_VALUE) + { + return true; + } + } + } + + return false; +} + +const char *CBaseFileSystem::FindNext(FileFindHandle_t handle) +{ + if (!m_FindData.Count() || handle >= m_FindData.Count()) { + return nullptr; + } + + FindData_t *pFindData = &m_FindData[handle]; + if (FindNextFileHelper(pFindData)) + { + return pFindData->m_FindData.cFileName; + } + + return nullptr; +} + +bool CBaseFileSystem::FindIsDirectory(FileFindHandle_t handle) +{ + FindData_t *pFindData = &m_FindData[handle]; + return (pFindData->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; +} + +void CBaseFileSystem::FindClose(FileFindHandle_t handle) +{ + if (!m_FindData.Count() || handle >= m_FindData.Count()) + return; + + FindData_t *pFindData = &m_FindData[handle]; + Assert(pFindData); + + if (pFindData->m_FindHandle != INVALID_HANDLE_VALUE) + { + FS_FindClose(pFindData->m_FindHandle); + } + + pFindData->m_FindHandle = INVALID_HANDLE_VALUE; + + pFindData->m_WildCardString.Purge(); + m_FindData.FastRemove(handle); +} + +char *CBaseFileSystem::ParseFile(char *pFileBytes, char *pToken, bool *pWasQuoted) +{ + return ::ParseFile(pFileBytes, pToken, pWasQuoted); +} + +long CBaseFileSystem::GetFileTime(const char *pFileName) +{ + for (int i = 0; i < m_SearchPaths.Count(); i++) + { + FileHandle_t filehandle = FindFile(&m_SearchPaths[i], pFileName, "rb"); + if (filehandle) + { + CFileHandle *fh = reinterpret_cast(filehandle); + long time = fh->m_nFileTime; + Close(filehandle); + return time; + } + } + + return 0L; +} + +void CBaseFileSystem::FileTimeToString(char *pString, int maxCharsIncludingTerminator, long fileTime) +{ + time_t time = fileTime; + Q_strncpy(pString, ctime(&time), maxCharsIncludingTerminator); + pString[maxCharsIncludingTerminator - 1] = '\0'; +} + +void CBaseFileSystem::SetWarningFunc(WarningFunc_t pfnWarning) +{ + m_pfnWarning = pfnWarning; +} + +void CBaseFileSystem::SetWarningLevel(FileWarningLevel_t level) +{ + m_fwLevel = level; +} + +void CBaseFileSystem::Warning(FileWarningLevel_t level, const char *fmt, ...) +{ + if (m_fwLevel < level) { + return; + } + + va_list argptr; + char warningtext[4096]; + + va_start(argptr, fmt); + Q_vsnprintf(warningtext, sizeof(warningtext), fmt, argptr); + va_end(argptr); + + if (m_pfnWarning) + { + m_pfnWarning(warningtext); + } + else + { +#ifdef _WIN32 + OutputDebugString(warningtext); +#else + fprintf(stderr, warningtext); +#endif + } +} + +bool CBaseFileSystem::FullPathToRelativePath(const char *pFullpath, char *pRelative) +{ + int inlen = Q_strlen(pFullpath); + if (inlen <= 0) { + *pRelative = '\0'; + return false; + } + + Q_strcpy(pRelative, pFullpath); + + char *inpath = (char *)alloca((inlen + 1) * sizeof(char)); + Q_strcpy(inpath, pFullpath); + +#ifdef _WIN32 // don't do this on linux! + Q_strlwr(inpath); +#endif + + FixSlashes(inpath); + + bool success = false; + for (int i = 0; i < m_SearchPaths.Count() && !success; i++) + { + CSearchPath *pSearchPath = &m_SearchPaths[i]; + if (pSearchPath->IsMapPath()) + continue; + + char *searchbase = new char [Q_strlen(pSearchPath->GetPath().String()) + 1]; + Q_strcpy(searchbase, pSearchPath->GetPath().String()); + FixSlashes(searchbase); + +#ifdef _WIN32 // don't do this on linux! + Q_strlwr(searchbase); +#endif + + int baselen = Q_strlen(searchbase); + if (!Q_strnicmp(searchbase, inpath, baselen)) + { + success = true; + Q_strcpy(pRelative, &inpath[baselen]); + } + + delete [] searchbase; + } + + return false; +} + +bool CBaseFileSystem::OpenedFileLessFunc(COpenedFile const &src1, COpenedFile const &src2) +{ + return src1.m_pFile < src2.m_pFile; +} + +void CBaseFileSystem::RemoveAllSearchPaths() +{ + m_SearchPaths.Purge(); + m_PackFileHandles.Purge(); +} + +void CBaseFileSystem::StripFilename(char *path) +{ + int length = Q_strlen(path) - 1; + while (length > 0) + { + if (path[length] == CORRECT_PATH_SEPARATOR + || path[length] == INCORRECT_PATH_SEPARATOR) + break; + + length--; + } + + path[length] = '\0'; +} + +void CBaseFileSystem::FixSlashes(char *str) +{ + auto ptr = str; + while (*str) + { + if (*str == INCORRECT_PATH_SEPARATOR) { + *str = CORRECT_PATH_SEPARATOR; + } + + str++; + } +} + +void CBaseFileSystem::FixPath(char *str) +{ + char *lastChar = &str[Q_strlen(str) - 1]; + if (*lastChar != CORRECT_PATH_SEPARATOR && *lastChar != INCORRECT_PATH_SEPARATOR) + { + lastChar[1] = CORRECT_PATH_SEPARATOR; + lastChar[2] = '\0'; + } + + FixSlashes(str); +} + +CBaseFileSystem::COpenedFile::COpenedFile() : m_pFile(nullptr), m_pName(nullptr) +{ +} + +CBaseFileSystem::COpenedFile::~COpenedFile() +{ + if (m_pName) { + delete[] m_pName; + m_pName = nullptr; + } +} + +CBaseFileSystem::COpenedFile::COpenedFile(COpenedFile const &src) +{ + m_pFile = src.m_pFile; + m_pName = nullptr; + + if (src.m_pName) + { + m_pName = new char [Q_strlen(src.m_pName) + 1]; + Q_strcpy(m_pName, src.m_pName); + } +} + +bool CBaseFileSystem::COpenedFile::operator==(COpenedFile const &src) const +{ + return m_pFile == src.m_pFile; +} + +void CBaseFileSystem::COpenedFile::SetName(const char *name) +{ + if (m_pName) { + delete[] m_pName; + } + + m_pName = new char [Q_strlen(name) + 1]; + Q_strcpy(m_pName, name); +} + +const char *CBaseFileSystem::COpenedFile::GetName() +{ + return m_pName ? m_pName : "???"; +} + +bool CBaseFileSystem::CSearchPath::PackFileLessFunc(CPackFileEntry const &src1, CPackFileEntry const &src2) +{ + return src1.m_Name < src2.m_Name; +} + +CBaseFileSystem::CSearchPath::CSearchPath() : + m_PackFiles(0, MAX_ENTRY_PATH, CSearchPath::PackFileLessFunc), + m_Path(CUtlSymbol("")), + m_bIsMapPath(false), + m_bIsPackFile(false), + m_bAllowWrite(true), + m_lPackFileTime(0), + m_nNumPackFiles(0), + m_iCurSearchFile(0), + m_hPackFile(nullptr) +{ +} + +CBaseFileSystem::CSearchPath::~CSearchPath() +{ + if (m_bIsPackFile && m_hPackFile) + { + BaseFileSystem()->m_PackFileHandles.FindAndRemove(m_hPackFile->m_pFile); + BaseFileSystem()->Close(m_hPackFile->m_pFile); + } +} diff --git a/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.h b/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.h new file mode 100644 index 0000000..5ae37e8 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/BaseFileSystem.h @@ -0,0 +1,378 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include "filesystem_helpers.h" + +#ifdef _WIN32 + #include // mkdir +#else + #include // unlink + #include "linux_support.h" + + // undo the prepended "_" 's + #define _chmod chmod + #define _stat stat + #define _alloca alloca + #define _S_IFDIR S_IFDIR + + #define HANDLE int + #define INVALID_HANDLE_VALUE ((HANDLE)~0) +#endif // _WIN32 + +#include "utlsymbol.h" + +class CBaseFileSystem: public IFileSystem { +public: + CBaseFileSystem(); + virtual ~CBaseFileSystem() {}; + + // Mount and unmount the filesystem + virtual void Mount(); + virtual void Unmount(); + + // opens a file + // if pathID is NULL, all paths will be searched for the file + virtual FileHandle_t Open(const char *pFileName, const char *pOptions, const char *pathID = 0L); + + // open a file but force the data to come from the steam cache, NOT from disk + virtual FileHandle_t OpenFromCacheForRead(const char *pFileName, const char *pOptions, const char *pathID = 0L); + + virtual void Close(FileHandle_t file); + + virtual void Seek(FileHandle_t file, int pos, FileSystemSeek_t seekType); + virtual size_t Tell(FileHandle_t file); + + virtual size_t Size(FileHandle_t file); + virtual size_t Size(const char *pFileName); + + virtual bool IsOk(FileHandle_t file); + + virtual void Flush(FileHandle_t file); + virtual bool EndOfFile(FileHandle_t file); + + virtual int Read(void* pOutput, int size, FileHandle_t file); + virtual int Write(void const* pInput, int size, FileHandle_t file); + virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file); + virtual int FPrintf(FileHandle_t file, const char *fmt, ...); + + // direct filesystem buffer access + // returns a handle to a buffer containing the file data + // this is the optimal way to access the complete data for a file, + // since the file preloader has probably already got it in memory + virtual void *GetReadBuffer(FileHandle_t file, int *outBufferSize, bool failIfNotInCache); + virtual void ReleaseReadBuffer(FileHandle_t file, void *readBuffer); + + // Gets the current working directory + virtual bool GetCurrentDirectory(char* pDirectory, int maxlen); + + // this isn't implementable on STEAM as is. + virtual void CreateDirHierarchy(const char *path, const char *pathID); + + // File I/O and info + virtual bool IsDirectory(const char *pFileName); + virtual const char *GetLocalPath(const char *pFileName, char *pLocalPath, int localPathBufferSize); + + // Deletes a file + virtual void RemoveFile(const char *pRelativePath, const char *pathID); + + // Remove all search paths (including write path?) + virtual void RemoveAllSearchPaths(); + + // Add paths in priority order (mod dir, game dir, ....) + // If one or more .pak files are in the specified directory, then they are + // added after the file system path + // If the path is the relative path to a .bsp file, then any previous .bsp file + // override is cleared and the current .bsp is searched for an embedded PAK file + // and this file becomes the highest priority search path (i.e., it's looked at first + // even before the mod's file system path). + virtual void AddSearchPath(const char *pPath, const char *pathID); + virtual bool RemoveSearchPath(const char *pPath); + + virtual bool FileExists(const char *pFileName); + + virtual long GetFileTime(const char *pFileName); + virtual void FileTimeToString(char* pStrip, int maxCharsIncludingTerminator, long fileTime); + + // FindFirst/FindNext + virtual const char *FindFirst(const char *pWildCard, FileFindHandle_t *pHandle, const char *pathID = 0L); + virtual const char *FindNext(FileFindHandle_t handle); + virtual bool FindIsDirectory(FileFindHandle_t handle); + virtual void FindClose(FileFindHandle_t handle); + + // Note: This is sort of a secondary feature; but it's really useful to have it here + virtual char *ParseFile(char* pFileBytes, char* pToken, bool* pWasQuoted); + + // Returns true on success (based on current list of search paths, otherwise false if it can't be resolved) + virtual bool FullPathToRelativePath(const char *pFullpath, char *pRelative); + + // Dump to printf/OutputDebugString the list of files that have not been closed + virtual void PrintOpenedFiles(); + + virtual void SetWarningFunc(WarningFunc_t pfnWarning); + virtual void SetWarningLevel(FileWarningLevel_t level); + + // interface for custom pack files > 4Gb + virtual bool AddPackFile(const char *fullpath, const char *pathID); + + FILE *Trace_FOpen(const char *filename, const char *options, bool bFromCache = false); + void Trace_FClose(FILE *fp); + void Trace_DumpUnclosedFiles(); + + // Purpose: Functions implementing basic file system behavior. + virtual FILE *FS_fopen(const char *filename, const char *options, bool bFromCache = false) = 0; + virtual void FS_fclose(FILE *fp) = 0; + virtual void FS_fseek(FILE *fp, int64_t pos, FileSystemSeek_t seekType) = 0; + virtual size_t FS_ftell(FILE *fp) = 0; + virtual int FS_feof(FILE *fp) = 0; + virtual size_t FS_fread(void *dest, size_t count, size_t size, FILE *fp) = 0; + virtual size_t FS_fwrite(const void *src, size_t count, size_t size, FILE *fp) = 0; + virtual size_t FS_vfprintf(FILE *fp, const char *fmt, va_list list) = 0; + virtual int FS_ferror(FILE *fp) = 0; + virtual int FS_fflush(FILE *fp) = 0; + virtual char *FS_fgets(char *dest, int destSize, FILE *fp) = 0; + virtual int FS_stat(const char *path, struct _stat *buf) = 0; + virtual HANDLE FS_FindFirstFile(char *findname, WIN32_FIND_DATA *dat) = 0; + virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat) = 0; + virtual bool FS_FindClose(HANDLE handle) = 0; + virtual bool IsThreadSafe() = 0; + virtual bool IsFileImmediatelyAvailable(const char *pFileName) = 0; + +protected: + // structures for building pack files + #pragma pack(1) + typedef struct + { + char name[56]; + int filepos; + int filelen; + } packfile_t; + + typedef struct + { + char id[4]; + int dirofs; + int dirlen; + } packheader_t; + + typedef struct + { + char name[112]; + int64_t filepos; + int64_t filelen; + } packfile64_t; + + typedef struct + { + char id[4]; + int64_t dirofs; + int64_t dirlen; + } packheader64_t; + + typedef struct + { + char id[8]; + int64_t packheaderpos; + int64_t originalfilesize; + } packappenededheader_t; + #pragma pack() + + // A Pack file directory entry: + class CPackFileEntry + { + public: + CUtlSymbol m_Name; + int64_t m_nPosition; + int64_t m_nLength; + }; + + class CFileHandle + { + public: + FILE* m_pFile; + bool m_bPack; + bool m_bErrorFlagged; + int64_t m_nStartOffset; + int64_t m_nLength; + long m_nFileTime; + }; + + enum { MAX_FILES_IN_PACK = 32768 }; + class CSearchPath + { + public: + CSearchPath(); + ~CSearchPath(); + + // Path ID ("game", "mod", "gamebin") accessors. + void SetPathID(CUtlSymbol id); + const CUtlSymbol &GetPathID() const; + + // Search path (c:\hl\hl) accessors. + void SetPath(CUtlSymbol id); + const CUtlSymbol &GetPath() const; + + void SetWritePath(bool st); + + bool IsAllowWrite() const; + bool IsPackFile() const; + bool IsMapPath() const; + + private: + friend class CBaseFileSystem; + + CUtlSymbol m_Path; + CUtlSymbol m_PathID; + + bool m_bIsMapPath; + bool m_bIsPackFile; + + long m_lPackFileTime; + CFileHandle* m_hPackFile; + int m_nNumPackFiles; + size_t m_iCurSearchFile; + bool m_bAllowWrite; + + enum { MAX_ENTRY_PATH = 32 }; + + // Entries to the individual files stored inside the pack file. + CUtlRBTree m_PackFiles; + static bool PackFileLessFunc(CPackFileEntry const &src1, CPackFileEntry const &src2); + }; + + // Purpose: For tracking unclosed files + // NOTE: The symbol table could take up memory that we don't want to eat here. + // In that case, we shouldn't store them in a table, or we should store them as locally allocates stings + // so we can control the size + class COpenedFile + { + public: + COpenedFile(); + ~COpenedFile(); + + COpenedFile(COpenedFile const &src); + bool operator==(COpenedFile const &src) const; + + void SetName(char const *name); + const char *GetName(); + + FILE *m_pFile; + char *m_pName; + }; + + struct FindData_t + { + WIN32_FIND_DATA m_FindData; + int m_CurrentSearchPathID; + int m_LimitedPathID; + CUtlVector m_WildCardString; + HANDLE m_FindHandle; + }; + + virtual void AddSearchPathNoWrite(const char *pPath, const char *pathID); + void AddSearchPathInternal(const char *pPath, const char *pathID, bool bAllowWrite); + + void Warning(FileWarningLevel_t level, const char *fmt, ...); + void FixSlashes(char *str); + void FixPath(char *str); + void StripFilename(char *path); + CSearchPath *GetWritePath(const char *pathID); + FileHandle_t FindFile(CSearchPath *path, const char *pFileName, const char *pOptions, bool bFromCache = false); + int FastFindFileSize(CSearchPath *path, const char *pFileName); + void RemoveAllMapSearchPaths(); + void AddPackFiles(const char *pPath); + bool AddPackFileFromPath(const char *pPath, const char *pakfile, bool bCheckForAppendedPack, const char *pathID); + bool PreparePackFile(CSearchPath &packfile, int64_t offsetofpackinmetafile); + bool Prepare64BitPackFile(CSearchPath &packfile, int64_t offsetofpackinmetafile); + const char *SearchPakFile(const char *pWildCard, int currentSearchPathID, bool first = true); + bool FileInSearchPaths(const char *pSearchWildcard, const char *pFileName, int minSearchPathID, int maxSearchPathID); + bool FindNextFileHelper(FindData_t *pFindData); + const char *FindFirstHelper(const char *pWildCard, FileFindHandle_t *pHandle, int searchPath, FindData_t *pFindData); + +public: + static CBaseFileSystem *s_pFileSystem; + +protected: + CUtlVector m_OpenedFiles; + static bool OpenedFileLessFunc(COpenedFile const &src1, COpenedFile const &src2); + + CUtlVector m_PackFileHandles; + CUtlVector m_FindData; + CUtlVector m_SearchPaths; + + FileWarningLevel_t m_fwLevel; + WarningFunc_t m_pfnWarning; + + int m_nOpenCount; +}; + +// singleton accessor +CBaseFileSystem *BaseFileSystem(); + +// Inlines +inline void CBaseFileSystem::CSearchPath::SetPathID(CUtlSymbol sym) +{ + m_PathID = sym; +} + +inline const CUtlSymbol &CBaseFileSystem::CSearchPath::GetPathID() const +{ + return m_PathID; +} + +inline void CBaseFileSystem::CSearchPath::SetPath(CUtlSymbol id) +{ + m_Path = id; +} + +inline const CUtlSymbol &CBaseFileSystem::CSearchPath::GetPath() const +{ + return m_Path; +} + +inline void CBaseFileSystem::CSearchPath::SetWritePath(bool st) +{ + m_bAllowWrite = st; +} + +inline bool CBaseFileSystem::CSearchPath::IsAllowWrite() const +{ + return m_bAllowWrite; +} + +inline bool CBaseFileSystem::CSearchPath::IsPackFile() const +{ + return m_bIsPackFile; +} + +inline bool CBaseFileSystem::CSearchPath::IsMapPath() const +{ + return m_bIsMapPath; +} diff --git a/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.cpp b/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.cpp new file mode 100644 index 0000000..78d1188 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.cpp @@ -0,0 +1,232 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +CFileSystem_Stdio::CFileSystem_Stdio() +{ + m_bMounted = false; +} + +CFileSystem_Stdio::~CFileSystem_Stdio() +{ + RemoveAllSearchPaths(); + Trace_DumpUnclosedFiles(); +} + +void CFileSystem_Stdio::Mount() +{ + m_bMounted = true; + CBaseFileSystem::Mount(); +} + +void CFileSystem_Stdio::Unmount() +{ + m_bMounted = false; + CBaseFileSystem::Unmount(); +} + +// low-level filesystem wrapper +FILE *CFileSystem_Stdio::FS_fopen(const char *filename, const char *options, bool bFromCache) +{ + FILE *tst = fopen(filename, options); + +#ifndef _WIN32 + if (!tst && !Q_strchr(options, 'w') && !Q_strchr(options, '+')) { + const char *file = findFileInDirCaseInsensitive(filename); + tst = fopen(filename, options); + } +#endif // _WIN32 + + return tst; +} + +void CFileSystem_Stdio::FS_fclose(FILE *fp) +{ + fclose(fp); +} + +void CFileSystem_Stdio::FS_fseek(FILE *fp, int64_t pos, FileSystemSeek_t seekType) +{ + fseek(fp, pos, seekType); +} + +size_t CFileSystem_Stdio::FS_ftell(FILE *fp) +{ + return ftell(fp); +} + +int CFileSystem_Stdio::FS_feof(FILE *fp) +{ + return feof(fp); +} + +size_t CFileSystem_Stdio::FS_fread(void *dest, size_t count, size_t size, FILE *fp) +{ + return fread(dest, count, size, fp); +} + +size_t CFileSystem_Stdio::FS_fwrite(const void *src, size_t count, size_t size, FILE *fp) +{ + return fwrite(src, count, size, fp); +} + +size_t CFileSystem_Stdio::FS_vfprintf(FILE *fp, const char *fmt, va_list list) +{ + return vfprintf(fp, fmt, list); +} + +int CFileSystem_Stdio::FS_ferror(FILE *fp) +{ + return ferror(fp); +} + +int CFileSystem_Stdio::FS_fflush(FILE *fp) +{ + return fflush(fp); +} + +char *CFileSystem_Stdio::FS_fgets(char *dest, int destSize, FILE *fp) +{ + return fgets(dest, destSize, fp); +} + +int CFileSystem_Stdio::FS_stat(const char *path, struct _stat *buf) +{ + int rt = _stat(path, buf); + +#ifndef _WIN32 + if (rt == -1) + { + const char *file = findFileInDirCaseInsensitive(path); + if (file) { + rt = _stat(file, buf); + } + } +#endif // _WIN32 + + return rt; +} + +HANDLE CFileSystem_Stdio::FS_FindFirstFile(char *findname, WIN32_FIND_DATA *dat) +{ + return ::FindFirstFile(findname, dat); +} + +bool CFileSystem_Stdio::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat) +{ + return (::FindNextFile(handle, dat) != 0); +} + +bool CFileSystem_Stdio::FS_FindClose(HANDLE handle) +{ + return (::FindClose(handle) != 0); +} + +bool CFileSystem_Stdio::IsThreadSafe() +{ + return true; +} + +bool CFileSystem_Stdio::IsFileImmediatelyAvailable(const char *pFileName) +{ + return true; +} + +void CFileSystem_Stdio::GetLocalCopy(const char *pFileName) +{ + ; +} + +void CFileSystem_Stdio::LogLevelLoadFinished(const char *name) +{ + ; +} + +void CFileSystem_Stdio::LogLevelLoadStarted(const char *name) +{ + ; +} + +int CFileSystem_Stdio::HintResourceNeed(const char *hintlist, int forgetEverything) +{ + return 0; +} + +int CFileSystem_Stdio::PauseResourcePreloading() +{ + return 0; +} + +int CFileSystem_Stdio::ResumeResourcePreloading() +{ + return 0; +} + +int CFileSystem_Stdio::SetVBuf(FileHandle_t stream, char *buffer, int mode, long size) +{ + CFileHandle *fh = reinterpret_cast(stream); + if (!fh) { + Warning(FILESYSTEM_WARNING, "FS: Tried to SetVBuf NULL file handle!\n"); + return 0; + } + + return setvbuf(fh->m_pFile, buffer, mode, size); +} + +void CFileSystem_Stdio::GetInterfaceVersion(char *p, int maxlen) +{ + Q_strncpy(p, "Stdio", maxlen); +} + +WaitForResourcesHandle_t CFileSystem_Stdio::WaitForResources(const char *resourcelist) +{ + return (WaitForResourcesHandle_t)FILESYSTEM_INVALID_HANDLE; +} + +bool CFileSystem_Stdio::GetWaitForResourcesProgress(WaitForResourcesHandle_t handle, float *progress, bool *complete) +{ + if (progress) *progress = 0; + if (complete) *complete = true; + + return false; +} + +void CFileSystem_Stdio::CancelWaitForResources(WaitForResourcesHandle_t handle) +{ + ; +} + +bool CFileSystem_Stdio::IsAppReadyForOfflinePlay(int appID) +{ + return true; +} + +#ifndef HOOK_FILESYSTEM +EXPOSE_SINGLE_INTERFACE(CFileSystem_Stdio, IFileSystem, FILESYSTEM_INTERFACE_VERSION); +#endif // HOOK_FILESYSTEM diff --git a/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.h b/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.h new file mode 100644 index 0000000..afe81e0 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/FileSystem_Stdio.h @@ -0,0 +1,91 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "BaseFileSystem.h" +#include "linux_support.h" + +class CFileSystem_Stdio: public CBaseFileSystem +{ +public: + CFileSystem_Stdio(); + virtual ~CFileSystem_Stdio(); + + // Mount and unmount the filesystem + virtual void Mount(); + virtual void Unmount(); + + virtual void GetLocalCopy(const char *pFileName); + + virtual void LogLevelLoadStarted(const char *name); + virtual void LogLevelLoadFinished(const char *name); + virtual int HintResourceNeed(const char *hintlist, int forgetEverything); + virtual int PauseResourcePreloading(); + virtual int ResumeResourcePreloading(); + virtual int SetVBuf(FileHandle_t stream, char *buffer, int mode, long size); + virtual void GetInterfaceVersion(char *p, int maxlen); + + // starts waiting for resources to be available + // returns FILESYSTEM_INVALID_HANDLE if there is nothing to wait on + virtual WaitForResourcesHandle_t WaitForResources(const char *resourcelist); + + // get progress on waiting for resources; progress is a float [0, 1], complete is true on the waiting being done + // returns false if no progress is available + // any calls after complete is true or on an invalid handle will return false, 0.0f, true + virtual bool GetWaitForResourcesProgress(WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */); + + // cancels a progress call + virtual void CancelWaitForResources(WaitForResourcesHandle_t handle); + + // returns true if the appID has all its caches fully preloaded + virtual bool IsAppReadyForOfflinePlay(int appID); + +protected: + // implementation of CBaseFileSystem virtual functions + virtual FILE *FS_fopen(const char *filename, const char *options, bool bFromCache = false); + virtual void FS_fclose(FILE *fp); + virtual void FS_fseek(FILE *fp, int64_t pos, FileSystemSeek_t seekType); + virtual size_t FS_ftell(FILE *fp); + virtual int FS_feof(FILE *fp); + virtual size_t FS_fread(void *dest, size_t count, size_t size, FILE *fp); + virtual size_t FS_fwrite(const void *src, size_t count, size_t size, FILE *fp); + virtual size_t FS_vfprintf(FILE *fp, const char *fmt, va_list list); + virtual int FS_ferror(FILE *fp); + virtual int FS_fflush(FILE *fp); + virtual char *FS_fgets(char *dest, int destSize, FILE *fp); + virtual int FS_stat(const char *path, struct _stat *buf); + virtual HANDLE FS_FindFirstFile(char *findname, WIN32_FIND_DATA *dat); + virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat); + virtual bool FS_FindClose(HANDLE handle); + virtual bool IsThreadSafe(); + virtual bool IsFileImmediatelyAvailable(const char *pFileName); + +private: + bool m_bMounted; +}; diff --git a/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.cpp b/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.cpp new file mode 100644 index 0000000..097fd60 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.cpp @@ -0,0 +1,178 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +// wordbreak parsing set +characterset_t g_BreakSet, g_BreakSetIncludingColons; + +void InitializeCharacterSets() +{ + static bool s_CharacterSetInitialized = false; + if (!s_CharacterSetInitialized) + { + CharacterSetBuild(&g_BreakSet, "{}()'"); + CharacterSetBuild(&g_BreakSetIncludingColons, "{}()':"); + s_CharacterSetInitialized = true; + } +} + +const char *ParseFile(const char *pFileBytes, char *pToken, bool *pWasQuoted, characterset_t *pCharSet) +{ + if (pWasQuoted) + *pWasQuoted = false; + + if (!pFileBytes) + return nullptr; + + InitializeCharacterSets(); + + // YWB: Ignore colons as token separators in COM_Parse + static const bool com_ignorecolons = false; + characterset_t &breaks = pCharSet ? *pCharSet : (com_ignorecolons ? g_BreakSet : g_BreakSetIncludingColons); + + int c; + int len = 0; + pToken[0] = '\0'; + +skipwhite: + // skip whitespace + while ((c = *pFileBytes) <= ' ') + { + if (c == 0) + { + // end of file; + return nullptr; + } + + pFileBytes++; + } + + // skip // comments till the next line + if (c == '/' && pFileBytes[1] == '/') + { + while (*pFileBytes && *pFileBytes != '\n') + pFileBytes++; + goto skipwhite; // start over new line + } + + // skip c-style comments + if (c == '/' && pFileBytes[1] == '*') + { + // Skip "/*" + pFileBytes += 2; + + while (*pFileBytes) + { + if (*pFileBytes == '*' && pFileBytes[1] == '/') + { + pFileBytes += 2; + break; + } + + pFileBytes++; + } + + goto skipwhite; + } + + // handle quoted strings specially: copy till the end or another quote + if (c == '\"') + { + if (pWasQuoted) + *pWasQuoted = true; + + pFileBytes++; // skip starting quote + while (true) + { + c = *pFileBytes++; // get char and advance + + if (!c) // EOL + { + pToken[len] = '\0'; + return pFileBytes - 1; // we are done with that, but return data to show that token is present + } + + if (c == '\"') // closing quote + { + pToken[len] = '\0'; + return pFileBytes; + } + + pToken[len++] = c; + } + } + + // parse single characters + if (IN_CHARACTERSET(breaks, c)) + { + pToken[len++] = c; + pToken[len] = '\0'; + return pFileBytes + 1; + } + + // parse a regular word + do + { + pToken[len++] = c; + pFileBytes++; + + c = *pFileBytes; + if (IN_CHARACTERSET(breaks, c)) + break; + } + while (c > 32); + + pToken[len] = '\0'; + return pFileBytes; +} + +char *ParseFile(char *pFileBytes, char *pToken, bool *pWasQuoted) +{ + return const_cast(ParseFile(static_cast(pFileBytes), pToken, pWasQuoted)); +} + +void NORETURN FileSystem_SysError(const char *fmt, ...) +{ + va_list argptr; + static char string[8192]; + + va_start(argptr, fmt); + vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + printf("%s\n", string); + + FILE* fl = fopen("filesystem_error.txt", "w"); + fprintf(fl, "%s\n", string); + fclose(fl); + + int *null = 0; + *null = 0; + exit(-1); +} diff --git a/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.h b/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.h new file mode 100644 index 0000000..f073acd --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/filesystem_helpers.h @@ -0,0 +1,36 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "tier0/characterset.h" + +// Call until it returns nullptr. Each time you call it, it will parse out a token. +struct characterset_t; + +const char *ParseFile(const char *pFileBytes, char *pToken, bool *pWasQuoted, characterset_t *pCharSet = nullptr); +char *ParseFile(char *pFileBytes, char *pToken, bool *pWasQuoted); // (same exact thing as the const version) +void NORETURN FileSystem_SysError(const char *fmt, ...); diff --git a/rehlds/filesystem/FileSystem_Stdio/src/linux_support.cpp b/rehlds/filesystem/FileSystem_Stdio/src/linux_support.cpp new file mode 100644 index 0000000..fba110d --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/linux_support.cpp @@ -0,0 +1,258 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#if !defined(_WIN32) + +char selectBuf[PATH_MAX]; +int FileSelect(const struct dirent *ent) +{ + const char *mask = selectBuf; + const char *name = ent->d_name; + + //printf("Test:%s %s\n", mask, name); + + if (!Q_strcmp(name, ".") || !Q_strcmp(name, "..")) + return 0; + + if (!Q_strcmp(selectBuf, "*.*")) + return 1; + + while (*mask && *name) + { + if (*mask == '*') + { + mask++; // move to the next char in the mask + if (!*mask) // if this is the end of the mask its a match + { + return 1; + } + while (*name && toupper(*name) != toupper(*mask)) + { + // while the two don't meet up again + name++; + } + if (!*name) + { + // end of the name + break; + } + } + else if (*mask != '?') + { + if (toupper(*mask) != toupper(*name)) + { + // mismatched! + return 0; + } + else + { + mask++; + name++; + if (!*mask && !*name) + { + // if its at the end of the buffer + return 1; + } + + } + + } + // mask is "?", we don't care + else + { + mask++; + name++; + } + } + + // both of the strings are at the end + return (!*mask && !*name); +} + +int FillDataStruct(FIND_DATA *dat) +{ + char szFileName[MAX_PATH]; + struct stat fileStat; + + if (dat->numMatches < 0) + return -1; + + Q_strlcpy(szFileName, dat->cDir); + Q_strlcat(szFileName, "/"); + Q_strlcat(szFileName, dat->namelist[dat->numMatches]->d_name); + Q_strlcpy(dat->cFileName, dat->namelist[dat->numMatches]->d_name); + + if (!stat(szFileName, &fileStat)) + { + dat->dwFileAttributes = fileStat.st_mode; + } + else + { + dat->dwFileAttributes = 0; + } + + // printf("%s\n", dat->namelist[dat->numMatches]->d_name); + free(dat->namelist[dat->numMatches]); + dat->numMatches--; + return 1; +} + +int FindFirstFile(char *fileName, FIND_DATA *dat) +{ + char nameStore[PATH_MAX]; + char *dir = nullptr; + int n, iret = -1; + + Q_strlcpy(nameStore, fileName); + + if (Q_strrchr(nameStore, '/')) + { + dir = nameStore; + while (Q_strrchr(dir, '/')) + { + struct stat dirChk; + + // zero this with the dir name + dir = Q_strrchr(nameStore, '/'); + *dir = '\0'; + + dir = nameStore; + stat(dir, &dirChk); + + if (S_ISDIR(dirChk.st_mode)) + { + break; + } + } + } + else + { + // couldn't find a dir seperator... + return -1; + } + + if (Q_strlen(dir) > 0) + { + Q_strlcpy(selectBuf, fileName + Q_strlen(dir) + 1); + Q_strlcpy(dat->cDir, dir); + + n = scandir(dir, &dat->namelist, FileSelect, alphasort); + if (n < 0) + { + // silently return, nothing interesting + printf("scandir failed:%s\n", dir); + } + else + { + // n is the number of matches + dat->numMatches = n - 1; + iret = FillDataStruct(dat); + if (iret < 0) + { + free(dat->namelist); + } + } + } + + // printf("Returning: %i \n", iret); + return iret; +} + +bool FindNextFile(int handle, FIND_DATA *dat) +{ + if (dat->numMatches < 0) + { + free(dat->namelist); + return false; // no matches left + } + + FillDataStruct(dat); + return true; +} + +bool FindClose(int handle) +{ + return true; +} + +static char fileName[MAX_PATH]; +int CheckName(const struct dirent *dir) +{ + return !Q_stricmp(dir->d_name, fileName); +} + +const char *findFileInDirCaseInsensitive(const char *file) +{ + const char *dirSep = Q_strrchr(file, '/'); + if (!dirSep) + { + dirSep = Q_strrchr(file, '\\'); + if (!dirSep) + { + return nullptr; + } + } + + char *dirName = static_cast(alloca(((dirSep - file) + 1) * sizeof(char))); + if (!dirName) { + return nullptr; + } + + Q_strncpy(dirName, file, dirSep - file); + dirName[dirSep - file] = '\0'; + + struct dirent **namelist; + int n; + + Q_strlcpy(fileName, dirSep + 1); + n = scandir(dirName, &namelist, CheckName, alphasort); + + if (n > 0) + { + while (n > 1) + { + // free the malloc'd strings + free(namelist[n]); + n--; + } + + Q_snprintf(fileName, sizeof(fileName), "%s/%s", dirName, namelist[0]->d_name); + free(namelist[0]); + return fileName; + } + else + { + Q_strlcpy(fileName, file); + Q_strlwr(fileName); + return fileName; + } +} + +#endif // #if !defined(_WIN32) diff --git a/rehlds/filesystem/FileSystem_Stdio/src/linux_support.h b/rehlds/filesystem/FileSystem_Stdio/src/linux_support.h new file mode 100644 index 0000000..96daef9 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/linux_support.h @@ -0,0 +1,62 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#ifndef _WIN32 + +#include // tolower() +#include // PATH_MAX define +#include // strcmp, strcpy +#include // stat() +#include +#include // scandir() +#include +#include + +#define FILE_ATTRIBUTE_DIRECTORY S_IFDIR + +typedef struct +{ + // public data + int dwFileAttributes; + char cFileName[MAX_PATH]; // the file name returned from the call + + int numMatches; + struct dirent **namelist; + char cDir[MAX_PATH]; +} FIND_DATA; + +#define WIN32_FIND_DATA FIND_DATA + +int FindFirstFile(char *findName, FIND_DATA *dat); +bool FindNextFile(int handle, FIND_DATA *dat); +bool FindClose(int handle); +const char *findFileInDirCaseInsensitive(const char *file); + +#endif // _WIN32 diff --git a/rehlds/filesystem/FileSystem_Stdio/src/pathmatch.cpp b/rehlds/filesystem/FileSystem_Stdio/src/pathmatch.cpp new file mode 100644 index 0000000..9d4f722 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/pathmatch.cpp @@ -0,0 +1,898 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +* ------------------------------------------------------------------------------------ +* This file implements the --wrap for ld on linux that lets file i/o api's +* behave as if it were running on a case insensitive file system. Unfortunately, +* this is needed by both steam2 and steam3. It was decided to check the source +* into both locations, otherwise someone would find the .o and have no idea +* where to go for the source if it was in the 'other' tree. Also, because this +* needs to be linked into every elf binary, the .o is checked in for Steam3 so that it is +* always available. In Steam2 it sits with the PosixWin32.cpp implementation and gets +* compiled along side of it through the make system. If you are reading this in Steam3, +* you will probably want to actually make your changes in steam2 and do a baseless merge +* to the steam3 copy. +* +* HOWTO: Add a new function. Add the function with _WRAP to the makefiles as noted below. +* Add the implementation to pathmatch.cpp - probably mimicking the existing functions. +* Build steam2 and copy to matching steam3/client. Take the pathmatch.o from steam 2 +* and check it in to steam3 (in the location noted below). Full rebuild (re-link really) +* of steam3. Test steam and check in. +* +* If you are looking at updating this file, please update the following as needed: +* +* STEAM2.../Projects/GazelleProto/Client/Engine/obj/RELEASE_NORMAL/libsteam_linux/Common/Misc/pathmatch.o +* This is where steam2 builds the pathmatch.o out to. +* +* STEAM2.../Projects/GazelleProto/Makefile.shlib.base - contains _WRAP references +* STEAM2.../Projects/Common/Misc/pathmatch.cpp - Where the source is checked in, keep in sync with: +* STEAM3.../src/common/pathmatch.cpp - should be identical to previous file, but discoverable in steam3. +* STEAM3.../src/lib/linux32/release/pathmatch.o - steam3 checked in version +* STEAM3.../src/devtools/makefile_base_posix.mak - look for the _WRAP references +* ------------------------------------------------------------------------------------ +*/ + +#include "precompiled.h" + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include + +// Enable to do pathmatch caching. Beware: this code isn't threadsafe. +// #define DO_PATHMATCH_CACHE + +#ifdef DO_PATHMATCH_CACHE +#include +#endif + +#ifdef UTF8_PATHMATCH +#define Q_stricmp utf8casecmp +#endif + +static bool s_bShowDiag; +#define DEBUG_MSG(...) if (s_bShowDiag) fprintf(stderr, ##__VA_ARGS__) +#define DEBUG_BREAK() __asm__ __volatile__ ("int $3") +#define _COMPILE_TIME_ASSERT(pred) switch(0) {case 0:case pred:;} + +#define WRAP(fn, ret, ...)\ + ret __real_##fn(__VA_ARGS__);\ + ret __wrap_##fn(__VA_ARGS__) + +#define CALL(fn) __real_##fn + +// Needed by pathmatch code +extern "C" int __real_access(const char *pathname, int mode); +extern "C" DIR *__real_opendir(const char *name); + +// UTF-8 work from PhysicsFS: http://icculus.org/physfs/ +// Even if it wasn't under the zlib license, Ryan wrote all this code originally. + +#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF +#define UNICODE_BOGUS_CHAR_CODEPOINT '?' + +inline __attribute__((always_inline)) static uint32_t utf8codepoint(const char **_str) +{ + const char *str = *_str; + uint32_t retval = 0; + uint32_t octet = (uint32_t)((uint8_t)*str); + uint32_t octet2, octet3, octet4; + + // null terminator, end of string. + if (octet == 0) + return 0; + + // one octet char: 0 to 127 + else if (octet < 128) + { + (*_str)++; // skip to next possible start of codepoint. + return octet; + } + else if ((octet > 127) && (octet < 192)) // bad (starts with 10xxxxxx). + { + // Apparently each of these is supposed to be flagged as a bogus + // char, instead of just resyncing to the next valid codepoint. + + (*_str)++; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_VALUE; + } + else if (octet < 224) // two octets + { + octet -= (128 + 64); + octet2 = (uint32_t)((uint8_t)*(++str)); + if ((octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 2; // skip to next possible start of codepoint. + retval = ((octet << 6) | (octet2 - 128)); + if ((retval >= 0x80) && (retval <= 0x7FF)) + return retval; + } + else if (octet < 240) // three octets + { + octet -= (128 + 64 + 32); + octet2 = (uint32_t)((uint8_t)*(++str)); + if ((octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (uint32_t)((uint8_t)*(++str)); + if ((octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 3; // skip to next possible start of codepoint. + retval = (((octet << 12)) | ((octet2 - 128) << 6) | ((octet3 - 128))); + + // There are seven "UTF-16 surrogates" that are illegal in UTF-8. + switch (retval) + { + case 0xD800: + case 0xDB7F: + case 0xDB80: + case 0xDBFF: + case 0xDC00: + case 0xDF80: + case 0xDFFF: + return UNICODE_BOGUS_CHAR_VALUE; + } + + // 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. + if ((retval >= 0x800) && (retval <= 0xFFFD)) + return retval; + } + else if (octet < 248) // four octets + { + octet -= (128 + 64 + 32 + 16); + octet2 = (uint32_t)((uint8_t)*(++str)); + if ((octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (uint32_t)((uint8_t)*(++str)); + if ((octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet4 = (uint32_t)((uint8_t)*(++str)); + if ((octet4 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 4; // skip to next possible start of codepoint. + retval = (((octet << 18)) | ((octet2 - 128) << 12) | ((octet3 - 128) << 6) | ((octet4 - 128))); + if ((retval >= 0x10000) && (retval <= 0x10FFFF)) + return retval; + } + + // Five and six octet sequences became illegal in rfc3629. + // We throw the codepoint away, but parse them to make sure we move + // ahead the right number of bytes and don't overflow the buffer. + else if (octet < 252) // five octets + { + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 5; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_VALUE; + } + else // six octets + { + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (uint32_t)((uint8_t)*(++str)); + if ((octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 6; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_VALUE; + } + + return UNICODE_BOGUS_CHAR_VALUE; +} + +typedef struct CaseFoldMapping +{ + uint32_t from; + uint32_t to0; + uint32_t to1; + uint32_t to2; +} CaseFoldMapping; + +typedef struct CaseFoldHashBucket +{ + const uint8_t count; + const CaseFoldMapping *list; +} CaseFoldHashBucket; + +#include "pathmatch_casefolding.h" + +inline static void locate_case_fold_mapping(const uint32_t from, uint32_t *to) +{ + const uint8_t hashed = ((from ^ (from >> 8)) & 0xFF); + const CaseFoldHashBucket *bucket = &case_fold_hash[hashed]; + const CaseFoldMapping *mapping = bucket->list; + uint32_t i; + + for (i = 0; i < bucket->count; i++, mapping++) + { + if (mapping->from == from) + { + to[0] = mapping->to0; + to[1] = mapping->to1; + to[2] = mapping->to2; + return; + } + } + + // Not found...there's no remapping for this codepoint. + to[0] = from; + to[1] = 0; + to[2] = 0; +} + +inline static uint32_t *fold_utf8(const char *str) +{ + uint32_t *retval = new uint32_t[(Q_strlen(str) * 3) + 1]; + uint32_t *dst = retval; + while (*str) + { + const char ch = *str; + if (ch & 0x80) // high bit set? UTF-8 sequence! + { + uint32_t fold[3]; + locate_case_fold_mapping(utf8codepoint(&str), fold); + *(dst++) = fold[0]; + if (fold[1]) + { + *(dst++) = fold[1]; + if (fold[2]) + *(dst++) = fold[2]; + } + } + // simple ASCII test. + else + { + *(dst++) = (uint32_t) (((ch >= 'A') && (ch <= 'Z')) ? ch + 32 : ch); + str++; + } + } + + *dst = 0; + return retval; +} + +inline static int utf8casecmp_loop(const uint32_t *folded1, const uint32_t *folded2) +{ + while (true) + { + const uint32_t ch1 = *(folded1++); + const uint32_t ch2 = *(folded2++); + + if (ch1 < ch2) + return -1; + else if (ch1 > ch2) + return 1; + else if (ch1 == 0) + { + // complete match. + return 0; + } + } +} + +static int utf8casecmp(const char *str1, const char *str2) +{ + uint32_t *folded1 = fold_utf8(str1); + uint32_t *folded2 = fold_utf8(str2); + const int retval = utf8casecmp_loop(folded1, folded2); + delete[] folded1; + delete[] folded2; + return retval; +} + +// Simple object to help make sure a DIR* from opendir +// gets closed when it goes out of scope. +class CDirPtr +{ +public: + CDirPtr() { m_pDir = nullptr; } + CDirPtr(DIR *pDir) : m_pDir(pDir) {} + ~CDirPtr() { Close(); } + + void operator=(DIR *pDir) { Close(); m_pDir = pDir; } + + operator DIR *() { return m_pDir; } + operator bool() { return m_pDir != nullptr; } + + private: + void Close() { if (m_pDir) closedir(m_pDir); } + + DIR *m_pDir; +}; + +// Object used to temporarily slice a path into a smaller componentent +// and then repair it when going out of scope. Typically used as an unnamed +// temp object that is a parameter to a function. +class CDirTrimmer +{ +public: + CDirTrimmer(char *pPath, size_t nTrimIdx) + { + m_pPath = pPath; + m_idx = nTrimIdx; + m_c = m_pPath[nTrimIdx]; + m_pPath[nTrimIdx] = '\0'; + } + ~CDirTrimmer() { m_pPath[m_idx] = m_c; } + + operator const char *() { return m_pPath; } + +private: + size_t m_idx; + char *m_pPath; + char m_c; +}; + +enum PathMod_t +{ + kPathUnchanged, + kPathLowered, + kPathChanged, + kPathFailed, +}; + +static bool Descend(char *pPath, size_t nStartIdx, bool bAllowBasenameMismatch, size_t nLevel = 0) +{ + DEBUG_MSG("(%zu) Descend: %s, (%s), %s\n", nLevel, pPath, pPath + nStartIdx, bAllowBasenameMismatch ? "true" : "false "); + + // We assume up through nStartIdx is valid and matching + size_t nNextSlash = nStartIdx + 1; + + // path might be a dir + if (pPath[nNextSlash] == '\0') { + return true; + } + + bool bIsDir = false; // is the new component a directory for certain? + while (pPath[nNextSlash] != '\0' && pPath[nNextSlash] != '/') { + nNextSlash++; + } + + // Modify the pPath string + if (pPath[nNextSlash] == '/') { + bIsDir = true; + } + + // See if we have an immediate match + if (__real_access(CDirTrimmer(pPath, nNextSlash), F_OK) == 0) + { + if (!bIsDir) { + return true; + } + + bool bRet = Descend(pPath, nNextSlash, bAllowBasenameMismatch, nLevel + 1); + if (bRet) { + return true; + } + } + + // Start enumerating dirents + CDirPtr spDir; + if (nStartIdx) + { + // we have a path + spDir = __real_opendir(CDirTrimmer(pPath, nStartIdx)); + nStartIdx++; + } + else + { + // we either start at root or cwd + const char *pRoot = "."; + if (*pPath == '/') + { + pRoot = "/"; + nStartIdx++; + } + + spDir = __real_opendir(pRoot); + } + + errno = 0; + + struct dirent *pEntry = spDir ? readdir(spDir) : nullptr; + char *pszComponent = pPath + nStartIdx; + size_t cbComponent = nNextSlash - nStartIdx; + while (pEntry) + { + DEBUG_MSG("\t(%zu) comparing %s with %s\n", nLevel, pEntry->d_name, (const char *)CDirTrimmer(pszComponent, cbComponent)); + + // the candidate must match the target, but not be a case-identical match (we would + // have looked there in the short-circuit code above, so don't look again) + bool bMatches = (Q_stricmp(CDirTrimmer(pszComponent, cbComponent), pEntry->d_name) == 0 && + Q_strcmp(CDirTrimmer(pszComponent, cbComponent), pEntry->d_name) != 0); + + if (bMatches) + { + char *pSrc = pEntry->d_name; + char *pDst = &pPath[nStartIdx]; + + // found a match; copy it in. + while (*pSrc && (*pSrc != '/')) { + *pDst++ = *pSrc++; + } + + if (!bIsDir) + return true; + + if (Descend(pPath, nNextSlash, bAllowBasenameMismatch, nLevel + 1)) + return true; + + // If descend fails, try more directories + } + + pEntry = readdir(spDir); + } + + if (bIsDir) { + DEBUG_MSG("(%zu) readdir failed to find '%s' in '%s'\n", nLevel, (const char *)CDirTrimmer(pszComponent, cbComponent), (const char *)CDirTrimmer(pPath, nStartIdx)); + } + + // Sometimes it's ok for the filename portion to not match + // since we might be opening for write. Note that if + // the filename matches case insensitive, that will be + // preferred over preserving the input name + if (!bIsDir && bAllowBasenameMismatch) { + return true; + } + + return false; +} + +char *GetSteamContentPath() +{ + char szContentLink[4096]; + Q_snprintf(szContentLink, sizeof(szContentLink), "%s/.steam/steam", getenv("HOME")); + + char *pszContentPath = realpath(szContentLink, nullptr); + if (pszContentPath) { + Q_strcat(pszContentPath, "/"); + } + else { + pszContentPath = Q_strdup("/"); + } + + return pszContentPath; +} + +#ifdef DO_PATHMATCH_CACHE +typedef std::map > resultCache_t; +typedef std::map >::iterator resultCacheItr_t; +static resultCache_t resultCache; +static const int k_cMaxCacheLifetimeSeconds = 2; +#endif // DO_PATHMATCH_CACHE + +PathMod_t pathmatch(const char *pszIn, char **ppszOut, bool bAllowBasenameMismatch, char *pszOutBuf, size_t OutBufLen) +{ + static const char *s_pszDbgPathMatch = getenv("DBG_PATHMATCH"); + + s_bShowDiag = (s_pszDbgPathMatch != nullptr); + + *ppszOut = nullptr; + + if (__real_access(pszIn, F_OK) == 0) + return kPathUnchanged; + +#ifdef DO_PATHMATCH_CACHE + resultCacheItr_t cachedResult = resultCache.find(pszIn); + if (cachedResult != resultCache.end()) + { + unsigned int age = time(nullptr) - cachedResult->second.second; + const char *pszResult = cachedResult->second.first.c_str(); + if (pszResult[0] != '\0' || age <= k_cMaxCacheLifetimeSeconds) + { + if (pszResult[0] != '\0') + { + *ppszOut = Q_strdup(pszResult); + DEBUG_MSG("Cached '%s' -> '%s'\n", pszIn, *ppszOut); + return kPathChanged; + } + else + { + DEBUG_MSG("Cached '%s' -> kPathFailed\n", pszIn); + return kPathFailed; + } + } + else if (age <= k_cMaxCacheLifetimeSeconds) + { + DEBUG_MSG("Rechecking '%s' - cache is %u seconds old\n", pszIn, age); + } + } +#endif // DO_PATHMATCH_CACHE + + char *pPath; + if (Q_strlen(pszIn) >= OutBufLen) + { + pPath = Q_strdup(pszIn); + } + else + { + Q_strncpy(pszOutBuf, pszIn, OutBufLen); + pPath = pszOutBuf; + } + + if (pPath) + { + // optimization, if the path contained steam somewhere + // assume the path up through the component with 'steam' in + // is valid (because we almost certainly obtained it + // progamatically + size_t nStartIdx = 0; + static char *pszSteamPath = nullptr; + static size_t nSteamPathLen = 0; + if (!pszSteamPath) + { + pszSteamPath = GetSteamContentPath(); + nSteamPathLen = Q_strlen(pszSteamPath); + } + + // optimization, if the path contained steam somewhere + // assume the path up through the component with 'steam' in + // is valid (because we almost certainly obtained it + // progamatically + if (strncasecmp(pPath, pszSteamPath, nSteamPathLen) == 0) + { + nStartIdx = nSteamPathLen - 1; + Q_memcpy(pPath, pszSteamPath, nStartIdx); + } + + char *p = pPath; + + // Try the lower casing of the remaining path + char *pBasename = p + nStartIdx; + while (*p) + { + if (*p == '/') { + pBasename = p + 1; + } + + *p = tolower(*p); + p++; + } + + if (__real_access(pPath, F_OK) == 0) + { + *ppszOut = pPath; + DEBUG_MSG("Lowered '%s' -> '%s'\n", pszIn, pPath); + return kPathLowered; + } + + // path didn't match lowered successfully, restore the basename + // if bAllowBasenameMismatch was true + if (bAllowBasenameMismatch && *pBasename) + { + const char *pSrc = pszIn + (pBasename - pPath); + while (*pBasename) + { + *pBasename++ = *pSrc++; + } + } + + if (s_pszDbgPathMatch && strcasestr(s_pszDbgPathMatch, pszIn)) + { + DEBUG_MSG("Breaking '%s' in '%s'\n", pszIn, s_pszDbgPathMatch); + DEBUG_BREAK(); + } + + bool bSuccess = Descend(pPath, nStartIdx, bAllowBasenameMismatch); + if (bSuccess) + { + *ppszOut = pPath; + DEBUG_MSG("Matched '%s' -> '%s'\n", pszIn, pPath); + } + else + { + DEBUG_MSG("Unmatched %s\n", pszIn); + } + +#ifndef DO_PATHMATCH_CACHE + return bSuccess ? kPathChanged : kPathFailed; +#else + time_t now = time(nullptr); + if (bSuccess) + { + resultCache[pszIn] = std::make_pair(*ppszOut, now); + return kPathChanged; + } + else + { + resultCache[pszIn] = std::make_pair("", now); + return kPathFailed; + } +#endif // DO_PATHMATCH_CACHE + } + + return kPathFailed; +} + +// Wrapper object that manages the 'typical' usage cases of pathmatch() +class CWrap +{ +public: + CWrap(const char *pSuppliedPath, bool bAllowMismatchedBasename) + : m_pSuppliedPath(pSuppliedPath), m_pBestMatch(nullptr) + { + m_eResult = pathmatch(m_pSuppliedPath, &m_pBestMatch, bAllowMismatchedBasename, m_BestMatchBuf, sizeof(m_BestMatchBuf)); + if (m_pBestMatch == nullptr) + { + m_pBestMatch = const_cast(m_pSuppliedPath); + } + } + + ~CWrap() + { + if ((m_pBestMatch != m_pSuppliedPath) && (m_pBestMatch != m_BestMatchBuf)) + free(m_pBestMatch); + } + + const char *GetBest() const { return m_pBestMatch; } + const char *GetOriginal() const { return m_pSuppliedPath; } + PathMod_t GetMatchResult() const { return m_eResult; } + + operator const char*() { return GetBest(); } + +private: + const char *m_pSuppliedPath; + char *m_pBestMatch; + char m_BestMatchBuf[512]; + PathMod_t m_eResult; +}; + +extern "C" { + + char *SteamRealPath(char *szOrigPath, char *szFinalPath, uint uiFinalPathCapacity) + { + char *szMatchPath = nullptr; + char szRealPath[MAX_PATH]; + char szPathMatchBuf[MAX_PATH]; + + if (uiFinalPathCapacity > sizeof(szRealPath) || !uiFinalPathCapacity || !szFinalPath) + return nullptr; + + PathMod_t pmStat = pathmatch(szOrigPath, &szMatchPath, true, szPathMatchBuf, sizeof(szPathMatchBuf)); + if (pmStat != kPathFailed) + { + if (realpath(szMatchPath ? szMatchPath : szOrigPath, szRealPath) == szRealPath) + { + Q_strncpy(szFinalPath, szRealPath, uiFinalPathCapacity); + return szFinalPath; + } + } + + return nullptr; + } + + WRAP(freopen, FILE *, const char *path, const char *mode, FILE *stream) + { + // if mode does not have w, a, or +, it's open for read. + bool bAllowBasenameMismatch = strpbrk(mode, "wa+") != nullptr; + CWrap mpath(path, bAllowBasenameMismatch); + + return CALL(freopen)(mpath, mode, stream); + } + + WRAP(fopen, FILE *, const char *path, const char *mode) + { + // if mode does not have w, a, or +, it's open for read. + bool bAllowBasenameMismatch = strpbrk(mode, "wa+") != nullptr; + CWrap mpath(path, bAllowBasenameMismatch); + + return CALL(fopen)(mpath, mode); + } + + WRAP(fopen64, FILE *, const char *path, const char *mode) + { + // if mode does not have w, a, or +, it's open for read. + bool bAllowBasenameMismatch = strpbrk(mode, "wa+") != nullptr; + CWrap mpath(path, bAllowBasenameMismatch); + + return CALL(fopen64)(mpath, mode); + } + + WRAP(open, int, const char *pathname, int flags, mode_t mode) + { + bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0); + CWrap mpath(pathname, bAllowBasenameMismatch); + return CALL(open)(mpath, flags, mode); + } + + WRAP(open64, int, const char *pathname, int flags, mode_t mode) + { + bool bAllowBasenameMismatch = ((flags & (O_WRONLY | O_RDWR)) != 0); + CWrap mpath(pathname, bAllowBasenameMismatch); + return CALL(open64)(mpath, flags, mode); + } + + int __wrap_creat(const char *pathname, mode_t mode) + { + return __wrap_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); + } + + int __wrap_access(const char *pathname, int mode) + { + return __real_access(CWrap(pathname, false), mode); + } + + WRAP(stat, int, const char *path, struct stat *buf) + { + return CALL(stat)(CWrap(path, false), buf); + } + + WRAP(lstat, int, const char *path, struct stat *buf) + { + return CALL(lstat)(CWrap(path, false), buf); + } + + WRAP(scandir, int, const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) + { + return CALL(scandir)(CWrap(dirp, false), namelist, filter, compar); + } + + WRAP(opendir, DIR *, const char *name) + { + return CALL(opendir)(CWrap(name, false)); + } + + WRAP(__xstat, int, int __ver, __const char *__filename, struct stat *__stat_buf) + { + return CALL(__xstat)(__ver, CWrap(__filename, false), __stat_buf); + } + + WRAP(__lxstat, int, int __ver, __const char *__filename, struct stat *__stat_buf) + { + return CALL(__lxstat)(__ver, CWrap(__filename, false), __stat_buf); + } + + WRAP(__xstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf) + { + return CALL(__xstat64)(__ver, CWrap(__filename, false), __stat_buf); + } + + WRAP(__lxstat64, int, int __ver, __const char *__filename, struct stat *__stat_buf) + { + return CALL(__lxstat64)(__ver, CWrap(__filename, false), __stat_buf); + } + + WRAP(chmod, int, const char *path, mode_t mode) + { + return CALL(chmod)(CWrap(path, false), mode); + } + + WRAP(chown, int, const char *path, uid_t owner, gid_t group) + { + return CALL(chown)(CWrap(path, false), owner, group); + } + + WRAP(lchown, int, const char *path, uid_t owner, gid_t group) + { + return CALL(lchown)(CWrap(path, false), owner, group); + } + + WRAP(symlink, int, const char *oldpath, const char *newpath) + { + return CALL(symlink)(CWrap(oldpath, false), CWrap(newpath, true)); + } + + WRAP(link, int, const char *oldpath, const char *newpath) + { + return CALL(link)(CWrap(oldpath, false), CWrap(newpath, true)); + } + + WRAP(mknod, int, const char *pathname, mode_t mode, dev_t dev) + { + return CALL(mknod)(CWrap(pathname, true), mode, dev); + } + + WRAP(mount, int, const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data) + { + return CALL(mount)(CWrap(source, false), CWrap(target, false), filesystemtype, mountflags, data); + } + + WRAP(unlink, int, const char *pathname) + { + return CALL(unlink)(CWrap(pathname, false)); + } + + WRAP(mkfifo, int, const char *pathname, mode_t mode) + { + return CALL(mkfifo)(CWrap(pathname, true), mode); + } + + WRAP(rename, int, const char *oldpath, const char *newpath) + { + return CALL(rename)(CWrap(oldpath, false), CWrap(newpath, true)); + } + + WRAP(utime, int, const char *filename, const struct utimbuf *times) + { + return CALL(utime)(CWrap(filename, false), times); + } + + WRAP(utimes, int, const char *filename, const struct timeval times[2]) + { + return CALL(utimes)(CWrap(filename, false), times); + } + + //WRAP(realpath, char *, const char *path, char *resolved_path) + //{ + // return CALL(realpath)(CWrap(path, true), resolved_path); + //} + + WRAP(mkdir, int, const char *pathname, mode_t mode) + { + return CALL(mkdir)(CWrap(pathname, true), mode); + } + + WRAP(rmdir, char *, const char *pathname) + { + return CALL(rmdir)(CWrap(pathname, false)); + } + +}; // extern "C" + +#endif // _WIN32 diff --git a/rehlds/filesystem/FileSystem_Stdio/src/pathmatch_casefolding.h b/rehlds/filesystem/FileSystem_Stdio/src/pathmatch_casefolding.h new file mode 100644 index 0000000..6b9056b --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/pathmatch_casefolding.h @@ -0,0 +1,2031 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +* This file was originally part of PhysicsFS (http://icculus.org/physfs/) +* (zlib license, even if Ryan hadn't written it originally.) +* +* This data was originally generated by physfs/extras/makecasefoldhashtable.pl +* +*/ + +static const CaseFoldMapping case_fold_000[] = { + { 0x0202, 0x0203, 0x0000, 0x0000 }, + { 0x0404, 0x0454, 0x0000, 0x0000 }, + { 0x1E1E, 0x1E1F, 0x0000, 0x0000 }, + { 0x2C2C, 0x2C5C, 0x0000, 0x0000 }, + { 0x10404, 0x1042C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_001[] = { + { 0x0100, 0x0101, 0x0000, 0x0000 }, + { 0x0405, 0x0455, 0x0000, 0x0000 }, + { 0x0504, 0x0505, 0x0000, 0x0000 }, + { 0x2C2D, 0x2C5D, 0x0000, 0x0000 }, + { 0x10405, 0x1042D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_002[] = { + { 0x0200, 0x0201, 0x0000, 0x0000 }, + { 0x0406, 0x0456, 0x0000, 0x0000 }, + { 0x1E1C, 0x1E1D, 0x0000, 0x0000 }, + { 0x1F1D, 0x1F15, 0x0000, 0x0000 }, + { 0x2C2E, 0x2C5E, 0x0000, 0x0000 }, + { 0x10406, 0x1042E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_003[] = { + { 0x0102, 0x0103, 0x0000, 0x0000 }, + { 0x0407, 0x0457, 0x0000, 0x0000 }, + { 0x0506, 0x0507, 0x0000, 0x0000 }, + { 0x1F1C, 0x1F14, 0x0000, 0x0000 }, + { 0x10407, 0x1042F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_004[] = { + { 0x0206, 0x0207, 0x0000, 0x0000 }, + { 0x0400, 0x0450, 0x0000, 0x0000 }, + { 0x1E1A, 0x1E1B, 0x0000, 0x0000 }, + { 0x1F1B, 0x1F13, 0x0000, 0x0000 }, + { 0x2C28, 0x2C58, 0x0000, 0x0000 }, + { 0x10400, 0x10428, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_005[] = { + { 0x0104, 0x0105, 0x0000, 0x0000 }, + { 0x0401, 0x0451, 0x0000, 0x0000 }, + { 0x0500, 0x0501, 0x0000, 0x0000 }, + { 0x1F1A, 0x1F12, 0x0000, 0x0000 }, + { 0x2C29, 0x2C59, 0x0000, 0x0000 }, + { 0x10401, 0x10429, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_006[] = { + { 0x0204, 0x0205, 0x0000, 0x0000 }, + { 0x0402, 0x0452, 0x0000, 0x0000 }, + { 0x1E18, 0x1E19, 0x0000, 0x0000 }, + { 0x1F19, 0x1F11, 0x0000, 0x0000 }, + { 0x2C2A, 0x2C5A, 0x0000, 0x0000 }, + { 0x10402, 0x1042A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_007[] = { + { 0x0106, 0x0107, 0x0000, 0x0000 }, + { 0x0403, 0x0453, 0x0000, 0x0000 }, + { 0x0502, 0x0503, 0x0000, 0x0000 }, + { 0x1F18, 0x1F10, 0x0000, 0x0000 }, + { 0x2126, 0x03C9, 0x0000, 0x0000 }, + { 0x2C2B, 0x2C5B, 0x0000, 0x0000 }, + { 0x10403, 0x1042B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_008[] = { + { 0x020A, 0x020B, 0x0000, 0x0000 }, + { 0x040C, 0x045C, 0x0000, 0x0000 }, + { 0x1E16, 0x1E17, 0x0000, 0x0000 }, + { 0x2C24, 0x2C54, 0x0000, 0x0000 }, + { 0x1040C, 0x10434, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_009[] = { + { 0x0108, 0x0109, 0x0000, 0x0000 }, + { 0x040D, 0x045D, 0x0000, 0x0000 }, + { 0x050C, 0x050D, 0x0000, 0x0000 }, + { 0x2C25, 0x2C55, 0x0000, 0x0000 }, + { 0x1040D, 0x10435, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_010[] = { + { 0x0208, 0x0209, 0x0000, 0x0000 }, + { 0x040E, 0x045E, 0x0000, 0x0000 }, + { 0x1E14, 0x1E15, 0x0000, 0x0000 }, + { 0x212B, 0x00E5, 0x0000, 0x0000 }, + { 0x2C26, 0x2C56, 0x0000, 0x0000 }, + { 0x1040E, 0x10436, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_011[] = { + { 0x010A, 0x010B, 0x0000, 0x0000 }, + { 0x040F, 0x045F, 0x0000, 0x0000 }, + { 0x050E, 0x050F, 0x0000, 0x0000 }, + { 0x212A, 0x006B, 0x0000, 0x0000 }, + { 0x2C27, 0x2C57, 0x0000, 0x0000 }, + { 0x1040F, 0x10437, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_012[] = { + { 0x020E, 0x020F, 0x0000, 0x0000 }, + { 0x0408, 0x0458, 0x0000, 0x0000 }, + { 0x1E12, 0x1E13, 0x0000, 0x0000 }, + { 0x2C20, 0x2C50, 0x0000, 0x0000 }, + { 0x10408, 0x10430, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_013[] = { + { 0x010C, 0x010D, 0x0000, 0x0000 }, + { 0x0409, 0x0459, 0x0000, 0x0000 }, + { 0x0508, 0x0509, 0x0000, 0x0000 }, + { 0x2C21, 0x2C51, 0x0000, 0x0000 }, + { 0x10409, 0x10431, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_014[] = { + { 0x020C, 0x020D, 0x0000, 0x0000 }, + { 0x040A, 0x045A, 0x0000, 0x0000 }, + { 0x1E10, 0x1E11, 0x0000, 0x0000 }, + { 0x2C22, 0x2C52, 0x0000, 0x0000 }, + { 0x1040A, 0x10432, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_015[] = { + { 0x010E, 0x010F, 0x0000, 0x0000 }, + { 0x040B, 0x045B, 0x0000, 0x0000 }, + { 0x050A, 0x050B, 0x0000, 0x0000 }, + { 0x2C23, 0x2C53, 0x0000, 0x0000 }, + { 0x1040B, 0x10433, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_016[] = { + { 0x0212, 0x0213, 0x0000, 0x0000 }, + { 0x0414, 0x0434, 0x0000, 0x0000 }, + { 0x1E0E, 0x1E0F, 0x0000, 0x0000 }, + { 0x1F0F, 0x1F07, 0x0000, 0x0000 }, + { 0x10414, 0x1043C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_017[] = { + { 0x0110, 0x0111, 0x0000, 0x0000 }, + { 0x0415, 0x0435, 0x0000, 0x0000 }, + { 0x1F0E, 0x1F06, 0x0000, 0x0000 }, + { 0x10415, 0x1043D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_018[] = { + { 0x0210, 0x0211, 0x0000, 0x0000 }, + { 0x0416, 0x0436, 0x0000, 0x0000 }, + { 0x1E0C, 0x1E0D, 0x0000, 0x0000 }, + { 0x1F0D, 0x1F05, 0x0000, 0x0000 }, + { 0x10416, 0x1043E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_019[] = { + { 0x0112, 0x0113, 0x0000, 0x0000 }, + { 0x0417, 0x0437, 0x0000, 0x0000 }, + { 0x1F0C, 0x1F04, 0x0000, 0x0000 }, + { 0x10417, 0x1043F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_020[] = { + { 0x0216, 0x0217, 0x0000, 0x0000 }, + { 0x0410, 0x0430, 0x0000, 0x0000 }, + { 0x1E0A, 0x1E0B, 0x0000, 0x0000 }, + { 0x1F0B, 0x1F03, 0x0000, 0x0000 }, + { 0x10410, 0x10438, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_021[] = { + { 0x0114, 0x0115, 0x0000, 0x0000 }, + { 0x0411, 0x0431, 0x0000, 0x0000 }, + { 0x1F0A, 0x1F02, 0x0000, 0x0000 }, + { 0x10411, 0x10439, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_022[] = { + { 0x0214, 0x0215, 0x0000, 0x0000 }, + { 0x0412, 0x0432, 0x0000, 0x0000 }, + { 0x1E08, 0x1E09, 0x0000, 0x0000 }, + { 0x1F09, 0x1F01, 0x0000, 0x0000 }, + { 0x10412, 0x1043A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_023[] = { + { 0x0116, 0x0117, 0x0000, 0x0000 }, + { 0x0413, 0x0433, 0x0000, 0x0000 }, + { 0x1F08, 0x1F00, 0x0000, 0x0000 }, + { 0x10413, 0x1043B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_024[] = { + { 0x021A, 0x021B, 0x0000, 0x0000 }, + { 0x041C, 0x043C, 0x0000, 0x0000 }, + { 0x1E06, 0x1E07, 0x0000, 0x0000 }, + { 0x1041C, 0x10444, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_025[] = { + { 0x0118, 0x0119, 0x0000, 0x0000 }, + { 0x041D, 0x043D, 0x0000, 0x0000 }, + { 0x1041D, 0x10445, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_026[] = { + { 0x0218, 0x0219, 0x0000, 0x0000 }, + { 0x041E, 0x043E, 0x0000, 0x0000 }, + { 0x1E04, 0x1E05, 0x0000, 0x0000 }, + { 0x1041E, 0x10446, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_027[] = { + { 0x011A, 0x011B, 0x0000, 0x0000 }, + { 0x041F, 0x043F, 0x0000, 0x0000 }, + { 0x1041F, 0x10447, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_028[] = { + { 0x021E, 0x021F, 0x0000, 0x0000 }, + { 0x0418, 0x0438, 0x0000, 0x0000 }, + { 0x1E02, 0x1E03, 0x0000, 0x0000 }, + { 0x10418, 0x10440, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_029[] = { + { 0x011C, 0x011D, 0x0000, 0x0000 }, + { 0x0419, 0x0439, 0x0000, 0x0000 }, + { 0x10419, 0x10441, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_030[] = { + { 0x021C, 0x021D, 0x0000, 0x0000 }, + { 0x041A, 0x043A, 0x0000, 0x0000 }, + { 0x1E00, 0x1E01, 0x0000, 0x0000 }, + { 0x1041A, 0x10442, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_031[] = { + { 0x011E, 0x011F, 0x0000, 0x0000 }, + { 0x041B, 0x043B, 0x0000, 0x0000 }, + { 0x1041B, 0x10443, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_032[] = { + { 0x0222, 0x0223, 0x0000, 0x0000 }, + { 0x0424, 0x0444, 0x0000, 0x0000 }, + { 0x1E3E, 0x1E3F, 0x0000, 0x0000 }, + { 0x1F3F, 0x1F37, 0x0000, 0x0000 }, + { 0x2C0C, 0x2C3C, 0x0000, 0x0000 }, + { 0x10424, 0x1044C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_033[] = { + { 0x0120, 0x0121, 0x0000, 0x0000 }, + { 0x0425, 0x0445, 0x0000, 0x0000 }, + { 0x1F3E, 0x1F36, 0x0000, 0x0000 }, + { 0x2C0D, 0x2C3D, 0x0000, 0x0000 }, + { 0x10425, 0x1044D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_034[] = { + { 0x0220, 0x019E, 0x0000, 0x0000 }, + { 0x0426, 0x0446, 0x0000, 0x0000 }, + { 0x1E3C, 0x1E3D, 0x0000, 0x0000 }, + { 0x1F3D, 0x1F35, 0x0000, 0x0000 }, + { 0x2C0E, 0x2C3E, 0x0000, 0x0000 }, + { 0x10426, 0x1044E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_035[] = { + { 0x0122, 0x0123, 0x0000, 0x0000 }, + { 0x0427, 0x0447, 0x0000, 0x0000 }, + { 0x1F3C, 0x1F34, 0x0000, 0x0000 }, + { 0x2C0F, 0x2C3F, 0x0000, 0x0000 }, + { 0x10427, 0x1044F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_036[] = { + { 0x0226, 0x0227, 0x0000, 0x0000 }, + { 0x0420, 0x0440, 0x0000, 0x0000 }, + { 0x1E3A, 0x1E3B, 0x0000, 0x0000 }, + { 0x1F3B, 0x1F33, 0x0000, 0x0000 }, + { 0x2C08, 0x2C38, 0x0000, 0x0000 }, + { 0x10420, 0x10448, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_037[] = { + { 0x0124, 0x0125, 0x0000, 0x0000 }, + { 0x0421, 0x0441, 0x0000, 0x0000 }, + { 0x1F3A, 0x1F32, 0x0000, 0x0000 }, + { 0x2C09, 0x2C39, 0x0000, 0x0000 }, + { 0x10421, 0x10449, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_038[] = { + { 0x0224, 0x0225, 0x0000, 0x0000 }, + { 0x0422, 0x0442, 0x0000, 0x0000 }, + { 0x1E38, 0x1E39, 0x0000, 0x0000 }, + { 0x1F39, 0x1F31, 0x0000, 0x0000 }, + { 0x2C0A, 0x2C3A, 0x0000, 0x0000 }, + { 0x10422, 0x1044A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_039[] = { + { 0x0126, 0x0127, 0x0000, 0x0000 }, + { 0x0423, 0x0443, 0x0000, 0x0000 }, + { 0x1F38, 0x1F30, 0x0000, 0x0000 }, + { 0x2C0B, 0x2C3B, 0x0000, 0x0000 }, + { 0x10423, 0x1044B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_040[] = { + { 0x022A, 0x022B, 0x0000, 0x0000 }, + { 0x042C, 0x044C, 0x0000, 0x0000 }, + { 0x1E36, 0x1E37, 0x0000, 0x0000 }, + { 0x2C04, 0x2C34, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_041[] = { + { 0x0128, 0x0129, 0x0000, 0x0000 }, + { 0x042D, 0x044D, 0x0000, 0x0000 }, + { 0x2C05, 0x2C35, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_042[] = { + { 0x0228, 0x0229, 0x0000, 0x0000 }, + { 0x042E, 0x044E, 0x0000, 0x0000 }, + { 0x1E34, 0x1E35, 0x0000, 0x0000 }, + { 0x2C06, 0x2C36, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_043[] = { + { 0x012A, 0x012B, 0x0000, 0x0000 }, + { 0x042F, 0x044F, 0x0000, 0x0000 }, + { 0x2C07, 0x2C37, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_044[] = { + { 0x022E, 0x022F, 0x0000, 0x0000 }, + { 0x0428, 0x0448, 0x0000, 0x0000 }, + { 0x1E32, 0x1E33, 0x0000, 0x0000 }, + { 0x2C00, 0x2C30, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_045[] = { + { 0x012C, 0x012D, 0x0000, 0x0000 }, + { 0x0429, 0x0449, 0x0000, 0x0000 }, + { 0x2C01, 0x2C31, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_046[] = { + { 0x022C, 0x022D, 0x0000, 0x0000 }, + { 0x042A, 0x044A, 0x0000, 0x0000 }, + { 0x1E30, 0x1E31, 0x0000, 0x0000 }, + { 0x2C02, 0x2C32, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_047[] = { + { 0x012E, 0x012F, 0x0000, 0x0000 }, + { 0x042B, 0x044B, 0x0000, 0x0000 }, + { 0x2C03, 0x2C33, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_048[] = { + { 0x0232, 0x0233, 0x0000, 0x0000 }, + { 0x0535, 0x0565, 0x0000, 0x0000 }, + { 0x1E2E, 0x1E2F, 0x0000, 0x0000 }, + { 0x1F2F, 0x1F27, 0x0000, 0x0000 }, + { 0x2C1C, 0x2C4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_049[] = { + { 0x0130, 0x0069, 0x0307, 0x0000 }, + { 0x0534, 0x0564, 0x0000, 0x0000 }, + { 0x1F2E, 0x1F26, 0x0000, 0x0000 }, + { 0x2C1D, 0x2C4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_050[] = { + { 0x0230, 0x0231, 0x0000, 0x0000 }, + { 0x0537, 0x0567, 0x0000, 0x0000 }, + { 0x1E2C, 0x1E2D, 0x0000, 0x0000 }, + { 0x1F2D, 0x1F25, 0x0000, 0x0000 }, + { 0x2C1E, 0x2C4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_051[] = { + { 0x0132, 0x0133, 0x0000, 0x0000 }, + { 0x0536, 0x0566, 0x0000, 0x0000 }, + { 0x1F2C, 0x1F24, 0x0000, 0x0000 }, + { 0x2C1F, 0x2C4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_052[] = { + { 0x0531, 0x0561, 0x0000, 0x0000 }, + { 0x1E2A, 0x1E2B, 0x0000, 0x0000 }, + { 0x1F2B, 0x1F23, 0x0000, 0x0000 }, + { 0x2C18, 0x2C48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_053[] = { + { 0x0134, 0x0135, 0x0000, 0x0000 }, + { 0x1F2A, 0x1F22, 0x0000, 0x0000 }, + { 0x2C19, 0x2C49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_054[] = { + { 0x0533, 0x0563, 0x0000, 0x0000 }, + { 0x1E28, 0x1E29, 0x0000, 0x0000 }, + { 0x1F29, 0x1F21, 0x0000, 0x0000 }, + { 0x2C1A, 0x2C4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_055[] = { + { 0x0136, 0x0137, 0x0000, 0x0000 }, + { 0x0532, 0x0562, 0x0000, 0x0000 }, + { 0x1F28, 0x1F20, 0x0000, 0x0000 }, + { 0x2C1B, 0x2C4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_056[] = { + { 0x0139, 0x013A, 0x0000, 0x0000 }, + { 0x053D, 0x056D, 0x0000, 0x0000 }, + { 0x1E26, 0x1E27, 0x0000, 0x0000 }, + { 0x2C14, 0x2C44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_057[] = { + { 0x023B, 0x023C, 0x0000, 0x0000 }, + { 0x053C, 0x056C, 0x0000, 0x0000 }, + { 0x2C15, 0x2C45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_058[] = { + { 0x013B, 0x013C, 0x0000, 0x0000 }, + { 0x053F, 0x056F, 0x0000, 0x0000 }, + { 0x1E24, 0x1E25, 0x0000, 0x0000 }, + { 0x2C16, 0x2C46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_059[] = { + { 0x053E, 0x056E, 0x0000, 0x0000 }, + { 0x2C17, 0x2C47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_060[] = { + { 0x013D, 0x013E, 0x0000, 0x0000 }, + { 0x0539, 0x0569, 0x0000, 0x0000 }, + { 0x1E22, 0x1E23, 0x0000, 0x0000 }, + { 0x2C10, 0x2C40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_061[] = { + { 0x0538, 0x0568, 0x0000, 0x0000 }, + { 0x2C11, 0x2C41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_062[] = { + { 0x013F, 0x0140, 0x0000, 0x0000 }, + { 0x053B, 0x056B, 0x0000, 0x0000 }, + { 0x1E20, 0x1E21, 0x0000, 0x0000 }, + { 0x2C12, 0x2C42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_063[] = { + { 0x023D, 0x019A, 0x0000, 0x0000 }, + { 0x053A, 0x056A, 0x0000, 0x0000 }, + { 0x2C13, 0x2C43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_064[] = { + { 0x0141, 0x0142, 0x0000, 0x0000 }, + { 0x0545, 0x0575, 0x0000, 0x0000 }, + { 0x1E5E, 0x1E5F, 0x0000, 0x0000 }, + { 0x1F5F, 0x1F57, 0x0000, 0x0000 }, + { 0x2161, 0x2171, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_065[] = { + { 0x0041, 0x0061, 0x0000, 0x0000 }, + { 0x0544, 0x0574, 0x0000, 0x0000 }, + { 0x2160, 0x2170, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_066[] = { + { 0x0042, 0x0062, 0x0000, 0x0000 }, + { 0x0143, 0x0144, 0x0000, 0x0000 }, + { 0x0547, 0x0577, 0x0000, 0x0000 }, + { 0x1E5C, 0x1E5D, 0x0000, 0x0000 }, + { 0x1F5D, 0x1F55, 0x0000, 0x0000 }, + { 0x2163, 0x2173, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_067[] = { + { 0x0043, 0x0063, 0x0000, 0x0000 }, + { 0x0241, 0x0294, 0x0000, 0x0000 }, + { 0x0546, 0x0576, 0x0000, 0x0000 }, + { 0x2162, 0x2172, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_068[] = { + { 0x0044, 0x0064, 0x0000, 0x0000 }, + { 0x0145, 0x0146, 0x0000, 0x0000 }, + { 0x0541, 0x0571, 0x0000, 0x0000 }, + { 0x1E5A, 0x1E5B, 0x0000, 0x0000 }, + { 0x1F5B, 0x1F53, 0x0000, 0x0000 }, + { 0x2165, 0x2175, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_069[] = { + { 0x0045, 0x0065, 0x0000, 0x0000 }, + { 0x0540, 0x0570, 0x0000, 0x0000 }, + { 0x2164, 0x2174, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_070[] = { + { 0x0046, 0x0066, 0x0000, 0x0000 }, + { 0x0147, 0x0148, 0x0000, 0x0000 }, + { 0x0345, 0x03B9, 0x0000, 0x0000 }, + { 0x0543, 0x0573, 0x0000, 0x0000 }, + { 0x1E58, 0x1E59, 0x0000, 0x0000 }, + { 0x1F59, 0x1F51, 0x0000, 0x0000 }, + { 0x2167, 0x2177, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_071[] = { + { 0x0047, 0x0067, 0x0000, 0x0000 }, + { 0x0542, 0x0572, 0x0000, 0x0000 }, + { 0x2166, 0x2176, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_072[] = { + { 0x0048, 0x0068, 0x0000, 0x0000 }, + { 0x0149, 0x02BC, 0x006E, 0x0000 }, + { 0x054D, 0x057D, 0x0000, 0x0000 }, + { 0x1E56, 0x1E57, 0x0000, 0x0000 }, + { 0x2169, 0x2179, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_073[] = { + { 0x0049, 0x0069, 0x0000, 0x0000 }, + { 0x054C, 0x057C, 0x0000, 0x0000 }, + { 0x1F56, 0x03C5, 0x0313, 0x0342 }, + { 0x2168, 0x2178, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_074[] = { + { 0x004A, 0x006A, 0x0000, 0x0000 }, + { 0x054F, 0x057F, 0x0000, 0x0000 }, + { 0x1E54, 0x1E55, 0x0000, 0x0000 }, + { 0x216B, 0x217B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_075[] = { + { 0x004B, 0x006B, 0x0000, 0x0000 }, + { 0x014A, 0x014B, 0x0000, 0x0000 }, + { 0x054E, 0x057E, 0x0000, 0x0000 }, + { 0x1F54, 0x03C5, 0x0313, 0x0301 }, + { 0x216A, 0x217A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_076[] = { + { 0x004C, 0x006C, 0x0000, 0x0000 }, + { 0x0549, 0x0579, 0x0000, 0x0000 }, + { 0x1E52, 0x1E53, 0x0000, 0x0000 }, + { 0x216D, 0x217D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_077[] = { + { 0x004D, 0x006D, 0x0000, 0x0000 }, + { 0x014C, 0x014D, 0x0000, 0x0000 }, + { 0x0548, 0x0578, 0x0000, 0x0000 }, + { 0x1F52, 0x03C5, 0x0313, 0x0300 }, + { 0x216C, 0x217C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_078[] = { + { 0x004E, 0x006E, 0x0000, 0x0000 }, + { 0x054B, 0x057B, 0x0000, 0x0000 }, + { 0x1E50, 0x1E51, 0x0000, 0x0000 }, + { 0x216F, 0x217F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_079[] = { + { 0x004F, 0x006F, 0x0000, 0x0000 }, + { 0x014E, 0x014F, 0x0000, 0x0000 }, + { 0x054A, 0x057A, 0x0000, 0x0000 }, + { 0x1F50, 0x03C5, 0x0313, 0x0000 }, + { 0x216E, 0x217E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_080[] = { + { 0x0050, 0x0070, 0x0000, 0x0000 }, + { 0x0555, 0x0585, 0x0000, 0x0000 }, + { 0x1E4E, 0x1E4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_081[] = { + { 0x0051, 0x0071, 0x0000, 0x0000 }, + { 0x0150, 0x0151, 0x0000, 0x0000 }, + { 0x0554, 0x0584, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_082[] = { + { 0x0052, 0x0072, 0x0000, 0x0000 }, + { 0x1E4C, 0x1E4D, 0x0000, 0x0000 }, + { 0x1F4D, 0x1F45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_083[] = { + { 0x0053, 0x0073, 0x0000, 0x0000 }, + { 0x0152, 0x0153, 0x0000, 0x0000 }, + { 0x0556, 0x0586, 0x0000, 0x0000 }, + { 0x1F4C, 0x1F44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_084[] = { + { 0x0054, 0x0074, 0x0000, 0x0000 }, + { 0x0551, 0x0581, 0x0000, 0x0000 }, + { 0x1E4A, 0x1E4B, 0x0000, 0x0000 }, + { 0x1F4B, 0x1F43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_085[] = { + { 0x0055, 0x0075, 0x0000, 0x0000 }, + { 0x0154, 0x0155, 0x0000, 0x0000 }, + { 0x0550, 0x0580, 0x0000, 0x0000 }, + { 0x1F4A, 0x1F42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_086[] = { + { 0x0056, 0x0076, 0x0000, 0x0000 }, + { 0x0553, 0x0583, 0x0000, 0x0000 }, + { 0x1E48, 0x1E49, 0x0000, 0x0000 }, + { 0x1F49, 0x1F41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_087[] = { + { 0x0057, 0x0077, 0x0000, 0x0000 }, + { 0x0156, 0x0157, 0x0000, 0x0000 }, + { 0x0552, 0x0582, 0x0000, 0x0000 }, + { 0x1F48, 0x1F40, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_088[] = { + { 0x0058, 0x0078, 0x0000, 0x0000 }, + { 0x1E46, 0x1E47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_089[] = { + { 0x0059, 0x0079, 0x0000, 0x0000 }, + { 0x0158, 0x0159, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_090[] = { + { 0x005A, 0x007A, 0x0000, 0x0000 }, + { 0x1E44, 0x1E45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_091[] = { + { 0x015A, 0x015B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_092[] = { + { 0x1E42, 0x1E43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_093[] = { + { 0x015C, 0x015D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_094[] = { + { 0x1E40, 0x1E41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_095[] = { + { 0x015E, 0x015F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_096[] = { + { 0x0464, 0x0465, 0x0000, 0x0000 }, + { 0x1E7E, 0x1E7F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_097[] = { + { 0x0160, 0x0161, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_098[] = { + { 0x0466, 0x0467, 0x0000, 0x0000 }, + { 0x1E7C, 0x1E7D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_099[] = { + { 0x0162, 0x0163, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_100[] = { + { 0x0460, 0x0461, 0x0000, 0x0000 }, + { 0x1E7A, 0x1E7B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_101[] = { + { 0x0164, 0x0165, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_102[] = { + { 0x0462, 0x0463, 0x0000, 0x0000 }, + { 0x1E78, 0x1E79, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_103[] = { + { 0x0166, 0x0167, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_104[] = { + { 0x046C, 0x046D, 0x0000, 0x0000 }, + { 0x1E76, 0x1E77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_105[] = { + { 0x0168, 0x0169, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_106[] = { + { 0x046E, 0x046F, 0x0000, 0x0000 }, + { 0x1E74, 0x1E75, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_107[] = { + { 0x016A, 0x016B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_108[] = { + { 0x0468, 0x0469, 0x0000, 0x0000 }, + { 0x1E72, 0x1E73, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_109[] = { + { 0x016C, 0x016D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_110[] = { + { 0x046A, 0x046B, 0x0000, 0x0000 }, + { 0x1E70, 0x1E71, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_111[] = { + { 0x016E, 0x016F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_112[] = { + { 0x0474, 0x0475, 0x0000, 0x0000 }, + { 0x1E6E, 0x1E6F, 0x0000, 0x0000 }, + { 0x1F6F, 0x1F67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_113[] = { + { 0x0170, 0x0171, 0x0000, 0x0000 }, + { 0x1F6E, 0x1F66, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_114[] = { + { 0x0476, 0x0477, 0x0000, 0x0000 }, + { 0x1E6C, 0x1E6D, 0x0000, 0x0000 }, + { 0x1F6D, 0x1F65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_115[] = { + { 0x0172, 0x0173, 0x0000, 0x0000 }, + { 0x1F6C, 0x1F64, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_116[] = { + { 0x0470, 0x0471, 0x0000, 0x0000 }, + { 0x1E6A, 0x1E6B, 0x0000, 0x0000 }, + { 0x1F6B, 0x1F63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_117[] = { + { 0x0174, 0x0175, 0x0000, 0x0000 }, + { 0x1F6A, 0x1F62, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_118[] = { + { 0x0472, 0x0473, 0x0000, 0x0000 }, + { 0x1E68, 0x1E69, 0x0000, 0x0000 }, + { 0x1F69, 0x1F61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_119[] = { + { 0x0176, 0x0177, 0x0000, 0x0000 }, + { 0x1F68, 0x1F60, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_120[] = { + { 0x0179, 0x017A, 0x0000, 0x0000 }, + { 0x047C, 0x047D, 0x0000, 0x0000 }, + { 0x1E66, 0x1E67, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_121[] = { + { 0x0178, 0x00FF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_122[] = { + { 0x017B, 0x017C, 0x0000, 0x0000 }, + { 0x047E, 0x047F, 0x0000, 0x0000 }, + { 0x1E64, 0x1E65, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_124[] = { + { 0x017D, 0x017E, 0x0000, 0x0000 }, + { 0x0478, 0x0479, 0x0000, 0x0000 }, + { 0x1E62, 0x1E63, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_126[] = { + { 0x017F, 0x0073, 0x0000, 0x0000 }, + { 0x047A, 0x047B, 0x0000, 0x0000 }, + { 0x1E60, 0x1E61, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_128[] = { + { 0x0181, 0x0253, 0x0000, 0x0000 }, + { 0x1F9F, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CAC, 0x2CAD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_129[] = { + { 0x1F9E, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_130[] = { + { 0x0587, 0x0565, 0x0582, 0x0000 }, + { 0x1F9D, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CAE, 0x2CAF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_131[] = { + { 0x0182, 0x0183, 0x0000, 0x0000 }, + { 0x1F9C, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_132[] = { + { 0x0480, 0x0481, 0x0000, 0x0000 }, + { 0x1E9A, 0x0061, 0x02BE, 0x0000 }, + { 0x1F9B, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA8, 0x2CA9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_133[] = { + { 0x0184, 0x0185, 0x0000, 0x0000 }, + { 0x0386, 0x03AC, 0x0000, 0x0000 }, + { 0x1E9B, 0x1E61, 0x0000, 0x0000 }, + { 0x1F9A, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_134[] = { + { 0x0187, 0x0188, 0x0000, 0x0000 }, + { 0x1E98, 0x0077, 0x030A, 0x0000 }, + { 0x1F99, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CAA, 0x2CAB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_135[] = { + { 0x0186, 0x0254, 0x0000, 0x0000 }, + { 0x1E99, 0x0079, 0x030A, 0x0000 }, + { 0x1F98, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_136[] = { + { 0x0189, 0x0256, 0x0000, 0x0000 }, + { 0x048C, 0x048D, 0x0000, 0x0000 }, + { 0x1E96, 0x0068, 0x0331, 0x0000 }, + { 0x1F97, 0x1F27, 0x03B9, 0x0000 }, + { 0x2CA4, 0x2CA5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_137[] = { + { 0x038A, 0x03AF, 0x0000, 0x0000 }, + { 0x1E97, 0x0074, 0x0308, 0x0000 }, + { 0x1F96, 0x1F26, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_138[] = { + { 0x018B, 0x018C, 0x0000, 0x0000 }, + { 0x0389, 0x03AE, 0x0000, 0x0000 }, + { 0x048E, 0x048F, 0x0000, 0x0000 }, + { 0x1E94, 0x1E95, 0x0000, 0x0000 }, + { 0x1F95, 0x1F25, 0x03B9, 0x0000 }, + { 0x2CA6, 0x2CA7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_139[] = { + { 0x018A, 0x0257, 0x0000, 0x0000 }, + { 0x0388, 0x03AD, 0x0000, 0x0000 }, + { 0x1F94, 0x1F24, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_140[] = { + { 0x038F, 0x03CE, 0x0000, 0x0000 }, + { 0x1E92, 0x1E93, 0x0000, 0x0000 }, + { 0x1F93, 0x1F23, 0x03B9, 0x0000 }, + { 0x2CA0, 0x2CA1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_141[] = { + { 0x038E, 0x03CD, 0x0000, 0x0000 }, + { 0x1F92, 0x1F22, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_142[] = { + { 0x018F, 0x0259, 0x0000, 0x0000 }, + { 0x048A, 0x048B, 0x0000, 0x0000 }, + { 0x1E90, 0x1E91, 0x0000, 0x0000 }, + { 0x1F91, 0x1F21, 0x03B9, 0x0000 }, + { 0x2CA2, 0x2CA3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_143[] = { + { 0x018E, 0x01DD, 0x0000, 0x0000 }, + { 0x038C, 0x03CC, 0x0000, 0x0000 }, + { 0x1F90, 0x1F20, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_144[] = { + { 0x0191, 0x0192, 0x0000, 0x0000 }, + { 0x0393, 0x03B3, 0x0000, 0x0000 }, + { 0x0494, 0x0495, 0x0000, 0x0000 }, + { 0x1E8E, 0x1E8F, 0x0000, 0x0000 }, + { 0x1F8F, 0x1F07, 0x03B9, 0x0000 }, + { 0x2CBC, 0x2CBD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_145[] = { + { 0x0190, 0x025B, 0x0000, 0x0000 }, + { 0x0392, 0x03B2, 0x0000, 0x0000 }, + { 0x1F8E, 0x1F06, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_146[] = { + { 0x0193, 0x0260, 0x0000, 0x0000 }, + { 0x0391, 0x03B1, 0x0000, 0x0000 }, + { 0x0496, 0x0497, 0x0000, 0x0000 }, + { 0x1E8C, 0x1E8D, 0x0000, 0x0000 }, + { 0x1F8D, 0x1F05, 0x03B9, 0x0000 }, + { 0x24B6, 0x24D0, 0x0000, 0x0000 }, + { 0x2CBE, 0x2CBF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_147[] = { + { 0x0390, 0x03B9, 0x0308, 0x0301 }, + { 0x1F8C, 0x1F04, 0x03B9, 0x0000 }, + { 0x24B7, 0x24D1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_148[] = { + { 0x0397, 0x03B7, 0x0000, 0x0000 }, + { 0x0490, 0x0491, 0x0000, 0x0000 }, + { 0x1E8A, 0x1E8B, 0x0000, 0x0000 }, + { 0x1F8B, 0x1F03, 0x03B9, 0x0000 }, + { 0x2CB8, 0x2CB9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_149[] = { + { 0x0194, 0x0263, 0x0000, 0x0000 }, + { 0x0396, 0x03B6, 0x0000, 0x0000 }, + { 0x1F8A, 0x1F02, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_150[] = { + { 0x0197, 0x0268, 0x0000, 0x0000 }, + { 0x0395, 0x03B5, 0x0000, 0x0000 }, + { 0x0492, 0x0493, 0x0000, 0x0000 }, + { 0x1E88, 0x1E89, 0x0000, 0x0000 }, + { 0x1F89, 0x1F01, 0x03B9, 0x0000 }, + { 0x2CBA, 0x2CBB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_151[] = { + { 0x0196, 0x0269, 0x0000, 0x0000 }, + { 0x0394, 0x03B4, 0x0000, 0x0000 }, + { 0x1F88, 0x1F00, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_152[] = { + { 0x039B, 0x03BB, 0x0000, 0x0000 }, + { 0x049C, 0x049D, 0x0000, 0x0000 }, + { 0x1E86, 0x1E87, 0x0000, 0x0000 }, + { 0x1F87, 0x1F07, 0x03B9, 0x0000 }, + { 0x24BC, 0x24D6, 0x0000, 0x0000 }, + { 0x2CB4, 0x2CB5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_153[] = { + { 0x0198, 0x0199, 0x0000, 0x0000 }, + { 0x039A, 0x03BA, 0x0000, 0x0000 }, + { 0x1F86, 0x1F06, 0x03B9, 0x0000 }, + { 0x24BD, 0x24D7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_154[] = { + { 0x0399, 0x03B9, 0x0000, 0x0000 }, + { 0x049E, 0x049F, 0x0000, 0x0000 }, + { 0x1E84, 0x1E85, 0x0000, 0x0000 }, + { 0x1F85, 0x1F05, 0x03B9, 0x0000 }, + { 0x24BE, 0x24D8, 0x0000, 0x0000 }, + { 0x2CB6, 0x2CB7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_155[] = { + { 0x0398, 0x03B8, 0x0000, 0x0000 }, + { 0x1F84, 0x1F04, 0x03B9, 0x0000 }, + { 0x24BF, 0x24D9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_156[] = { + { 0x019D, 0x0272, 0x0000, 0x0000 }, + { 0x039F, 0x03BF, 0x0000, 0x0000 }, + { 0x0498, 0x0499, 0x0000, 0x0000 }, + { 0x1E82, 0x1E83, 0x0000, 0x0000 }, + { 0x1F83, 0x1F03, 0x03B9, 0x0000 }, + { 0x24B8, 0x24D2, 0x0000, 0x0000 }, + { 0x2CB0, 0x2CB1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_157[] = { + { 0x019C, 0x026F, 0x0000, 0x0000 }, + { 0x039E, 0x03BE, 0x0000, 0x0000 }, + { 0x1F82, 0x1F02, 0x03B9, 0x0000 }, + { 0x24B9, 0x24D3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_158[] = { + { 0x019F, 0x0275, 0x0000, 0x0000 }, + { 0x039D, 0x03BD, 0x0000, 0x0000 }, + { 0x049A, 0x049B, 0x0000, 0x0000 }, + { 0x1E80, 0x1E81, 0x0000, 0x0000 }, + { 0x1F81, 0x1F01, 0x03B9, 0x0000 }, + { 0x24BA, 0x24D4, 0x0000, 0x0000 }, + { 0x2CB2, 0x2CB3, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_159[] = { + { 0x039C, 0x03BC, 0x0000, 0x0000 }, + { 0x1F80, 0x1F00, 0x03B9, 0x0000 }, + { 0x24BB, 0x24D5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_160[] = { + { 0x03A3, 0x03C3, 0x0000, 0x0000 }, + { 0x04A4, 0x04A5, 0x0000, 0x0000 }, + { 0x10B0, 0x2D10, 0x0000, 0x0000 }, + { 0x1EBE, 0x1EBF, 0x0000, 0x0000 }, + { 0x2C8C, 0x2C8D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_161[] = { + { 0x01A0, 0x01A1, 0x0000, 0x0000 }, + { 0x10B1, 0x2D11, 0x0000, 0x0000 }, + { 0x1FBE, 0x03B9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_162[] = { + { 0x03A1, 0x03C1, 0x0000, 0x0000 }, + { 0x04A6, 0x04A7, 0x0000, 0x0000 }, + { 0x10B2, 0x2D12, 0x0000, 0x0000 }, + { 0x1EBC, 0x1EBD, 0x0000, 0x0000 }, + { 0x2C8E, 0x2C8F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_163[] = { + { 0x01A2, 0x01A3, 0x0000, 0x0000 }, + { 0x03A0, 0x03C0, 0x0000, 0x0000 }, + { 0x10B3, 0x2D13, 0x0000, 0x0000 }, + { 0x1FBC, 0x03B1, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_164[] = { + { 0x03A7, 0x03C7, 0x0000, 0x0000 }, + { 0x04A0, 0x04A1, 0x0000, 0x0000 }, + { 0x10B4, 0x2D14, 0x0000, 0x0000 }, + { 0x1EBA, 0x1EBB, 0x0000, 0x0000 }, + { 0x1FBB, 0x1F71, 0x0000, 0x0000 }, + { 0x2C88, 0x2C89, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_165[] = { + { 0x01A4, 0x01A5, 0x0000, 0x0000 }, + { 0x03A6, 0x03C6, 0x0000, 0x0000 }, + { 0x10B5, 0x2D15, 0x0000, 0x0000 }, + { 0x1FBA, 0x1F70, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_166[] = { + { 0x01A7, 0x01A8, 0x0000, 0x0000 }, + { 0x03A5, 0x03C5, 0x0000, 0x0000 }, + { 0x04A2, 0x04A3, 0x0000, 0x0000 }, + { 0x10B6, 0x2D16, 0x0000, 0x0000 }, + { 0x1EB8, 0x1EB9, 0x0000, 0x0000 }, + { 0x1FB9, 0x1FB1, 0x0000, 0x0000 }, + { 0x2C8A, 0x2C8B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_167[] = { + { 0x01A6, 0x0280, 0x0000, 0x0000 }, + { 0x03A4, 0x03C4, 0x0000, 0x0000 }, + { 0x10B7, 0x2D17, 0x0000, 0x0000 }, + { 0x1FB8, 0x1FB0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_168[] = { + { 0x01A9, 0x0283, 0x0000, 0x0000 }, + { 0x03AB, 0x03CB, 0x0000, 0x0000 }, + { 0x04AC, 0x04AD, 0x0000, 0x0000 }, + { 0x10B8, 0x2D18, 0x0000, 0x0000 }, + { 0x1EB6, 0x1EB7, 0x0000, 0x0000 }, + { 0x1FB7, 0x03B1, 0x0342, 0x03B9 }, + { 0x2C84, 0x2C85, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_169[] = { + { 0x03AA, 0x03CA, 0x0000, 0x0000 }, + { 0x10B9, 0x2D19, 0x0000, 0x0000 }, + { 0x1FB6, 0x03B1, 0x0342, 0x0000 } +}; + +static const CaseFoldMapping case_fold_170[] = { + { 0x03A9, 0x03C9, 0x0000, 0x0000 }, + { 0x04AE, 0x04AF, 0x0000, 0x0000 }, + { 0x10BA, 0x2D1A, 0x0000, 0x0000 }, + { 0x1EB4, 0x1EB5, 0x0000, 0x0000 }, + { 0x2C86, 0x2C87, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_171[] = { + { 0x03A8, 0x03C8, 0x0000, 0x0000 }, + { 0x10BB, 0x2D1B, 0x0000, 0x0000 }, + { 0x1FB4, 0x03AC, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_172[] = { + { 0x04A8, 0x04A9, 0x0000, 0x0000 }, + { 0x10BC, 0x2D1C, 0x0000, 0x0000 }, + { 0x1EB2, 0x1EB3, 0x0000, 0x0000 }, + { 0x1FB3, 0x03B1, 0x03B9, 0x0000 }, + { 0x2C80, 0x2C81, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_173[] = { + { 0x01AC, 0x01AD, 0x0000, 0x0000 }, + { 0x10BD, 0x2D1D, 0x0000, 0x0000 }, + { 0x1FB2, 0x1F70, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_174[] = { + { 0x01AF, 0x01B0, 0x0000, 0x0000 }, + { 0x04AA, 0x04AB, 0x0000, 0x0000 }, + { 0x10BE, 0x2D1E, 0x0000, 0x0000 }, + { 0x1EB0, 0x1EB1, 0x0000, 0x0000 }, + { 0x2C82, 0x2C83, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_175[] = { + { 0x01AE, 0x0288, 0x0000, 0x0000 }, + { 0x10BF, 0x2D1F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_176[] = { + { 0x01B1, 0x028A, 0x0000, 0x0000 }, + { 0x04B4, 0x04B5, 0x0000, 0x0000 }, + { 0x10A0, 0x2D00, 0x0000, 0x0000 }, + { 0x1EAE, 0x1EAF, 0x0000, 0x0000 }, + { 0x1FAF, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C9C, 0x2C9D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_177[] = { + { 0x10A1, 0x2D01, 0x0000, 0x0000 }, + { 0x1FAE, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_178[] = { + { 0x01B3, 0x01B4, 0x0000, 0x0000 }, + { 0x04B6, 0x04B7, 0x0000, 0x0000 }, + { 0x10A2, 0x2D02, 0x0000, 0x0000 }, + { 0x1EAC, 0x1EAD, 0x0000, 0x0000 }, + { 0x1FAD, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C9E, 0x2C9F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_179[] = { + { 0x01B2, 0x028B, 0x0000, 0x0000 }, + { 0x03B0, 0x03C5, 0x0308, 0x0301 }, + { 0x10A3, 0x2D03, 0x0000, 0x0000 }, + { 0x1FAC, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_180[] = { + { 0x01B5, 0x01B6, 0x0000, 0x0000 }, + { 0x04B0, 0x04B1, 0x0000, 0x0000 }, + { 0x10A4, 0x2D04, 0x0000, 0x0000 }, + { 0x1EAA, 0x1EAB, 0x0000, 0x0000 }, + { 0x1FAB, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C98, 0x2C99, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_181[] = { + { 0x00B5, 0x03BC, 0x0000, 0x0000 }, + { 0x10A5, 0x2D05, 0x0000, 0x0000 }, + { 0x1FAA, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_182[] = { + { 0x01B7, 0x0292, 0x0000, 0x0000 }, + { 0x04B2, 0x04B3, 0x0000, 0x0000 }, + { 0x10A6, 0x2D06, 0x0000, 0x0000 }, + { 0x1EA8, 0x1EA9, 0x0000, 0x0000 }, + { 0x1FA9, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C9A, 0x2C9B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_183[] = { + { 0x10A7, 0x2D07, 0x0000, 0x0000 }, + { 0x1FA8, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_184[] = { + { 0x04BC, 0x04BD, 0x0000, 0x0000 }, + { 0x10A8, 0x2D08, 0x0000, 0x0000 }, + { 0x1EA6, 0x1EA7, 0x0000, 0x0000 }, + { 0x1FA7, 0x1F67, 0x03B9, 0x0000 }, + { 0x2C94, 0x2C95, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_185[] = { + { 0x01B8, 0x01B9, 0x0000, 0x0000 }, + { 0x10A9, 0x2D09, 0x0000, 0x0000 }, + { 0x1FA6, 0x1F66, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_186[] = { + { 0x04BE, 0x04BF, 0x0000, 0x0000 }, + { 0x10AA, 0x2D0A, 0x0000, 0x0000 }, + { 0x1EA4, 0x1EA5, 0x0000, 0x0000 }, + { 0x1FA5, 0x1F65, 0x03B9, 0x0000 }, + { 0x2C96, 0x2C97, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_187[] = { + { 0x10AB, 0x2D0B, 0x0000, 0x0000 }, + { 0x1FA4, 0x1F64, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_188[] = { + { 0x04B8, 0x04B9, 0x0000, 0x0000 }, + { 0x10AC, 0x2D0C, 0x0000, 0x0000 }, + { 0x1EA2, 0x1EA3, 0x0000, 0x0000 }, + { 0x1FA3, 0x1F63, 0x03B9, 0x0000 }, + { 0x2C90, 0x2C91, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_189[] = { + { 0x01BC, 0x01BD, 0x0000, 0x0000 }, + { 0x10AD, 0x2D0D, 0x0000, 0x0000 }, + { 0x1FA2, 0x1F62, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_190[] = { + { 0x04BA, 0x04BB, 0x0000, 0x0000 }, + { 0x10AE, 0x2D0E, 0x0000, 0x0000 }, + { 0x1EA0, 0x1EA1, 0x0000, 0x0000 }, + { 0x1FA1, 0x1F61, 0x03B9, 0x0000 }, + { 0x2C92, 0x2C93, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_191[] = { + { 0x10AF, 0x2D0F, 0x0000, 0x0000 }, + { 0x1FA0, 0x1F60, 0x03B9, 0x0000 } +}; + +static const CaseFoldMapping case_fold_192[] = { + { 0x00C0, 0x00E0, 0x0000, 0x0000 }, + { 0x1EDE, 0x1EDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_193[] = { + { 0x00C1, 0x00E1, 0x0000, 0x0000 }, + { 0x03C2, 0x03C3, 0x0000, 0x0000 }, + { 0x04C5, 0x04C6, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_194[] = { + { 0x00C2, 0x00E2, 0x0000, 0x0000 }, + { 0x1EDC, 0x1EDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_195[] = { + { 0x00C3, 0x00E3, 0x0000, 0x0000 }, + { 0x04C7, 0x04C8, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_196[] = { + { 0x00C4, 0x00E4, 0x0000, 0x0000 }, + { 0x01C5, 0x01C6, 0x0000, 0x0000 }, + { 0x1EDA, 0x1EDB, 0x0000, 0x0000 }, + { 0x1FDB, 0x1F77, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_197[] = { + { 0x00C5, 0x00E5, 0x0000, 0x0000 }, + { 0x01C4, 0x01C6, 0x0000, 0x0000 }, + { 0x04C1, 0x04C2, 0x0000, 0x0000 }, + { 0x1FDA, 0x1F76, 0x0000, 0x0000 }, + { 0xFF3A, 0xFF5A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_198[] = { + { 0x00C6, 0x00E6, 0x0000, 0x0000 }, + { 0x01C7, 0x01C9, 0x0000, 0x0000 }, + { 0x1ED8, 0x1ED9, 0x0000, 0x0000 }, + { 0x1FD9, 0x1FD1, 0x0000, 0x0000 }, + { 0xFF39, 0xFF59, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_199[] = { + { 0x00C7, 0x00E7, 0x0000, 0x0000 }, + { 0x04C3, 0x04C4, 0x0000, 0x0000 }, + { 0x1FD8, 0x1FD0, 0x0000, 0x0000 }, + { 0xFF38, 0xFF58, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_200[] = { + { 0x00C8, 0x00E8, 0x0000, 0x0000 }, + { 0x1ED6, 0x1ED7, 0x0000, 0x0000 }, + { 0x1FD7, 0x03B9, 0x0308, 0x0342 }, + { 0xFF37, 0xFF57, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_201[] = { + { 0x00C9, 0x00E9, 0x0000, 0x0000 }, + { 0x01C8, 0x01C9, 0x0000, 0x0000 }, + { 0x04CD, 0x04CE, 0x0000, 0x0000 }, + { 0x1FD6, 0x03B9, 0x0342, 0x0000 }, + { 0xFF36, 0xFF56, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_202[] = { + { 0x00CA, 0x00EA, 0x0000, 0x0000 }, + { 0x01CB, 0x01CC, 0x0000, 0x0000 }, + { 0x1ED4, 0x1ED5, 0x0000, 0x0000 }, + { 0xFF35, 0xFF55, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_203[] = { + { 0x00CB, 0x00EB, 0x0000, 0x0000 }, + { 0x01CA, 0x01CC, 0x0000, 0x0000 }, + { 0xFF34, 0xFF54, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_204[] = { + { 0x00CC, 0x00EC, 0x0000, 0x0000 }, + { 0x01CD, 0x01CE, 0x0000, 0x0000 }, + { 0x1ED2, 0x1ED3, 0x0000, 0x0000 }, + { 0x1FD3, 0x03B9, 0x0308, 0x0301 }, + { 0x2CE0, 0x2CE1, 0x0000, 0x0000 }, + { 0xFF33, 0xFF53, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_205[] = { + { 0x00CD, 0x00ED, 0x0000, 0x0000 }, + { 0x04C9, 0x04CA, 0x0000, 0x0000 }, + { 0x1FD2, 0x03B9, 0x0308, 0x0300 }, + { 0xFF32, 0xFF52, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_206[] = { + { 0x00CE, 0x00EE, 0x0000, 0x0000 }, + { 0x01CF, 0x01D0, 0x0000, 0x0000 }, + { 0x1ED0, 0x1ED1, 0x0000, 0x0000 }, + { 0x2CE2, 0x2CE3, 0x0000, 0x0000 }, + { 0xFF31, 0xFF51, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_207[] = { + { 0x00CF, 0x00EF, 0x0000, 0x0000 }, + { 0x04CB, 0x04CC, 0x0000, 0x0000 }, + { 0xFF30, 0xFF50, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_208[] = { + { 0x00D0, 0x00F0, 0x0000, 0x0000 }, + { 0x01D1, 0x01D2, 0x0000, 0x0000 }, + { 0x04D4, 0x04D5, 0x0000, 0x0000 }, + { 0x10C0, 0x2D20, 0x0000, 0x0000 }, + { 0x1ECE, 0x1ECF, 0x0000, 0x0000 }, + { 0xFF2F, 0xFF4F, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_209[] = { + { 0x00D1, 0x00F1, 0x0000, 0x0000 }, + { 0x10C1, 0x2D21, 0x0000, 0x0000 }, + { 0xFF2E, 0xFF4E, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_210[] = { + { 0x00D2, 0x00F2, 0x0000, 0x0000 }, + { 0x01D3, 0x01D4, 0x0000, 0x0000 }, + { 0x03D1, 0x03B8, 0x0000, 0x0000 }, + { 0x04D6, 0x04D7, 0x0000, 0x0000 }, + { 0x10C2, 0x2D22, 0x0000, 0x0000 }, + { 0x1ECC, 0x1ECD, 0x0000, 0x0000 }, + { 0xFF2D, 0xFF4D, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_211[] = { + { 0x00D3, 0x00F3, 0x0000, 0x0000 }, + { 0x03D0, 0x03B2, 0x0000, 0x0000 }, + { 0x10C3, 0x2D23, 0x0000, 0x0000 }, + { 0x1FCC, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF2C, 0xFF4C, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_212[] = { + { 0x00D4, 0x00F4, 0x0000, 0x0000 }, + { 0x01D5, 0x01D6, 0x0000, 0x0000 }, + { 0x04D0, 0x04D1, 0x0000, 0x0000 }, + { 0x10C4, 0x2D24, 0x0000, 0x0000 }, + { 0x1ECA, 0x1ECB, 0x0000, 0x0000 }, + { 0x1FCB, 0x1F75, 0x0000, 0x0000 }, + { 0xFF2B, 0xFF4B, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_213[] = { + { 0x00D5, 0x00F5, 0x0000, 0x0000 }, + { 0x03D6, 0x03C0, 0x0000, 0x0000 }, + { 0x10C5, 0x2D25, 0x0000, 0x0000 }, + { 0x1FCA, 0x1F74, 0x0000, 0x0000 }, + { 0xFF2A, 0xFF4A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_214[] = { + { 0x00D6, 0x00F6, 0x0000, 0x0000 }, + { 0x01D7, 0x01D8, 0x0000, 0x0000 }, + { 0x03D5, 0x03C6, 0x0000, 0x0000 }, + { 0x04D2, 0x04D3, 0x0000, 0x0000 }, + { 0x1EC8, 0x1EC9, 0x0000, 0x0000 }, + { 0x1FC9, 0x1F73, 0x0000, 0x0000 }, + { 0xFF29, 0xFF49, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_215[] = { + { 0x1FC8, 0x1F72, 0x0000, 0x0000 }, + { 0xFF28, 0xFF48, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_216[] = { + { 0x00D8, 0x00F8, 0x0000, 0x0000 }, + { 0x01D9, 0x01DA, 0x0000, 0x0000 }, + { 0x04DC, 0x04DD, 0x0000, 0x0000 }, + { 0x1EC6, 0x1EC7, 0x0000, 0x0000 }, + { 0x1FC7, 0x03B7, 0x0342, 0x03B9 }, + { 0xFF27, 0xFF47, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_217[] = { + { 0x00D9, 0x00F9, 0x0000, 0x0000 }, + { 0x03DA, 0x03DB, 0x0000, 0x0000 }, + { 0x1FC6, 0x03B7, 0x0342, 0x0000 }, + { 0xFF26, 0xFF46, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_218[] = { + { 0x00DA, 0x00FA, 0x0000, 0x0000 }, + { 0x01DB, 0x01DC, 0x0000, 0x0000 }, + { 0x04DE, 0x04DF, 0x0000, 0x0000 }, + { 0x1EC4, 0x1EC5, 0x0000, 0x0000 }, + { 0xFF25, 0xFF45, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_219[] = { + { 0x00DB, 0x00FB, 0x0000, 0x0000 }, + { 0x03D8, 0x03D9, 0x0000, 0x0000 }, + { 0x1FC4, 0x03AE, 0x03B9, 0x0000 }, + { 0xFF24, 0xFF44, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_220[] = { + { 0x00DC, 0x00FC, 0x0000, 0x0000 }, + { 0x04D8, 0x04D9, 0x0000, 0x0000 }, + { 0x1EC2, 0x1EC3, 0x0000, 0x0000 }, + { 0x1FC3, 0x03B7, 0x03B9, 0x0000 }, + { 0xFF23, 0xFF43, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_221[] = { + { 0x00DD, 0x00FD, 0x0000, 0x0000 }, + { 0x03DE, 0x03DF, 0x0000, 0x0000 }, + { 0x1FC2, 0x1F74, 0x03B9, 0x0000 }, + { 0xFF22, 0xFF42, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_222[] = { + { 0x00DE, 0x00FE, 0x0000, 0x0000 }, + { 0x04DA, 0x04DB, 0x0000, 0x0000 }, + { 0x1EC0, 0x1EC1, 0x0000, 0x0000 }, + { 0xFF21, 0xFF41, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_223[] = { + { 0x00DF, 0x0073, 0x0073, 0x0000 }, + { 0x01DE, 0x01DF, 0x0000, 0x0000 }, + { 0x03DC, 0x03DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_224[] = { + { 0x04E4, 0x04E5, 0x0000, 0x0000 }, + { 0x24C4, 0x24DE, 0x0000, 0x0000 }, + { 0x2CCC, 0x2CCD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_225[] = { + { 0x01E0, 0x01E1, 0x0000, 0x0000 }, + { 0x03E2, 0x03E3, 0x0000, 0x0000 }, + { 0x24C5, 0x24DF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_226[] = { + { 0x04E6, 0x04E7, 0x0000, 0x0000 }, + { 0x24C6, 0x24E0, 0x0000, 0x0000 }, + { 0x2CCE, 0x2CCF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_227[] = { + { 0x01E2, 0x01E3, 0x0000, 0x0000 }, + { 0x03E0, 0x03E1, 0x0000, 0x0000 }, + { 0x1FFC, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C7, 0x24E1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_228[] = { + { 0x04E0, 0x04E1, 0x0000, 0x0000 }, + { 0x1FFB, 0x1F7D, 0x0000, 0x0000 }, + { 0x24C0, 0x24DA, 0x0000, 0x0000 }, + { 0x2CC8, 0x2CC9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_229[] = { + { 0x01E4, 0x01E5, 0x0000, 0x0000 }, + { 0x03E6, 0x03E7, 0x0000, 0x0000 }, + { 0x1FFA, 0x1F7C, 0x0000, 0x0000 }, + { 0x24C1, 0x24DB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_230[] = { + { 0x04E2, 0x04E3, 0x0000, 0x0000 }, + { 0x1EF8, 0x1EF9, 0x0000, 0x0000 }, + { 0x1FF9, 0x1F79, 0x0000, 0x0000 }, + { 0x24C2, 0x24DC, 0x0000, 0x0000 }, + { 0x2CCA, 0x2CCB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_231[] = { + { 0x01E6, 0x01E7, 0x0000, 0x0000 }, + { 0x03E4, 0x03E5, 0x0000, 0x0000 }, + { 0x1FF8, 0x1F78, 0x0000, 0x0000 }, + { 0x24C3, 0x24DD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_232[] = { + { 0x04EC, 0x04ED, 0x0000, 0x0000 }, + { 0x1EF6, 0x1EF7, 0x0000, 0x0000 }, + { 0x1FF7, 0x03C9, 0x0342, 0x03B9 }, + { 0x24CC, 0x24E6, 0x0000, 0x0000 }, + { 0x2CC4, 0x2CC5, 0x0000, 0x0000 }, + { 0xFB13, 0x0574, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_233[] = { + { 0x01E8, 0x01E9, 0x0000, 0x0000 }, + { 0x03EA, 0x03EB, 0x0000, 0x0000 }, + { 0x1FF6, 0x03C9, 0x0342, 0x0000 }, + { 0x24CD, 0x24E7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_234[] = { + { 0x04EE, 0x04EF, 0x0000, 0x0000 }, + { 0x1EF4, 0x1EF5, 0x0000, 0x0000 }, + { 0x24CE, 0x24E8, 0x0000, 0x0000 }, + { 0x2CC6, 0x2CC7, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_235[] = { + { 0x01EA, 0x01EB, 0x0000, 0x0000 }, + { 0x03E8, 0x03E9, 0x0000, 0x0000 }, + { 0x1FF4, 0x03CE, 0x03B9, 0x0000 }, + { 0x24CF, 0x24E9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_236[] = { + { 0x04E8, 0x04E9, 0x0000, 0x0000 }, + { 0x1EF2, 0x1EF3, 0x0000, 0x0000 }, + { 0x1FF3, 0x03C9, 0x03B9, 0x0000 }, + { 0x24C8, 0x24E2, 0x0000, 0x0000 }, + { 0x2CC0, 0x2CC1, 0x0000, 0x0000 }, + { 0xFB17, 0x0574, 0x056D, 0x0000 } +}; + +static const CaseFoldMapping case_fold_237[] = { + { 0x01EC, 0x01ED, 0x0000, 0x0000 }, + { 0x03EE, 0x03EF, 0x0000, 0x0000 }, + { 0x1FF2, 0x1F7C, 0x03B9, 0x0000 }, + { 0x24C9, 0x24E3, 0x0000, 0x0000 }, + { 0xFB16, 0x057E, 0x0576, 0x0000 } +}; + +static const CaseFoldMapping case_fold_238[] = { + { 0x04EA, 0x04EB, 0x0000, 0x0000 }, + { 0x1EF0, 0x1EF1, 0x0000, 0x0000 }, + { 0x24CA, 0x24E4, 0x0000, 0x0000 }, + { 0x2CC2, 0x2CC3, 0x0000, 0x0000 }, + { 0xFB15, 0x0574, 0x056B, 0x0000 } +}; + +static const CaseFoldMapping case_fold_239[] = { + { 0x01EE, 0x01EF, 0x0000, 0x0000 }, + { 0x03EC, 0x03ED, 0x0000, 0x0000 }, + { 0x24CB, 0x24E5, 0x0000, 0x0000 }, + { 0xFB14, 0x0574, 0x0565, 0x0000 } +}; + +static const CaseFoldMapping case_fold_240[] = { + { 0x01F1, 0x01F3, 0x0000, 0x0000 }, + { 0x04F4, 0x04F5, 0x0000, 0x0000 }, + { 0x1EEE, 0x1EEF, 0x0000, 0x0000 }, + { 0x2CDC, 0x2CDD, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_241[] = { + { 0x01F0, 0x006A, 0x030C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_242[] = { + { 0x03F1, 0x03C1, 0x0000, 0x0000 }, + { 0x04F6, 0x04F7, 0x0000, 0x0000 }, + { 0x1EEC, 0x1EED, 0x0000, 0x0000 }, + { 0x2CDE, 0x2CDF, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_243[] = { + { 0x01F2, 0x01F3, 0x0000, 0x0000 }, + { 0x03F0, 0x03BA, 0x0000, 0x0000 }, + { 0x1FEC, 0x1FE5, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_244[] = { + { 0x03F7, 0x03F8, 0x0000, 0x0000 }, + { 0x04F0, 0x04F1, 0x0000, 0x0000 }, + { 0x1EEA, 0x1EEB, 0x0000, 0x0000 }, + { 0x1FEB, 0x1F7B, 0x0000, 0x0000 }, + { 0x2CD8, 0x2CD9, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_245[] = { + { 0x01F4, 0x01F5, 0x0000, 0x0000 }, + { 0x1FEA, 0x1F7A, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_246[] = { + { 0x01F7, 0x01BF, 0x0000, 0x0000 }, + { 0x03F5, 0x03B5, 0x0000, 0x0000 }, + { 0x04F2, 0x04F3, 0x0000, 0x0000 }, + { 0x1EE8, 0x1EE9, 0x0000, 0x0000 }, + { 0x1FE9, 0x1FE1, 0x0000, 0x0000 }, + { 0x2CDA, 0x2CDB, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_247[] = { + { 0x01F6, 0x0195, 0x0000, 0x0000 }, + { 0x03F4, 0x03B8, 0x0000, 0x0000 }, + { 0x1FE8, 0x1FE0, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_248[] = { + { 0x1EE6, 0x1EE7, 0x0000, 0x0000 }, + { 0x1FE7, 0x03C5, 0x0308, 0x0342 }, + { 0x2CD4, 0x2CD5, 0x0000, 0x0000 }, + { 0xFB03, 0x0066, 0x0066, 0x0069 } +}; + +static const CaseFoldMapping case_fold_249[] = { + { 0x01F8, 0x01F9, 0x0000, 0x0000 }, + { 0x03FA, 0x03FB, 0x0000, 0x0000 }, + { 0x1FE6, 0x03C5, 0x0342, 0x0000 }, + { 0xFB02, 0x0066, 0x006C, 0x0000 } +}; + +static const CaseFoldMapping case_fold_250[] = { + { 0x03F9, 0x03F2, 0x0000, 0x0000 }, + { 0x1EE4, 0x1EE5, 0x0000, 0x0000 }, + { 0x2CD6, 0x2CD7, 0x0000, 0x0000 }, + { 0xFB01, 0x0066, 0x0069, 0x0000 } +}; + +static const CaseFoldMapping case_fold_251[] = { + { 0x01FA, 0x01FB, 0x0000, 0x0000 }, + { 0x1FE4, 0x03C1, 0x0313, 0x0000 }, + { 0xFB00, 0x0066, 0x0066, 0x0000 } +}; + +static const CaseFoldMapping case_fold_252[] = { + { 0x04F8, 0x04F9, 0x0000, 0x0000 }, + { 0x1EE2, 0x1EE3, 0x0000, 0x0000 }, + { 0x1FE3, 0x03C5, 0x0308, 0x0301 }, + { 0x2CD0, 0x2CD1, 0x0000, 0x0000 } +}; + +static const CaseFoldMapping case_fold_253[] = { + { 0x01FC, 0x01FD, 0x0000, 0x0000 }, + { 0x1FE2, 0x03C5, 0x0308, 0x0300 }, + { 0xFB06, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_254[] = { + { 0x1EE0, 0x1EE1, 0x0000, 0x0000 }, + { 0x2CD2, 0x2CD3, 0x0000, 0x0000 }, + { 0xFB05, 0x0073, 0x0074, 0x0000 } +}; + +static const CaseFoldMapping case_fold_255[] = { + { 0x01FE, 0x01FF, 0x0000, 0x0000 }, + { 0xFB04, 0x0066, 0x0066, 0x006C } +}; + +static const CaseFoldHashBucket case_fold_hash[256] = { + { ARRAYSIZE(case_fold_000), case_fold_000 }, + { ARRAYSIZE(case_fold_001), case_fold_001 }, + { ARRAYSIZE(case_fold_002), case_fold_002 }, + { ARRAYSIZE(case_fold_003), case_fold_003 }, + { ARRAYSIZE(case_fold_004), case_fold_004 }, + { ARRAYSIZE(case_fold_005), case_fold_005 }, + { ARRAYSIZE(case_fold_006), case_fold_006 }, + { ARRAYSIZE(case_fold_007), case_fold_007 }, + { ARRAYSIZE(case_fold_008), case_fold_008 }, + { ARRAYSIZE(case_fold_009), case_fold_009 }, + { ARRAYSIZE(case_fold_010), case_fold_010 }, + { ARRAYSIZE(case_fold_011), case_fold_011 }, + { ARRAYSIZE(case_fold_012), case_fold_012 }, + { ARRAYSIZE(case_fold_013), case_fold_013 }, + { ARRAYSIZE(case_fold_014), case_fold_014 }, + { ARRAYSIZE(case_fold_015), case_fold_015 }, + { ARRAYSIZE(case_fold_016), case_fold_016 }, + { ARRAYSIZE(case_fold_017), case_fold_017 }, + { ARRAYSIZE(case_fold_018), case_fold_018 }, + { ARRAYSIZE(case_fold_019), case_fold_019 }, + { ARRAYSIZE(case_fold_020), case_fold_020 }, + { ARRAYSIZE(case_fold_021), case_fold_021 }, + { ARRAYSIZE(case_fold_022), case_fold_022 }, + { ARRAYSIZE(case_fold_023), case_fold_023 }, + { ARRAYSIZE(case_fold_024), case_fold_024 }, + { ARRAYSIZE(case_fold_025), case_fold_025 }, + { ARRAYSIZE(case_fold_026), case_fold_026 }, + { ARRAYSIZE(case_fold_027), case_fold_027 }, + { ARRAYSIZE(case_fold_028), case_fold_028 }, + { ARRAYSIZE(case_fold_029), case_fold_029 }, + { ARRAYSIZE(case_fold_030), case_fold_030 }, + { ARRAYSIZE(case_fold_031), case_fold_031 }, + { ARRAYSIZE(case_fold_032), case_fold_032 }, + { ARRAYSIZE(case_fold_033), case_fold_033 }, + { ARRAYSIZE(case_fold_034), case_fold_034 }, + { ARRAYSIZE(case_fold_035), case_fold_035 }, + { ARRAYSIZE(case_fold_036), case_fold_036 }, + { ARRAYSIZE(case_fold_037), case_fold_037 }, + { ARRAYSIZE(case_fold_038), case_fold_038 }, + { ARRAYSIZE(case_fold_039), case_fold_039 }, + { ARRAYSIZE(case_fold_040), case_fold_040 }, + { ARRAYSIZE(case_fold_041), case_fold_041 }, + { ARRAYSIZE(case_fold_042), case_fold_042 }, + { ARRAYSIZE(case_fold_043), case_fold_043 }, + { ARRAYSIZE(case_fold_044), case_fold_044 }, + { ARRAYSIZE(case_fold_045), case_fold_045 }, + { ARRAYSIZE(case_fold_046), case_fold_046 }, + { ARRAYSIZE(case_fold_047), case_fold_047 }, + { ARRAYSIZE(case_fold_048), case_fold_048 }, + { ARRAYSIZE(case_fold_049), case_fold_049 }, + { ARRAYSIZE(case_fold_050), case_fold_050 }, + { ARRAYSIZE(case_fold_051), case_fold_051 }, + { ARRAYSIZE(case_fold_052), case_fold_052 }, + { ARRAYSIZE(case_fold_053), case_fold_053 }, + { ARRAYSIZE(case_fold_054), case_fold_054 }, + { ARRAYSIZE(case_fold_055), case_fold_055 }, + { ARRAYSIZE(case_fold_056), case_fold_056 }, + { ARRAYSIZE(case_fold_057), case_fold_057 }, + { ARRAYSIZE(case_fold_058), case_fold_058 }, + { ARRAYSIZE(case_fold_059), case_fold_059 }, + { ARRAYSIZE(case_fold_060), case_fold_060 }, + { ARRAYSIZE(case_fold_061), case_fold_061 }, + { ARRAYSIZE(case_fold_062), case_fold_062 }, + { ARRAYSIZE(case_fold_063), case_fold_063 }, + { ARRAYSIZE(case_fold_064), case_fold_064 }, + { ARRAYSIZE(case_fold_065), case_fold_065 }, + { ARRAYSIZE(case_fold_066), case_fold_066 }, + { ARRAYSIZE(case_fold_067), case_fold_067 }, + { ARRAYSIZE(case_fold_068), case_fold_068 }, + { ARRAYSIZE(case_fold_069), case_fold_069 }, + { ARRAYSIZE(case_fold_070), case_fold_070 }, + { ARRAYSIZE(case_fold_071), case_fold_071 }, + { ARRAYSIZE(case_fold_072), case_fold_072 }, + { ARRAYSIZE(case_fold_073), case_fold_073 }, + { ARRAYSIZE(case_fold_074), case_fold_074 }, + { ARRAYSIZE(case_fold_075), case_fold_075 }, + { ARRAYSIZE(case_fold_076), case_fold_076 }, + { ARRAYSIZE(case_fold_077), case_fold_077 }, + { ARRAYSIZE(case_fold_078), case_fold_078 }, + { ARRAYSIZE(case_fold_079), case_fold_079 }, + { ARRAYSIZE(case_fold_080), case_fold_080 }, + { ARRAYSIZE(case_fold_081), case_fold_081 }, + { ARRAYSIZE(case_fold_082), case_fold_082 }, + { ARRAYSIZE(case_fold_083), case_fold_083 }, + { ARRAYSIZE(case_fold_084), case_fold_084 }, + { ARRAYSIZE(case_fold_085), case_fold_085 }, + { ARRAYSIZE(case_fold_086), case_fold_086 }, + { ARRAYSIZE(case_fold_087), case_fold_087 }, + { ARRAYSIZE(case_fold_088), case_fold_088 }, + { ARRAYSIZE(case_fold_089), case_fold_089 }, + { ARRAYSIZE(case_fold_090), case_fold_090 }, + { ARRAYSIZE(case_fold_091), case_fold_091 }, + { ARRAYSIZE(case_fold_092), case_fold_092 }, + { ARRAYSIZE(case_fold_093), case_fold_093 }, + { ARRAYSIZE(case_fold_094), case_fold_094 }, + { ARRAYSIZE(case_fold_095), case_fold_095 }, + { ARRAYSIZE(case_fold_096), case_fold_096 }, + { ARRAYSIZE(case_fold_097), case_fold_097 }, + { ARRAYSIZE(case_fold_098), case_fold_098 }, + { ARRAYSIZE(case_fold_099), case_fold_099 }, + { ARRAYSIZE(case_fold_100), case_fold_100 }, + { ARRAYSIZE(case_fold_101), case_fold_101 }, + { ARRAYSIZE(case_fold_102), case_fold_102 }, + { ARRAYSIZE(case_fold_103), case_fold_103 }, + { ARRAYSIZE(case_fold_104), case_fold_104 }, + { ARRAYSIZE(case_fold_105), case_fold_105 }, + { ARRAYSIZE(case_fold_106), case_fold_106 }, + { ARRAYSIZE(case_fold_107), case_fold_107 }, + { ARRAYSIZE(case_fold_108), case_fold_108 }, + { ARRAYSIZE(case_fold_109), case_fold_109 }, + { ARRAYSIZE(case_fold_110), case_fold_110 }, + { ARRAYSIZE(case_fold_111), case_fold_111 }, + { ARRAYSIZE(case_fold_112), case_fold_112 }, + { ARRAYSIZE(case_fold_113), case_fold_113 }, + { ARRAYSIZE(case_fold_114), case_fold_114 }, + { ARRAYSIZE(case_fold_115), case_fold_115 }, + { ARRAYSIZE(case_fold_116), case_fold_116 }, + { ARRAYSIZE(case_fold_117), case_fold_117 }, + { ARRAYSIZE(case_fold_118), case_fold_118 }, + { ARRAYSIZE(case_fold_119), case_fold_119 }, + { ARRAYSIZE(case_fold_120), case_fold_120 }, + { ARRAYSIZE(case_fold_121), case_fold_121 }, + { ARRAYSIZE(case_fold_122), case_fold_122 }, + { 0, nullptr }, + { ARRAYSIZE(case_fold_124), case_fold_124 }, + { 0, nullptr }, + { ARRAYSIZE(case_fold_126), case_fold_126 }, + { 0, nullptr }, + { ARRAYSIZE(case_fold_128), case_fold_128 }, + { ARRAYSIZE(case_fold_129), case_fold_129 }, + { ARRAYSIZE(case_fold_130), case_fold_130 }, + { ARRAYSIZE(case_fold_131), case_fold_131 }, + { ARRAYSIZE(case_fold_132), case_fold_132 }, + { ARRAYSIZE(case_fold_133), case_fold_133 }, + { ARRAYSIZE(case_fold_134), case_fold_134 }, + { ARRAYSIZE(case_fold_135), case_fold_135 }, + { ARRAYSIZE(case_fold_136), case_fold_136 }, + { ARRAYSIZE(case_fold_137), case_fold_137 }, + { ARRAYSIZE(case_fold_138), case_fold_138 }, + { ARRAYSIZE(case_fold_139), case_fold_139 }, + { ARRAYSIZE(case_fold_140), case_fold_140 }, + { ARRAYSIZE(case_fold_141), case_fold_141 }, + { ARRAYSIZE(case_fold_142), case_fold_142 }, + { ARRAYSIZE(case_fold_143), case_fold_143 }, + { ARRAYSIZE(case_fold_144), case_fold_144 }, + { ARRAYSIZE(case_fold_145), case_fold_145 }, + { ARRAYSIZE(case_fold_146), case_fold_146 }, + { ARRAYSIZE(case_fold_147), case_fold_147 }, + { ARRAYSIZE(case_fold_148), case_fold_148 }, + { ARRAYSIZE(case_fold_149), case_fold_149 }, + { ARRAYSIZE(case_fold_150), case_fold_150 }, + { ARRAYSIZE(case_fold_151), case_fold_151 }, + { ARRAYSIZE(case_fold_152), case_fold_152 }, + { ARRAYSIZE(case_fold_153), case_fold_153 }, + { ARRAYSIZE(case_fold_154), case_fold_154 }, + { ARRAYSIZE(case_fold_155), case_fold_155 }, + { ARRAYSIZE(case_fold_156), case_fold_156 }, + { ARRAYSIZE(case_fold_157), case_fold_157 }, + { ARRAYSIZE(case_fold_158), case_fold_158 }, + { ARRAYSIZE(case_fold_159), case_fold_159 }, + { ARRAYSIZE(case_fold_160), case_fold_160 }, + { ARRAYSIZE(case_fold_161), case_fold_161 }, + { ARRAYSIZE(case_fold_162), case_fold_162 }, + { ARRAYSIZE(case_fold_163), case_fold_163 }, + { ARRAYSIZE(case_fold_164), case_fold_164 }, + { ARRAYSIZE(case_fold_165), case_fold_165 }, + { ARRAYSIZE(case_fold_166), case_fold_166 }, + { ARRAYSIZE(case_fold_167), case_fold_167 }, + { ARRAYSIZE(case_fold_168), case_fold_168 }, + { ARRAYSIZE(case_fold_169), case_fold_169 }, + { ARRAYSIZE(case_fold_170), case_fold_170 }, + { ARRAYSIZE(case_fold_171), case_fold_171 }, + { ARRAYSIZE(case_fold_172), case_fold_172 }, + { ARRAYSIZE(case_fold_173), case_fold_173 }, + { ARRAYSIZE(case_fold_174), case_fold_174 }, + { ARRAYSIZE(case_fold_175), case_fold_175 }, + { ARRAYSIZE(case_fold_176), case_fold_176 }, + { ARRAYSIZE(case_fold_177), case_fold_177 }, + { ARRAYSIZE(case_fold_178), case_fold_178 }, + { ARRAYSIZE(case_fold_179), case_fold_179 }, + { ARRAYSIZE(case_fold_180), case_fold_180 }, + { ARRAYSIZE(case_fold_181), case_fold_181 }, + { ARRAYSIZE(case_fold_182), case_fold_182 }, + { ARRAYSIZE(case_fold_183), case_fold_183 }, + { ARRAYSIZE(case_fold_184), case_fold_184 }, + { ARRAYSIZE(case_fold_185), case_fold_185 }, + { ARRAYSIZE(case_fold_186), case_fold_186 }, + { ARRAYSIZE(case_fold_187), case_fold_187 }, + { ARRAYSIZE(case_fold_188), case_fold_188 }, + { ARRAYSIZE(case_fold_189), case_fold_189 }, + { ARRAYSIZE(case_fold_190), case_fold_190 }, + { ARRAYSIZE(case_fold_191), case_fold_191 }, + { ARRAYSIZE(case_fold_192), case_fold_192 }, + { ARRAYSIZE(case_fold_193), case_fold_193 }, + { ARRAYSIZE(case_fold_194), case_fold_194 }, + { ARRAYSIZE(case_fold_195), case_fold_195 }, + { ARRAYSIZE(case_fold_196), case_fold_196 }, + { ARRAYSIZE(case_fold_197), case_fold_197 }, + { ARRAYSIZE(case_fold_198), case_fold_198 }, + { ARRAYSIZE(case_fold_199), case_fold_199 }, + { ARRAYSIZE(case_fold_200), case_fold_200 }, + { ARRAYSIZE(case_fold_201), case_fold_201 }, + { ARRAYSIZE(case_fold_202), case_fold_202 }, + { ARRAYSIZE(case_fold_203), case_fold_203 }, + { ARRAYSIZE(case_fold_204), case_fold_204 }, + { ARRAYSIZE(case_fold_205), case_fold_205 }, + { ARRAYSIZE(case_fold_206), case_fold_206 }, + { ARRAYSIZE(case_fold_207), case_fold_207 }, + { ARRAYSIZE(case_fold_208), case_fold_208 }, + { ARRAYSIZE(case_fold_209), case_fold_209 }, + { ARRAYSIZE(case_fold_210), case_fold_210 }, + { ARRAYSIZE(case_fold_211), case_fold_211 }, + { ARRAYSIZE(case_fold_212), case_fold_212 }, + { ARRAYSIZE(case_fold_213), case_fold_213 }, + { ARRAYSIZE(case_fold_214), case_fold_214 }, + { ARRAYSIZE(case_fold_215), case_fold_215 }, + { ARRAYSIZE(case_fold_216), case_fold_216 }, + { ARRAYSIZE(case_fold_217), case_fold_217 }, + { ARRAYSIZE(case_fold_218), case_fold_218 }, + { ARRAYSIZE(case_fold_219), case_fold_219 }, + { ARRAYSIZE(case_fold_220), case_fold_220 }, + { ARRAYSIZE(case_fold_221), case_fold_221 }, + { ARRAYSIZE(case_fold_222), case_fold_222 }, + { ARRAYSIZE(case_fold_223), case_fold_223 }, + { ARRAYSIZE(case_fold_224), case_fold_224 }, + { ARRAYSIZE(case_fold_225), case_fold_225 }, + { ARRAYSIZE(case_fold_226), case_fold_226 }, + { ARRAYSIZE(case_fold_227), case_fold_227 }, + { ARRAYSIZE(case_fold_228), case_fold_228 }, + { ARRAYSIZE(case_fold_229), case_fold_229 }, + { ARRAYSIZE(case_fold_230), case_fold_230 }, + { ARRAYSIZE(case_fold_231), case_fold_231 }, + { ARRAYSIZE(case_fold_232), case_fold_232 }, + { ARRAYSIZE(case_fold_233), case_fold_233 }, + { ARRAYSIZE(case_fold_234), case_fold_234 }, + { ARRAYSIZE(case_fold_235), case_fold_235 }, + { ARRAYSIZE(case_fold_236), case_fold_236 }, + { ARRAYSIZE(case_fold_237), case_fold_237 }, + { ARRAYSIZE(case_fold_238), case_fold_238 }, + { ARRAYSIZE(case_fold_239), case_fold_239 }, + { ARRAYSIZE(case_fold_240), case_fold_240 }, + { ARRAYSIZE(case_fold_241), case_fold_241 }, + { ARRAYSIZE(case_fold_242), case_fold_242 }, + { ARRAYSIZE(case_fold_243), case_fold_243 }, + { ARRAYSIZE(case_fold_244), case_fold_244 }, + { ARRAYSIZE(case_fold_245), case_fold_245 }, + { ARRAYSIZE(case_fold_246), case_fold_246 }, + { ARRAYSIZE(case_fold_247), case_fold_247 }, + { ARRAYSIZE(case_fold_248), case_fold_248 }, + { ARRAYSIZE(case_fold_249), case_fold_249 }, + { ARRAYSIZE(case_fold_250), case_fold_250 }, + { ARRAYSIZE(case_fold_251), case_fold_251 }, + { ARRAYSIZE(case_fold_252), case_fold_252 }, + { ARRAYSIZE(case_fold_253), case_fold_253 }, + { ARRAYSIZE(case_fold_254), case_fold_254 }, + { ARRAYSIZE(case_fold_255), case_fold_255 }, +}; diff --git a/rehlds/filesystem/FileSystem_Stdio/src/precompiled.cpp b/rehlds/filesystem/FileSystem_Stdio/src/precompiled.cpp new file mode 100644 index 0000000..82b5132 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/precompiled.cpp @@ -0,0 +1,29 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" diff --git a/rehlds/filesystem/FileSystem_Stdio/src/precompiled.h b/rehlds/filesystem/FileSystem_Stdio/src/precompiled.h new file mode 100644 index 0000000..323af5e --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/precompiled.h @@ -0,0 +1,40 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "osconfig.h" +#include "archtypes.h" +#include "interface.h" +#include "strtools.h" + +// Hooks stuff +#include "hookers/filesystem/hooklist.h" + +// FileSystem stuff +#include "FileSystem_Stdio.h" diff --git a/rehlds/filesystem/FileSystem_Stdio/src/public_amalgamation.cpp b/rehlds/filesystem/FileSystem_Stdio/src/public_amalgamation.cpp new file mode 100644 index 0000000..1776bc6 --- /dev/null +++ b/rehlds/filesystem/FileSystem_Stdio/src/public_amalgamation.cpp @@ -0,0 +1,7 @@ +#include "precompiled.h" + +#include "interface.cpp" +#include "utlsymbol.cpp" + +#include "tier0/dbg.cpp" +#include "tier0/characterset.cpp" diff --git a/rehlds/filesystem/build.gradle b/rehlds/filesystem/build.gradle new file mode 100644 index 0000000..3c2d092 --- /dev/null +++ b/rehlds/filesystem/build.gradle @@ -0,0 +1,5 @@ +evaluationDependsOn(':rehlds/filesystem/FileSystem_Stdio'); + +task build { + dependsOn project(':rehlds/filesystem/FileSystem_Stdio').tasks.build +} diff --git a/rehlds/hookers/filesystem/hooklist.cpp b/rehlds/hookers/filesystem/hooklist.cpp new file mode 100644 index 0000000..7fb0b2d --- /dev/null +++ b/rehlds/hookers/filesystem/hooklist.cpp @@ -0,0 +1,182 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +// Hooks stuff +#include "hookers/memory.cpp" +#include "hookers/hooker.cpp" + +//#define Mem_region +//#define BaseFileSystem_region +//#define CUtlSymbol_Region +//#define Function_References_region +//#define Data_References_region + +FunctionHook g_FunctionHooks[] = +{ + // DO NOT DISABLE, other functions depends on memory allocation routines +#ifndef Mem_region + +#ifdef _WIN32 + HOOK_SYMBOLDEF(0x01D06F5E, "_malloca", malloc_wrapper), + HOOK_SYMBOLDEF(0x01D067CA, "_free", free_wrapper), + HOOK_SYMBOLDEF(0x01D07098, "realloc", realloc_wrapper), + HOOK_SYMBOLDEF(0x01D06F70, "__nh_malloc", __nh_malloc_wrapper), +#endif //_WIN32 + +#endif // Mem_region + +#ifndef BaseFileSystem_region + + HOOK_DEF(0x01D01080, MethodThunk::Destructor), + HOOK_DEF(0x01D01010, (MethodThunk::Constructor)), + + // virtual functions - CFileSystem_Stdio + HOOK_VIRTUAL_DEF(0x01D05340, CFileSystem_Stdio::Mount), + HOOK_VIRTUAL_DEF(0x01D011C0, CFileSystem_Stdio::Unmount), + HOOK_VIRTUAL_DEF(0x01D05510, CFileSystem_Stdio::GetLocalCopy), + HOOK_VIRTUAL_DEF(0x01D05520, CFileSystem_Stdio::LogLevelLoadStarted), + HOOK_VIRTUAL_DEF(0x01D05530, CFileSystem_Stdio::LogLevelLoadFinished), + HOOK_VIRTUAL_DEF(0x01D05540, CFileSystem_Stdio::HintResourceNeed), + HOOK_VIRTUAL_DEF(0x01D05550, CFileSystem_Stdio::PauseResourcePreloading), + HOOK_VIRTUAL_DEF(0x01D05560, CFileSystem_Stdio::ResumeResourcePreloading), + HOOK_VIRTUAL_DEF(0x01D05570, CFileSystem_Stdio::SetVBuf), + HOOK_VIRTUAL_DEF(0x01D055B0, CFileSystem_Stdio::GetInterfaceVersion), + HOOK_VIRTUAL_DEF(0x01D055D0, CFileSystem_Stdio::WaitForResources), + HOOK_VIRTUAL_DEF(0x01D055E0, CFileSystem_Stdio::GetWaitForResourcesProgress), + HOOK_VIRTUAL_DEF(0x01D05600, CFileSystem_Stdio::CancelWaitForResources), + HOOK_VIRTUAL_DEF(0x01D05610, CFileSystem_Stdio::IsAppReadyForOfflinePlay), + + // virtual functions - BaseFileSystem + HOOK_SYMBOL_VIRTUAL_EX(0x01D01AF0, CFileSystem_Stdio, Open), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01CC0, CFileSystem_Stdio, OpenFromCacheForRead), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01D50, CFileSystem_Stdio, Close), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01DD0, CFileSystem_Stdio, Seek), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01EA0, CFileSystem_Stdio, Tell), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01EF0, CFileSystem_Stdio, Size, size_t (FileHandle_t)), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01F30, CFileSystem_Stdio, Size, size_t (const char *)), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02310, CFileSystem_Stdio, IsOk), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02370, CFileSystem_Stdio, Flush), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02150, CFileSystem_Stdio, EndOfFile), + HOOK_SYMBOL_VIRTUAL_EX(0x01D021E0, CFileSystem_Stdio, Read), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02240, CFileSystem_Stdio, Write), + HOOK_SYMBOL_VIRTUAL_EX(0x01D023B0, CFileSystem_Stdio, ReadLine), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02290, CFileSystem_Stdio, FPrintf), + HOOK_SYMBOL_VIRTUAL_EX(0x01D022F0, CFileSystem_Stdio, GetReadBuffer), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02300, CFileSystem_Stdio, ReleaseReadBuffer), + HOOK_SYMBOL_VIRTUAL_EX(0x01D012B0, CFileSystem_Stdio, GetCurrentDirectory), + HOOK_SYMBOL_VIRTUAL_EX(0x01D012B0, CFileSystem_Stdio, GetCurrentDirectory), + HOOK_SYMBOL_VIRTUAL_EX(0x01D011D0, CFileSystem_Stdio, CreateDirHierarchy), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01680, CFileSystem_Stdio, IsDirectory), + HOOK_SYMBOL_VIRTUAL_EX(0x01D024A0, CFileSystem_Stdio, GetLocalPath), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02400, CFileSystem_Stdio, RemoveFile), + HOOK_SYMBOL_VIRTUAL_EX(0x01D042B0, CFileSystem_Stdio, RemoveAllSearchPaths), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01320, CFileSystem_Stdio, AddSearchPath), + HOOK_SYMBOL_VIRTUAL_EX(0x01D014E0, CFileSystem_Stdio, RemoveSearchPath), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01AA0, CFileSystem_Stdio, FileExists), + HOOK_SYMBOL_VIRTUAL_EX(0x01D04000, CFileSystem_Stdio, GetFileTime), + HOOK_SYMBOL_VIRTUAL_EX(0x01D04060, CFileSystem_Stdio, FileTimeToString), + HOOK_SYMBOL_VIRTUAL_EX(0x01D036B0, CFileSystem_Stdio, FindFirst), + HOOK_SYMBOL_VIRTUAL_EX(0x01D03DB0, CFileSystem_Stdio, FindNext), + HOOK_SYMBOL_VIRTUAL_EX(0x01D03DF0, CFileSystem_Stdio, FindIsDirectory), + HOOK_SYMBOL_VIRTUAL_EX(0x01D03E10, CFileSystem_Stdio, FindClose), + HOOK_SYMBOL_VIRTUAL_EX(0x01D03EF0, CFileSystem_Stdio, ParseFile), + HOOK_SYMBOL_VIRTUAL_EX(0x01D04130, CFileSystem_Stdio, FullPathToRelativePath), + HOOK_SYMBOL_VIRTUAL_EX(0x01D012A0, CFileSystem_Stdio, PrintOpenedFiles), + HOOK_SYMBOL_VIRTUAL_EX(0x01D04090, CFileSystem_Stdio, SetWarningFunc), + HOOK_SYMBOL_VIRTUAL_EX(0x01D040A0, CFileSystem_Stdio, SetWarningLevel), + HOOK_SYMBOL_VIRTUAL_EX(0x01D02A20, CFileSystem_Stdio, AddPackFile), + HOOK_SYMBOL_VIRTUAL_EX(0x01D01300, CFileSystem_Stdio, AddSearchPathNoWrite), + + // non-virtual functions - BaseFileSystem + HOOK_DEF(0x01D02780, CBaseFileSystem::Trace_FOpen), + HOOK_DEF(0x01D028A0, CBaseFileSystem::Trace_FClose), + HOOK_DEF(0x01D029E0, CBaseFileSystem::Trace_DumpUnclosedFiles), + HOOK_DEF(0x01D01340, CBaseFileSystem::AddSearchPathInternal), + HOOK_DEF(0x01D040B0, CBaseFileSystem::Warning), + HOOK_DEF(0x01D04350, CBaseFileSystem::FixSlashes), + HOOK_DEF(0x01D04370, CBaseFileSystem::FixPath), + //HOOK_DEF(0x01D04320, CBaseFileSystem::StripFilename), // NOXREF + HOOK_DEF(0x01D01610, CBaseFileSystem::GetWritePath), + HOOK_DEF(0x01D01790, CBaseFileSystem::FindFile), + HOOK_DEF(0x01D01F70, CBaseFileSystem::FastFindFileSize), + //HOOK_DEF(0x01D034B0, CBaseFileSystem::RemoveAllMapSearchPaths), // NOXREF + HOOK_DEF(0x01D033A0, CBaseFileSystem::AddPackFiles), + HOOK_DEF(0x01D02A40, CBaseFileSystem::AddPackFileFromPath), + HOOK_DEF(0x01D02D20, CBaseFileSystem::PreparePackFile), + HOOK_DEF(0x01D03070, CBaseFileSystem::Prepare64BitPackFile), + HOOK_DEF(0x01D03510, CBaseFileSystem::SearchPakFile), + //HOOK_DEF(0x01D039B0, CBaseFileSystem::FileInSearchPaths), // NOXREF + HOOK_DEF(0x01D03B70, CBaseFileSystem::FindNextFileHelper), + HOOK_DEF(0x01D03890, CBaseFileSystem::FindFirstHelper), + +#endif // BaseFileSystem_region + +#ifndef CUtlSymbol_Region + + //HOOK_DEF(0x01D05AA0, MethodThunk::Destructor), // NOXREF + HOOK_DEF(0x01D05A30, (MethodThunk::Constructor), void(int, int, bool)), + HOOK_DEF(0x01D05C50, CUtlSymbolTable::AddString), + HOOK_DEF(0x01D05B10, CUtlSymbolTable::Find), + HOOK_DEF(0x01D05DA0, CUtlSymbolTable::String), + //HOOK_DEF(0x0, CUtlSymbolTable::RemoveAll), // NOXREF + HOOK_DEF(0x01D05960, CUtlSymbolTable::SymLess), + HOOK_DEF(0x01D059E0, CUtlSymbolTable::SymLessi), + + HOOK_DEF(0x01D05890, (MethodThunk::Constructor), void(const char *)), + HOOK_DEF(0x01D05830, CUtlSymbol::Initialize), // Don't touch it + HOOK_DEF(0x01D05880, CUtlSymbol::CurrTable), // Don't touch it + HOOK_DEF(0x01D058C0, CUtlSymbol::String), + //HOOK_DEF(0x01D058E0, CUtlSymbol::operator==, bool (const char *) const), // NOXREF + +#endif // CUtlSymbol_Region + + { NULL, NULL, NULL }, +}; + +AddressRef g_FunctionRefs[] = +{ +#ifndef Function_References_region + +#endif // Function_References_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_DataRefs[] = +{ +#ifndef Data_References_region + + //GLOBALVAR_LINK(0x01D1B020, "CBaseFileSystem::s_pFileSystem", CBaseFileSystem::s_pFileSystem), + GLOBALVAR_LINK(0x01D1B0B8, "g_LessCtx", pg_LessCtx), + +#endif // Data_References_region + + { NULL, NULL, NULL }, +}; diff --git a/rehlds/hookers/filesystem/hooklist.h b/rehlds/hookers/filesystem/hooklist.h new file mode 100644 index 0000000..7b28577 --- /dev/null +++ b/rehlds/hookers/filesystem/hooklist.h @@ -0,0 +1,42 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#ifdef HOOK_FILESYSTEM + +#define rehlds_syserror FileSystem_SysError +#define g_LessCtx (*pg_LessCtx) + +#include "hookers/memory.h" +#include "hookers/helper.h" +#include "hookers/hooker.h" + +extern struct LessCtx_t g_LessCtx; + +#endif // HOOK_FILESYSTEM diff --git a/rehlds/hookers/filesystem/main.cpp b/rehlds/hookers/filesystem/main.cpp new file mode 100644 index 0000000..4a9fd7c --- /dev/null +++ b/rehlds/hookers/filesystem/main.cpp @@ -0,0 +1,86 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#if defined(HOOK_FILESYSTEM) + +const char *ORIGINAL_FILESYSTEM_DLL_NAME = "filesystem_stdio2.dll"; + +CSysModule *g_pOriginalFileSystemModule = nullptr; +CreateInterfaceFn g_OriginalFileSystemFactory = nullptr; +IFileSystem *g_pOriginalFileSystem = nullptr; + +IBaseInterface *CreateFileSystemInterface() +{ + if (g_pOriginalFileSystem) { + return g_pOriginalFileSystem; + } + + if (g_pOriginalFileSystemModule) + { + g_OriginalFileSystemFactory = (CreateInterfaceFn)Sys_GetFactory(g_pOriginalFileSystemModule); + + if (g_OriginalFileSystemFactory) + { + int returnCode = 0; + g_pOriginalFileSystem = (IFileSystem *)g_OriginalFileSystemFactory(FILESYSTEM_INTERFACE_VERSION, &returnCode); + return g_pOriginalFileSystem; + } + } + + return nullptr; +} + +EXPOSE_INTERFACE_FN(CreateFileSystemInterface, IFileSystem, FILESYSTEM_INTERFACE_VERSION); + +// DLL entry point +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + g_pOriginalFileSystemModule = (CSysModule *)LoadLibrary(ORIGINAL_FILESYSTEM_DLL_NAME); + size_t addr = (size_t)Sys_GetProcAddress(ORIGINAL_FILESYSTEM_DLL_NAME, CREATEINTERFACE_PROCNAME); + HookModule("hlds.exe", addr); + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + if (g_pOriginalFileSystemModule) + { + Sys_UnloadModule(g_pOriginalFileSystemModule); + g_pOriginalFileSystemModule = nullptr; + + g_OriginalFileSystemFactory = nullptr; + g_pOriginalFileSystem = nullptr; + } + } + + return TRUE; +} + +#endif // #if defined(HOOK_FILESYSTEM) diff --git a/rehlds/hookers/helper.h b/rehlds/hookers/helper.h index e3e2c4c..ec521a3 100644 --- a/rehlds/hookers/helper.h +++ b/rehlds/hookers/helper.h @@ -8,7 +8,7 @@ #include #include -#if defined(HOOK_ENGINE) || defined(HOOK_HLTV) +#if defined(HOOK_ENGINE) || defined(HOOK_HLTV) || defined(HOOK_FILESYSTEM) #define private public #define protected public @@ -116,6 +116,9 @@ namespace MsvcMethod { return (vIndexGetter->*vIndexGetterFunction)(); } + template + TMethod &declmethod(TMethod T::*method); + template std::enable_if_t, std::uintptr_t> GetVirtualAddress(TMethod T::*method) @@ -145,15 +148,16 @@ namespace MsvcMethod { } // namespace MsvcMethod #ifdef _MSC_VER - #define GLOBALVAR_LINK(offset, symbol, var, ...) { offset, #symbol, (size_t)&##var, __VA_ARGS__ } - #define HOOK_SYMBOLDEF(offset, symbol, func, ...) { offset, #symbol, MsvcMethod::GetAddress<__VA_ARGS__>(&func) } - #define HOOK_SYMBOL_VIRTUAL_DEF(offset, symbol, func, ...) { offset, #symbol, MsvcMethod::GetVirtualAddress<__VA_ARGS__>(&func) } + #define GLOBALVAR_LINK(offset, symbol, var, ...) { offset, #symbol, (size_t)&##var, __VA_ARGS__ } + #define HOOK_SYMBOLDEF(offset, symbol, func, ...) { offset, #symbol, MsvcMethod::GetAddress<__VA_ARGS__>(&func) } + #define HOOK_SYMBOL_VIRTUAL_DEF(offset, symbol, func, ...) { offset, #symbol, MsvcMethod::GetVirtualAddress<__VA_ARGS__>(&func) } + #define HOOK_SYMBOL_VIRTUAL_EX(offset, class, func, ...) { offset, #class#func, MsvcMethod::GetVirtualAddress(&class::func))>::type, class>(&class::func) } - #define HOOK_DEF(offset, func, ...) HOOK_SYMBOLDEF(offset, func, func, __VA_ARGS__) - #define HOOK_VIRTUAL_DEF(offset, func, ...) HOOK_SYMBOL_VIRTUAL_DEF(offset, func, func, __VA_ARGS__) + #define HOOK_DEF(offset, func, ...) HOOK_SYMBOLDEF(offset, func, func, __VA_ARGS__) + #define HOOK_VIRTUAL_DEF(offset, func, ...) HOOK_SYMBOL_VIRTUAL_DEF(offset, func, func, __VA_ARGS__) #else #error Hooking stuff is only available using MSVC compiler. #endif // _MSC_VER -#endif // #if defined(HOOK_ENGINE) || defined(HOOK_HLTV) +#endif // #if defined(HOOK_ENGINE) || defined(HOOK_HLTV) || defined(HOOK_FILESYSTEM) diff --git a/rehlds/public/FileSystem.h b/rehlds/public/FileSystem.h index 8e202ba..fa7e8e8 100644 --- a/rehlds/public/FileSystem.h +++ b/rehlds/public/FileSystem.h @@ -64,20 +64,22 @@ enum typedef enum { + FILESYSTEM_WARNING = -1, // A problem! FILESYSTEM_WARNING_QUIET = 0, // Don't print anything FILESYSTEM_WARNING_REPORTUNCLOSED, // On shutdown, report names of files left unclosed FILESYSTEM_WARNING_REPORTUSAGE, // Report number of times a file was opened, closed FILESYSTEM_WARNING_REPORTALLACCESSES // Report all open/close events to console (!slow!) } FileWarningLevel_t; -#define FILESYSTEM_INVALID_HANDLE (FileHandle_t)0 +const FileHandle_t FILESYSTEM_INVALID_HANDLE = nullptr; + #endif // FILESYSTEM_INTERNAL_H // turn off any windows defines #undef GetCurrentDirectory // Purpose: Main file system interface -class IFileSystem : public IBaseInterface +class IFileSystem: public IBaseInterface { public: // Mount and unmount the filesystem @@ -114,10 +116,10 @@ public: virtual void Close(FileHandle_t file) = 0; virtual void Seek(FileHandle_t file, int pos, FileSystemSeek_t seekType) = 0; - virtual unsigned int Tell(FileHandle_t file) = 0; + virtual size_t Tell(FileHandle_t file) = 0; - virtual unsigned int Size(FileHandle_t file) = 0; - virtual unsigned int Size(const char *pFileName) = 0; + virtual size_t Size(FileHandle_t file) = 0; + virtual size_t Size(const char *pFileName) = 0; virtual long GetFileTime(const char *pFileName) = 0; virtual void FileTimeToString(char *pStrip, int maxCharsIncludingTerminator, long fileTime) = 0; @@ -130,7 +132,7 @@ public: virtual int Read(void *pOutput, int size, FileHandle_t file) = 0; virtual int Write(void const *pInput, int size, FileHandle_t file) = 0; virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file) = 0; - virtual int FPrintf(FileHandle_t file, char *pFormat, ...) = 0; + virtual int FPrintf(FileHandle_t file, const char *fmt, ...) = 0; // direct filesystem buffer access // returns a handle to a buffer containing the file data diff --git a/rehlds/public/interface.h b/rehlds/public/interface.h index bba69f7..c16ce67 100644 --- a/rehlds/public/interface.h +++ b/rehlds/public/interface.h @@ -70,7 +70,7 @@ public: // // Use this if you want to write the factory function. #define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName)\ - static InterfaceReg __g_Create##className##_reg(functionName, versionName); + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); #define EXPOSE_INTERFACE(className, interfaceName, versionName)\ static IBaseInterface *__Create##className##_interface() {return (interfaceName *)new className;}\ diff --git a/rehlds/public/tier0/characterset.cpp b/rehlds/public/tier0/characterset.cpp new file mode 100644 index 0000000..9088b64 --- /dev/null +++ b/rehlds/public/tier0/characterset.cpp @@ -0,0 +1,50 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include +#include "characterset.h" + +// Builds a simple lookup table of a group of important characters +// Input : *pParseGroup - pointer to the buffer for the group +// *pGroupString - null terminated list of characters to flag +void CharacterSetBuild(characterset_t *pSetBuffer, const char *pszSetString) +{ + int i = 0; + + // Test our pointers + if (!pSetBuffer || !pszSetString) + return; + + memset(pSetBuffer->set, 0, sizeof(pSetBuffer->set)); + + while (pszSetString[i]) + { + pSetBuffer->set[pszSetString[i]] = 1; + i++; + } +} diff --git a/rehlds/public/tier0/characterset.h b/rehlds/public/tier0/characterset.h new file mode 100644 index 0000000..4a6601e --- /dev/null +++ b/rehlds/public/tier0/characterset.h @@ -0,0 +1,46 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +struct characterset_t +{ + char set[256]; +}; + +// This is essentially a strpbrk() using a precalculated lookup table +// +// Builds a simple lookup table of a group of important characters +// Input : *pSetBuffer - pointer to the buffer for the group +// *pSetString - list of characters to flag +extern void CharacterSetBuild(characterset_t *pSetBuffer, const char *pSetString); + +// Input : *pSetBuffer - pre-build group buffer +// character - character to lookup +// Output : int - 1 if the character was in the set +#define IN_CHARACTERSET(SetBuffer, character) ((SetBuffer).set[(character)]) diff --git a/rehlds/public/tier0/dbg.h b/rehlds/public/tier0/dbg.h index df088d6..9feb4ac 100644 --- a/rehlds/public/tier0/dbg.h +++ b/rehlds/public/tier0/dbg.h @@ -45,12 +45,12 @@ // Usage model for the Dbg library // // 1. Spew. -// +// // Spew can be used in a static and a dynamic mode. The static // mode allows us to display assertions and other messages either only // in debug builds, or in non-release builds. The dynamic mode allows us to // turn on and off certain spew messages while the application is running. -// +// // Static Spew messages: // // Assertions are used to detect and warn about invalid states @@ -65,14 +65,14 @@ // AssertFloatEquals( f, 5.0f, 1e-3 ); // // The first will simply report that an assertion failed on a particular -// code file and line. The second version will display a print-f formatted message +// code file and line. The second version will display a print-f formatted message // along with the file and line, the third will display a generic message and // will also cause the function BadFunc to be executed, and the last two // will report an error if f is not equal to 5 (the last one asserts within // a particular tolerance). // // To use a warning, use -// +// // Warning("Oh I feel so %s all over\n", "yummy"); // // Warning will do its magic in only Debug builds. To perform spew in *all* @@ -86,24 +86,24 @@ // // Dynamic Spew messages // -// It is possible to dynamically turn spew on and off. Dynamic spew is -// identified by a spew group and priority level. To turn spew on for a -// particular spew group, use SpewActivate( "group", level ). This will -// cause all spew in that particular group with priority levels <= the -// level specified in the SpewActivate function to be printed. Use DSpew +// It is possible to dynamically turn spew on and off. Dynamic spew is +// identified by a spew group and priority level. To turn spew on for a +// particular spew group, use SpewActivate( "group", level ). This will +// cause all spew in that particular group with priority levels <= the +// level specified in the SpewActivate function to be printed. Use DSpew // to perform the spew: // // DWarning( "group", level, "Oh I feel even yummier!\n" ); // // Priority level 0 means that the spew will *always* be printed, and group -// '*' is the default spew group. If a DWarning is encountered using a group -// whose priority has not been set, it will use the priority of the default -// group. The priority of the default group is initially set to 0. +// '*' is the default spew group. If a DWarning is encountered using a group +// whose priority has not been set, it will use the priority of the default +// group. The priority of the default group is initially set to 0. // // Spew output -// +// // The output of the spew system can be redirected to an externally-supplied -// function which is responsible for outputting the spew. By default, the +// function which is responsible for outputting the spew. By default, the // spew is simply printed using printf. // // To redirect spew output, call SpewOutput. @@ -113,8 +113,8 @@ // This will cause OutputFunc to be called every time a spew message is // generated. OutputFunc will be passed a spew type and a message to print. // It must return a value indicating whether the debugger should be invoked, -// whether the program should continue running, or whether the program -// should abort. +// whether the program should continue running, or whether the program +// should abort. // // 2. Code activation // @@ -126,10 +126,10 @@ // int x = 5; // ++x; // } -// ); +// ); // // Code can be activated based on the dynamic spew groups also. Use -// +// // DBG_DCODE( "group", level, // { int x = 5; ++x; } // ); @@ -254,7 +254,7 @@ SpewRetval_t _DSpewMessage(char const *pGroupName, int level, char const* pMsg #define Assert( _exp ) _Assert( _exp ) #define AssertMsg( _exp, _msg ) _AssertMsg( _exp, _msg ) #define AssertFunc( _exp, _f ) _AssertFunc( _exp, _f ) -#define AssertEquals( _exp, _expectedValue ) _AssertEquals( _exp, _expectedValue ) +#define AssertEquals( _exp, _expectedValue ) _AssertEquals( _exp, _expectedValue ) #define AssertFloatEquals( _exp, _expectedValue, _tol ) _AssertFloatEquals( _exp, _expectedValue, _tol ) #define Verify( _exp ) _Assert( _exp ) @@ -336,7 +336,7 @@ void DevLog(char const *pMsg, ...); #define DBG_CODE( _code ) if (0) ; else { _code } #define DBG_DCODE( _g, _l, _code ) if (IsSpewActive( _g, _l )) { _code } else {} -#define DBG_BREAK() DebuggerBreak() /* defined in platform.h */ +#define DBG_BREAK() DebuggerBreak() /* defined in platform.h */ #else /* not _DEBUG */ diff --git a/rehlds/public/utlmemory.h b/rehlds/public/utlmemory.h index 5f76e30..4f2fc0f 100644 --- a/rehlds/public/utlmemory.h +++ b/rehlds/public/utlmemory.h @@ -59,18 +59,18 @@ public: bool operator!=(const Iterator_t it) const { return m_index != it.m_index; } }; - Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } - Iterator_t Next(const Iterator_t &it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } - I GetIndex(const Iterator_t &it) const { return it.index; } - bool IsIdxAfter(I i, const Iterator_t &it) const { return i > it.index; } - bool IsValidIterator(const Iterator_t &it) const { return IsIdxValid(it.index); } - Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } + Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } + Iterator_t Next(const Iterator_t &it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } + I GetIndex(const Iterator_t &it) const { return it.index; } + bool IsIdxAfter(I i, const Iterator_t &it) const { return i > it.index; } + bool IsValidIterator(const Iterator_t &it) const { return IsIdxValid(it.index); } + Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } // element access - T& Element(I i); - T const& Element(I i) const; - T& operator[](I i); - T const& operator[](I i) const; + T& Element(I i); + T const& Element(I i) const; + T& operator[](I i); + T const& operator[](I i) const; // Can we use this index? bool IsIdxValid(I i) const; @@ -116,7 +116,7 @@ private: int m_nGrowSize; }; -// constructor, destructor +// Constructor, Destructor template CUtlMemory::CUtlMemory(int nGrowSize, int nInitSize) : m_pMemory(0), m_nAllocationCount(nInitSize), m_nGrowSize(nGrowSize) @@ -170,7 +170,7 @@ void CUtlMemory::SetExternalBuffer(T *pMemory, int numElements) m_nGrowSize = EXTERNAL_BUFFER_MARKER; } -// element access +// Element access template inline T& CUtlMemory::operator[](I i) { @@ -199,7 +199,7 @@ inline T const& CUtlMemory::Element(I i) const return m_pMemory[i]; } -// is the memory externally allocated? +// Is the memory externally allocated? template bool CUtlMemory::IsExternallyAllocated() const { @@ -329,6 +329,7 @@ void CUtlMemory::Purge() free((void *)m_pMemory); m_pMemory = 0; } + m_nAllocationCount = 0; } } diff --git a/rehlds/public/utlrbtree.h b/rehlds/public/utlrbtree.h index 8c5bb4c..45bed14 100644 --- a/rehlds/public/utlrbtree.h +++ b/rehlds/public/utlrbtree.h @@ -1,161 +1,178 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Header: $ -// $NoKeywords: $ -//============================================================================= +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ -#ifndef UTLRBTREE_H -#define UTLRBTREE_H +#pragma once -//#include #include "utlmemory.h" -//----------------------------------------------------------------------------- // Tool to generate a default compare function for any type that implements // operator<, including all simple types -//----------------------------------------------------------------------------- - -template +template class CDefOps { public: - static bool LessFunc( const T &lhs, const T &rhs ) { return ( lhs < rhs ); } + static bool LessFunc(const T &lhs, const T &rhs) { return (lhs < rhs); } }; -#define DefLessFunc( type ) CDefOps::LessFunc +#define DefLessFunc(type) CDefOps::LessFunc -//------------------------------------- +inline bool StringLessThan(const char *const &lhs, const char *const &rhs) { return (strcmp(lhs, rhs) < 0); } +inline bool CaselessStringLessThan(const char *const &lhs, const char *const &rhs) { return (_stricmp(lhs, rhs) < 0); } -inline bool StringLessThan( const char * const &lhs, const char * const &rhs) { return ( strcmp( lhs, rhs) < 0 ); } -inline bool CaselessStringLessThan( const char * const &lhs, const char * const &rhs ) { return ( _stricmp( lhs, rhs) < 0 ); } - -//------------------------------------- // inline these two templates to stop multiple definitions of the same code -template <> inline bool CDefOps::LessFunc( const char * const &lhs, const char * const &rhs ) { return StringLessThan( lhs, rhs ); } -template <> inline bool CDefOps::LessFunc( char * const &lhs, char * const &rhs ) { return StringLessThan( lhs, rhs ); } - -//------------------------------------- +template <> inline bool CDefOps::LessFunc(const char *const &lhs, const char *const &rhs) { return StringLessThan(lhs, rhs); } +template <> inline bool CDefOps::LessFunc(char *const &lhs, char *const &rhs) { return StringLessThan(lhs, rhs); } template -void SetDefLessFunc( RBTREE_T &RBTree ) +void SetDefLessFunc(RBTREE_T &RBTree) { -#ifdef _WIN32 - RBTree.SetLessFunc( DefLessFunc( RBTREE_T::KeyType_t ) ); -#elif _LINUX - RBTree.SetLessFunc( DefLessFunc( typename RBTREE_T::KeyType_t ) ); -#endif + RBTree.SetLessFunc(DefLessFunc(typename RBTREE_T::KeyType_t)); } -//----------------------------------------------------------------------------- // A red-black binary search tree -//----------------------------------------------------------------------------- +template +struct UtlRBTreeLinks_t +{ + I m_Left; + I m_Right; + I m_Parent; + I m_Tag; +}; -template +template +struct UtlRBTreeNode_t: public UtlRBTreeLinks_t +{ + T m_Data; +}; + +// A red-black binary search tree +template , I>> class CUtlRBTree { public: - // Less func typedef - // Returns true if the first parameter is "less" than the second - typedef bool (*LessFunc_t)( T const &, T const & ); - typedef T KeyType_t; typedef T ElemType_t; typedef I IndexType_t; + // Less func typedef + // Returns true if the first parameter is "less" than the second + typedef L LessFunc_t; + // constructor, destructor // Left at growSize = 0, the memory will first allocate 1 element and double in size // at each increment. // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below - CUtlRBTree( int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0 ); - ~CUtlRBTree( ); - + CUtlRBTree(int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0); + ~CUtlRBTree(); + // gets particular elements - T& Element( I i ); - T const &Element( I i ) const; - T& operator[]( I i ); - T const &operator[]( I i ) const; - + T& Element(I i); + T const& Element(I i) const; + T& operator[](I i); + T const& operator[](I i) const; + // Gets the root - I Root() const; - + I Root() const; + // Num elements unsigned int Count() const; - + // Max "size" of the vector - I MaxElement() const; - - // Gets the children - I Parent( I i ) const; - I LeftChild( I i ) const; - I RightChild( I i ) const; - + I MaxElement() const; + + // Gets the children + I Parent(I i) const; + I LeftChild(I i) const; + I RightChild(I i) const; + // Tests if a node is a left or right child - bool IsLeftChild( I i ) const; - bool IsRightChild( I i ) const; - + bool IsLeftChild(I i) const; + bool IsRightChild(I i) const; + // Tests if root or leaf - bool IsRoot( I i ) const; - bool IsLeaf( I i ) const; - + bool IsRoot(I i) const; + bool IsLeaf(I i) const; + // Checks if a node is valid and in the tree - bool IsValidIndex( I i ) const; - + bool IsValidIndex(I i) const; + // Checks if the tree as a whole is valid - bool IsValid() const; - + bool IsValid() const; + // Invalid index static I InvalidIndex(); - + // returns the tree depth (not a very fast operation) - int Depth( I node ) const; - int Depth() const; - + int Depth(I node) const; + int Depth() const; + // Sets the less func - void SetLessFunc( LessFunc_t func ); - + void SetLessFunc(LessFunc_t func); + // Allocation method - I NewNode(); + I NewNode(); // Insert method (inserts in order) - I Insert( T const &insert ); - void Insert( const T *pArray, int nItems ); - - // Find method - I Find( T const &search ) const; - - // Remove methods - void RemoveAt( I i ); - bool Remove( T const &remove ); - void RemoveAll( ); - - // Allocation, deletion - void FreeNode( I i ); - - // Iteration - I FirstInorder() const; - I NextInorder( I i ) const; - I PrevInorder( I i ) const; - I LastInorder() const; - - I FirstPreorder() const; - I NextPreorder( I i ) const; - I PrevPreorder( I i ) const; - I LastPreorder( ) const; - - I FirstPostorder() const; - I NextPostorder( I i ) const; + I Insert(T const &insert); + void Insert(const T *pArray, int nItems); - // If you change the search key, this can be used to reinsert the + // Find method + I Find(T const &search) const; + + // Remove methods + void RemoveAt(I i); + bool Remove(T const &remove); + void RemoveAll(); + + // Allocation, deletion + void FreeNode(I i); + + // Iteration + I FirstInorder() const; + I NextInorder(I i) const; + I PrevInorder(I i) const; + I LastInorder() const; + + I FirstPreorder() const; + I NextPreorder(I i) const; + I PrevPreorder(I i) const; + I LastPreorder() const; + + I FirstPostorder() const; + I NextPostorder(I i) const; + + // If you change the search key, this can be used to reinsert the // element into the tree. - void Reinsert( I elem ); - + void Reinsert(I elem); + +private: + // Can't copy the tree this way! + CUtlRBTree &operator=(const CUtlRBTree &other); protected: enum NodeColor_t @@ -163,336 +180,258 @@ protected: RED = 0, BLACK }; - - struct Links_t - { - I m_Left; - I m_Right; - I m_Parent; - I m_Tag; - }; - - struct Node_t : public Links_t - { - T m_Data; - }; + + typedef UtlRBTreeNode_t Node_t; + typedef UtlRBTreeLinks_t Links_t; // Sets the children - void SetParent( I i, I parent ); - void SetLeftChild( I i, I child ); - void SetRightChild( I i, I child ); - void LinkToParent( I i, I parent, bool isLeft ); + void SetParent(I i, I parent); + void SetLeftChild(I i, I child); + void SetRightChild(I i, I child); + void LinkToParent(I i, I parent, bool isLeft); // Gets at the links - Links_t const &Links( I i ) const; - Links_t &Links( I i ); - + Links_t const& Links(I i) const; + Links_t& Links(I i); + // Checks if a link is red or black - bool IsRed( I i ) const; - bool IsBlack( I i ) const; - + bool IsRed(I i) const; + bool IsBlack(I i) const; + // Sets/gets node color - NodeColor_t Color( I i ) const; - void SetColor( I i, NodeColor_t c ); - + NodeColor_t Color(I i) const; + void SetColor(I i, NodeColor_t c); + // operations required to preserve tree balance void RotateLeft(I i); void RotateRight(I i); void InsertRebalance(I i); void RemoveRebalance(I i); - + // Insertion, removal - I InsertAt( I parent, bool leftchild ); - + I InsertAt(I parent, bool leftchild); + // copy constructors not allowed - CUtlRBTree( CUtlRBTree const &tree ); - + CUtlRBTree(CUtlRBTree const &tree); + // Inserts a node into the tree, doesn't copy the data in. - void FindInsertionPosition( T const &insert, I &parent, bool &leftchild ); + void FindInsertionPosition(T const &insert, I &parent, bool &leftchild); // Remove and add back an element in the tree. - void Unlink( I elem ); - void Link( I elem ); + void Unlink(I elem); + void Link(I elem); // Used for sorting. LessFunc_t m_LessFunc; - - CUtlMemory m_Elements; + + M m_Elements; I m_Root; I m_NumElements; I m_FirstFree; I m_TotalElements; - - Node_t* m_pElements; - - void ResetDbgInfo() - { - m_pElements = (Node_t*)m_Elements.Base(); - } }; - -//----------------------------------------------------------------------------- -// constructor, destructor -//----------------------------------------------------------------------------- - -template -CUtlRBTree::CUtlRBTree( int growSize, int initSize, LessFunc_t lessfunc ) : - m_Elements( growSize, initSize ), - m_LessFunc( lessfunc ), - m_Root( InvalidIndex() ), - m_NumElements( 0 ), m_TotalElements( 0 ), - m_FirstFree( InvalidIndex() ) -{ - ResetDbgInfo(); -} - -template -CUtlRBTree::~CUtlRBTree() +// Constructor, Destructor +template +CUtlRBTree::CUtlRBTree(int growSize, int initSize, LessFunc_t lessfunc) : + m_Elements(growSize, initSize), + m_LessFunc(lessfunc), + m_Root(InvalidIndex()), + m_NumElements(0), m_TotalElements(0), + m_FirstFree(InvalidIndex()) { } -//----------------------------------------------------------------------------- -// gets particular elements -//----------------------------------------------------------------------------- - -template -inline T &CUtlRBTree::Element( I i ) -{ - return m_Elements[i].m_Data; +template +CUtlRBTree::~CUtlRBTree() +{ } -template -inline T const &CUtlRBTree::Element( I i ) const -{ - return m_Elements[i].m_Data; +// Gets particular elements +template +inline T &CUtlRBTree::Element(I i) +{ + return m_Elements[i].m_Data; } -template -inline T &CUtlRBTree::operator[]( I i ) -{ - return Element(i); +template +inline T const &CUtlRBTree::Element(I i) const +{ + return m_Elements[i].m_Data; } -template -inline T const &CUtlRBTree::operator[]( I i ) const -{ - return Element(i); +template +inline T &CUtlRBTree::operator[](I i) +{ + return Element(i); } -//----------------------------------------------------------------------------- -// -// various accessors -// -//----------------------------------------------------------------------------- +template +inline T const &CUtlRBTree::operator[](I i) const +{ + return Element(i); +} -//----------------------------------------------------------------------------- // Gets the root -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::Root() const -{ - return m_Root; +template +inline I CUtlRBTree::Root() const +{ + return m_Root; } - -//----------------------------------------------------------------------------- + // Num elements -//----------------------------------------------------------------------------- - -template -inline unsigned int CUtlRBTree::Count() const -{ - return (unsigned int)m_NumElements; +template +inline unsigned int CUtlRBTree::Count() const +{ + return (unsigned int)m_NumElements; } - -//----------------------------------------------------------------------------- + // Max "size" of the vector -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::MaxElement() const -{ - return (I)m_TotalElements; -} - - -//----------------------------------------------------------------------------- -// Gets the children -//----------------------------------------------------------------------------- - -template -inline I CUtlRBTree::Parent( I i ) const -{ - return Links(i).m_Parent; +template +inline I CUtlRBTree::MaxElement() const +{ + return (I)m_TotalElements; } -template -inline I CUtlRBTree::LeftChild( I i ) const -{ - return Links(i).m_Left; +// Gets the children +template +inline I CUtlRBTree::Parent(I i) const +{ + return Links(i).m_Parent; } -template -inline I CUtlRBTree::RightChild( I i ) const -{ - return Links(i).m_Right; +template +inline I CUtlRBTree::LeftChild(I i) const +{ + return Links(i).m_Left; } - -//----------------------------------------------------------------------------- + +template +inline I CUtlRBTree::RightChild(I i) const +{ + return Links(i).m_Right; +} + // Tests if a node is a left or right child -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsLeftChild( I i ) const -{ - return LeftChild(Parent(i)) == i; +template +inline bool CUtlRBTree::IsLeftChild(I i) const +{ + return LeftChild(Parent(i)) == i; } -template -inline bool CUtlRBTree::IsRightChild( I i ) const -{ - return RightChild(Parent(i)) == i; +template +inline bool CUtlRBTree::IsRightChild(I i) const +{ + return RightChild(Parent(i)) == i; } - -//----------------------------------------------------------------------------- // Tests if root or leaf -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsRoot( I i ) const -{ - return i == m_Root; +template +inline bool CUtlRBTree::IsRoot(I i) const +{ + return i == m_Root; } -template -inline bool CUtlRBTree::IsLeaf( I i ) const -{ - return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex()); +template +inline bool CUtlRBTree::IsLeaf(I i) const +{ + return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex()); } - -//----------------------------------------------------------------------------- // Checks if a node is valid and in the tree -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsValidIndex( I i ) const -{ - return LeftChild(i) != i; +template +inline bool CUtlRBTree::IsValidIndex(I i) const +{ + return LeftChild(i) != i; } - - -//----------------------------------------------------------------------------- + // Invalid index -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::InvalidIndex() -{ - return (I)~0; +template +I CUtlRBTree::InvalidIndex() +{ + return (I)M::InvalidIndex(); } - -//----------------------------------------------------------------------------- // returns the tree depth (not a very fast operation) -//----------------------------------------------------------------------------- - -template -inline int CUtlRBTree::Depth() const -{ - return Depth(Root()); +template +inline int CUtlRBTree::Depth() const +{ + return Depth(Root()); } -//----------------------------------------------------------------------------- // Sets the children -//----------------------------------------------------------------------------- - -template -inline void CUtlRBTree::SetParent( I i, I parent ) -{ - Links(i).m_Parent = parent; +template +inline void CUtlRBTree::SetParent(I i, I parent) +{ + Links(i).m_Parent = parent; } -template -inline void CUtlRBTree::SetLeftChild( I i, I child ) -{ - Links(i).m_Left = child; +template +inline void CUtlRBTree::SetLeftChild(I i, I child) +{ + Links(i).m_Left = child; } -template -inline void CUtlRBTree::SetRightChild( I i, I child ) -{ - Links(i).m_Right = child; +template +inline void CUtlRBTree::SetRightChild(I i, I child) +{ + Links(i).m_Right = child; } -//----------------------------------------------------------------------------- // Gets at the links -//----------------------------------------------------------------------------- - -template -inline typename CUtlRBTree::Links_t const &CUtlRBTree::Links( I i ) const +template +inline typename CUtlRBTree::Links_t const &CUtlRBTree::Links(I i) const { // Sentinel node, makes life easier - static Links_t s_Sentinel = - { - InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree::BLACK + static Links_t s_Sentinel = + { + InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree::BLACK }; - - return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : - *(Links_t*)&s_Sentinel; + + return (i != InvalidIndex()) ? + *(Links_t*)&m_Elements[i] : + *(Links_t*)&s_Sentinel; } -template -inline typename CUtlRBTree::Links_t &CUtlRBTree::Links( I i ) -{ - Assert(i != InvalidIndex()); +template +inline typename CUtlRBTree::Links_t &CUtlRBTree::Links(I i) +{ + Assert(i != InvalidIndex()); return *(Links_t *)&m_Elements[i]; } -//----------------------------------------------------------------------------- // Checks if a link is red or black -//----------------------------------------------------------------------------- - -template -inline bool CUtlRBTree::IsRed( I i ) const -{ - return (Links(i).m_Tag == RED); +template +inline bool CUtlRBTree::IsRed(I i) const +{ + return (Links(i).m_Tag == RED); } -template -inline bool CUtlRBTree::IsBlack( I i ) const -{ - return (Links(i).m_Tag == BLACK); +template +inline bool CUtlRBTree::IsBlack(I i) const +{ + return (Links(i).m_Tag == BLACK); } - -//----------------------------------------------------------------------------- // Sets/gets node color -//----------------------------------------------------------------------------- - -template -inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color( I i ) const -{ - return (NodeColor_t)Links(i).m_Tag; +template +inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color(I i) const +{ + return (NodeColor_t)Links(i).m_Tag; } -template -inline void CUtlRBTree::SetColor( I i, typename CUtlRBTree::NodeColor_t c ) -{ - Links(i).m_Tag = (I)c; +template +inline void CUtlRBTree::SetColor(I i, typename CUtlRBTree::NodeColor_t c) +{ + Links(i).m_Tag = (I)c; } -//----------------------------------------------------------------------------- // Allocates/ deallocates nodes -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::NewNode() +template +I CUtlRBTree::NewNode() { I newElem; - + // Nothing in the free list; add. if (m_FirstFree == InvalidIndex()) { @@ -505,407 +444,379 @@ I CUtlRBTree::NewNode() newElem = m_FirstFree; m_FirstFree = RightChild(m_FirstFree); } - + #ifdef _DEBUG // reset links to invalid.... Links_t &node = Links(newElem); node.m_Left = node.m_Right = node.m_Parent = InvalidIndex(); #endif - - Construct( &Element(newElem) ); - ResetDbgInfo(); - + + Construct(&Element(newElem)); return newElem; } -template -void CUtlRBTree::FreeNode( I i ) +template +void CUtlRBTree::FreeNode(I i) { - Assert( IsValidIndex(i) && (i != InvalidIndex()) ); - Destruct( &Element(i) ); - SetLeftChild( i, i ); // indicates it's in not in the tree - SetRightChild( i, m_FirstFree ); + Assert(IsValidIndex(i) && (i != InvalidIndex())); + Destruct(&Element(i)); + SetLeftChild(i, i); // indicates it's in not in the tree + SetRightChild(i, m_FirstFree); m_FirstFree = i; } - -//----------------------------------------------------------------------------- // Rotates node i to the left -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RotateLeft(I elem) +template +void CUtlRBTree::RotateLeft(I elem) { I rightchild = RightChild(elem); - SetRightChild( elem, LeftChild(rightchild) ); + SetRightChild(elem, LeftChild(rightchild)); if (LeftChild(rightchild) != InvalidIndex()) - SetParent( LeftChild(rightchild), elem ); - + SetParent(LeftChild(rightchild), elem); + if (rightchild != InvalidIndex()) - SetParent( rightchild, Parent(elem) ); + SetParent(rightchild, Parent(elem)); if (!IsRoot(elem)) { if (IsLeftChild(elem)) - SetLeftChild( Parent(elem), rightchild ); + SetLeftChild(Parent(elem), rightchild); else - SetRightChild( Parent(elem), rightchild ); + SetRightChild(Parent(elem), rightchild); } else m_Root = rightchild; - - SetLeftChild( rightchild, elem ); + + SetLeftChild(rightchild, elem); if (elem != InvalidIndex()) - SetParent( elem, rightchild ); + SetParent(elem, rightchild); } - -//----------------------------------------------------------------------------- // Rotates node i to the right -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RotateRight(I elem) +template +void CUtlRBTree::RotateRight(I elem) { I leftchild = LeftChild(elem); - SetLeftChild( elem, RightChild(leftchild) ); + SetLeftChild(elem, RightChild(leftchild)); if (RightChild(leftchild) != InvalidIndex()) - SetParent( RightChild(leftchild), elem ); - + SetParent(RightChild(leftchild), elem); + if (leftchild != InvalidIndex()) - SetParent( leftchild, Parent(elem) ); + SetParent(leftchild, Parent(elem)); if (!IsRoot(elem)) { if (IsRightChild(elem)) - SetRightChild( Parent(elem), leftchild ); + SetRightChild(Parent(elem), leftchild); else - SetLeftChild( Parent(elem), leftchild ); + SetLeftChild(Parent(elem), leftchild); } else m_Root = leftchild; - - SetRightChild( leftchild, elem ); + + SetRightChild(leftchild, elem); if (elem != InvalidIndex()) - SetParent( elem, leftchild ); + SetParent(elem, leftchild); } - -//----------------------------------------------------------------------------- // Rebalances the tree after an insertion -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::InsertRebalance(I elem) +template +void CUtlRBTree::InsertRebalance(I elem) { - while ( !IsRoot(elem) && (Color(Parent(elem)) == RED) ) + while (!IsRoot(elem) && (Color(Parent(elem)) == RED)) { I parent = Parent(elem); I grandparent = Parent(parent); - - /* we have a violation */ + + // we have a violation if (IsLeftChild(parent)) { I uncle = RightChild(grandparent); - if (IsRed(uncle)) - { - /* uncle is RED */ + if (IsRed(uncle)) + { + // uncle is RED SetColor(parent, BLACK); SetColor(uncle, BLACK); SetColor(grandparent, RED); elem = grandparent; - } - else - { - /* uncle is BLACK */ + } + else + { + // uncle is BLACK if (IsRightChild(elem)) { - /* make x a left child, will change parent and grandparent */ + // make x a left child, will change parent and grandparent elem = parent; RotateLeft(elem); parent = Parent(elem); grandparent = Parent(parent); } - /* recolor and rotate */ + + // recolor and rotate SetColor(parent, BLACK); SetColor(grandparent, RED); RotateRight(grandparent); - } - } - else + } + } + else { - /* mirror image of above code */ + // mirror image of above code I uncle = LeftChild(grandparent); - if (IsRed(uncle)) - { - /* uncle is RED */ + if (IsRed(uncle)) + { + // uncle is RED SetColor(parent, BLACK); SetColor(uncle, BLACK); SetColor(grandparent, RED); elem = grandparent; - } - else - { - /* uncle is BLACK */ + } + else + { + // uncle is BLACK if (IsLeftChild(elem)) { - /* make x a right child, will change parent and grandparent */ + // make x a right child, will change parent and grandparent elem = parent; RotateRight(parent); parent = Parent(elem); grandparent = Parent(parent); } - /* recolor and rotate */ + // recolor and rotate SetColor(parent, BLACK); SetColor(grandparent, RED); RotateLeft(grandparent); - } + } } } - SetColor( m_Root, BLACK ); + SetColor(m_Root, BLACK); } - -//----------------------------------------------------------------------------- // Insert a node into the tree -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::InsertAt( I parent, bool leftchild ) +template +I CUtlRBTree::InsertAt(I parent, bool leftchild) { I i = NewNode(); - LinkToParent( i, parent, leftchild ); - ++m_NumElements; + LinkToParent(i, parent, leftchild); + m_NumElements++; return i; } -template -void CUtlRBTree::LinkToParent( I i, I parent, bool isLeft ) +template +void CUtlRBTree::LinkToParent(I i, I parent, bool isLeft) { Links_t &elem = Links(i); elem.m_Parent = parent; elem.m_Left = elem.m_Right = InvalidIndex(); elem.m_Tag = RED; - - /* insert node in tree */ - if (parent != InvalidIndex()) + + // insert node in tree + if (parent != InvalidIndex()) { if (isLeft) Links(parent).m_Left = i; else Links(parent).m_Right = i; - } - else + } + else { m_Root = i; } - - InsertRebalance(i); + + InsertRebalance(i); Assert(IsValid()); } -//----------------------------------------------------------------------------- // Rebalance the tree after a deletion -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveRebalance(I elem) +template +void CUtlRBTree::RemoveRebalance(I elem) { - while (elem != m_Root && IsBlack(elem)) + while (elem != m_Root && IsBlack(elem)) { I parent = Parent(elem); - + // If elem is the left child of the parent - if (elem == LeftChild(parent)) + if (elem == LeftChild(parent)) { // Get our sibling I sibling = RightChild(parent); - if (IsRed(sibling)) - { + if (IsRed(sibling)) + { SetColor(sibling, BLACK); SetColor(parent, RED); RotateLeft(parent); - + // We may have a new parent now parent = Parent(elem); sibling = RightChild(parent); - } - if ( (IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling))) ) - { + } + if ((IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling)))) + { if (sibling != InvalidIndex()) SetColor(sibling, RED); elem = parent; - } + } else - { + { if (IsBlack(RightChild(sibling))) { SetColor(LeftChild(sibling), BLACK); SetColor(sibling, RED); RotateRight(sibling); - + // rotation may have changed this parent = Parent(elem); sibling = RightChild(parent); } - SetColor( sibling, Color(parent) ); - SetColor( parent, BLACK ); - SetColor( RightChild(sibling), BLACK ); - RotateLeft( parent ); + SetColor(sibling, Color(parent)); + SetColor(parent, BLACK); + SetColor(RightChild(sibling), BLACK); + RotateLeft(parent); elem = m_Root; - } + } } - else + else { // Elem is the right child of the parent I sibling = LeftChild(parent); - if (IsRed(sibling)) - { + if (IsRed(sibling)) + { SetColor(sibling, BLACK); SetColor(parent, RED); RotateRight(parent); - + // We may have a new parent now parent = Parent(elem); sibling = LeftChild(parent); - } - if ( (IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling))) ) - { - if (sibling != InvalidIndex()) - SetColor( sibling, RED ); + } + if ((IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling)))) + { + if (sibling != InvalidIndex()) + SetColor(sibling, RED); elem = parent; - } - else - { + } + else + { if (IsBlack(LeftChild(sibling))) { - SetColor( RightChild(sibling), BLACK ); - SetColor( sibling, RED ); - RotateLeft( sibling ); - + SetColor(RightChild(sibling), BLACK); + SetColor(sibling, RED); + RotateLeft(sibling); + // rotation may have changed this parent = Parent(elem); sibling = LeftChild(parent); } - SetColor( sibling, Color(parent) ); - SetColor( parent, BLACK ); - SetColor( LeftChild(sibling), BLACK ); - RotateRight( parent ); + SetColor(sibling, Color(parent)); + SetColor(parent, BLACK); + SetColor(LeftChild(sibling), BLACK); + RotateRight(parent); elem = m_Root; - } + } } } - SetColor( elem, BLACK ); + SetColor(elem, BLACK); } -template -void CUtlRBTree::Unlink( I elem ) +template +void CUtlRBTree::Unlink(I elem) { - if ( elem != InvalidIndex() ) + if (elem != InvalidIndex()) { I x, y; - - if ((LeftChild(elem) == InvalidIndex()) || + + if ((LeftChild(elem) == InvalidIndex()) || (RightChild(elem) == InvalidIndex())) { - /* y has a NIL node as a child */ + // y has a NIL node as a child y = elem; } else { - /* find tree successor with a NIL node as a child */ + // find tree successor with a NIL node as a child y = RightChild(elem); while (LeftChild(y) != InvalidIndex()) y = LeftChild(y); } - - /* x is y's only child */ + + // x is y's only child if (LeftChild(y) != InvalidIndex()) x = LeftChild(y); else x = RightChild(y); - - /* remove y from the parent chain */ + + // remove y from the parent chain if (x != InvalidIndex()) - SetParent( x, Parent(y) ); + SetParent(x, Parent(y)); if (!IsRoot(y)) { if (IsLeftChild(y)) - SetLeftChild( Parent(y), x ); + SetLeftChild(Parent(y), x); else - SetRightChild( Parent(y), x ); + SetRightChild(Parent(y), x); } else m_Root = x; - + // need to store this off now, we'll be resetting y's color NodeColor_t ycolor = Color(y); if (y != elem) { // Standard implementations copy the data around, we cannot here. // Hook in y to link to the same stuff elem used to. - SetParent( y, Parent(elem) ); - SetRightChild( y, RightChild(elem) ); - SetLeftChild( y, LeftChild(elem) ); - + SetParent(y, Parent(elem)); + SetRightChild(y, RightChild(elem)); + SetLeftChild(y, LeftChild(elem)); + if (!IsRoot(elem)) if (IsLeftChild(elem)) - SetLeftChild( Parent(elem), y ); + SetLeftChild(Parent(elem), y); else - SetRightChild( Parent(elem), y ); + SetRightChild(Parent(elem), y); else m_Root = y; - + if (LeftChild(y) != InvalidIndex()) - SetParent( LeftChild(y), y ); + SetParent(LeftChild(y), y); if (RightChild(y) != InvalidIndex()) - SetParent( RightChild(y), y ); - - SetColor( y, Color(elem) ); + SetParent(RightChild(y), y); + + SetColor(y, Color(elem)); } - + if ((x != InvalidIndex()) && (ycolor == BLACK)) RemoveRebalance(x); } } -template -void CUtlRBTree::Link( I elem ) +template +void CUtlRBTree::Link(I elem) { - if ( elem != InvalidIndex() ) + if (elem != InvalidIndex()) { I parent; bool leftchild; - - FindInsertionPosition( Element( elem ), parent, leftchild ); - - LinkToParent( elem, parent, leftchild ); + FindInsertionPosition(Element(elem), parent, leftchild); + LinkToParent(elem, parent, leftchild); } } -//----------------------------------------------------------------------------- // Delete a node from the tree -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveAt(I elem) +template +void CUtlRBTree::RemoveAt(I elem) { - if ( elem != InvalidIndex() ) + if (elem != InvalidIndex()) { - Unlink( elem ); + Unlink(elem); FreeNode(elem); - --m_NumElements; + m_NumElements--; } } - -//----------------------------------------------------------------------------- // remove a node in the tree -//----------------------------------------------------------------------------- - -template bool CUtlRBTree::Remove( T const &search ) +template +bool CUtlRBTree::Remove(T const &search) { - I node = Find( search ); + I node = Find(search); if (node != InvalidIndex()) { RemoveAt(node); @@ -914,26 +825,22 @@ template bool CUtlRBTree::Remove( T const &search ) return false; } - -//----------------------------------------------------------------------------- // Removes all nodes from the tree -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::RemoveAll() +template +void CUtlRBTree::RemoveAll() { // Just iterate through the whole list and add to free list // much faster than doing all of the rebalancing // also, do it so the free list is pointing to stuff in order // to get better cache coherence when re-adding stuff to this tree. I prev = InvalidIndex(); - for (int i = (int)m_TotalElements; --i >= 0; ) + for (int i = (int)m_TotalElements; --i >= 0;) { I idx = (I)i; if (IsValidIndex(idx)) - Destruct( &Element(idx) ); - SetRightChild( idx, prev ); - SetLeftChild( idx, idx ); + Destruct(&Element(idx)); + SetRightChild(idx, prev); + SetLeftChild(idx, idx); prev = idx; } m_FirstFree = m_TotalElements ? (I)0 : InvalidIndex(); @@ -941,13 +848,9 @@ void CUtlRBTree::RemoveAll() m_NumElements = 0; } - -//----------------------------------------------------------------------------- -// iteration -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::FirstInorder() const +// Iteration +template +I CUtlRBTree::FirstInorder() const { I i = m_Root; while (LeftChild(i) != InvalidIndex()) @@ -955,11 +858,11 @@ I CUtlRBTree::FirstInorder() const return i; } -template -I CUtlRBTree::NextInorder( I i ) const +template +I CUtlRBTree::NextInorder(I i) const { Assert(IsValidIndex(i)); - + if (RightChild(i) != InvalidIndex()) { i = RightChild(i); @@ -967,7 +870,7 @@ I CUtlRBTree::NextInorder( I i ) const i = LeftChild(i); return i; } - + I parent = Parent(i); while (IsRightChild(i)) { @@ -978,11 +881,11 @@ I CUtlRBTree::NextInorder( I i ) const return parent; } -template -I CUtlRBTree::PrevInorder( I i ) const +template +I CUtlRBTree::PrevInorder(I i) const { Assert(IsValidIndex(i)); - + if (LeftChild(i) != InvalidIndex()) { i = LeftChild(i); @@ -990,7 +893,7 @@ I CUtlRBTree::PrevInorder( I i ) const i = RightChild(i); return i; } - + I parent = Parent(i); while (IsLeftChild(i)) { @@ -1001,8 +904,8 @@ I CUtlRBTree::PrevInorder( I i ) const return parent; } -template -I CUtlRBTree::LastInorder() const +template +I CUtlRBTree::LastInorder() const { I i = m_Root; while (RightChild(i) != InvalidIndex()) @@ -1010,23 +913,23 @@ I CUtlRBTree::LastInorder() const return i; } -template -I CUtlRBTree::FirstPreorder() const +template +I CUtlRBTree::FirstPreorder() const { return m_Root; } -template -I CUtlRBTree::NextPreorder( I i ) const +template +I CUtlRBTree::NextPreorder(I i) const { if (LeftChild(i) != InvalidIndex()) return LeftChild(i); - + if (RightChild(i) != InvalidIndex()) return RightChild(i); - + I parent = Parent(i); - while( parent != InvalidIndex()) + while(parent != InvalidIndex()) { if (IsLeftChild(i) && (RightChild(parent) != InvalidIndex())) return RightChild(parent); @@ -1036,22 +939,22 @@ I CUtlRBTree::NextPreorder( I i ) const return InvalidIndex(); } -template -I CUtlRBTree::PrevPreorder( I i ) const +template +I CUtlRBTree::PrevPreorder(I i) const { - Assert(0); // not implemented yet + Assert(0); // Not implemented yet return InvalidIndex(); } -template -I CUtlRBTree::LastPreorder() const +template +I CUtlRBTree::LastPreorder() const { I i = m_Root; while (1) { while (RightChild(i) != InvalidIndex()) i = RightChild(i); - + if (LeftChild(i) != InvalidIndex()) i = LeftChild(i); else @@ -1060,8 +963,8 @@ I CUtlRBTree::LastPreorder() const return i; } -template -I CUtlRBTree::FirstPostorder() const +template +I CUtlRBTree::FirstPostorder() const { I i = m_Root; while (!IsLeaf(i)) @@ -1074,19 +977,19 @@ I CUtlRBTree::FirstPostorder() const return i; } -template -I CUtlRBTree::NextPostorder( I i ) const +template +I CUtlRBTree::NextPostorder(I i) const { I parent = Parent(i); if (parent == InvalidIndex()) return InvalidIndex(); - + if (IsRightChild(i)) return parent; - + if (RightChild(parent) == InvalidIndex()) return parent; - + i = RightChild(parent); while (!IsLeaf(i)) { @@ -1098,198 +1001,172 @@ I CUtlRBTree::NextPostorder( I i ) const return i; } - -template -void CUtlRBTree::Reinsert( I elem ) +template +void CUtlRBTree::Reinsert(I elem) { - Unlink( elem ); - Link( elem ); + Unlink(elem); + Link(elem); } - -//----------------------------------------------------------------------------- -// returns the tree depth (not a very fast operation) -//----------------------------------------------------------------------------- - -template -int CUtlRBTree::Depth( I node ) const +// Returns the tree depth (not a very fast operation) +template +int CUtlRBTree::Depth(I node) const { if (node == InvalidIndex()) return 0; - - int depthright = Depth( RightChild(node) ); - int depthleft = Depth( LeftChild(node) ); - return Q_max(depthright, depthleft) + 1; + + int depthright = Depth(RightChild(node)); + int depthleft = Depth(LeftChild(node)); + return max(depthright, depthleft) + 1; } - -//----------------------------------------------------------------------------- // Makes sure the tree is valid after every operation -//----------------------------------------------------------------------------- - -template -bool CUtlRBTree::IsValid() const +template +bool CUtlRBTree::IsValid() const { - if ( !Count() ) + if (!Count()) return true; - - if (( Root() >= MaxElement()) || ( Parent( Root() ) != InvalidIndex() )) + + if ((Root() >= MaxElement()) || (Parent(Root()) != InvalidIndex())) goto InvalidTree; - + #ifdef UTLTREE_PARANOID - + // First check to see that mNumEntries matches reality. // count items on the free list int numFree = 0; int curr = m_FirstFree; while (curr != InvalidIndex()) { - ++numFree; + numFree++; curr = RightChild(curr); - if ( (curr > MaxElement()) && (curr != InvalidIndex()) ) + if ((curr > MaxElement()) && (curr != InvalidIndex())) goto InvalidTree; } if (MaxElement() - numFree != Count()) goto InvalidTree; - - // iterate over all elements, looking for validity + + // Iterate over all elements, looking for validity // based on the self pointers int numFree2 = 0; - for (curr = 0; curr < MaxElement(); ++curr) + for (curr = 0; curr < MaxElement(); curr++) { if (!IsValidIndex(curr)) - ++numFree2; + numFree2++; else { int right = RightChild(curr); int left = LeftChild(curr); - if ((right == left) && (right != InvalidIndex()) ) + if ((right == left) && (right != InvalidIndex())) goto InvalidTree; - + if (right != InvalidIndex()) - { - if (!IsValidIndex(right)) + { + if (!IsValidIndex(right)) goto InvalidTree; - if (Parent(right) != curr) + if (Parent(right) != curr) goto InvalidTree; - if (IsRed(curr) && IsRed(right)) + if (IsRed(curr) && IsRed(right)) goto InvalidTree; - } - + } + if (left != InvalidIndex()) - { - if (!IsValidIndex(left)) + { + if (!IsValidIndex(left)) goto InvalidTree; - if (Parent(left) != curr) + if (Parent(left) != curr) goto InvalidTree; - if (IsRed(curr) && IsRed(left)) + if (IsRed(curr) && IsRed(left)) goto InvalidTree; - } + } } } if (numFree2 != numFree) goto InvalidTree; - + #endif // UTLTREE_PARANOID - + return true; - + InvalidTree: return false; } - -//----------------------------------------------------------------------------- // Sets the less func -//----------------------------------------------------------------------------- - -template -void CUtlRBTree::SetLessFunc( typename CUtlRBTree::LessFunc_t func ) +template +void CUtlRBTree::SetLessFunc(typename CUtlRBTree::LessFunc_t func) { if (!m_LessFunc) m_LessFunc = func; else { - // need to re-sort the tree here.... + // Need to re-sort the tree here.... Assert(0); } } - -//----------------------------------------------------------------------------- -// inserts a node into the tree -//----------------------------------------------------------------------------- - // Inserts a node into the tree, doesn't copy the data in. -template -void CUtlRBTree::FindInsertionPosition( T const &insert, I &parent, bool &leftchild ) +template +void CUtlRBTree::FindInsertionPosition(T const &insert, I &parent, bool &leftchild) { - Assert( m_LessFunc ); - - /* find where node belongs */ + Assert(m_LessFunc); + + // Find where node belongs I current = m_Root; parent = InvalidIndex(); leftchild = false; - while (current != InvalidIndex()) + while (current != InvalidIndex()) { parent = current; - if (m_LessFunc( insert, Element(current) )) + if (m_LessFunc(insert, Element(current))) { - leftchild = true; current = LeftChild(current); + leftchild = true; + current = LeftChild(current); } else { - leftchild = false; current = RightChild(current); + leftchild = false; + current = RightChild(current); } } } -template -I CUtlRBTree::Insert( T const &insert ) +template +I CUtlRBTree::Insert(T const &insert) { - // use copy constructor to copy it in + // Use copy constructor to copy it in I parent; bool leftchild; - FindInsertionPosition( insert, parent, leftchild ); - I newNode = InsertAt( parent, leftchild ); - CopyConstruct( &Element( newNode ), insert ); + FindInsertionPosition(insert, parent, leftchild); + I newNode = InsertAt(parent, leftchild); + CopyConstruct(&Element(newNode), insert); return newNode; } - -template -void CUtlRBTree::Insert( const T *pArray, int nItems ) +template +void CUtlRBTree::Insert(const T *pArray, int nItems) { - while ( nItems-- ) + while (nItems--) { - Insert( *pArray++ ); + Insert(*pArray++); } } -//----------------------------------------------------------------------------- -// finds a node in the tree -//----------------------------------------------------------------------------- - -template -I CUtlRBTree::Find( T const &search ) const +// Finds a node in the tree +template +I CUtlRBTree::Find(T const &search) const { - Assert( m_LessFunc ); - + Assert(m_LessFunc); + I current = m_Root; - while (current != InvalidIndex()) + while (current != InvalidIndex()) { - if (m_LessFunc( search, Element(current) )) + if (m_LessFunc(search, Element(current))) current = LeftChild(current); - else if (m_LessFunc( Element(current), search )) + else if (m_LessFunc(Element(current), search)) current = RightChild(current); - else + else break; } return current; } - - - - - -#endif // UTLRBTREE_H diff --git a/rehlds/public/utlsymbol.cpp b/rehlds/public/utlsymbol.cpp new file mode 100644 index 0000000..6c2e03b --- /dev/null +++ b/rehlds/public/utlsymbol.cpp @@ -0,0 +1,152 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "utlsymbol.h" + +LessCtx_t g_LessCtx; +CUtlSymbolTable *CUtlSymbol::s_pSymbolTable = nullptr; + +void CUtlSymbol::Initialize() +{ + // necessary to allow us to create global symbols + static bool symbolsInitialized = false; + if (!symbolsInitialized) + { + s_pSymbolTable = new CUtlSymbolTable; + symbolsInitialized = true; + } +} + +CUtlSymbolTable *CUtlSymbol::CurrTable() +{ + Initialize(); + return s_pSymbolTable; +} + +CUtlSymbol::CUtlSymbol(const char *pStr) +{ + m_Id = CurrTable()->AddString(pStr); +} + +const char *CUtlSymbol::String() const +{ + return CurrTable()->String(m_Id); +} + +// Checks if the symbol matches a string +bool CUtlSymbol::operator==(const char *pStr) const +{ + if (m_Id == UTL_INVAL_SYMBOL) + return false; + + return Q_strcmp(String(), pStr) == 0; +} + +bool CUtlSymbolTable::SymLess(const unsigned int &i1, const unsigned int &i2) +{ + const char *str1 = (i1 == INVALID_STRING_INDEX) ? g_LessCtx.m_pUserString : g_LessCtx.m_pTable->StringFromIndex(i1); + const char *str2 = (i2 == INVALID_STRING_INDEX) ? g_LessCtx.m_pUserString : g_LessCtx.m_pTable->StringFromIndex(i2); + + return Q_strcmp(str1, str2) < 0; +} + +bool CUtlSymbolTable::SymLessi(const unsigned int &i1, const unsigned int &i2) +{ + const char *str1 = (i1 == INVALID_STRING_INDEX) ? g_LessCtx.m_pUserString : g_LessCtx.m_pTable->StringFromIndex(i1); + const char *str2 = (i2 == INVALID_STRING_INDEX) ? g_LessCtx.m_pUserString : g_LessCtx.m_pTable->StringFromIndex(i2); + + return Q_stricmp(str1, str2) < 0; +} + +// Constructor, Destructor +CUtlSymbolTable::CUtlSymbolTable(int growSize, int initSize, bool caseInsensitive) : + m_Lookup(growSize, initSize, caseInsensitive ? SymLess : SymLessi), + m_StringPools(MAX_STRING_POOL_SIZE) +{ + ; +} + +CUtlSymbolTable::~CUtlSymbolTable() +{ + // Release the stringpool string data + RemoveAll(); +} + +CUtlSymbol CUtlSymbolTable::Find(const char *pString) +{ + if (!pString) + return CUtlSymbol(); + + // Store a special context used to help with insertion + g_LessCtx.m_pUserString = pString; + g_LessCtx.m_pTable = this; + + // Passing this special invalid symbol makes the comparison function + // use the string passed in the context + UtlSymId_t idx = m_Lookup.Find(INVALID_STRING_INDEX); + return CUtlSymbol(idx); +} + +// Finds and/or creates a symbol based on the string +CUtlSymbol CUtlSymbolTable::AddString(const char *pString) +{ + if (!pString) + return CUtlSymbol(UTL_INVAL_SYMBOL); + + // Find a pool with space for this string, or allocate a new one + CUtlSymbol id = Find(pString); + + if (id.IsValid()) + return id; + + // Add a new pool + int len = Q_strlen(pString) + 1; + int stridx = m_StringPools.AddMultipleToTail(len); + Q_memcpy(&m_StringPools[stridx], pString, len); + + // Didn't find, insert the string into the vector + UtlSymId_t idx = m_Lookup.Insert(stridx); + return CUtlSymbol(idx); +} + +// Look up the string associated with a particular symbol +const char *CUtlSymbolTable::String(CUtlSymbol id) const +{ + if (!id.IsValid()) + return ""; + + Assert(m_Lookup.IsValidIndex((UtlSymId_t)id)); + return StringFromIndex(m_Lookup[id]); +} + +// Remove all symbols in the table +void CUtlSymbolTable::RemoveAll() +{ + m_Lookup.RemoveAll(); + m_StringPools.RemoveAll(); +} diff --git a/rehlds/public/utlsymbol.h b/rehlds/public/utlsymbol.h new file mode 100644 index 0000000..1abae52 --- /dev/null +++ b/rehlds/public/utlsymbol.h @@ -0,0 +1,113 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "utlrbtree.h" +#include "utlvector.h" + +class CUtlSymbolTable; + +using UtlSymId_t = unsigned short; + +const int MAX_STRING_POOL_SIZE = 256; +const UtlSymId_t UTL_INVAL_SYMBOL = ((UtlSymId_t)~0); +const unsigned int INVALID_STRING_INDEX = -1; + +struct LessCtx_t +{ + const char *m_pUserString; + CUtlSymbolTable *m_pTable; + + LessCtx_t() : m_pUserString(0), m_pTable(0) {} +}; + +class CUtlSymbol +{ +public: + // constructor, destructor + CUtlSymbol() : m_Id(UTL_INVAL_SYMBOL) {} + CUtlSymbol(UtlSymId_t id) : m_Id(id) {} + CUtlSymbol(const char *pStr); + CUtlSymbol(CUtlSymbol const &sym) : m_Id(sym.m_Id) {} + + // operator= + CUtlSymbol &operator=(CUtlSymbol const &src) + { + m_Id = src.m_Id; + return *this; + } + + // operator== + bool operator==(CUtlSymbol const &src) const { return m_Id == src.m_Id; } + bool operator==(const char *pStr) const; + + // Is valid? + bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; } + + operator UtlSymId_t() const { return m_Id; } // Gets at the symbol + const char *String() const; // Gets the string associated with the symbol + +protected: + static void Initialize(); // Initializes the symbol table + static CUtlSymbolTable *CurrTable(); // returns the current symbol table + + UtlSymId_t m_Id; + static CUtlSymbolTable *s_pSymbolTable; +}; + +class CUtlSymbolTable +{ +public: + // constructor, destructor + CUtlSymbolTable(int growSize = 0, int initSize = 32, bool caseInsensitive = false); + ~CUtlSymbolTable(); + + CUtlSymbol AddString(const char *pString); // Finds and/or creates a symbol based on the string + CUtlSymbol Find(const char *pString); // Finds the symbol for pString + + const char *String(CUtlSymbol id) const; // Look up the string associated with a particular symbol + void RemoveAll(); // Remove all symbols in the table + +public: + static bool SymLess(const unsigned int &i1, const unsigned int &i2); + static bool SymLessi(const unsigned int &i1, const unsigned int &i2); + +private: + const char *StringFromIndex(const UtlSymId_t &index) const; + +protected: + CUtlRBTree m_Lookup; + CUtlVector m_StringPools; +}; + +inline const char *CUtlSymbolTable::StringFromIndex(const UtlSymId_t &index) const +{ + Assert(index < m_StringPools.Count()); + return &m_StringPools[index]; +} diff --git a/rehlds/public/utlvector.h b/rehlds/public/utlvector.h index 0b25871..a59b7f7 100644 --- a/rehlds/public/utlvector.h +++ b/rehlds/public/utlvector.h @@ -1,24 +1,34 @@ -//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// $Header: $ -// $NoKeywords: $ -// -// A growable memory class. -//============================================================================= +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ -#ifndef UTLVECTOR_H -#define UTLVECTOR_H -#ifdef _WIN32 #pragma once -#endif #include "utlmemory.h" -#include "tier0/platform.h" template class CUtlVector @@ -27,22 +37,29 @@ public: typedef T ElemType_t; // constructor, destructor - CUtlVector( int growSize = 0, int initSize = 0 ); - CUtlVector( T* pMemory, int numElements ); + CUtlVector(int growSize = 0, int initSize = 0); + CUtlVector(T *pMemory, int numElements); ~CUtlVector(); + // features C++11 ranged based for + T *begin() { return &m_Memory[0]; } + T *end() { return &m_Memory[m_Size - 1]; } + + T const *begin() const { return &m_Memory[0]; } + T const *end() const { return &m_Memory[m_Size - 1]; } + // Copy the array. - CUtlVector& operator=( const CUtlVector &other ); + CUtlVector &operator=(const CUtlVector &other); // element access - T& operator[]( int i ); - T const& operator[]( int i ) const; - T& Element( int i ); - T const& Element( int i ) const; + T &operator[](int i); + T const &operator[](int i) const; + T &Element(int i); + T const &Element(int i) const; // Gets the base address (can change when adding elements!) - T* Base(); - T const* Base() const; + T *Base(); + T const *Base() const; // Returns the number of elements in the vector // SIZE IS DEPRECATED! @@ -50,54 +67,54 @@ public: int Size() const; // don't use me! // Is element index valid? - bool IsValidIndex( int i ) const; - static int InvalidIndex( void ); + bool IsValidIndex(int i) const; + static int InvalidIndex(void); // Adds an element, uses default constructor int AddToHead(); int AddToTail(); - int InsertBefore( int elem ); - int InsertAfter( int elem ); + int InsertBefore(int elem); + int InsertAfter(int elem); // Adds an element, uses copy constructor - int AddToHead( T const& src ); - int AddToTail( T const& src ); - int InsertBefore( int elem, T const& src ); - int InsertAfter( int elem, T const& src ); + int AddToHead(T const &src); + int AddToTail(T const &src); + int InsertBefore(int elem, T const &src); + int InsertAfter(int elem, T const &src); // Adds multiple elements, uses default constructor - int AddMultipleToHead( int num ); - int AddMultipleToTail( int num, const T *pToCopy=NULL ); - int InsertMultipleBefore( int elem, int num, const T *pToCopy=NULL ); // If pToCopy is set, then it's an array of length 'num' and - int InsertMultipleAfter( int elem, int num ); + int AddMultipleToHead(int num); + int AddMultipleToTail(int num, const T *pToCopy = nullptr); + int InsertMultipleBefore(int elem, int num, const T *pToCopy = nullptr); // If pToCopy is set, then it's an array of length 'num' and + int InsertMultipleAfter(int elem, int num); // Calls RemoveAll() then AddMultipleToTail. - void SetSize( int size ); - void SetCount( int count ); + void SetSize(int size); + void SetCount(int count); // Calls SetSize and copies each element. - void CopyArray( T const *pArray, int size ); + void CopyArray(T const *pArray, int size); // Add the specified array to the tail. - int AddVectorToTail( CUtlVector const &src ); + int AddVectorToTail(CUtlVector const &src); // Finds an element (element needs operator== defined) - int Find( T const& src ) const; + int Find(T const &src) const; - bool HasElement( T const& src ); + bool HasElement(T const &src); // Makes sure we have enough memory allocated to store a requested # of elements - void EnsureCapacity( int num ); + void EnsureCapacity(int num); // Makes sure we have at least this many elements - void EnsureCount( int num ); + void EnsureCount(int num); // Element removal - void FastRemove( int elem ); // doesn't preserve order - void Remove( int elem ); // preserves order, shifts elements - void FindAndRemove( T const& src ); // removes first occurrence of src, preserves order, shifts elements - void RemoveMultiple( int elem, int num ); // preserves order, shifts elements - void RemoveAll(); // doesn't deallocate memory + void FastRemove(int elem); // doesn't preserve order + void Remove(int elem); // preserves order, shifts elements + void FindAndRemove(T const &src); // removes first occurrence of src, preserves order, shifts elements + void RemoveMultiple(int elem, int num); // preserves order, shifts elements + void RemoveAll(); // doesn't deallocate memory // Memory deallocation void Purge(); @@ -106,19 +123,18 @@ public: void PurgeAndDeleteElements(); // Set the size by which it grows when it needs to allocate more memory. - void SetGrowSize( int size ); + void SetGrowSize(int size); protected: // Can't copy this unless we explicitly do it! - CUtlVector( CUtlVector const& vec ) { assert(0); - } + CUtlVector(CUtlVector const &vec) { Assert(0); } // Grows the vector - void GrowVector( int num = 1 ); + void GrowVector(int num = 1); // Shifts elements.... - void ShiftElementsRight( int elem, int num = 1 ); - void ShiftElementsLeft( int elem, int num = 1 ); + void ShiftElementsRight(int elem, int num = 1); + void ShiftElementsLeft(int elem, int num = 1); // For easier access to the elements through the debugger void ResetDbgInfo(); @@ -131,444 +147,408 @@ protected: T *m_pElements; }; -//----------------------------------------------------------------------------- // For easier access to the elements through the debugger -//----------------------------------------------------------------------------- -template< class T > +template inline void CUtlVector::ResetDbgInfo() { m_pElements = m_Memory.Base(); } -//----------------------------------------------------------------------------- // constructor, destructor -//----------------------------------------------------------------------------- -template< class T > -inline CUtlVector::CUtlVector( int growSize, int initSize ) : +template +inline CUtlVector::CUtlVector(int growSize, int initSize) : m_Memory(growSize, initSize), m_Size(0) { ResetDbgInfo(); } -template< class T > -inline CUtlVector::CUtlVector( T* pMemory, int numElements ) : +template +inline CUtlVector::CUtlVector(T *pMemory, int numElements) : m_Memory(pMemory, numElements), m_Size(0) { ResetDbgInfo(); } -template< class T > +template inline CUtlVector::~CUtlVector() { Purge(); } -template -inline CUtlVector& CUtlVector::operator=( const CUtlVector &other ) +template +inline CUtlVector &CUtlVector::operator=(const CUtlVector &other) { - CopyArray( other.Base(), other.Count() ); + CopyArray(other.Base(), other.Count()); return *this; } -//----------------------------------------------------------------------------- // element access -//----------------------------------------------------------------------------- -template< class T > -inline T& CUtlVector::operator[]( int i ) +template +inline T &CUtlVector::operator[](int i) { - assert( IsValidIndex(i) ); + Assert(IsValidIndex(i)); return m_Memory[i]; } -template< class T > -inline T const& CUtlVector::operator[]( int i ) const +template +inline T const &CUtlVector::operator[](int i) const { - assert( IsValidIndex(i) ); + Assert(IsValidIndex(i)); return m_Memory[i]; } -template< class T > -inline T& CUtlVector::Element( int i ) +template +inline T &CUtlVector::Element(int i) { - assert( IsValidIndex(i) ); + Assert(IsValidIndex(i)); return m_Memory[i]; } -template< class T > -inline T const& CUtlVector::Element( int i ) const +template +inline T const &CUtlVector::Element(int i) const { - assert( IsValidIndex(i) ); + Assert(IsValidIndex(i)); return m_Memory[i]; } -//----------------------------------------------------------------------------- // Gets the base address (can change when adding elements!) -//----------------------------------------------------------------------------- -template< class T > -inline T* CUtlVector::Base() +template +inline T *CUtlVector::Base() { return m_Memory.Base(); } -template< class T > -inline T const* CUtlVector::Base() const +template +inline T const *CUtlVector::Base() const { return m_Memory.Base(); } -//----------------------------------------------------------------------------- // Count -//----------------------------------------------------------------------------- -template< class T > +template inline int CUtlVector::Size() const { return m_Size; } -template< class T > +template inline int CUtlVector::Count() const { return m_Size; } -//----------------------------------------------------------------------------- // Is element index valid? -//----------------------------------------------------------------------------- -template< class T > -inline bool CUtlVector::IsValidIndex( int i ) const +template +inline bool CUtlVector::IsValidIndex(int i) const { return (i >= 0) && (i < m_Size); } -//----------------------------------------------------------------------------- // Returns in invalid index -//----------------------------------------------------------------------------- -template< class T > -inline int CUtlVector::InvalidIndex( void ) +template +inline int CUtlVector::InvalidIndex(void) { return -1; } -//----------------------------------------------------------------------------- // Grows the vector -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::GrowVector( int num ) +template +void CUtlVector::GrowVector(int num) { if (m_Size + num - 1 >= m_Memory.NumAllocated()) { - m_Memory.Grow( m_Size + num - m_Memory.NumAllocated() ); + m_Memory.Grow(m_Size + num - m_Memory.NumAllocated()); } m_Size += num; ResetDbgInfo(); } -//----------------------------------------------------------------------------- // Makes sure we have enough memory allocated to store a requested # of elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::EnsureCapacity( int num ) +template +void CUtlVector::EnsureCapacity(int num) { m_Memory.EnsureCapacity(num); ResetDbgInfo(); } -//----------------------------------------------------------------------------- // Makes sure we have at least this many elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::EnsureCount( int num ) +template +void CUtlVector::EnsureCount(int num) { if (Count() < num) - AddMultipleToTail( num - Count() ); + AddMultipleToTail(num - Count()); } -//----------------------------------------------------------------------------- // Shifts elements -//----------------------------------------------------------------------------- -template< class T > -void CUtlVector::ShiftElementsRight( int elem, int num ) +template +void CUtlVector::ShiftElementsRight(int elem, int num) { - assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); + Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); int numToMove = m_Size - elem - num; if ((numToMove > 0) && (num > 0)) - memmove( &Element(elem+num), &Element(elem), numToMove * sizeof(T) ); + memmove(&Element(elem+num), &Element(elem), numToMove * sizeof(T)); } -template< class T > -void CUtlVector::ShiftElementsLeft( int elem, int num ) +template +void CUtlVector::ShiftElementsLeft(int elem, int num) { - assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); + Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); int numToMove = m_Size - elem - num; if ((numToMove > 0) && (num > 0)) { - memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) ); + memmove(&Element(elem), &Element(elem + num), numToMove * sizeof(T)); #ifdef _DEBUG - memset( &Element(m_Size-num), 0xDD, num * sizeof(T) ); + memset(&Element(m_Size-num), 0xDD, num * sizeof(T)); #endif } } -//----------------------------------------------------------------------------- // Adds an element, uses default constructor -//----------------------------------------------------------------------------- - -template< class T > +template inline int CUtlVector::AddToHead() { return InsertBefore(0); } -template< class T > +template inline int CUtlVector::AddToTail() { - return InsertBefore( m_Size ); + return InsertBefore(m_Size); } -template< class T > -inline int CUtlVector::InsertAfter( int elem ) +template +inline int CUtlVector::InsertAfter(int elem) { - return InsertBefore( elem + 1 ); + return InsertBefore(elem + 1); } -template< class T > -int CUtlVector::InsertBefore( int elem ) +template +int CUtlVector::InsertBefore(int elem) { // Can insert at the end - assert( (elem == Count()) || IsValidIndex(elem) ); + Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(); ShiftElementsRight(elem); - Construct( &Element(elem) ); + Construct(&Element(elem)); return elem; } -//----------------------------------------------------------------------------- // Adds an element, uses copy constructor -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::AddToHead( T const& src ) +template +inline int CUtlVector::AddToHead(T const &src) { - return InsertBefore( 0, src ); + return InsertBefore(0, src); } template< class T > -inline int CUtlVector::AddToTail( T const& src ) +inline int CUtlVector::AddToTail(T const &src) { - return InsertBefore( m_Size, src ); + return InsertBefore(m_Size, src); } template< class T > -inline int CUtlVector::InsertAfter( int elem, T const& src ) +inline int CUtlVector::InsertAfter(int elem, T const &src) { - return InsertBefore( elem + 1, src ); + return InsertBefore(elem + 1, src); } template< class T > -int CUtlVector::InsertBefore( int elem, T const& src ) +int CUtlVector::InsertBefore(int elem, T const &src) { // Can insert at the end - assert( (elem == Count()) || IsValidIndex(elem) ); + Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(); ShiftElementsRight(elem); - CopyConstruct( &Element(elem), src ); + CopyConstruct(&Element(elem), src); return elem; } - -//----------------------------------------------------------------------------- // Adds multiple elements, uses default constructor -//----------------------------------------------------------------------------- - -template< class T > -inline int CUtlVector::AddMultipleToHead( int num ) +template +inline int CUtlVector::AddMultipleToHead(int num) { - return InsertMultipleBefore( 0, num ); + return InsertMultipleBefore(0, num); } -template< class T > -inline int CUtlVector::AddMultipleToTail( int num, const T *pToCopy ) +template +inline int CUtlVector::AddMultipleToTail(int num, const T *pToCopy) { - return InsertMultipleBefore( m_Size, num, pToCopy ); + return InsertMultipleBefore(m_Size, num, pToCopy); } -template< class T > -int CUtlVector::InsertMultipleAfter( int elem, int num ) +template +int CUtlVector::InsertMultipleAfter(int elem, int num) { - return InsertMultipleBefore( elem + 1, num ); + return InsertMultipleBefore(elem + 1, num); } - -template< class T > -void CUtlVector::SetCount( int count ) +template +void CUtlVector::SetCount(int count) { RemoveAll(); - AddMultipleToTail( count ); + AddMultipleToTail(count); } -template< class T > -inline void CUtlVector::SetSize( int size ) +template +inline void CUtlVector::SetSize(int size) { - SetCount( size ); + SetCount(size); } -template< class T > -void CUtlVector::CopyArray( T const *pArray, int size ) +template +void CUtlVector::CopyArray(T const *pArray, int size) { - SetSize( size ); - for( int i=0; i < size; i++ ) + SetSize(size); + for(int i = 0; i < size; i++) + { (*this)[i] = pArray[i]; + } } -template< class T > -int CUtlVector::AddVectorToTail( CUtlVector const &src ) +template +int CUtlVector::AddVectorToTail(CUtlVector const &src) { int base = Count(); // Make space. - AddMultipleToTail( src.Count() ); + AddMultipleToTail(src.Count()); // Copy the elements. - for ( int i=0; i < src.Count(); i++ ) + for (int i = 0; i < src.Count(); i++) + { (*this)[base + i] = src[i]; + } return base; } -template< class T > -inline int CUtlVector::InsertMultipleBefore( int elem, int num, const T *pToInsert ) +template +inline int CUtlVector::InsertMultipleBefore(int elem, int num, const T *pToInsert) { - if( num == 0 ) + if(num == 0) return elem; // Can insert at the end - assert( (elem == Count()) || IsValidIndex(elem) ); + Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(num); ShiftElementsRight(elem, num); // Invoke default constructors - for (int i = 0; i < num; ++i) - Construct( &Element(elem+i) ); + for (int i = 0; i < num; i++) + { + Construct(&Element(elem+i)); + } // Copy stuff in? - if ( pToInsert ) + if (pToInsert) { - for ( int i=0; i < num; i++ ) + for (int i = 0; i < num; i++) { - Element( elem+i ) = pToInsert[i]; + Element(elem+i) = pToInsert[i]; } } return elem; } -//----------------------------------------------------------------------------- // Finds an element (element needs operator== defined) -//----------------------------------------------------------------------------- -template< class T > -int CUtlVector::Find( T const& src ) const +template +int CUtlVector::Find(T const &src) const { - for ( int i = 0; i < Count(); ++i ) + for (int i = 0; i < Count(); i++) { if (Element(i) == src) return i; } - return -1; + + return InvalidIndex(); } -template< class T > -bool CUtlVector::HasElement( T const& src ) +template +bool CUtlVector::HasElement(T const &src) { - return ( Find(src) >= 0 ); + return (Find(src) >= 0); } -//----------------------------------------------------------------------------- // Element removal -//----------------------------------------------------------------------------- - -template< class T > -void CUtlVector::FastRemove( int elem ) +template +void CUtlVector::FastRemove(int elem) { - assert( IsValidIndex(elem) ); + Assert(IsValidIndex(elem)); - Destruct( &Element(elem) ); + Destruct(&Element(elem)); if (m_Size > 0) { - memcpy( &Element(elem), &Element(m_Size-1), sizeof(T) ); - --m_Size; + memcpy(&Element(elem), &Element(m_Size - 1), sizeof(T)); + m_Size--; } } -template< class T > -void CUtlVector::Remove( int elem ) +template +void CUtlVector::Remove(int elem) { - Destruct( &Element(elem) ); + Destruct(&Element(elem)); ShiftElementsLeft(elem); - --m_Size; + m_Size--; } -template< class T > -void CUtlVector::FindAndRemove( T const& src ) +template +void CUtlVector::FindAndRemove(T const &src) { - int elem = Find( src ); - if ( elem != -1 ) + int elem = Find(src); + if (elem != InvalidIndex()) { - Remove( elem ); + Remove(elem); } } -template< class T > -void CUtlVector::RemoveMultiple( int elem, int num ) +template +void CUtlVector::RemoveMultiple(int elem, int num) { - assert( IsValidIndex(elem) ); - assert( elem + num <= Count() ); + Assert(IsValidIndex(elem)); + Assert(elem + num <= Count()); - for (int i = elem + num; --i >= elem; ) + for (int i = elem + num; --i >= elem;) Destruct(&Element(i)); ShiftElementsLeft(elem, num); m_Size -= num; } -template< class T > +template void CUtlVector::RemoveAll() { - for (int i = m_Size; --i >= 0; ) + for (int i = m_Size; --i >= 0;) Destruct(&Element(i)); m_Size = 0; } -//----------------------------------------------------------------------------- // Memory deallocation -//----------------------------------------------------------------------------- - -template< class T > +template void CUtlVector::Purge() { RemoveAll(); - m_Memory.Purge( ); + m_Memory.Purge(); ResetDbgInfo(); } -template +template inline void CUtlVector::PurgeAndDeleteElements() { - for( int i=0; i < m_Size; i++ ) + for (int i = 0; i < m_Size; i++) delete Element(i); Purge(); } -template< class T > -void CUtlVector::SetGrowSize( int size ) +template +void CUtlVector::SetGrowSize(int size) { - m_Memory.SetGrowSize( size ); + m_Memory.SetGrowSize(size); } - -#endif // CCVECTOR_H diff --git a/settings.gradle b/settings.gradle index e8ab98c..acdb2b7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,5 +3,6 @@ include 'dep/cppunitlite' include 'dep/bzip2' include 'rehlds' include 'rehlds/dedicated' +include 'rehlds/filesystem/FileSystem_Stdio' include 'rehlds/HLTV', 'rehlds/HLTV/Core', 'rehlds/HLTV/Proxy', 'rehlds/HLTV/Console', 'rehlds/HLTV/Director', 'rehlds/HLTV/DemoPlayer' include 'flightrec/decoder_api', 'flightrec/decoder' diff --git a/shared_gcc.gradle b/shared_gcc.gradle index 8639882..2c1d1f4 100644 --- a/shared_gcc.gradle +++ b/shared_gcc.gradle @@ -66,6 +66,6 @@ rootProject.ext.createGccConfig = { boolean release, BinaryKind binKind -> ) } - cfg.singleDefines('LINUX') + cfg.singleDefines('LINUX', '_LINUX') return cfg } diff --git a/shared_icc.gradle b/shared_icc.gradle index 506386a..db1ae28 100644 --- a/shared_icc.gradle +++ b/shared_icc.gradle @@ -66,6 +66,6 @@ rootProject.ext.createIccConfig = { boolean release, BinaryKind binKind -> ) } - cfg.singleDefines('LINUX') + cfg.singleDefines('LINUX', '_LINUX') return cfg } From 4fad5255ba7e0ee9d9ba6ca7864ad5a27f3de2ca Mon Sep 17 00:00:00 2001 From: s1lent Date: Sat, 9 Dec 2017 12:21:09 +0700 Subject: [PATCH 2/4] Refactoring world.cpp, sv_phys.cpp, sv_move.cpp --- .gitignore | 11 +- rehlds/engine/mathlib.cpp | 17 +- rehlds/engine/mathlib_e.h | 99 +- rehlds/engine/net.h | 14 + rehlds/engine/net_ws.cpp | 2 +- rehlds/engine/pmovetst.cpp | 2 +- rehlds/engine/pr_cmds.cpp | 15 +- rehlds/engine/r_studio.cpp | 2 +- rehlds/engine/sv_main.cpp | 10 +- rehlds/engine/sv_move.cpp | 593 ++++++----- rehlds/engine/sv_move.h | 14 +- rehlds/engine/sv_phys.cpp | 1374 +++++++++++++------------- rehlds/engine/sv_phys.h | 6 +- rehlds/engine/sv_user.cpp | 99 +- rehlds/engine/textures.cpp | 4 +- rehlds/engine/world.cpp | 1456 ++++++++++++++-------------- rehlds/engine/world.h | 20 +- rehlds/hookers/engine/hooklist.cpp | 6 +- rehlds/public/rehlds/progs.h | 2 +- 19 files changed, 1970 insertions(+), 1776 deletions(-) diff --git a/.gitignore b/.gitignore index 63383d8..574a0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,22 +2,23 @@ **/.gradle .idea *.iml +*.bat +*.log +*.lnk **/msvc/Debug* **/msvc/Release* **/msvc/Tests **/msvc/Test Fixes -**/msvc/.vs -**/msvc/*.db -**/msvc/*.opendb **/msvc/*.sdf **/msvc/*.opensdf **/msvc/*.user **/msvc/*.suo **/msvc/*.db **/msvc/*.opendb -**/msvc/PublishPath*.txt -**/msvc/ipch +**/msvc/*.txt +**/msvc/*.amplxeproj **/msvc/.vs +**/msvc/ipch rehlds/version/appversion.h rehlds/_rehldsTestImg diff --git a/rehlds/engine/mathlib.cpp b/rehlds/engine/mathlib.cpp index 107c3cd..8b81108 100644 --- a/rehlds/engine/mathlib.cpp +++ b/rehlds/engine/mathlib.cpp @@ -558,11 +558,11 @@ NOBODY void InterpolateAngles(float *start, float *end, float *output, float fra // NormalizeAngles(float *angles); // 453 //} -void VectorTransform(const vec_t *in1, float *in2, vec_t *out) +void VectorTransform(const vec_t *in1, float (*in2)[4], vec_t *out) { - out[0] = _DotProduct(in1, in2 + 0) + in2[3]; - out[1] = _DotProduct(in1, in2 + 4) + in2[7]; - out[2] = _DotProduct(in1, in2 + 8) + in2[11]; + out[0] = _DotProduct(in1, in2[0]) + in2[0][3]; + out[1] = _DotProduct(in1, in2[1]) + in2[1][3]; + out[2] = _DotProduct(in1, in2[2]) + in2[2][3]; } int VectorCompare(const vec_t *v1, const vec_t *v2) @@ -595,7 +595,7 @@ void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out) #endif #ifndef REHLDS_FIXES -long double _DotProduct(const vec_t *v1, const vec_t *v2) +real_t _DotProduct(const vec_t *v1, const vec_t *v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } @@ -689,13 +689,6 @@ NOBODY void VectorInverse(vec_t *v); //{ //} -void VectorScale(const vec_t *in, float scale, vec_t *out) -{ - out[0] = scale * in[0]; - out[1] = scale * in[1]; - out[2] = scale * in[2]; -} - NOBODY int Q_log2(int val); //{ // int answer; // 568 diff --git a/rehlds/engine/mathlib_e.h b/rehlds/engine/mathlib_e.h index ff9c89f..7bfcb2a 100644 --- a/rehlds/engine/mathlib_e.h +++ b/rehlds/engine/mathlib_e.h @@ -31,6 +31,16 @@ #include "maintypes.h" #include "model.h" +#ifndef REHLDS_FIXES +// NOTE: In some cases we need high precision of floating-point, +// so use double instead of float, otherwise unittest will fail +typedef double real_t; +#else +typedef float real_t; +#endif + +typedef real_t real3_t[3]; + enum { PITCH = 0, // up / down @@ -45,6 +55,22 @@ enum #define vec3_origin (*pvec3_origin) #endif // HOOK_ENGINE +#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ + (((p)->type < 3) ? \ + ( \ + ((p)->dist <= (emins)[(p)->type]) ? \ + 1 \ + : \ + ( \ + ((p)->dist >= (emaxs)[(p)->type]) ? \ + 2 \ + : \ + 3 \ + ) \ + ) \ + : \ + BoxOnPlaneSide((emins), (emaxs), (p))) + extern vec3_t vec3_origin; static const int nanmask = 0x7F800000; @@ -104,6 +130,71 @@ inline T M_clamp(T a, T min, T max) { return clamp(a, min, max); } +inline void VectorAdd(const vec_t *veca, const vec_t *vecb, vec_t *out) +{ + out[0] = veca[0] + vecb[0]; + out[1] = veca[1] + vecb[1]; + out[2] = veca[2] + vecb[2]; +} + +template +inline void VectorSubtract(const vec_t *veca, const vec_t *vecb, T *out) +{ + out[0] = veca[0] - vecb[0]; + out[1] = veca[1] - vecb[1]; + out[2] = veca[2] - vecb[2]; +} + +#ifndef REHLDS_FIXES +template +inline void VectorMA(const vec_t *veca, float scale, const T *vecm, vec_t *out) +{ + out[0] = scale * vecm[0] + veca[0]; + out[1] = scale * vecm[1] + veca[1]; + out[2] = scale * vecm[2] + veca[2]; +} +#endif + +inline void VectorScale(const vec_t *in, float scale, vec_t *out) +{ + out[0] = scale * in[0]; + out[1] = scale * in[1]; + out[2] = scale * in[2]; +} + +inline void VectorClear(vec_t *in) +{ + in[0] = 0.0f; + in[1] = 0.0f; + in[2] = 0.0f; +} + +inline void VectorCopy(const vec_t *in, vec_t *out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +inline void VectorNegate(const vec_t *in, vec_t *out) +{ + out[0] = -in[0]; + out[1] = -in[1]; + out[2] = -in[2]; +} + +inline void VectorAverage(const vec_t *veca, const vec_t *vecb, vec_t *out) +{ + out[0] = (veca[0] + vecb[0]) * 0.5f; + out[1] = (veca[1] + vecb[1]) * 0.5f; + out[2] = (veca[2] + vecb[2]) * 0.5f; +} + +inline bool VectorIsZero(const vec_t *in) +{ + return (in[0] == 0.0f && in[1] == 0.0f && in[2] == 0.0f); +} + float anglemod(float a); void BOPS_Error(void); @@ -116,14 +207,10 @@ void AngleMatrix(const vec_t *angles, float(*matrix)[4]); NOBODY void AngleIMatrix(const vec_t *angles, float *matrix); NOBODY void NormalizeAngles(float *angles); NOBODY void InterpolateAngles(float *start, float *end, float *output, float frac); -void VectorTransform(const vec_t *in1, float *in2, vec_t *out); +void VectorTransform(const vec_t *in1, float (*in2)[4], vec_t *out); int VectorCompare(const vec_t *v1, const vec_t *v2); void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out); -#ifdef REHLDS_FIXES -float _DotProduct(const vec_t *v1, const vec_t *v2); // with sse support -#else // REHLDS_FIXES -long double _DotProduct(const vec_t *v1, const vec_t *v2); // precise -#endif // REHLDS_FIXES +real_t _DotProduct(const vec_t *v1, const vec_t *v2); NOBODY void _VectorSubtract(vec_t *veca, vec_t *vecb, vec_t *out); void _VectorAdd(vec_t *veca, vec_t *vecb, vec_t *out); NOBODY void _VectorCopy(vec_t *in, vec_t *out); diff --git a/rehlds/engine/net.h b/rehlds/engine/net.h index c21e305..9f3fa98 100644 --- a/rehlds/engine/net.h +++ b/rehlds/engine/net.h @@ -54,6 +54,17 @@ const char S2C_CONNECTION = 'B'; // HLMaster rejected a server's connection because the server needs to be updated const char M2S_REQUESTRESTART = 'O'; +// Response details about each player on the server +const char S2A_PLAYERS = 'D'; + +// Response as multi-packeted the rules the server is using +const char S2A_RULES = 'E'; + +// info request +const char S2A_INFO = 'C'; // deprecated goldsrc response + +const char S2A_INFO_DETAILED = 'm'; // New Query protocol, returns dedicated or not, + other performance info. + // send a log event as key value const char S2A_LOGSTRING = 'R'; @@ -78,6 +89,9 @@ const char A2A_PING = 'i'; // respond with an A2A_ACK // Generic Ack const char A2A_ACK = 'j'; // general acknowledgement without info +// Print to client console +const char A2A_PRINT = 'l'; // print a message on client + // Challenge response from master const char M2A_CHALLENGE = 's'; // + challenge value diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp index 265a683..a18b641 100644 --- a/rehlds/engine/net_ws.cpp +++ b/rehlds/engine/net_ws.cpp @@ -1273,7 +1273,7 @@ qboolean NET_GetPacket(netsrc_t sock) { Q_memcpy(net_message.data, in_message.data, in_message.cursize); net_message.cursize = in_message.cursize; - Q_memcpy(&net_from, &in_from, 0x14u); + Q_memcpy(&net_from, &in_from, sizeof(netadr_t)); NET_ThreadUnlock(); return bret; } diff --git a/rehlds/engine/pmovetst.cpp b/rehlds/engine/pmovetst.cpp index a2c6480..0b36057 100644 --- a/rehlds/engine/pmovetst.cpp +++ b/rehlds/engine/pmovetst.cpp @@ -501,7 +501,7 @@ pmtrace_t _PM_PlayerTrace(vec_t *start, vec_t *end, int traceFlags, int numphyse closest = 0; for (int j = 0; j < pNumHulls; j++) { - Q_memset(&testtrace, 0, 0x44u); + Q_memset(&testtrace, 0, sizeof(testtrace)); testtrace.endpos[0] = end[0]; testtrace.endpos[1] = end[1]; testtrace.endpos[2] = end[2]; diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp index e9d13ab..f7ab9a8 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -339,7 +339,7 @@ void EXT_FUNC PF_traceline_Shared(const float *v1, const float *v2, int nomonste #ifdef REHLDS_OPT_PEDANTIC trace_t trace = SV_Move_Point(v1, v2, nomonsters, ent); #else // REHLDS_OPT_PEDANTIC - trace_t trace = SV_Move(v1, vec3_origin, vec3_origin, v2, nomonsters, ent, 0); + trace_t trace = SV_Move(v1, vec3_origin, vec3_origin, v2, nomonsters, ent, FALSE); #endif // REHLDS_OPT_PEDANTIC gGlobalVariables.trace_flags = 0; @@ -370,7 +370,8 @@ void EXT_FUNC TraceHull(const float *v1, const float *v2, int fNoMonsters, int h hullNumber = hullNumber; if (hullNumber < 0 || hullNumber > 3) hullNumber = 0; - trace_t trace = SV_Move(v1, gHullMins[hullNumber], gHullMaxs[hullNumber], v2, fNoMonsters, pentToSkip, 0); + + trace_t trace = SV_Move(v1, gHullMins[hullNumber], gHullMaxs[hullNumber], v2, fNoMonsters, pentToSkip, FALSE); ptr->fAllSolid = trace.allsolid; ptr->fStartSolid = trace.startsolid; @@ -608,7 +609,7 @@ void EXT_FUNC PF_TraceToss_DLL(edict_t *pent, edict_t *pentToIgnore, TraceResult int EXT_FUNC TraceMonsterHull(edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr) { - qboolean monsterClip = (pEdict->v.flags & FL_MONSTERCLIP) ? 1 : 0; + qboolean monsterClip = (pEdict->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; trace_t trace = SV_Move(v1, pEdict->v.mins, pEdict->v.maxs, v2, fNoMonsters, pentToSkip, monsterClip); if (ptr) { @@ -1676,12 +1677,12 @@ int EXT_FUNC PF_droptofloor_I(edict_t *ent) { vec3_t end; trace_t trace; - qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? 1 : 0; + qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; end[0] = ent->v.origin[0]; end[1] = ent->v.origin[1]; end[2] = ent->v.origin[2] - 256.0; - trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, 0, ent, monsterClip); + trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClip); if (trace.allsolid) return -1; @@ -1772,7 +1773,7 @@ void EXT_FUNC PF_aim_I(edict_t *ent, float speed, float *rgflReturn) start[1] += ent->v.view_ofs[1]; start[2] += ent->v.view_ofs[2]; VectorMA(start, 2048.0, dir, end); - tr = SV_Move(start, vec3_origin, vec3_origin, end, 0, ent, 0); + tr = SV_Move(start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent, FALSE); if (tr.ent && tr.ent->v.takedamage == 2.0f && (ent->v.team <= 0 || ent->v.team != tr.ent->v.team)) { @@ -1811,7 +1812,7 @@ void EXT_FUNC PF_aim_I(edict_t *ent, float speed, float *rgflReturn) if (dist >= bestdist) { - tr = SV_Move(start, vec3_origin, vec3_origin, end, 0, ent, 0); + tr = SV_Move(start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent, FALSE); if (tr.ent == check) { bestdist = dist; diff --git a/rehlds/engine/r_studio.cpp b/rehlds/engine/r_studio.cpp index 7a692a8..e9b5be0 100644 --- a/rehlds/engine/r_studio.cpp +++ b/rehlds/engine/r_studio.cpp @@ -950,7 +950,7 @@ void EXT_FUNC GetAttachment(const edict_t *pEdict, int iAttachment, float *rgflO ); if (rgflOrigin) - VectorTransform(pattachment->org, (float *)bonetransform[pattachment->bone], rgflOrigin); + VectorTransform(pattachment->org, bonetransform[pattachment->bone], rgflOrigin); } int ModelFrameCount(model_t *model) diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 085ad3f..30bb71c 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -2832,7 +2832,7 @@ NOXREF void ReplyServerChallenge(netadr_t *adr) buf.flags = SIZEBUF_ALLOW_OVERFLOW; MSG_WriteLong(&buf, 0xffffffff); - MSG_WriteByte(&buf, 65); + MSG_WriteByte(&buf, S2C_CHALLENGE); MSG_WriteLong(&buf, GetChallengeNr(adr)); NET_SendPacket(NS_SERVER, buf.cursize, (char *)buf.data, *adr); } @@ -3000,7 +3000,7 @@ NOXREF void SVC_Info(qboolean bDetailed) } MSG_WriteLong(&buf, 0xffffffff); - MSG_WriteByte(&buf, bDetailed ? 109 : 67); + MSG_WriteByte(&buf, bDetailed ? S2A_INFO_DETAILED : S2A_INFO); if (noip) { @@ -3102,7 +3102,7 @@ NOXREF void SVC_PlayerInfo(void) buf.flags = SIZEBUF_ALLOW_OVERFLOW; MSG_WriteLong(&buf, 0xffffffff); - MSG_WriteByte(&buf, 68); + MSG_WriteByte(&buf, S2A_PLAYERS); for (i = 0; i < g_psvs.maxclients; i++) { @@ -3154,7 +3154,7 @@ NOXREF void SVC_RuleInfo(void) return; MSG_WriteLong(&buf, 0xffffffff); - MSG_WriteByte(&buf, 69); + MSG_WriteByte(&buf, S2A_RULES); MSG_WriteShort(&buf, nNumRules); var = cvar_vars; @@ -3216,7 +3216,7 @@ void SV_FlushRedirect(void) buf.flags = SIZEBUF_ALLOW_OVERFLOW; MSG_WriteLong(&buf, -1); - MSG_WriteByte(&buf, 0x6Cu); + MSG_WriteByte(&buf, A2A_PRINT); MSG_WriteString(&buf, outputbuf); MSG_WriteByte(&buf, 0); NET_SendPacket(NS_SERVER, buf.cursize, buf.data, sv_redirectto); diff --git a/rehlds/engine/sv_move.cpp b/rehlds/engine/sv_move.cpp index 7d1a91a..472a135 100644 --- a/rehlds/engine/sv_move.cpp +++ b/rehlds/engine/sv_move.cpp @@ -28,21 +28,7 @@ #include "precompiled.h" -/* -* Local initialization -*/ -#ifndef HOOK_ENGINE - -static int c_yes = 0; -static int c_no = 0; - -#else // HOOK_ENGINE - -int c_yes; -int c_no; - -#endif // HOOK_ENGINE - +// Returns false if any part of the bottom of the entity is off an edge that is not a staircase. qboolean SV_CheckBottom(edict_t *ent) { vec3_t mins; @@ -54,12 +40,17 @@ qboolean SV_CheckBottom(edict_t *ent) int y; float mid; float bottom; - qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? 1 : 0; - _VectorAdd(ent->v.origin, ent->v.mins, mins); - _VectorAdd(ent->v.origin, ent->v.maxs, maxs); + qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; + + VectorAdd(ent->v.origin, ent->v.mins, mins); + VectorAdd(ent->v.origin, ent->v.maxs, maxs); + + // if all of the points under the corners are solid world, don't bother + // with the tougher checks + // the corners must be within 16 of the midpoint + start[2] = mins[2] - 1.0f; - start[2] = mins[2] - 1; for (x = 0; x <= 1; x++) { for (y = 0; y <= 1; y++) @@ -73,23 +64,28 @@ qboolean SV_CheckBottom(edict_t *ent) goto realcheck; } } - ++c_yes; - return 1; + + // we got out easy + return TRUE; realcheck: - ++c_no; + + // check it for real... start[2] = mins[2]; + // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5f; start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5f; - stop[2] = start[2] - 2 * sv_stepsize.value; - trace = SV_Move(start, vec3_origin, vec3_origin, stop, 1, ent, monsterClip); + + stop[2] = start[2] - 2.0f * sv_stepsize.value; + trace = SV_Move(start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip); if (trace.fraction == 1.0f) - return 0; + return FALSE; mid = bottom = trace.endpos[2]; + // the corners must be within 16 of the midpoint for (x = 0; x <= 1; x++) { for (y = 0; y <= 1; y++) @@ -97,18 +93,23 @@ realcheck: start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; - trace = SV_Move(start, vec3_origin, vec3_origin, stop, 1, ent, monsterClip); + trace = SV_Move(start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip); if (trace.fraction != 1.0f && trace.endpos[2] > bottom) bottom = trace.endpos[2]; + if (trace.fraction == 1.0f || mid - trace.endpos[2] > sv_stepsize.value) - return 0; + return FALSE; } } - return 1; + return TRUE; } +// Called by monster program code. +// The move will be adjusted for slopes and stairs, but if the move isn't +// possible, no move is done, false is returned, and +// pr_global_struct->trace_normal is set to the normal of the blocking wall qboolean SV_movetest(edict_t *ent, vec_t *move, qboolean relink) { vec3_t oldorg; @@ -116,223 +117,255 @@ qboolean SV_movetest(edict_t *ent, vec_t *move, qboolean relink) vec3_t end; trace_t trace; - oldorg[0] = ent->v.origin[0]; - oldorg[1] = ent->v.origin[1]; - oldorg[2] = ent->v.origin[2]; - - neworg[0] = ent->v.origin[0] + move[0]; - neworg[1] = ent->v.origin[1] + move[1]; - neworg[2] = ent->v.origin[2] + move[2]; - end[0] = neworg[0]; - end[1] = neworg[1]; + // try the move + VectorCopy(ent->v.origin, oldorg); + VectorAdd(ent->v.origin, move, neworg); + // push down from a step height above the wished position neworg[2] += sv_stepsize.value; - end[2] = neworg[2] - (2 * sv_stepsize.value); - trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, 0, ent); + VectorCopy(neworg, end); + end[2] -= sv_stepsize.value * 2.0f; + + trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); + if (trace.allsolid) - return 0; + return FALSE; if (trace.startsolid) { neworg[2] -= sv_stepsize.value; - trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, 0, ent); + trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); + if (trace.allsolid || trace.startsolid) - return 0; + return FALSE; } + if (trace.fraction == 1.0f) { + // if monster had the ground pulled out, go ahead and fall if (ent->v.flags & FL_PARTIALGROUND) { - ent->v.origin[0] = *move + ent->v.origin[0]; - ent->v.origin[1] = ent->v.origin[1] + move[1]; - ent->v.origin[2] = ent->v.origin[2] + move[2]; - if (relink) - SV_LinkEdict(ent, TRUE); + VectorAdd(ent->v.origin, move, ent->v.origin); + if (relink) + { + SV_LinkEdict(ent, TRUE); + } + + // fall down ent->v.flags &= ~FL_ONGROUND; - return 1; + return TRUE; } - return 0; + + // walked off an edge + return FALSE; } - ent->v.origin[0] = trace.endpos[0]; - ent->v.origin[1] = trace.endpos[1]; - ent->v.origin[2] = trace.endpos[2]; - if (SV_CheckBottom(ent) == 0) + // check point traces down for dangling corners + VectorCopy(trace.endpos, ent->v.origin); + + if (!SV_CheckBottom(ent)) { if (!(ent->v.flags & FL_PARTIALGROUND)) { - ent->v.origin[0] = oldorg[0]; - ent->v.origin[1] = oldorg[1]; - ent->v.origin[2] = oldorg[2]; - return 0; + VectorCopy(oldorg, ent->v.origin); + return FALSE; } + + // entity had floor mostly pulled out from underneath it + // and is trying to correct } else { if (ent->v.flags & FL_PARTIALGROUND) { + // back on ground ent->v.flags &= ~FL_PARTIALGROUND; } + ent->v.groundentity = trace.ent; } + // the move is ok if (relink) + { SV_LinkEdict(ent, TRUE); + } - return 1; + return TRUE; } +// Called by monster program code. +// The move will be adjusted for slopes and stairs, but if the move isn't +// possible, no move is done, false is returned, and +// pr_global_struct->trace_normal is set to the normal of the blocking wall qboolean SV_movestep(edict_t *ent, vec_t *move, qboolean relink) { trace_t trace; - vec3_t end; - vec3_t oldorg; - float dz; - qboolean monsterClipBrush; - vec3_t start; + vec3_t neworg, oldorg, end; - oldorg[0] = ent->v.origin[0]; - oldorg[1] = ent->v.origin[1]; - oldorg[2] = ent->v.origin[2]; + // try the move + VectorCopy(ent->v.origin, oldorg); + VectorAdd(ent->v.origin, move, neworg); - start[0] = ent->v.origin[0] + move[0]; - start[1] = ent->v.origin[1] + move[1]; - start[2] = ent->v.origin[2] + move[2]; - monsterClipBrush = (ent->v.flags & FL_MONSTERCLIP) != 0; + qboolean monsterClipBrush = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; + + // flying monsters don't step up if (ent->v.flags & (FL_FLY | FL_SWIM)) { - int i = 0; - while (i < 2) + // try one move with vertical motion, then one without + for (int i = 0; i < 2; i++) { - start[0] = ent->v.origin[0] + move[0]; - start[1] = ent->v.origin[1] + move[1]; - start[2] = ent->v.origin[2] + move[2]; - edict_t* enemy = ent->v.enemy; - + VectorAdd(ent->v.origin, move, neworg); + edict_t *enemy = ent->v.enemy; if (i == 0 && enemy) { - dz = ent->v.origin[2] - enemy->v.origin[2]; - if (dz > 40.0) - start[2] = start[2] - 8.0; - if (dz < 30.0) - start[2] = start[2] + 8.0; + float dz = ent->v.origin[2] - enemy->v.origin[2]; + + if (dz > 40.0f) + neworg[2] -= 8.0f; + else if (dz < 30.0f) + neworg[2] += 8.0f; } - trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, start, 0, ent, monsterClipBrush); + + trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, neworg, MOVE_NORMAL, ent, monsterClipBrush); + if (trace.fraction == 1.0f) - break; + { + g_groupmask = ent->v.groupinfo; + + if ((ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY) + { + // swim monster left water + return FALSE; + } + + VectorCopy(trace.endpos, ent->v.origin); + + if (relink) + { + SV_LinkEdict(ent, TRUE); + } + + return TRUE; + } if (!enemy) - return 0; - - if (i == 1) - return 0; - - i++; + { + break; + } } - g_groupmask = ent->v.groupinfo; - if ((ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == -1) - return 0; - - ent->v.origin[0] = trace.endpos[0]; - ent->v.origin[1] = trace.endpos[1]; - ent->v.origin[2] = trace.endpos[2]; - if (relink) - SV_LinkEdict(ent, TRUE); - - return 1; + return FALSE; } - start[2] += sv_stepsize.value; - end[0] = start[0]; - end[1] = start[1]; - end[2] = start[2] - (2 * sv_stepsize.value); - trace = SV_Move(start, ent->v.mins, ent->v.maxs, end, 0, ent, (ent->v.flags & FL_MONSTERCLIP) != 0); + // push down from a step height above the wished position + neworg[2] += sv_stepsize.value; + VectorCopy(neworg, end); + end[2] -= sv_stepsize.value * 2.0f; + + trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClipBrush); + if (trace.allsolid) - return 0; + return FALSE; if (trace.startsolid) { - start[2] = start[2] - sv_stepsize.value; - trace = SV_Move(start, ent->v.mins, ent->v.maxs, end, 0, ent, monsterClipBrush); + neworg[2] = neworg[2] - sv_stepsize.value; + trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClipBrush); + if (trace.allsolid || trace.startsolid) - return 0; + return FALSE; } - if (trace.fraction != 1.0f) + if (trace.fraction == 1.0f) { - ent->v.origin[0] = trace.endpos[0]; - ent->v.origin[1] = trace.endpos[1]; - ent->v.origin[2] = trace.endpos[2]; - if (SV_CheckBottom(ent) == 0) + // if monster had the ground pulled out, go ahead and fall + if (ent->v.flags & FL_PARTIALGROUND) { - if (!(ent->v.flags & FL_PARTIALGROUND)) + VectorAdd(ent->v.origin, move, ent->v.origin); + + if (relink) { - ent->v.origin[0] = oldorg[0]; - ent->v.origin[1] = oldorg[1]; - ent->v.origin[2] = oldorg[2]; - return 0; + SV_LinkEdict(ent, TRUE); } - } - else - { - if (ent->v.flags & FL_PARTIALGROUND) - ent->v.flags &= ~FL_PARTIALGROUND; - ent->v.groundentity = trace.ent; + // fall down + ent->v.flags &= ~FL_ONGROUND; + return TRUE; } - if (relink) - SV_LinkEdict(ent, TRUE); - - return 1; + // walked off an edge + return FALSE; } - if (!(ent->v.flags & FL_PARTIALGROUND)) - return 0; + // check point traces down for dangling corners + VectorCopy(trace.endpos, ent->v.origin); - ent->v.origin[0] += move[0]; - ent->v.origin[1] += move[1]; - ent->v.origin[2] += move[2]; + if (!SV_CheckBottom(ent)) + { + if (!(ent->v.flags & FL_PARTIALGROUND)) + { + VectorCopy(oldorg, ent->v.origin); + return FALSE; + } + + // entity had floor mostly pulled out from underneath it + // and is trying to correct + } + else + { + if (ent->v.flags & FL_PARTIALGROUND) + { + // back on ground + ent->v.flags &= ~FL_PARTIALGROUND; + } + + ent->v.groundentity = trace.ent; + } + + // the move is ok if (relink) + { SV_LinkEdict(ent, TRUE); + } - ent->v.flags &= ~FL_ONGROUND; - return 1; + return TRUE; } +// Turns to the movement direction, and walks the current distance if facing it. qboolean SV_StepDirection(edict_t *ent, float yaw, float dist) { vec3_t move; - move[0] = cos(yaw * (2 * M_PI) / 360.0) * dist; - move[1] = sin(yaw * (2 * M_PI) / 360.0) * dist; - move[2] = 0; - if (SV_movestep(ent, move, 0)) + yaw = yaw * (M_PI * 2.0f) / 360.0f; + + move[0] = cos(yaw) * dist; + move[1] = sin(yaw) * dist; + move[2] = 0.0f; + + if (SV_movestep(ent, move, FALSE)) { SV_LinkEdict(ent, TRUE); - return 1; - } - else - { - SV_LinkEdict(ent, TRUE); - return 0; + return TRUE; } + + SV_LinkEdict(ent, TRUE); + return FALSE; } qboolean SV_FlyDirection(edict_t *ent, vec_t *direction) { - if (SV_movestep(ent, direction, 0)) + if (SV_movestep(ent, direction, FALSE)) { SV_LinkEdict(ent, TRUE); - return 1; + return TRUE; } else { SV_LinkEdict(ent, TRUE); - return 0; + return FALSE; } } @@ -341,51 +374,91 @@ void SV_FixCheckBottom(edict_t *ent) ent->v.flags |= FL_PARTIALGROUND; } +const int DI_NODIR = -1; + NOXREF void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist) { NOXREFCHECK; - float deltax; - float deltay; - float d[3]; - float tdir; - float olddir; - float turnaround; - olddir = anglemod(45.0 * (int)(actor->v.ideal_yaw / 45.0)); - turnaround = anglemod(olddir - 180.0); + SV_NewChaseDir2(actor, enemy->v.origin, dist); +} - deltax = enemy->v.origin[0] - actor->v.origin[0]; - deltay = enemy->v.origin[1] - actor->v.origin[1]; +NOXREF qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist) +{ + NOXREFCHECK; - if (deltax > 10.0) - d[1] = 0.0; - else if (deltax <- 10.0) - d[1]= 180.0; + for (int i = 0; i < 3; i++) + { + if (goal->v.absmin[i] > ent->v.absmax[i] + dist) + return FALSE; + + if (goal->v.absmax[i] < ent->v.absmin[i] - dist) + return FALSE; + } + + return TRUE; +} + +NOXREF qboolean SV_ReachedGoal(edict_t *ent, vec_t *vecGoal, float flDist) +{ + NOXREFCHECK; + + for (int i = 0; i < 3; i++) + { + if (vecGoal[i] > ent->v.absmax[i] + flDist) + return FALSE; + + if (vecGoal[i] < ent->v.absmin[i] - flDist) + return FALSE; + } + + return TRUE; +} + +void SV_NewChaseDir2(edict_t *actor, vec_t *vecGoal, float dist) +{ + vec3_t d; + float deltax, deltay; + float tempdir, olddir, turnaround; + + olddir = anglemod(45.0f * (int)(actor->v.ideal_yaw / 45.0f)); + turnaround = anglemod(olddir - 180.0f); + + deltax = vecGoal[0] - actor->v.origin[0]; + deltay = vecGoal[1] - actor->v.origin[1]; + + if (deltax > 10.0f) + d[1] = 0.0f; + else if (deltax < -10.0f) + d[1] = 180.0f; else - d[1]= DI_NODIR; - if (deltay < -10.0) - d[2] = 270.0; - else if (deltay > 10.0) - d[2] = 90.0; - else - d[2]= DI_NODIR; + d[1] = DI_NODIR; + if (deltay < -10.0f) + d[2] = 270.0f; + else if (deltay > 10.0f) + d[2] = 90.0f; + else + d[2] = DI_NODIR; + + // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { - if (d[1] == 0.0) - tdir = d[2] == 90.0 ? 45.0 : 315.0; + if (d[1] == 0.0f) + tempdir = d[2] == 90.0f ? 45.0f : 315.0f; else - tdir = d[2] == 90.0 ? 135.0 : 215.0; + tempdir = d[2] == 90.0f ? 135.0f : 215.0f; - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) + if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist)) return; } - if (RandomLong(0, 1) || abs(deltay) > abs(deltax)) + // try other directions + if (RandomLong(0, 1) || abs(deltay) > abs(deltax)) { - tdir = d[1]; + tempdir = d[1]; d[1] = d[2]; - d[2] = tdir; + d[2] = tempdir; } if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist)) @@ -394,168 +467,68 @@ NOXREF void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist) if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist)) return; + // there is no direct path to the player, so pick another direction if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; + // randomly determine direction of search if (RandomLong(0, 1)) { - for (tdir = 0.0; tdir <= 315.0; tdir += 45.0) - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; + for (tempdir = 0.0f; tempdir <= 315.0f; tempdir += 45.0f) + { + if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist)) + return; + } } else { - for (tdir = 315.0 ; tdir >= 0.0; tdir -= 45.0) - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; + for (tempdir = 315.0f; tempdir >= 0.0f; tempdir -= 45.0f) + { + if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist)) + return; + } } + // we tried. run backwards. that ought to work... if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist)) return; + // can't move, we're stuck somehow actor->v.ideal_yaw = olddir; + // if a bridge was pulled out from underneath a monster, it may not have + // a valid standing position at all if (!SV_CheckBottom(actor)) + { SV_FixCheckBottom(actor); -} - -NOXREF qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist) -{ - NOXREFCHECK; - int i; - for (i = 0; i < 3; i++) - { - if (goal->v.absmin[i] > ent->v.absmax[i] + dist) - return FALSE; - if (goal->v.absmax[i] < ent->v.absmin[i] - dist) - return FALSE; - } - return TRUE; -} - -NOXREF qboolean SV_ReachedGoal(edict_t *ent, vec_t *vecGoal, float flDist) -{ - NOXREFCHECK; - int i; - for (i = 0; i < 3; i++) - { - if (vecGoal[i] > ent->v.absmax[i] + flDist) - return FALSE; - if (vecGoal[i] < ent->v.absmin[i] - flDist) - return FALSE; - } - return TRUE; -} - -void SV_NewChaseDir2(edict_t *actor, vec_t *vecGoal, float dist) -{ - float deltax; - float deltay; - float d_1, d_2; - float tdir; - float olddir; - float turnaround; - - olddir = anglemod(45 * (int)(actor->v.ideal_yaw / 45.0)); - turnaround = anglemod(olddir - 180.0); - deltax = vecGoal[0] - actor->v.origin[0]; - deltay = vecGoal[1] - actor->v.origin[1]; - - if (deltax > 10) - d_1 = 0; - else if (deltax < -10) - d_1 = 180; - else - d_1 = DI_NODIR; - - if (deltay < -10) - d_2 = 270; - else if (deltay > 10) - d_2 = 90; - else - d_2 = DI_NODIR; - - if (d_1 != DI_NODIR && d_2 != DI_NODIR) - { - if (d_1 == 0.0) - tdir = d_2 == 90 ? 45 : 315; - else - tdir = d_2 == 90 ? 135 : 215; - - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; - } - - if (RandomLong(0, 1) || abs(deltay) > abs(deltax)) - { - tdir = d_1; - d_1 = d_2; - d_2 = tdir; - } - - if (d_1 != DI_NODIR && d_1 != turnaround - && SV_StepDirection(actor, d_1, dist)) - return; - - if (d_2 != DI_NODIR && d_2 != turnaround - && SV_StepDirection(actor, d_2, dist)) - return; - - if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) - return; - - if (RandomLong(0, 1)) - { - for (tdir = 0; tdir <= 315; tdir += 45) - { - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; - } - } - else - { - for (tdir = 315; tdir >= 0; tdir -= 45) - { - if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) - return; - } - } - - if (turnaround == DI_NODIR || !SV_StepDirection(actor, turnaround, dist)) - { - actor->v.ideal_yaw = olddir; - if (!SV_CheckBottom(actor)) - SV_FixCheckBottom(actor); } } -void EXT_FUNC SV_MoveToOrigin_I(edict_t *ent, const float *pflGoal, float dist, int iStrafe) +void EXT_FUNC SV_MoveToOrigin_I(edict_t *ent, const float *pflGoal, float dist, int iMoveType) { vec3_t vecGoal; - vec3_t vecDir; + VectorCopy(pflGoal, vecGoal); - vecGoal[0] = pflGoal[0]; - vecGoal[1] = pflGoal[1]; - vecGoal[2] = pflGoal[2]; - - if (ent->v.flags & (FL_ONGROUND | FL_SWIM | FL_FLY)) + if (ent->v.flags & (FL_FLY | FL_SWIM | FL_ONGROUND)) { - if (iStrafe) + if (iMoveType == MOVE_NORMAL) { - vecDir[0] = vecGoal[0] - ent->v.origin[0]; - vecDir[1] = vecGoal[1] - ent->v.origin[1]; - vecDir[2] = vecGoal[2] - ent->v.origin[2]; + if (!SV_StepDirection(ent, ent->v.ideal_yaw, dist)) + { + SV_NewChaseDir2(ent, vecGoal, dist); + } + } + else + { + vec3_t vecDir; + VectorSubtract(vecGoal, ent->v.origin, vecDir); + if (!(ent->v.flags & (FL_SWIM | FL_FLY))) - vecDir[2] = 0; + vecDir[2] = 0.0f; VectorNormalize(vecDir); VectorScale(vecDir, dist, vecDir); SV_FlyDirection(ent, vecDir); } - else - { - if (!SV_StepDirection(ent, ent->v.ideal_yaw, dist)) - SV_NewChaseDir2(ent, vecGoal, dist); - } } } diff --git a/rehlds/engine/sv_move.h b/rehlds/engine/sv_move.h index d53cd58..29a3829 100644 --- a/rehlds/engine/sv_move.h +++ b/rehlds/engine/sv_move.h @@ -31,18 +31,6 @@ #include "maintypes.h" #include "server.h" -const int DI_NODIR = -1; - -#ifdef HOOK_ENGINE - -#define c_yes (*pc_yes) -#define c_no (*pc_no) - -extern int c_yes; -extern int c_no; - -#endif // HOOK_ENGINE - qboolean SV_CheckBottom(edict_t *ent); qboolean SV_movetest(edict_t *ent, vec_t *move, qboolean relink); qboolean SV_movestep(edict_t *ent, vec_t *move, qboolean relink); @@ -53,4 +41,4 @@ NOXREF void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist); NOXREF qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist); NOXREF qboolean SV_ReachedGoal(edict_t *ent, vec_t *vecGoal, float flDist); void SV_NewChaseDir2(edict_t *actor, vec_t *vecGoal, float dist); -void SV_MoveToOrigin_I(edict_t *ent, const float *pflGoal, float dist, int iStrafe); +void SV_MoveToOrigin_I(edict_t *ent, const float *pflGoal, float dist, int iMoveType); diff --git a/rehlds/engine/sv_phys.cpp b/rehlds/engine/sv_phys.cpp index b922566..fe14441 100644 --- a/rehlds/engine/sv_phys.cpp +++ b/rehlds/engine/sv_phys.cpp @@ -28,8 +28,22 @@ #include "precompiled.h" -vec3_t* g_moved_from; -edict_t** g_moved_edict; +// Pushmove objects do not obey gravity, and do not interact with each other or trigger fields, +// but block normal movement and push normal objects when they move. +// +// onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects +// +// doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH +// bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS +// corpses are SOLID_NOT and MOVETYPE_TOSS +// crates are SOLID_BBOX and MOVETYPE_TOSS +// walking monsters are SOLID_BBOX and MOVETYPE_STEP +// flying/floating monsters are SOLID_BBOX and MOVETYPE_FLY +// +// solid_edge items only clip against bsp models. + +vec3_t *g_moved_from; +edict_t **g_moved_edict; /* * Globals initialization @@ -54,12 +68,13 @@ cvar_t sv_stopspeed; #endif // HOOK_ENGINE -NOXREF void SV_CheckAllEnts(void) +NOXREF void SV_CheckAllEnts() { NOXREFCHECK; int e; edict_t *check; + // see if any solid entities are inside the final position for (e = 1; e < g_psv.num_edicts; e++) { check = &g_psv.edicts[e]; @@ -67,22 +82,20 @@ NOXREF void SV_CheckAllEnts(void) if (check->free) continue; - int movetype = check->v.movetype; - if (check->v.movetype == MOVETYPE_NONE) - continue; + if (check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NOCLIP + || check->v.movetype == MOVETYPE_FOLLOW) + continue; - if (movetype != MOVETYPE_PUSH - && movetype != MOVETYPE_NOCLIP - && movetype != MOVETYPE_FOLLOW) - { - if (SV_TestEntityPosition(check) != NULL) - Con_Printf("entity in invalid position\n"); - } + if (SV_TestEntityPosition(check) != NULL) + Con_Printf("entity in invalid position\n"); } } void SV_CheckVelocity(edict_t *ent) { + // bound velocity for (int i = 0; i < 3; i++) { if (IS_NAN(ent->v.velocity[i])) @@ -90,6 +103,7 @@ void SV_CheckVelocity(edict_t *ent) Con_Printf("Got a NaN velocity on %s\n", &pr_strings[ent->v.classname]); ent->v.velocity[i] = 0; } + if (IS_NAN(ent->v.origin[i])) { Con_Printf("Got a NaN origin on %s\n", &pr_strings[ent->v.classname]); @@ -121,16 +135,14 @@ qboolean SV_RunThink(edict_t *ent) { thinktime = ent->v.nextthink; if (thinktime <= 0.0 || thinktime > g_psv.time + host_frametime) - { return TRUE; - } + if (thinktime < g_psv.time) - { - thinktime = (float) g_psv.time; // don't let things stay in the past. - // it is possible to start that way - // by a trigger with a local time. - } - ent->v.nextthink = 0.0; + thinktime = g_psv.time; // don't let things stay in the past. + // it is possible to start that way + // by a trigger with a local time. + + ent->v.nextthink = 0.0f; gGlobalVariables.time = thinktime; gEntityInterface.pfnThink(ent); } @@ -143,9 +155,11 @@ qboolean SV_RunThink(edict_t *ent) return !ent->free; } +// Two entities have touched, so run their touch functions void SV_Impact(edict_t *e1, edict_t *e2, trace_t *ptrace) { - gGlobalVariables.time = (float) g_psv.time; + gGlobalVariables.time = g_psv.time; + if ((e1->v.flags & FL_KILLME) || (e2->v.flags & FL_KILLME)) return; @@ -163,7 +177,7 @@ void SV_Impact(edict_t *e1, edict_t *e2, trace_t *ptrace) } } - if (e1->v.solid) + if (e1->v.solid != SOLID_NOT) { SV_SetGlobalTrace(ptrace); gEntityInterface.pfnTouch(e1, e2); @@ -173,176 +187,145 @@ void SV_Impact(edict_t *e1, edict_t *e2, trace_t *ptrace) return; #endif // REHLDS_FIXES } - if (e2->v.solid) + + if (e2->v.solid != SOLID_NOT) { SV_SetGlobalTrace(ptrace); gEntityInterface.pfnTouch(e2, e1); } } +const float STOP_EPSILON = 0.1f; + +// Slide off of the impacting object int ClipVelocity(vec_t *in, vec_t *normal, vec_t *out, float overbounce) { int blocked = 0; - if (normal[2] > 0.0) - blocked = 1; - if (normal[2] == 0.0) - blocked |= 2u; + if (normal[2] > 0.0f) + blocked = 1; // floor + if (normal[2] == 0.0f) + blocked |= 2; // step + float change; float backoff = _DotProduct(in, normal) * overbounce; + for (int i = 0; i < 3; i++) { - float tmp = normal[i] * backoff; - float change = in[i] - tmp; - out[i] = (change > -0.1f && change < 0.1f) ? 0.0f : change; + change = normal[i] * backoff; + out[i] = in[i] - change; + + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + out[i] = 0.0f; } return blocked; } +#define MAX_CLIP_PLANES 5 + +// The basic solid body movement clip that slides along multiple planes +// steptrace - if not NULL, the trace results of any vertical wall hit will be stored +// Returns the clipflags if the velocity was modified (hit something solid) +// 1 = floor +// 2 = wall / step +// 4 = dead stop int SV_FlyMove(edict_t *ent, float time, trace_t *steptrace) { - vec_t planes[5][3]; - int numplanes = 0; - - vec3_t primal_velocity; - primal_velocity[0] = ent->v.velocity[0]; - primal_velocity[1] = ent->v.velocity[1]; - primal_velocity[2] = ent->v.velocity[2]; - - vec3_t original_velocity; - original_velocity[0] = ent->v.velocity[0]; - original_velocity[1] = ent->v.velocity[1]; - original_velocity[2] = ent->v.velocity[2]; - - vec3_t new_velocity; - - qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? 1 : 0; int blocked = 0; - for (int bumpcount = 0; bumpcount < 4; bumpcount++) + int numplanes = 0; + float time_left = time; + + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity, new_velocity; + + VectorCopy(ent->v.velocity, original_velocity); + VectorCopy(ent->v.velocity, primal_velocity); + + qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; + for (int bumpcount = 0; bumpcount < MAX_CLIP_PLANES - 1; bumpcount++) { - if (ent->v.velocity[0] == 0.0f && ent->v.velocity[1] == 0.0f && ent->v.velocity[2] == 0.0f) + if (VectorIsZero(ent->v.velocity)) break; vec3_t end; - for (int i = 0; i < 3; i++) - VectorMA(ent->v.origin, time, ent->v.velocity, end); + VectorMA(ent->v.origin, time_left, ent->v.velocity, end); - trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, 0, ent, monsterClip); + trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClip); if (trace.allsolid) { - ent->v.velocity[0] = vec3_origin[0]; - ent->v.velocity[1] = vec3_origin[1]; - ent->v.velocity[2] = vec3_origin[2]; - return 4; + // entity is trapped in another solid + VectorClear(ent->v.velocity); + return 4; // dead end } if (trace.fraction > 0.0f) { - trace_t test = SV_Move(trace.endpos, ent->v.mins, ent->v.maxs, trace.endpos, 0, ent, monsterClip); - if (test.allsolid != 1) + trace_t test = SV_Move(trace.endpos, ent->v.mins, ent->v.maxs, trace.endpos, MOVE_NORMAL, ent, monsterClip); + if (!test.allsolid) { - ent->v.origin[0] = trace.endpos[0]; - ent->v.origin[1] = trace.endpos[1]; - ent->v.origin[2] = trace.endpos[2]; - primal_velocity[0] = ent->v.velocity[0]; - primal_velocity[1] = ent->v.velocity[1]; - primal_velocity[2] = ent->v.velocity[2]; + // actually covered some distance + VectorCopy(trace.endpos, ent->v.origin); + VectorCopy(ent->v.velocity, original_velocity); numplanes = 0; } } + // moved the entire distance if (trace.fraction == 1.0f) break; if (!trace.ent) Sys_Error("%s: !trace.ent", __func__); - if (trace.plane.normal[2] > 0.7) + if (trace.plane.normal[2] > 0.7f) { - blocked |= 1u; + blocked |= 1; // floor + if (trace.ent->v.solid == SOLID_BSP - || trace.ent->v.movetype == MOVETYPE_PUSHSTEP || trace.ent->v.solid == SOLID_SLIDEBOX - || ent->v.flags & FL_CLIENT) + || trace.ent->v.movetype == MOVETYPE_PUSHSTEP + || (ent->v.flags & FL_CLIENT)) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; } } - if (trace.plane.normal[2] == 0.0) + if (trace.plane.normal[2] == 0.0f) { - blocked |= 2u; + blocked |= 2; // step + if (steptrace) - Q_memcpy(steptrace, &trace, 0x38u); + { + // save for player extrafriction + Q_memcpy(steptrace, &trace, sizeof(trace_t)); + } } + // run the impact function SV_Impact(ent, trace.ent, &trace); + + // break if removed by the impact function if (ent->free) break; - time = time - trace.fraction * time; - if (numplanes >= 5) + time_left -= time_left * trace.fraction; + + // clipped to another plane + if (numplanes >= MAX_CLIP_PLANES) { - ent->v.velocity[0] = vec3_origin[0]; - ent->v.velocity[1] = vec3_origin[1]; - ent->v.velocity[2] = vec3_origin[2]; - return blocked; + // this shouldn't really happen + VectorClear(ent->v.velocity); + break; } - planes[numplanes][0] = trace.plane.normal[0]; - planes[numplanes][1] = trace.plane.normal[1]; - planes[numplanes][2] = trace.plane.normal[2]; - ++numplanes; - if (numplanes != 1 - || ent->v.movetype != MOVETYPE_WALK - || ((ent->v.flags & FL_ONGROUND) && ent->v.friction == 1.0f)) - { - int i = 0; - for (; i < numplanes; i++) - { - ClipVelocity(primal_velocity, planes[i], new_velocity, 1.0); - int j = 0; - for (; j < numplanes; j++) - { - if (j == i) - continue; + VectorCopy(trace.plane.normal, planes[numplanes]); + numplanes++; - if (_DotProduct(new_velocity, planes[j]) < 0.0f) - break; - } - if (j == numplanes) - break; - } - if (i == numplanes) - { - if (numplanes != 2) - return blocked; - - vec3_t dir; - CrossProduct(planes[0], planes[1], dir); - float vscale = _DotProduct(dir, ent->v.velocity); - VectorScale(dir, vscale, ent->v.velocity); - } - else - { - ent->v.velocity[1] = new_velocity[1]; - ent->v.velocity[2] = new_velocity[2]; - ent->v.velocity[0] = new_velocity[0]; - } - - if (_DotProduct(original_velocity, ent->v.velocity) <= 0.0f) - { - ent->v.velocity[0] = vec3_origin[0]; - ent->v.velocity[1] = vec3_origin[1]; - ent->v.velocity[2] = vec3_origin[2]; - return blocked; - } - } - else + if (numplanes == 1 && ent->v.movetype == MOVETYPE_WALK && (!(ent->v.flags & FL_ONGROUND) || ent->v.friction != 1.0f)) { float d; - if (planes[0][2] <= 0.7) + if (planes[0][2] <= 0.7f) { d = (1.0f - ent->v.friction) * sv_bounce.value + 1.0f; } @@ -350,13 +333,61 @@ int SV_FlyMove(edict_t *ent, float time, trace_t *steptrace) { d = 1.0f; } - ClipVelocity(primal_velocity, planes[0], new_velocity, d); - ent->v.velocity[0] = new_velocity[0]; - ent->v.velocity[1] = new_velocity[1]; - ent->v.velocity[2] = new_velocity[2]; - primal_velocity[0] = new_velocity[0]; - primal_velocity[1] = new_velocity[1]; - primal_velocity[2] = new_velocity[2]; + + ClipVelocity(original_velocity, planes[0], new_velocity, d); + + VectorCopy(new_velocity, ent->v.velocity); + VectorCopy(new_velocity, original_velocity); + } + else + { + int i, j; + + // modify original_velocity so it parallels all of the clip planes + for (i = 0; i < numplanes; i++) + { + ClipVelocity(original_velocity, planes[i], new_velocity, 1.0f); + + for (j = 0; j < numplanes; j++) + { + if (j != i) + { + if (_DotProduct(new_velocity, planes[j]) < 0.0f) + break; // not ok + } + } + + if (j == numplanes) + break; + } + + if (i != numplanes) + { + // go along this plane + VectorCopy(new_velocity, ent->v.velocity); + } + else + { + // go along the crease + if (numplanes != 2) + { + //VectorClear(ent->v.velocity); + break; + } + + vec3_t dir; + CrossProduct(planes[0], planes[1], dir); + float d = _DotProduct(dir, ent->v.velocity); + VectorScale(dir, d, ent->v.velocity); + } + + // if current velocity is against the original velocity, + // stop dead to avoid tiny occilations in sloping corners + if (_DotProduct(ent->v.velocity, primal_velocity) <= 0.0f) + { + VectorClear(ent->v.velocity); + break; + } } } @@ -366,36 +397,42 @@ int SV_FlyMove(edict_t *ent, float time, trace_t *steptrace) void SV_AddGravity(edict_t *ent) { float ent_gravity; - if (ent->v.gravity == 0.0) - ent_gravity = 1.0; - else + if (ent->v.gravity) ent_gravity = ent->v.gravity; - ent_gravity = (float)(ent->v.velocity[2] - sv_gravity.value * ent_gravity * host_frametime); - ent->v.velocity[2] = (float)(ent_gravity + ent->v.basevelocity[2] * host_frametime); - ent->v.basevelocity[2] = 0; + else + ent_gravity = 1.0f; + + ent->v.velocity[2] -= (ent_gravity * sv_gravity.value * host_frametime); + ent->v.velocity[2] += (ent->v.basevelocity[2] * host_frametime); + ent->v.basevelocity[2] = 0.0f; + SV_CheckVelocity(ent); } NOXREF void SV_AddCorrectGravity(edict_t *ent) { NOXREFCHECK; - float ent_gravity = 1.0f; - if (ent->v.gravity != 0.0f) + float ent_gravity; + if (ent->v.gravity) ent_gravity = ent->v.gravity; + else + ent_gravity = 1.0f; ent->v.velocity[2] -= (ent_gravity * sv_gravity.value * 0.5f * host_frametime); - ent->v.velocity[2] += ent->v.basevelocity[2] * host_frametime; + ent->v.velocity[2] += (ent->v.basevelocity[2] * host_frametime); + ent->v.basevelocity[2] = 0.0f; - pmove->basevelocity[2] = 0.0f; SV_CheckVelocity(ent); } NOXREF void SV_FixupGravityVelocity(edict_t *ent) { NOXREFCHECK; - float ent_gravity = 1.0f; - if (ent->v.gravity != 0.0f) + float ent_gravity; + if (ent->v.gravity) ent_gravity = ent->v.gravity; + else + ent_gravity = 1.0f; ent->v.velocity[2] -= (ent_gravity * sv_gravity.value * 0.5f * host_frametime); SV_CheckVelocity(ent); @@ -403,145 +440,152 @@ NOXREF void SV_FixupGravityVelocity(edict_t *ent) trace_t SV_PushEntity(edict_t *ent, vec_t *push) { - trace_t trace; vec3_t end; + VectorAdd(push, ent->v.origin, end); + int moveType; - - end[0] = push[0] + ent->v.origin[0]; - end[1] = push[1] + ent->v.origin[1]; - end[2] = push[2] + ent->v.origin[2]; - if (ent->v.movetype == MOVETYPE_FLYMISSILE) - moveType = 2; + moveType = MOVE_MISSILE; + else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) + moveType = MOVE_NOMONSTERS; // only clip against bmodels else - moveType = (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) ? 1 : 0; + moveType = MOVE_NORMAL; - qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? 1 : 0; - trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, moveType, ent, monsterClip); - if (trace.fraction != 0.0) + qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; + trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, moveType, ent, monsterClip); + + if (trace.fraction != 0.0f) { - ent->v.origin[0] = trace.endpos[0]; - ent->v.origin[1] = trace.endpos[1]; - ent->v.origin[2] = trace.endpos[2]; +#ifdef REHLDS_FIXES + // don't move in the direction, since this is a dead end + if (!trace.startsolid && !trace.allsolid) +#endif + { + VectorCopy(trace.endpos, ent->v.origin); + } } SV_LinkEdict(ent, TRUE); + if (trace.ent) + { SV_Impact(ent, trace.ent, &trace); + } return trace; } void SV_PushMove(edict_t *pusher, float movetime) { - vec3_t mins; - vec3_t maxs; - vec3_t move; - vec3_t pushorig; - - if (pusher->v.velocity[0] == 0.0f && pusher->v.velocity[1] == 0.0f && pusher->v.velocity[2] == 0.0f) + if (VectorIsZero(pusher->v.velocity)) { pusher->v.ltime = movetime + pusher->v.ltime; return; } - pushorig[0] = pusher->v.origin[0]; - pushorig[1] = pusher->v.origin[1]; - pushorig[2] = pusher->v.origin[2]; + vec3_t move; + vec3_t mins, maxs; for (int i = 0; i < 3; i++) { - float movedist = movetime * pusher->v.velocity[i]; - move[i] = movedist; - mins[i] = movedist + pusher->v.absmin[i]; - maxs[i] = movedist + pusher->v.absmax[i]; - pusher->v.origin[i] = movedist + pusher->v.origin[i]; + move[i] = pusher->v.velocity[i] * movetime; + mins[i] = pusher->v.absmin[i] + move[i]; + maxs[i] = pusher->v.absmax[i] + move[i]; } - pusher->v.ltime = movetime + pusher->v.ltime; + vec3_t pushorig; + VectorCopy(pusher->v.origin, pushorig); + + // move the pusher to it's final position + VectorAdd(pusher->v.origin, move, pusher->v.origin); + pusher->v.ltime += movetime; SV_LinkEdict(pusher, FALSE); if (pusher->v.solid == SOLID_NOT) return; + // see if any solid entities are inside the final position int num_moved = 0; for (int i = 1; i < g_psv.num_edicts; i++) { - edict_t* check = &g_psv.edicts[i]; + edict_t *check = &g_psv.edicts[i]; if (check->free) continue; - if (check->v.movetype == MOVETYPE_PUSH) - continue; - - if (check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) - continue; - - if (((check->v.flags & FL_ONGROUND) && check->v.groundentity == pusher) - || (check->v.absmin[0] < maxs[0] - && check->v.absmin[1] < maxs[1] - && check->v.absmin[2] < maxs[2] - && check->v.absmax[0] > mins[0] - && check->v.absmax[1] > mins[1] - && check->v.absmax[2] > mins[2] - && SV_TestEntityPosition(check))) - { - if (check->v.movetype != MOVETYPE_WALK) - check->v.flags &= ~FL_ONGROUND; - - vec3_t entorigin; - entorigin[0] = check->v.origin[0]; - entorigin[1] = check->v.origin[1]; - entorigin[2] = check->v.origin[2]; - - pusher->v.solid = SOLID_NOT; - g_moved_from[num_moved][0] = check->v.origin[0]; - g_moved_from[num_moved][1] = check->v.origin[1]; - g_moved_from[num_moved][2] = check->v.origin[2]; - g_moved_edict[num_moved] = check; - ++num_moved; - - SV_PushEntity(check, move); - - pusher->v.solid = SOLID_BSP; - if (!SV_TestEntityPosition(check)) + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_FOLLOW + || check->v.movetype == MOVETYPE_NOCLIP) continue; + // if the entity is standing on the pusher, it will definately be moved + if (!(check->v.flags & FL_ONGROUND) || check->v.groundentity != pusher) + { + if (check->v.absmin[0] >= maxs[0] + || check->v.absmin[1] >= maxs[1] + || check->v.absmin[2] >= maxs[2] + || check->v.absmax[0] <= mins[0] + || check->v.absmax[1] <= mins[1] + || check->v.absmax[2] <= mins[2]) + continue; + + // see if the ent's bbox is inside the pusher's final position + if (!SV_TestEntityPosition(check)) + continue; + } + + // remove the onground flag for non-players + if (check->v.movetype != MOVETYPE_WALK) + check->v.flags &= ~FL_ONGROUND; + + vec3_t entorig; + VectorCopy(check->v.origin, entorig); + VectorCopy(check->v.origin, g_moved_from[num_moved]); + g_moved_edict[num_moved] = check; + num_moved++; + + // try moving the contacted entity + pusher->v.solid = SOLID_NOT; + SV_PushEntity(check, move); + pusher->v.solid = SOLID_BSP; + + // if it is still inside the pusher, block + if (SV_TestEntityPosition(check)) + { + // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { - check->v.mins[1] = 0; + // corpse check->v.mins[0] = 0; + check->v.mins[1] = 0; + check->v.maxs[0] = 0; check->v.maxs[1] = 0; check->v.maxs[2] = check->v.mins[2]; + continue; } - check->v.origin[0] = entorigin[0]; - check->v.origin[1] = entorigin[1]; - check->v.origin[2] = entorigin[2]; + VectorCopy(entorig, check->v.origin); SV_LinkEdict(check, TRUE); - pusher->v.origin[0] = pushorig[0]; - pusher->v.origin[1] = pushorig[1]; - pusher->v.origin[2] = pushorig[2]; + VectorCopy(pushorig, pusher->v.origin); SV_LinkEdict(pusher, FALSE); - pusher->v.ltime = pusher->v.ltime - movetime; + pusher->v.ltime -= movetime; gEntityInterface.pfnBlocked(pusher, check); + // move back any entities we already moved for (int e = 0; e < num_moved; e++) { - g_moved_edict[e]->v.origin[0] = g_moved_from[e][0]; - g_moved_edict[e]->v.origin[1] = g_moved_from[e][1]; - g_moved_edict[e]->v.origin[2] = g_moved_from[e][2]; - + VectorCopy(g_moved_from[e], g_moved_edict[e]->v.origin); SV_LinkEdict(g_moved_edict[e], FALSE); } + return; } } @@ -549,159 +593,154 @@ void SV_PushMove(edict_t *pusher, float movetime) int SV_PushRotate(edict_t *pusher, float movetime) { - vec3_t amove; - vec3_t pushorig; - vec3_t forward; - vec3_t right; - vec3_t up; - vec3_t forwardNow; - vec3_t rightNow; - vec3_t upNow; + vec3_t amove, pushorig; + vec3_t forward, right, up; + vec3_t forwardNow, rightNow, upNow; - if (pusher->v.avelocity[0] == 0.0 && pusher->v.avelocity[1] == 0.0 && pusher->v.avelocity[2] == 0.0) + if (VectorIsZero(pusher->v.avelocity)) { - pusher->v.ltime = movetime + pusher->v.ltime; + pusher->v.ltime += movetime; return 1; } - for (int i = 0; i < 3; i++) - amove[i] = movetime * pusher->v.avelocity[i]; - + VectorScale(pusher->v.avelocity, movetime, amove); AngleVectors(pusher->v.angles, forward, right, up); - pushorig[0] = pusher->v.angles[0]; - pushorig[1] = pusher->v.angles[1]; - pushorig[2] = pusher->v.angles[2]; + VectorCopy(pusher->v.angles, pushorig); - pusher->v.angles[0] = amove[0] + pusher->v.angles[0]; - pusher->v.angles[1] = amove[1] + pusher->v.angles[1]; - pusher->v.angles[2] = amove[2] + pusher->v.angles[2]; + // move the pusher to it's final position + VectorAdd(pusher->v.angles, amove, pusher->v.angles); AngleVectorsTranspose(pusher->v.angles, forwardNow, rightNow, upNow); - pusher->v.ltime = movetime + pusher->v.ltime; - + pusher->v.ltime += movetime; SV_LinkEdict(pusher, FALSE); + + // non-solid pushers can't push anything if (pusher->v.solid == SOLID_NOT) return 1; + // see if any solid entities are inside the final position int num_moved = 0; for (int i = 1; i < g_psv.num_edicts; i++) { - edict_t* check = &g_psv.edicts[i]; + edict_t *check = &g_psv.edicts[i]; + if (check->free) continue; - if (check->v.movetype == MOVETYPE_PUSH) - continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_FOLLOW + || check->v.movetype == MOVETYPE_NOCLIP) + continue; - if (check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) - continue; - - if (((check->v.flags & FL_ONGROUND) && check->v.groundentity == pusher) - || (check->v.absmin[0] < pusher->v.absmax[0] - && check->v.absmin[1] < pusher->v.absmax[1] - && check->v.absmin[2] < pusher->v.absmax[2] - && check->v.absmax[0] > pusher->v.absmin[0] - && check->v.absmax[1] > pusher->v.absmin[1] - && check->v.absmax[2] > pusher->v.absmin[2] - && SV_TestEntityPosition(check))) + // if the entity is standing on the pusher, it will definately be moved + if (!(check->v.flags & FL_ONGROUND) || check->v.groundentity != pusher) { - if (check->v.movetype != MOVETYPE_WALK) - check->v.flags &= ~FL_ONGROUND; + if (check->v.absmin[0] >= pusher->v.absmax[0] + || check->v.absmin[1] >= pusher->v.absmax[1] + || check->v.absmin[2] >= pusher->v.absmax[2] + || check->v.absmax[0] <= pusher->v.absmin[0] + || check->v.absmax[1] <= pusher->v.absmin[1] + || check->v.absmax[2] <= pusher->v.absmin[2]) + continue; - vec3_t entorig; - entorig[0] = check->v.origin[0]; - entorig[1] = check->v.origin[1]; - entorig[2] = check->v.origin[2]; + // see if the ent's bbox is inside the pusher's final position + if (!SV_TestEntityPosition(check)) + continue; + } - g_moved_from[num_moved][0] = check->v.origin[0]; - g_moved_from[num_moved][1] = check->v.origin[1]; - g_moved_from[num_moved][2] = check->v.origin[2]; - g_moved_edict[num_moved] = check; - ++num_moved; + // remove the onground flag for non-players + if (check->v.movetype != MOVETYPE_WALK) + check->v.flags &= ~FL_ONGROUND; - if (num_moved >= g_psv.max_edicts) - Sys_Error("%s: Out of edicts in simulator!\n", __func__); + vec3_t entorig; + VectorCopy(check->v.origin, entorig); + VectorCopy(check->v.origin, g_moved_from[num_moved]); + g_moved_edict[num_moved] = check; + num_moved++; - vec3_t start, end, push, move; + if (num_moved >= g_psv.max_edicts) + Sys_Error("%s: Out of edicts in simulator!\n", __func__); - if (check->v.movetype == MOVETYPE_PUSHSTEP) + vec3_t start, end, push, move; + + if (check->v.movetype == MOVETYPE_PUSHSTEP) + { + vec3_t org; + VectorAverage(check->v.absmax, check->v.absmin, org); + VectorSubtract(org, pusher->v.origin, start); + } + else + { + VectorSubtract(check->v.origin, pusher->v.origin, start); + } + + // calculate destination position + move[0] = _DotProduct(forward, start); + move[1] = -_DotProduct(right, start); + move[2] = _DotProduct(up, start); + + end[0] = _DotProduct(forwardNow, move); + end[1] = _DotProduct(rightNow, move); + end[2] = _DotProduct(upNow, move); + + VectorSubtract(end, start, push); + + // try moving the contacted entity + pusher->v.solid = SOLID_NOT; + SV_PushEntity(check, push); + pusher->v.solid = SOLID_BSP; + + if (check->v.movetype != MOVETYPE_PUSHSTEP) + { + if (check->v.flags & FL_CLIENT) { - vec3_t org; - org[0] = (check->v.absmax[0] + check->v.absmin[0]) * 0.5f; - org[1] = (check->v.absmax[1] + check->v.absmin[1]) * 0.5f; - org[2] = (check->v.absmax[2] + check->v.absmin[2]) * 0.5f; - start[0] = org[0] - pusher->v.origin[0]; - start[1] = org[1] - pusher->v.origin[1]; - start[2] = org[2] - pusher->v.origin[2]; + check->v.fixangle = 2; + check->v.avelocity[1] = check->v.avelocity[1] + amove[1]; } else { - start[0] = check->v.origin[0] - pusher->v.origin[0]; - start[1] = check->v.origin[1] - pusher->v.origin[1]; - start[2] = check->v.origin[2] - pusher->v.origin[2]; + //check->v.angles[0] = check->v.angles[0] + 0.0f; //TODO: The 'check->v.angles[0]' variable is assigned to itself. + check->v.angles[1] = check->v.angles[1] + amove[1]; + //check->v.angles[2] = check->v.angles[2] + 0.0f; //TODO: The 'check->v.angles[2]' variable is assigned to itself. } + } - pusher->v.solid = SOLID_NOT; - - move[0] = _DotProduct(forward, start); - move[1] = -_DotProduct(right, start); - move[2] = _DotProduct(up, start); - end[0] = _DotProduct(forwardNow, move); - end[1] = _DotProduct(rightNow, move); - end[2] = _DotProduct(upNow, move); - push[0] = end[0] - start[0]; - push[1] = end[1] - start[1]; - push[2] = end[2] - start[2]; - SV_PushEntity(check, push); - - pusher->v.solid = SOLID_BSP; - if (check->v.movetype != MOVETYPE_PUSHSTEP) - { - if (check->v.flags & FL_CLIENT) - { - check->v.fixangle = 2; - check->v.avelocity[1] = amove[1] + check->v.avelocity[1]; - } - else - { - //check->v.angles[0] = check->v.angles[0] + 0.0f; //TODO: The 'check->v.angles[0]' variable is assigned to itself. - check->v.angles[1] = amove[1] + check->v.angles[1]; - //check->v.angles[2] = check->v.angles[2] + 0.0f; //TODO: The 'check->v.angles[2]' variable is assigned to itself. - } - } - - if (!SV_TestEntityPosition(check) || check->v.mins[0] == check->v.maxs[0]) + // if it is still inside the pusher, block + if (SV_TestEntityPosition(check)) + { + // fail the move + if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { - check->v.mins[1] = 0.0f; + // corpse check->v.mins[0] = 0.0f; + check->v.mins[1] = 0.0f; check->v.maxs[0] = 0.0f; check->v.maxs[1] = 0.0f; + check->v.maxs[2] = check->v.mins[2]; continue; } - check->v.origin[0] = entorig[0]; - check->v.origin[1] = entorig[1]; - check->v.origin[2] = entorig[2]; + VectorCopy(entorig, check->v.origin); SV_LinkEdict(check, TRUE); - pusher->v.angles[0] = pushorig[0]; - pusher->v.angles[1] = pushorig[1]; - pusher->v.angles[2] = pushorig[2]; + VectorCopy(pushorig, pusher->v.angles); SV_LinkEdict(pusher, FALSE); - pusher->v.ltime = pusher->v.ltime - movetime; + pusher->v.ltime -= movetime; gEntityInterface.pfnBlocked(pusher, check); + + // move back any entities we already moved for (int e = 0; e < num_moved; e++) { - edict_t* movedEnt = g_moved_edict[e]; - movedEnt->v.origin[0] = g_moved_from[e][0]; - movedEnt->v.origin[1] = g_moved_from[e][1]; - movedEnt->v.origin[2] = g_moved_from[e][2]; + edict_t *movedEnt = g_moved_edict[e]; + VectorCopy(g_moved_from[e], movedEnt->v.origin); + if (movedEnt->v.flags & FL_CLIENT) { movedEnt->v.avelocity[1] = 0.0f; @@ -715,6 +754,7 @@ int SV_PushRotate(edict_t *pusher, float movetime) //movedEnt->v.angles[2] = movedEnt->v.angles[2]; //TODO: V570 The 'movedEnt->v.angles[2]' variable is assigned to itself. } } + SV_LinkEdict(movedEnt, FALSE); } @@ -731,27 +771,30 @@ void SV_Physics_Pusher(edict_t *ent) float oldltime = ent->v.ltime; float movetime; - if (oldltime + host_frametime <= thinktime) + if (thinktime < oldltime + host_frametime) { - movetime = (float) host_frametime; + movetime = thinktime - ent->v.ltime; + + if (movetime < 0.0f) + movetime = 0.0f; } else { - movetime = thinktime - ent->v.ltime; - if (movetime < 0.0) - movetime = 0; + movetime = host_frametime; } - if (movetime != 0.0) + + if (movetime) { - if (ent->v.avelocity[0] != 0.0 || ent->v.avelocity[1] != 0.0 || ent->v.avelocity[2] != 0.0) + if (!VectorIsZero(ent->v.avelocity)) { - if (ent->v.velocity[0] != 0.0 || ent->v.velocity[1] != 0.0 || ent->v.velocity[2] != 0.0) + if (!VectorIsZero(ent->v.velocity)) { if (SV_PushRotate(ent, movetime)) { float savetime = ent->v.ltime; ent->v.ltime = oldltime; SV_PushMove(ent, movetime); + if (ent->v.ltime < savetime) ent->v.ltime = savetime; } @@ -780,7 +823,7 @@ void SV_Physics_Pusher(edict_t *ent) ) { ent->v.nextthink = 0; - gGlobalVariables.time = (float) g_psv.time; + gGlobalVariables.time = g_psv.time; gEntityInterface.pfnThink(ent); } } @@ -788,166 +831,170 @@ void SV_Physics_Pusher(edict_t *ent) qboolean SV_CheckWater(edict_t *ent) { vec3_t point; - - ent->v.waterlevel = 0; - ent->v.watertype = -1; - g_groupmask = ent->v.groupinfo; point[0] = (ent->v.absmax[0] + ent->v.absmin[0]) * 0.5f; point[1] = (ent->v.absmax[1] + ent->v.absmin[1]) * 0.5f; - point[2] = ent->v.absmin[2] + 1.0f; - int cont = SV_PointContents(point); - if (cont > CONTENTS_WATER || cont <= CONTENTS_TRANSLUCENT) - return 0; + point[2] = (ent->v.absmin[2] + 1.0f); - ent->v.watertype = cont; - ent->v.waterlevel = 1; - if (ent->v.absmin[2] == ent->v.absmax[2]) + ent->v.watertype = CONTENTS_EMPTY; + ent->v.waterlevel = 0; + + g_groupmask = ent->v.groupinfo; + + int cont = SV_PointContents(point); + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) { - ent->v.waterlevel = 3; - } - else - { - g_groupmask = ent->v.groupinfo; - point[2] = (ent->v.absmax[2] + ent->v.absmin[2]) * 0.5f; - int truecont = SV_PointContents(point); - if (truecont <= CONTENTS_WATER && truecont > CONTENTS_TRANSLUCENT) + ent->v.watertype = cont; + ent->v.waterlevel = 1; + + if (ent->v.absmin[2] == ent->v.absmax[2]) + { + ent->v.waterlevel = 3; + } + else { - ent->v.waterlevel = 2; g_groupmask = ent->v.groupinfo; - point[0] = point[0] + ent->v.view_ofs[0]; - point[1] = point[1] + ent->v.view_ofs[1]; - point[2] = point[2] + ent->v.view_ofs[2]; - truecont = SV_PointContents(point); + point[2] = (ent->v.absmax[2] + ent->v.absmin[2]) * 0.5f; + + int truecont = SV_PointContents(point); if (truecont <= CONTENTS_WATER && truecont > CONTENTS_TRANSLUCENT) - ent->v.waterlevel = 3; + { + ent->v.waterlevel = 2; + g_groupmask = ent->v.groupinfo; + point[0] = point[0] + ent->v.view_ofs[0]; + point[1] = point[1] + ent->v.view_ofs[1]; + point[2] = point[2] + ent->v.view_ofs[2]; + truecont = SV_PointContents(point); + if (truecont <= CONTENTS_WATER && truecont > CONTENTS_TRANSLUCENT) + ent->v.waterlevel = 3; + } + } + + if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) + { + static vec3_t current_table[] = + { + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { -1.0f, 0.0f, 0.0f }, + { 0.0f, -1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + { 0.0f, 0.0f, -1.0f }, + }; + + VectorMA(ent->v.basevelocity, 150.0f * ent->v.waterlevel / 3.0f, current_table[-(cont - CONTENTS_CURRENT_0)], ent->v.basevelocity); } } - if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) - { - static vec_t current_table[6][3] = - { - { 1.0f, 0.0f, 0.0f }, - { 0.0f, 1.0f, 0.0f }, - { -1.0f, 0.0f, 0.0f }, - { 0.0f, -1.0f, 0.0f }, - { 0.0f, 0.0f, 1.0f }, - { 0.0f, 0.0f, -1.0f } - }; - - VectorMA(ent->v.basevelocity, ent->v.waterlevel * 50.0f, current_table[-(cont - CONTENTS_CURRENT_0)], ent->v.basevelocity); - } - - return (ent->v.waterlevel > 1) ? 1 : 0; + return (ent->v.waterlevel > 1) ? TRUE : FALSE; } -float SV_RecursiveWaterLevel(vec_t *center, float out, float in, int count) +float SV_RecursiveWaterLevel(const vec_t *origin, float mins, float maxs, int depth) { - float offset = (float)((out - in) * 0.5 + in); - if (count + 1 < 6) - { - vec3_t test; - test[0] = center[0]; - test[1] = center[1]; - test[2] = offset + center[2]; - if (SV_PointContents(test) == CONTENTS_WATER) - return SV_RecursiveWaterLevel(center, out, offset, count + 1); - else - return SV_RecursiveWaterLevel(center, offset, in, count + 1); - } - else - { - return offset; - } + float waterlevel = ((mins - maxs) * 0.5f + maxs); + if (++depth > 5) + return waterlevel; + + vec3_t test; + test[0] = origin[0]; + test[1] = origin[1]; + test[2] = origin[2] + waterlevel; + + if (SV_PointContents(test) == CONTENTS_WATER) + return SV_RecursiveWaterLevel(origin, mins, waterlevel, depth); + + return SV_RecursiveWaterLevel(origin, waterlevel, maxs, depth); } float SV_Submerged(edict_t *ent) { - float end; vec3_t center; + VectorAverage(ent->v.absmax, ent->v.absmin, center); - center[0] = (ent->v.absmax[0] + ent->v.absmin[0]) * 0.5f; - center[1] = (ent->v.absmax[1] + ent->v.absmin[1]) * 0.5f; - center[2] = (ent->v.absmin[2] + ent->v.absmax[2]) * 0.5f; - float start = ent->v.absmin[2] - center[2]; - if (ent->v.waterlevel == 1) + float waterlevel = ent->v.absmin[2] - center[2]; + + switch (ent->v.waterlevel) { - return SV_RecursiveWaterLevel(center, 0.0, start, 0) - start; - } - - if (ent->v.waterlevel == 2) - { - end = ent->v.absmax[2] - center[2]; - return SV_RecursiveWaterLevel(center, end, 0.0, 0) - start; - } - - if (ent->v.waterlevel == 3) + case 1: + return SV_RecursiveWaterLevel(center, 0.0f, waterlevel, 0) - waterlevel; + case 2: + return SV_RecursiveWaterLevel(center, ent->v.absmax[2] - center[2], 0.0f, 0) - waterlevel; + case 3: { vec3_t point; point[0] = center[0]; point[1] = center[1]; point[2] = ent->v.absmax[2]; + g_groupmask = ent->v.groupinfo; - if (SV_PointContents(point) == -3) + + if (SV_PointContents(point) == CONTENTS_WATER) + { return ent->v.maxs[2] - ent->v.mins[2]; + } - end = ent->v.absmax[2] - center[2]; - return SV_RecursiveWaterLevel(center, end, 0.0, 0) - start; + return SV_RecursiveWaterLevel(center, ent->v.absmax[2] - center[2], 0.0f, 0) - waterlevel; + } + default: + return 0.0f; } - - return 0; } // Non moving objects can only think. void SV_Physics_None(edict_t *ent) { + // regular thinking SV_RunThink(ent); } +// Just copy angles and origin of parent void SV_Physics_Follow(edict_t *ent) { - if (SV_RunThink(ent)) + // regular thinking + if (!SV_RunThink(ent)) + return; + + edict_t *parent = ent->v.aiment; + if (!ent->v.aiment) { - if (ent->v.aiment) - { - ent->v.angles[0] = ent->v.aiment->v.angles[0]; - ent->v.angles[1] = ent->v.aiment->v.angles[1]; - ent->v.angles[2] = ent->v.aiment->v.angles[2]; - ent->v.origin[0] = ent->v.aiment->v.origin[0] + ent->v.v_angle[0]; - ent->v.origin[1] = ent->v.aiment->v.origin[1] + ent->v.v_angle[1]; - ent->v.origin[2] = ent->v.aiment->v.origin[2] + ent->v.v_angle[2]; - SV_LinkEdict(ent, TRUE); - } - else - { - Con_DPrintf("%s movetype FOLLOW with NULL aiment\n", &pr_strings[ent->v.classname]); - ent->v.movetype = 0; - } + Con_DPrintf("%s movetype FOLLOW with NULL aiment\n", &pr_strings[ent->v.classname]); + ent->v.movetype = MOVETYPE_NONE; + return; } + + VectorAdd(parent->v.origin, ent->v.v_angle, ent->v.origin); + VectorCopy(parent->v.angles, ent->v.angles); + + SV_LinkEdict(ent, TRUE); } +// A moving object that doesn't obey physics void SV_Physics_Noclip(edict_t *ent) { - if (SV_RunThink(ent)) - { - VectorMA(ent->v.angles, (float)host_frametime, ent->v.avelocity, ent->v.angles); - VectorMA(ent->v.origin, (float)host_frametime, ent->v.velocity, ent->v.origin); - SV_LinkEdict(ent, FALSE); - } + // regular thinking + if (!SV_RunThink(ent)) + return; + + VectorMA(ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); + VectorMA(ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + + // noclip ents never touch triggers + SV_LinkEdict(ent, FALSE); } void SV_CheckWaterTransition(edict_t *ent) { vec3_t point; - g_groupmask = ent->v.groupinfo; point[0] = (ent->v.absmax[0] + ent->v.absmin[0]) * 0.5f; point[1] = (ent->v.absmax[1] + ent->v.absmin[1]) * 0.5f; - point[2] = ent->v.absmin[2] + 1.0f; - int cont = SV_PointContents(point); + point[2] = (ent->v.absmin[2] + 1.0f); + g_groupmask = ent->v.groupinfo; + + int cont = SV_PointContents(point); if (ent->v.watertype == 0) { + // just spawned here ent->v.watertype = cont; ent->v.waterlevel = 1; return; @@ -955,38 +1002,47 @@ void SV_CheckWaterTransition(edict_t *ent) if (cont > CONTENTS_WATER || cont <= CONTENTS_TRANSLUCENT) { - if (ent->v.watertype != -1) - SV_StartSound(0, ent, 0, "player/pl_wade2.wav", 255, 1.0, 0, 100); - ent->v.watertype = -1; + if (ent->v.watertype != CONTENTS_EMPTY) + { + // just crossed into water + SV_StartSound(0, ent, CHAN_AUTO, "player/pl_wade2.wav", 255, 1.0f, 0, PITCH_NORM); + } + + ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = 0; return; } - if (ent->v.watertype == -1) + if (ent->v.watertype == CONTENTS_EMPTY) { - SV_StartSound(0, ent, 0, "player/pl_wade1.wav", 255, 1.0, 0, 100); - ent->v.velocity[2] = ent->v.velocity[2] * 0.5f; + // just crossed into water + SV_StartSound(0, ent, CHAN_AUTO, "player/pl_wade1.wav", 255, 1.0f, 0, PITCH_NORM); + ent->v.velocity[2] *= 0.5f; } ent->v.watertype = cont; ent->v.waterlevel = 1; + if (ent->v.absmin[2] == ent->v.absmax[2]) { + // a point entity ent->v.waterlevel = 3; return; } - g_groupmask = ent->v.groupinfo; point[2] = (ent->v.absmin[2] + ent->v.absmax[2]) * 0.5f; + + g_groupmask = ent->v.groupinfo; cont = SV_PointContents(point); + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) { ent->v.waterlevel = 2; + VectorAdd(point, ent->v.view_ofs, point); + g_groupmask = ent->v.groupinfo; - point[0] = point[0] + ent->v.view_ofs[0]; - point[1] = point[1] + ent->v.view_ofs[1]; - point[2] = point[2] + ent->v.view_ofs[2]; cont = SV_PointContents(point); + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) { ent->v.waterlevel = 3; @@ -994,50 +1050,61 @@ void SV_CheckWaterTransition(edict_t *ent) } } +// Toss, bounce, and fly movement. When onground, do nothing. void SV_Physics_Toss(edict_t *ent) { SV_CheckWater(ent); + + // regular thinking if (!SV_RunThink(ent)) return; - if (ent->v.velocity[2] > 0.0 || ent->v.groundentity == NULL || ent->v.groundentity->v.flags & (FL_MONSTER | FL_CLIENT)) + if (ent->v.velocity[2] > 0.0f || !ent->v.groundentity || (ent->v.groundentity->v.flags & (FL_MONSTER | FL_CLIENT))) { ent->v.flags &= ~FL_ONGROUND; + } - if ((ent->v.flags & FL_ONGROUND) && VectorCompare(ent->v.velocity, vec_origin)) { - ent->v.avelocity[0] = vec3_origin[0]; - ent->v.avelocity[1] = vec3_origin[1]; - ent->v.avelocity[2] = vec3_origin[2]; - if (VectorCompare(ent->v.basevelocity, vec_origin)) - return; + // if on ground and not moving, return. + if ((ent->v.flags & FL_ONGROUND) && VectorIsZero(ent->v.velocity)) + { + VectorClear(ent->v.avelocity); + + if (VectorIsZero(ent->v.basevelocity)) + return; // at rest } SV_CheckVelocity(ent); - if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE) - SV_AddGravity(ent); - VectorMA(ent->v.angles, (float)host_frametime, ent->v.avelocity, ent->v.angles); - ent->v.velocity[0] = ent->v.basevelocity[0] + ent->v.velocity[0]; - ent->v.velocity[1] = ent->v.basevelocity[1] + ent->v.velocity[1]; - ent->v.velocity[2] = ent->v.basevelocity[2] + ent->v.velocity[2]; + // add gravity + switch (ent->v.movetype) + { + case MOVETYPE_FLY: + case MOVETYPE_FLYMISSILE: + case MOVETYPE_BOUNCEMISSILE: + break; + default: + SV_AddGravity(ent); + break; + } + + // move angles + VectorMA(ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + + // move origin + VectorAdd(ent->v.velocity, ent->v.basevelocity, ent->v.velocity); SV_CheckVelocity(ent); vec3_t move; - VectorScale(ent->v.velocity, (float)host_frametime, move); - ent->v.velocity[0] = ent->v.velocity[0] - ent->v.basevelocity[0]; - ent->v.velocity[1] = ent->v.velocity[1] - ent->v.basevelocity[1]; - ent->v.velocity[2] = ent->v.velocity[2] - ent->v.basevelocity[2]; + VectorScale(ent->v.velocity, host_frametime, move); + VectorSubtract(ent->v.velocity, ent->v.basevelocity, ent->v.velocity); trace_t trace = SV_PushEntity(ent, move); SV_CheckVelocity(ent); if (trace.allsolid) { - ent->v.velocity[0] = vec3_origin[0]; - ent->v.velocity[1] = vec3_origin[1]; - ent->v.velocity[2] = vec3_origin[2]; - ent->v.avelocity[0] = vec3_origin[0]; - ent->v.avelocity[1] = vec3_origin[1]; - ent->v.avelocity[2] = vec3_origin[2]; + // entity is trapped in another solid + VectorClear(ent->v.avelocity); + VectorClear(ent->v.velocity); return; } @@ -1052,312 +1119,330 @@ void SV_Physics_Toss(edict_t *ent) float backoff; if (ent->v.movetype == MOVETYPE_BOUNCE) - { backoff = 2.0f - ent->v.friction; - } - else - { + else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) backoff = 2.0f; - if (ent->v.movetype != MOVETYPE_BOUNCEMISSILE) - backoff = 1.0f; - } + else + backoff = 1.0f; + ClipVelocity(ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); - if (trace.plane.normal[2] <= 0.7) + // stop if on ground + if (trace.plane.normal[2] > 0.7f) { - SV_CheckWaterTransition(ent); - return; - } + VectorAdd(ent->v.velocity, ent->v.basevelocity, move); + float vel = _DotProduct(move, move); - move[0] = ent->v.basevelocity[0] + ent->v.velocity[0]; - move[1] = ent->v.basevelocity[1] + ent->v.velocity[1]; - move[2] = ent->v.basevelocity[2] + ent->v.velocity[2]; - if (sv_gravity.value * host_frametime > move[2]) - { - ent->v.flags |= FL_ONGROUND; - ent->v.velocity[2]= 0; - ent->v.groundentity = trace.ent; - } - - if (_DotProduct(move, move) >= 900.0f) - { - if (ent->v.movetype == MOVETYPE_BOUNCE || ent->v.movetype == MOVETYPE_BOUNCEMISSILE) + if (move[2] < sv_gravity.value * host_frametime) { - float scale = (1.0 - trace.fraction) * host_frametime * 0.9f; + // we're rolling on the ground, add static friction + ent->v.flags |= FL_ONGROUND; + ent->v.velocity[2]= 0; + ent->v.groundentity = trace.ent; + } + + if (_DotProduct(move, move) < 900.0f || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) + { + ent->v.flags |= FL_ONGROUND; + ent->v.groundentity = trace.ent; + + VectorClear(ent->v.velocity); + VectorClear(ent->v.avelocity); + } + else + { + float scale = (1.0f - trace.fraction) * host_frametime * 0.9f; VectorScale(ent->v.velocity, scale, move); VectorMA(move, scale, ent->v.basevelocity, move); - SV_PushEntity(ent, move); - SV_CheckWaterTransition(ent); - return; + + trace = SV_PushEntity(ent, move); + +#ifdef REHLDS_FIXES + if (ent->free) + return; +#endif } } - ent->v.flags |= FL_ONGROUND; - ent->v.groundentity = trace.ent; - ent->v.velocity[0] = vec3_origin[0]; - ent->v.velocity[1] = vec3_origin[1]; - ent->v.velocity[2] = vec3_origin[2]; - ent->v.avelocity[0] = vec3_origin[0]; - ent->v.avelocity[1] = vec3_origin[1]; - ent->v.avelocity[2] = vec3_origin[2]; + // check for in water SV_CheckWaterTransition(ent); } void PF_WaterMove(edict_t *pSelf) { - int flags; - int waterlevel; - int watertype; - float drownlevel; - if (pSelf->v.movetype == MOVETYPE_NOCLIP) { - pSelf->v.air_finished = (float)(g_psv.time + 12.0f); + pSelf->v.air_finished = g_psv.time + 12.0f; return; } - if (pSelf->v.health < 0.0) + if (pSelf->v.health < 0.0f) return; - drownlevel = pSelf->v.deadflag ? 1.0f : 3.0f; - waterlevel = pSelf->v.waterlevel; - watertype = pSelf->v.watertype; - flags = pSelf->v.flags; + float drownlevel = (pSelf->v.deadflag == DEAD_NO) ? 3.0f : 1.0f; + int waterlevel = pSelf->v.waterlevel; + int watertype = pSelf->v.watertype; + int flags = pSelf->v.flags; + if (!(flags & (FL_IMMUNE_WATER | FL_GODMODE))) { - if (flags & FL_SWIM && (waterlevel < drownlevel) - || (waterlevel >= drownlevel)) + if ((flags & FL_SWIM) && (waterlevel < drownlevel) || (waterlevel >= drownlevel)) { if (pSelf->v.air_finished < g_psv.time && pSelf->v.pain_finished < g_psv.time) { - pSelf->v.dmg += 2.0; - if (pSelf->v.dmg > 15.0) + pSelf->v.dmg += 2.0f; + + if (pSelf->v.dmg > 15.0f) pSelf->v.dmg = 10.0f; - pSelf->v.pain_finished = (float)(g_psv.time + 1.0f); + + pSelf->v.pain_finished = g_psv.time + 1.0f; } } else { pSelf->v.dmg = 2.0f; - pSelf->v.air_finished = (float)(g_psv.time + 12.0); + pSelf->v.air_finished = g_psv.time + 12.0f; } } + if (!waterlevel) { + // play leave water sound if (flags & FL_INWATER) { switch (RandomLong(0, 3)) { case 0: - SV_StartSound(0, pSelf, 4, "player/pl_wade1.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade1.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 1: - SV_StartSound(0, pSelf, 4, "player/pl_wade2.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade2.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 2: - SV_StartSound(0, pSelf, 4, "player/pl_wade3.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade3.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 3: - SV_StartSound(0, pSelf, 4, "player/pl_wade4.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade4.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; default: break; } + pSelf->v.flags &= ~FL_INWATER; } - pSelf->v.air_finished = (float)(g_psv.time + 12.0); + + pSelf->v.air_finished = g_psv.time + 12.0f; return; } - if (watertype == -5) + if (watertype == CONTENTS_LAVA) { if (!(flags & (FL_IMMUNE_LAVA | FL_GODMODE)) && pSelf->v.dmgtime < g_psv.time) { if (g_psv.time <= pSelf->v.radsuit_finished) - pSelf->v.dmgtime = (float)(g_psv.time + 1.0); + pSelf->v.dmgtime = g_psv.time + 1.0f; else - pSelf->v.dmgtime = (float)(g_psv.time + 0.2); + pSelf->v.dmgtime = g_psv.time + 0.2f; } } - else + else if (watertype == CONTENTS_SLIME) { - if (watertype == -4 - && !(flags & (FL_IMMUNE_SLIME | FL_GODMODE)) - && pSelf->v.dmgtime < g_psv.time - && pSelf->v.radsuit_finished < g_psv.time) + if (!(flags & (FL_IMMUNE_SLIME | FL_GODMODE)) && pSelf->v.dmgtime < g_psv.time) { - pSelf->v.dmgtime = (float)(g_psv.time + 1.0); + if (pSelf->v.radsuit_finished < g_psv.time) + pSelf->v.dmgtime = g_psv.time + 1.0f; } } if (!(flags & FL_INWATER)) { - if (watertype == -3) + // player enter water sound + if (watertype == CONTENTS_WATER) { switch (RandomLong(0, 3)) { case 0: - SV_StartSound(0, pSelf, 4, "player/pl_wade1.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade1.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 1: - SV_StartSound(0, pSelf, 4, "player/pl_wade2.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade2.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 2: - SV_StartSound(0, pSelf, 4, "player/pl_wade3.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade3.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; case 3: - SV_StartSound(0, pSelf, 4, "player/pl_wade4.wav", 255, 0.8f, 0, 100); + SV_StartSound(0, pSelf, CHAN_BODY, "player/pl_wade4.wav", 255, ATTN_NORM, 0, PITCH_NORM); break; default: break; } } - pSelf->v.dmgtime = 0; - pSelf->v.flags = flags | FL_INWATER; + + pSelf->v.dmgtime = 0.0f; + pSelf->v.flags |= FL_INWATER; } if (!(flags & FL_WATERJUMP)) { - VectorMA(pSelf->v.velocity, (float)(pSelf->v.waterlevel * host_frametime * -0.8), pSelf->v.velocity, pSelf->v.velocity); + VectorMA(pSelf->v.velocity, (-0.8 * pSelf->v.waterlevel * host_frametime), pSelf->v.velocity, pSelf->v.velocity); } } - +// Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. +// This is also used for objects that have become still on the ground, +// but will fall if the floor is pulled out from under them. void SV_Physics_Step(edict_t *ent) { - vec3_t maxs; - vec3_t mins; - vec3_t point; - PF_WaterMove(ent); SV_CheckVelocity(ent); - qboolean wasonground = (ent->v.flags & FL_ONGROUND) ? 1 : 0; + + qboolean wasonground = (ent->v.flags & FL_ONGROUND) ? TRUE : FALSE; qboolean inwater = SV_CheckWater(ent); - if ((ent->v.flags & FL_FLOAT) && ent->v.waterlevel > 0)// FL_FLOAT + + if ((ent->v.flags & FL_FLOAT) && ent->v.waterlevel > 0) { - float waterLevel = (float)(SV_Submerged(ent) * ent->v.skin * host_frametime); + float buoyancy = SV_Submerged(ent) * ent->v.skin * host_frametime; + SV_AddGravity(ent); - ent->v.velocity[2] += waterLevel; + ent->v.velocity[2] += buoyancy; } - if (!wasonground) + + // add gravity except: + // flying monsters + // swimming monsters who are in the water + if (!wasonground && !(ent->v.flags & FL_FLY) && (!(ent->v.flags & FL_SWIM) || ent->v.waterlevel <= 0)) { - if (!(ent->v.flags & FL_FLY)) - { - if (!(ent->v.flags & FL_SWIM) || ent->v.waterlevel <= 0) - { - if (!inwater) - SV_AddGravity(ent); - } - } + if (!inwater) + SV_AddGravity(ent); } - if (VectorCompare(ent->v.velocity, vec_origin) && VectorCompare(ent->v.basevelocity, vec_origin)) - { - if (gGlobalVariables.force_retouch != 0.0) - { - trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent, (ent->v.flags & FL_MONSTERCLIP) ? 1 : 0); - if (trace.fraction < 1.0 || trace.startsolid) - { - if (trace.ent) - SV_Impact(ent, trace.ent, &trace); - } - } - } - else + + if (!VectorIsZero(ent->v.velocity) || !VectorIsZero(ent->v.basevelocity)) { ent->v.flags &= ~FL_ONGROUND; - if (wasonground && (ent->v.health > 0.0 || SV_CheckBottom(ent))) + + // apply friction + // let dead monsters who aren't completely onground slide + if (wasonground && (ent->v.health > 0.0f || SV_CheckBottom(ent))) { - float speed = (float)Q_sqrt((long double)(ent->v.velocity[0] * ent->v.velocity[0] + ent->v.velocity[1] * ent->v.velocity[1])); - if (speed != 0.0) + float speed = Q_sqrt((double)(ent->v.velocity[0] * ent->v.velocity[0] + ent->v.velocity[1] * ent->v.velocity[1])); + if (speed) { float friction = sv_friction.value * ent->v.friction; ent->v.friction = 1.0f; - float control = (speed >= sv_stopspeed.value) ? speed : sv_stopspeed.value; - float newspeed = (float)(speed - control * friction * host_frametime); - if (newspeed < 0.0) - newspeed = 0.0; + float control = (speed < sv_stopspeed.value) ? sv_stopspeed.value : speed; + float newspeed = speed - (host_frametime * control * friction); + if (newspeed < 0.0f) + newspeed = 0.0f; + newspeed = newspeed / speed; ent->v.velocity[0] *= newspeed; ent->v.velocity[1] *= newspeed; } } - ent->v.velocity[0] += ent->v.basevelocity[0]; - ent->v.velocity[1] += ent->v.basevelocity[1]; - ent->v.velocity[2] += ent->v.basevelocity[2]; - SV_CheckVelocity(ent); - SV_FlyMove(ent, (float)host_frametime, 0); - SV_CheckVelocity(ent); - ent->v.velocity[0] -= ent->v.basevelocity[0]; - ent->v.velocity[1] -= ent->v.basevelocity[1]; - ent->v.velocity[2] -= ent->v.basevelocity[2]; - SV_CheckVelocity(ent); - mins[0] = ent->v.mins[0] + ent->v.origin[0]; - mins[1] = ent->v.mins[1] + ent->v.origin[1]; - mins[2] = ent->v.mins[2] + ent->v.origin[2]; - maxs[0] = ent->v.maxs[0] + ent->v.origin[0]; - maxs[1] = ent->v.maxs[1] + ent->v.origin[1]; - point[2] = mins[2] - 1.0f; - for (int x = 0; x <= 1; x++) + VectorAdd(ent->v.velocity, ent->v.basevelocity, ent->v.velocity); + SV_CheckVelocity(ent); + + SV_FlyMove(ent, host_frametime, nullptr); + SV_CheckVelocity(ent); + + VectorSubtract(ent->v.velocity, ent->v.basevelocity, ent->v.velocity); + SV_CheckVelocity(ent); + + // determine if it's on solid ground at all { - for (int y = 0; y <= 1; y++) + int x, y; + vec3_t point, mins, maxs; + + VectorAdd(ent->v.origin, ent->v.mins, mins); + VectorAdd(ent->v.origin, ent->v.maxs, maxs); + + point[2] = mins[2] - 1.0f; + + for (x = 0; x <= 1; x++) { - point[0] = x ? (maxs[0]) : (mins[0]); - point[1] = y ? (maxs[1]) : (mins[1]); - g_groupmask = ent->v.groupinfo; - if (SV_PointContents(point) == -2) + for (y = 0; y <= 1; y++) { - ent->v.flags |= FL_ONGROUND; - break; + point[0] = x ? maxs[0] : mins[0]; + point[1] = y ? maxs[1] : mins[1]; + + g_groupmask = ent->v.groupinfo; + + if (SV_PointContents(point) == CONTENTS_SOLID) + { + ent->v.flags |= FL_ONGROUND; + break; + } } } - }; + } + SV_LinkEdict(ent, TRUE); } + else + { + if (gGlobalVariables.force_retouch != 0.0f) + { + trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent, (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE); + + // hentacle impact code + if ((trace.fraction < 1.0f || trace.startsolid) && trace.ent) + { + SV_Impact(ent, trace.ent, &trace); + } + } + } + + // regular thinking SV_RunThink(ent); SV_CheckWaterTransition(ent); } -void SV_Physics(void) +void SV_Physics() { - gGlobalVariables.time = (float)g_psv.time; + // let the progs know that a new frame has started + gGlobalVariables.time = g_psv.time; gEntityInterface.pfnStartFrame(); + + // treat each object in turn for (int i = 0; i < g_psv.num_edicts; i++) { - edict_t* ent = &g_psv.edicts[i]; + edict_t *ent = &g_psv.edicts[i]; if (ent->free) continue; - if (gGlobalVariables.force_retouch != 0.0) + if (gGlobalVariables.force_retouch != 0.0f) + { + // force retouch even for stationary SV_LinkEdict(ent, TRUE); + } if (i > 0 && i <= g_psvs.maxclients) continue; if (ent->v.flags & FL_ONGROUND) { - edict_t* groundentity = ent->v.groundentity; - if (groundentity) + edict_t *groundentity = ent->v.groundentity; + if (groundentity && (groundentity->v.flags & FL_CONVEYOR)) { - if (groundentity->v.flags & FL_CONVEYOR) - { - if (ent->v.flags & FL_BASEVELOCITY) - VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity); - else - VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); - ent->v.flags |= FL_BASEVELOCITY; - } + if (ent->v.flags & FL_BASEVELOCITY) + VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity); + else + VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); + + ent->v.flags |= FL_BASEVELOCITY; } } + if (!(ent->v.flags & FL_BASEVELOCITY)) { - VectorMA(ent->v.velocity, (float)(host_frametime * 0.5 + 1.0), ent->v.basevelocity, ent->v.velocity); - ent->v.basevelocity[0] = vec3_origin[0]; - ent->v.basevelocity[1] = vec3_origin[1]; - ent->v.basevelocity[2] = vec3_origin[2]; + // Apply momentum (add in half of the previous frame of velocity first) + VectorMA(ent->v.velocity, (host_frametime * 0.5f + 1.0f), ent->v.basevelocity, ent->v.velocity); + VectorClear(ent->v.basevelocity); } + ent->v.flags &= ~FL_BASEVELOCITY; switch (ent->v.movetype) @@ -1365,24 +1450,19 @@ void SV_Physics(void) case MOVETYPE_NONE: SV_Physics_None(ent); break; - case MOVETYPE_PUSH: SV_Physics_Pusher(ent); break; - case MOVETYPE_FOLLOW: SV_Physics_Follow(ent); break; - case MOVETYPE_NOCLIP: SV_Physics_Noclip(ent); break; - case MOVETYPE_STEP: case MOVETYPE_PUSHSTEP: SV_Physics_Step(ent); break; - case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: case MOVETYPE_BOUNCEMISSILE: @@ -1390,7 +1470,6 @@ void SV_Physics(void) case MOVETYPE_FLYMISSILE: SV_Physics_Toss(ent); break; - default: Sys_Error("%s: %s bad movetype %d", __func__, &pr_strings[ent->v.classname], ent->v.movetype); } @@ -1405,34 +1484,29 @@ void SV_Physics(void) trace_t SV_Trace_Toss(edict_t *ent, edict_t *ignore) { - edict_t tempent; + edict_t *tent, tempent; trace_t trace; vec3_t move; vec3_t end; double save_frametime; save_frametime = host_frametime; - host_frametime = 5.0; + host_frametime = 5.0f; + Q_memcpy(&tempent, ent, sizeof(tempent)); + tent = &tempent; + do { - do - { - SV_CheckVelocity(&tempent); - SV_AddGravity(&tempent); - VectorMA(tempent.v.angles, (float)host_frametime, tempent.v.avelocity, tempent.v.angles); - VectorScale(tempent.v.velocity, (float)host_frametime, move); - end[0] = tempent.v.origin[0] + move[0]; - end[1] = tempent.v.origin[1] + move[1]; - end[2] = tempent.v.origin[2] + move[2]; - trace = SV_Move(tempent.v.origin, tempent.v.mins, tempent.v.maxs, end, 0, &tempent, 0); - tempent.v.origin[1] = trace.endpos[1]; - tempent.v.origin[0] = trace.endpos[0]; - tempent.v.origin[2] = trace.endpos[2]; - } - while (!trace.ent); + SV_CheckVelocity(tent); + SV_AddGravity(tent); + VectorMA(tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles); + VectorScale(tent->v.velocity, host_frametime, move); + VectorAdd(tent->v.origin, move, end); + trace = SV_Move(tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent, FALSE); + VectorCopy(trace.endpos, tent->v.origin); } - while (trace.ent == ignore); + while (!trace.ent || trace.ent == ignore); host_frametime = save_frametime; return trace; diff --git a/rehlds/engine/sv_phys.h b/rehlds/engine/sv_phys.h index 9ba3a67..4ac0e7f 100644 --- a/rehlds/engine/sv_phys.h +++ b/rehlds/engine/sv_phys.h @@ -32,7 +32,6 @@ #include "model.h" #include "cvar.h" - #ifdef HOOK_ENGINE #define sv_maxvelocity (*psv_maxvelocity) #define sv_gravity (*psv_gravity) @@ -54,8 +53,7 @@ extern cvar_t sv_stopspeed; extern vec3_t *g_moved_from; extern edict_t **g_moved_edict; - -NOXREF void SV_CheckAllEnts(void); +NOXREF void SV_CheckAllEnts(); void SV_CheckVelocity(edict_t *ent); qboolean SV_RunThink(edict_t *ent); void SV_Impact(edict_t *e1, edict_t *e2, trace_t *ptrace); @@ -69,7 +67,7 @@ void SV_PushMove(edict_t *pusher, float movetime); int SV_PushRotate(edict_t *pusher, float movetime); void SV_Physics_Pusher(edict_t *ent); qboolean SV_CheckWater(edict_t *ent); -float SV_RecursiveWaterLevel(vec_t *center, float out, float in, int count); +float SV_RecursiveWaterLevel(const vec_t *origin, float mins, float maxs, int depth); float SV_Submerged(edict_t *ent); void SV_Physics_None(edict_t *ent); void SV_Physics_Follow(edict_t *ent); diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index c07197e..18ffa41 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -396,8 +396,10 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->origin[0] = check->v.origin[0]; pe->origin[1] = check->v.origin[1]; - pe->info = e; pe->origin[2] = check->v.origin[2]; + + pe->info = e; + if (e < 1 || e > g_psvs.maxclients) { pe->player = 0; @@ -411,30 +413,32 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->angles[0] = check->v.angles[0]; pe->angles[1] = check->v.angles[1]; pe->angles[2] = check->v.angles[2]; - pe->studiomodel = 0; + + pe->studiomodel = nullptr; pe->rendermode = check->v.rendermode; + if (check->v.solid == SOLID_BSP) { pe->model = g_psv.models[check->v.modelindex]; - Q_strncpy(pe->name, pe->model->name, 0x20u); - pe->name[31] = 0; + Q_strncpy(pe->name, pe->model->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; } else if (check->v.solid == SOLID_NOT) { if (check->v.modelindex) { pe->model = g_psv.models[check->v.modelindex]; - Q_strncpy(pe->name, pe->model->name, 0x20u); - pe->name[31] = 0; + Q_strncpy(pe->name, pe->model->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; } else { - pe->model = 0; + pe->model = nullptr; } } else { - pe->model = NULL; + pe->model = nullptr; if (check->v.solid != SOLID_BBOX) { pe->mins[0] = check->v.mins[0]; @@ -443,10 +447,11 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->mins[2] = check->v.mins[2]; pe->maxs[1] = check->v.maxs[1]; pe->maxs[2] = check->v.maxs[2]; + if (check->v.classname) { - Q_strncpy(pe->name, &pr_strings[check->v.classname], 0x20u); - pe->name[31] = 0; + Q_strncpy(pe->name, &pr_strings[check->v.classname], sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; } else { @@ -460,13 +465,14 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pModel = g_psv.models[check->v.modelindex]; if (pModel) { - if (pModel->flags & 0x200) + if (pModel->flags & STUDIO_TRACE_HITBOX) pe->studiomodel = pModel; - Q_strncpy(pe->name, pModel->name, 0x20u); - pe->name[31] = 0; + Q_strncpy(pe->name, pModel->name, sizeof(pe->name) - 1); + pe->name[sizeof(pe->name) - 1] = 0; } } + pe->mins[0] = check->v.mins[0]; pe->mins[1] = check->v.mins[1]; pe->mins[2] = check->v.mins[2]; @@ -475,13 +481,16 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->maxs[2] = check->v.maxs[2]; } } + pe->skin = check->v.skin; pe->frame = check->v.frame; pe->solid = check->v.solid; pe->sequence = check->v.sequence; - Q_memcpy(pe->controller, check->v.controller, 4); - Q_memcpy(pe->blending, check->v.blending, 2); pe->movetype = check->v.movetype; + + Q_memcpy(&pe->controller[0], &check->v.controller[0], 4 * sizeof(byte)); + Q_memcpy(&pe->blending[0], &check->v.blending[0], 2 * sizeof(byte)); + pe->iuser1 = check->v.iuser1; pe->iuser2 = check->v.iuser2; pe->iuser3 = check->v.iuser3; @@ -490,6 +499,7 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->fuser2 = check->v.fuser2; pe->fuser3 = check->v.fuser3; pe->fuser4 = check->v.fuser4; + pe->vuser1[0] = check->v.vuser1[0]; pe->vuser1[1] = check->v.vuser1[1]; pe->vuser1[2] = check->v.vuser1[2]; @@ -499,8 +509,10 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->vuser3[0] = check->v.vuser3[0]; pe->vuser3[1] = check->v.vuser3[1]; pe->vuser3[2] = check->v.vuser3[2]; + pe->takedamage = 0; pe->blooddecal = 0; + pe->vuser4[0] = check->v.vuser4[0]; pe->vuser4[1] = check->v.vuser4[1]; pe->vuser4[2] = check->v.vuser4[2]; @@ -619,8 +631,8 @@ void SV_AddLinksToPM(areanode_t *node, vec_t *origin) pmove->physents[0].model = g_psv.worldmodel; if (g_psv.worldmodel != NULL) { - Q_strncpy(pmove->physents[0].name, g_psv.worldmodel->name, 0x20u); - pmove->physents[0].name[31] = 0; + Q_strncpy(pmove->physents[0].name, g_psv.worldmodel->name, sizeof(pmove->physents[0].name) - 1); + pmove->physents[0].name[sizeof(pmove->physents[0].name) - 1] = 0; } pmove->physents[0].origin[0] = vec3_origin[0]; pmove->physents[0].origin[1] = vec3_origin[1]; @@ -813,50 +825,62 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.clbasevelocity[1] = sv_player->v.basevelocity[1]; sv_player->v.clbasevelocity[2] = sv_player->v.basevelocity[2]; } - pmove->server = 1; - pmove->multiplayer = (qboolean)(g_psvs.maxclients > 1); + + pmove->server = TRUE; + pmove->multiplayer = (g_psvs.maxclients > 1) ? TRUE : FALSE; pmove->time = float(1000.0 * host_client->svtimebase); - pmove->usehull = (sv_player->v.flags & 0x4000) != 0; + pmove->usehull = (sv_player->v.flags & FL_DUCKING) == FL_DUCKING; pmove->maxspeed = sv_maxspeed.value; pmove->clientmaxspeed = sv_player->v.maxspeed; - pmove->flDuckTime = (float) sv_player->v.flDuckTime; + pmove->flDuckTime = (float)sv_player->v.flDuckTime; pmove->bInDuck = sv_player->v.bInDuck; pmove->flTimeStepSound = sv_player->v.flTimeStepSound; pmove->iStepLeft = sv_player->v.iStepLeft; pmove->flFallVelocity = sv_player->v.flFallVelocity; pmove->flSwimTime = (float)sv_player->v.flSwimTime; pmove->oldbuttons = sv_player->v.oldbuttons; - Q_strncpy(pmove->physinfo, host_client->physinfo, 0xFFu); - pmove->physinfo[255] = 0; + + Q_strncpy(pmove->physinfo, host_client->physinfo, sizeof(pmove->physinfo) - 1); + pmove->physinfo[sizeof(pmove->physinfo) - 1] = 0; + pmove->velocity[0] = sv_player->v.velocity[0]; pmove->velocity[1] = sv_player->v.velocity[1]; pmove->velocity[2] = sv_player->v.velocity[2]; + pmove->movedir[0] = sv_player->v.movedir[0]; pmove->movedir[1] = sv_player->v.movedir[1]; pmove->movedir[2] = sv_player->v.movedir[2]; + pmove->angles[0] = sv_player->v.v_angle[0]; pmove->angles[1] = sv_player->v.v_angle[1]; pmove->angles[2] = sv_player->v.v_angle[2]; + pmove->basevelocity[0] = sv_player->v.basevelocity[0]; pmove->basevelocity[1] = sv_player->v.basevelocity[1]; pmove->basevelocity[2] = sv_player->v.basevelocity[2]; + pmove->view_ofs[0] = sv_player->v.view_ofs[0]; pmove->view_ofs[1] = sv_player->v.view_ofs[1]; pmove->view_ofs[2] = sv_player->v.view_ofs[2]; + pmove->punchangle[0] = sv_player->v.punchangle[0]; pmove->punchangle[1] = sv_player->v.punchangle[1]; pmove->punchangle[2] = sv_player->v.punchangle[2]; + pmove->deadflag = sv_player->v.deadflag; pmove->effects = sv_player->v.effects; pmove->gravity = sv_player->v.gravity; pmove->friction = sv_player->v.friction; pmove->spectator = 0; pmove->waterjumptime = sv_player->v.teleport_time; + Q_memcpy(&pmove->cmd, &cmd, sizeof(pmove->cmd)); + pmove->dead = sv_player->v.health <= 0.0; pmove->movetype = sv_player->v.movetype; pmove->flags = sv_player->v.flags; pmove->player_index = NUM_FOR_EDICT(sv_player) - 1; + pmove->iuser1 = sv_player->v.iuser1; pmove->iuser2 = sv_player->v.iuser2; pmove->iuser3 = sv_player->v.iuser3; @@ -865,28 +889,37 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) pmove->fuser2 = sv_player->v.fuser2; pmove->fuser3 = sv_player->v.fuser3; pmove->fuser4 = sv_player->v.fuser4; + pmove->vuser1[0] = sv_player->v.vuser1[0]; pmove->vuser1[1] = sv_player->v.vuser1[1]; pmove->vuser1[2] = sv_player->v.vuser1[2]; + pmove->vuser2[0] = sv_player->v.vuser2[0]; pmove->vuser2[1] = sv_player->v.vuser2[1]; pmove->vuser2[2] = sv_player->v.vuser2[2]; + pmove->vuser3[0] = sv_player->v.vuser3[0]; pmove->vuser3[1] = sv_player->v.vuser3[1]; pmove->vuser3[2] = sv_player->v.vuser3[2]; + pmove->vuser4[0] = sv_player->v.vuser4[0]; pmove->vuser4[1] = sv_player->v.vuser4[1]; pmove->vuser4[2] = sv_player->v.vuser4[2]; + pmove->origin[0] = sv_player->v.origin[0]; pmove->origin[1] = sv_player->v.origin[1]; pmove->origin[2] = sv_player->v.origin[2]; + SV_AddLinksToPM(sv_areanodes, pmove->origin); + pmove->frametime = frametime; - pmove->runfuncs = 1; + pmove->runfuncs = TRUE; pmove->PM_PlaySound = PM_SV_PlaySound; pmove->PM_TraceTexture = PM_SV_TraceTexture; pmove->PM_PlaybackEventFull = PM_SV_PlaybackEventFull; - gEntityInterface.pfnPM_Move(pmove, 1); + + gEntityInterface.pfnPM_Move(pmove, TRUE); + sv_player->v.deadflag = pmove->deadflag; sv_player->v.effects = pmove->effects; sv_player->v.teleport_time = pmove->waterjumptime; @@ -897,15 +930,19 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.movetype = pmove->movetype; sv_player->v.maxspeed = pmove->clientmaxspeed; sv_player->v.iStepLeft = pmove->iStepLeft; + sv_player->v.view_ofs[0] = pmove->view_ofs[0]; sv_player->v.view_ofs[1] = pmove->view_ofs[1]; sv_player->v.view_ofs[2] = pmove->view_ofs[2]; + sv_player->v.movedir[0] = pmove->movedir[0]; sv_player->v.movedir[1] = pmove->movedir[1]; sv_player->v.movedir[2] = pmove->movedir[2]; + sv_player->v.punchangle[0] = pmove->punchangle[0]; sv_player->v.punchangle[1] = pmove->punchangle[1]; sv_player->v.punchangle[2] = pmove->punchangle[2]; + if (pmove->onground == -1) { sv_player->v.flags &= ~FL_ONGROUND; @@ -915,15 +952,19 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.flags |= FL_ONGROUND; sv_player->v.groundentity = EDICT_NUM(pmove->physents[pmove->onground].info); } + sv_player->v.origin[0] = pmove->origin[0]; sv_player->v.origin[1] = pmove->origin[1]; sv_player->v.origin[2] = pmove->origin[2]; + sv_player->v.velocity[0] = pmove->velocity[0]; sv_player->v.velocity[1] = pmove->velocity[1]; sv_player->v.velocity[2] = pmove->velocity[2]; + sv_player->v.basevelocity[0] = pmove->basevelocity[0]; sv_player->v.basevelocity[1] = pmove->basevelocity[1]; sv_player->v.basevelocity[2] = pmove->basevelocity[2]; + if (!sv_player->v.fixangle) { sv_player->v.v_angle[0] = pmove->angles[0]; @@ -933,12 +974,14 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.angles[1] = pmove->angles[1]; sv_player->v.angles[2] = pmove->angles[2]; } + sv_player->v.bInDuck = pmove->bInDuck; sv_player->v.flDuckTime = (int)pmove->flDuckTime; sv_player->v.flTimeStepSound = pmove->flTimeStepSound; sv_player->v.flFallVelocity = pmove->flFallVelocity; sv_player->v.flSwimTime = (int)pmove->flSwimTime; sv_player->v.oldbuttons = pmove->cmd.buttons; + sv_player->v.iuser1 = pmove->iuser1; sv_player->v.iuser2 = pmove->iuser2; sv_player->v.iuser3 = pmove->iuser3; @@ -947,18 +990,23 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.fuser2 = pmove->fuser2; sv_player->v.fuser3 = pmove->fuser3; sv_player->v.fuser4 = pmove->fuser4; + sv_player->v.vuser1[0] = pmove->vuser1[0]; sv_player->v.vuser1[1] = pmove->vuser1[1]; sv_player->v.vuser1[2] = pmove->vuser1[2]; + sv_player->v.vuser2[0] = pmove->vuser2[0]; sv_player->v.vuser2[1] = pmove->vuser2[1]; sv_player->v.vuser2[2] = pmove->vuser2[2]; + sv_player->v.vuser3[0] = pmove->vuser3[0]; sv_player->v.vuser3[1] = pmove->vuser3[1]; sv_player->v.vuser3[2] = pmove->vuser3[2]; + sv_player->v.vuser4[0] = pmove->vuser4[0]; sv_player->v.vuser4[1] = pmove->vuser4[1]; sv_player->v.vuser4[2] = pmove->vuser4[2]; + SetMinMaxSize(sv_player, player_mins[pmove->usehull], player_maxs[pmove->usehull], 0); if (host_client->edict->v.solid) { @@ -982,6 +1030,7 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) sv_player->v.velocity[1] = vel[1]; sv_player->v.velocity[2] = vel[2]; } + gGlobalVariables.time = (float)host_client->svtimebase; gGlobalVariables.frametime = frametime; gEntityInterface.pfnPlayerPostThink(sv_player); diff --git a/rehlds/engine/textures.cpp b/rehlds/engine/textures.cpp index 3c8708d..022e7e3 100644 --- a/rehlds/engine/textures.cpp +++ b/rehlds/engine/textures.cpp @@ -214,8 +214,8 @@ void TEX_AddAnimatingTextures(void) if (miptex[i][0] != '+' && miptex[i][0] != '-') continue; - Q_strncpy(name, miptex[i], 0x1Fu); - name[31] = 0; + Q_strncpy(name, miptex[i], sizeof(name) - 1); + name[sizeof(name) - 1] = 0; for (int j = 0; j < 20; j++) { diff --git a/rehlds/engine/world.cpp b/rehlds/engine/world.cpp index 5ad4eb3..07337c5 100644 --- a/rehlds/engine/world.cpp +++ b/rehlds/engine/world.cpp @@ -30,23 +30,26 @@ hull_t box_hull; hull_t beam_hull; + box_clipnodes_t box_clipnodes; box_planes_t box_planes; beam_planes_t beam_planes; areanode_t sv_areanodes[32]; int sv_numareanodes; + #ifdef REHLDS_FIXES -static link_t *touchLinksNext = NULL; +static link_t *touchLinksNext = nullptr; #endif // REHLDS_FIXES cvar_t sv_force_ent_intersection = { "sv_force_ent_intersection", "0", 0, 0.0f, NULL }; +// ClearLink is used for new headnodes void ClearLink(link_t *l) { - l->next = l; - l->prev = l; + l->next = l->prev = l; } +// Remove link from chain void RemoveLink(link_t *l) { #ifdef REHLDS_FIXES @@ -55,16 +58,19 @@ void RemoveLink(link_t *l) touchLinksNext = l->next; } #endif // REHLDS_FIXES + l->next->prev = l->prev; l->prev->next = l->next; } +// Kept trigger and solid entities seperate void InsertLinkBefore(link_t *l, link_t *before) { l->next = before; l->prev = before->prev; l->next->prev = l; l->prev->next = l; + #ifdef REHLDS_FIXES if (touchLinksNext == before) { @@ -76,6 +82,7 @@ void InsertLinkBefore(link_t *l, link_t *before) NOXREF void InsertLinkAfter(link_t *l, link_t *after) { NOXREFCHECK; + l->prev = after; l->next = after->next; @@ -83,14 +90,17 @@ NOXREF void InsertLinkAfter(link_t *l, link_t *after) l->next->prev = l; } -void SV_InitBoxHull(void) +// Set up the planes and clipnodes so that the six floats of a bounding box +// can just be stored out and get a proper hull_t structure. +void SV_InitBoxHull() { - box_hull.clipnodes = &box_clipnodes[0]; - box_hull.planes = &box_planes[0]; + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; + Q_memcpy(&beam_hull, &box_hull, sizeof(beam_hull)); - beam_hull.planes = &beam_planes[0]; + beam_hull.planes = beam_planes; for (int i = 0; i < 6; i++) { @@ -98,12 +108,15 @@ void SV_InitBoxHull(void) box_clipnodes[i].planenum = i; box_clipnodes[i].children[side] = CONTENTS_EMPTY; box_clipnodes[i].children[side ^ 1] = (i != 5) ? i + 1 : CONTENTS_SOLID; + box_planes[i].type = i >> 1; - beam_planes[i].type = 5; box_planes[i].normal[i >> 1] = 1.0f; + beam_planes[i].type = 5; } } +// To keep everything totally uniform, bounding boxes are turned into small +// BSP trees instead of being compared directly. hull_t *SV_HullForBox(const vec_t *mins, const vec_t *maxs) { box_planes[0].dist = maxs[0]; @@ -112,6 +125,7 @@ hull_t *SV_HullForBox(const vec_t *mins, const vec_t *maxs) box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; + return &box_hull; } @@ -120,18 +134,12 @@ NOXREF hull_t *SV_HullForBeam(const vec_t *start, const vec_t *end, const vec_t NOXREFCHECK; vec3_t tmp = { 0, 0, 0 }; - beam_planes[0].normal[0] = end[0] - start[0]; - beam_planes[0].normal[1] = end[1] - start[1]; - beam_planes[0].normal[2] = end[2] - start[2]; - + VectorSubtract(end, start, beam_planes[0].normal); VectorNormalize(beam_planes[0].normal); + VectorCopy(beam_planes[0].normal, beam_planes[1].normal); - beam_planes[1].normal[0] = beam_planes[0].normal[0]; - beam_planes[1].normal[1] = beam_planes[0].normal[1]; - beam_planes[1].normal[2] = beam_planes[0].normal[2]; - - beam_planes[0].dist = _DotProduct((vec_t *)end, beam_planes[0].normal); - beam_planes[1].dist = _DotProduct((vec_t *)start, beam_planes[0].normal); + beam_planes[0].dist = _DotProduct(end, beam_planes[0].normal); + beam_planes[1].dist = _DotProduct(start, beam_planes[0].normal); if (fabs(beam_planes[0].normal[2]) < 0.9f) tmp[2] = 1.0f; @@ -140,89 +148,78 @@ NOXREF hull_t *SV_HullForBeam(const vec_t *start, const vec_t *end, const vec_t CrossProduct(beam_planes[0].normal, tmp, beam_planes[2].normal); VectorNormalize(beam_planes[2].normal); - - beam_planes[3].normal[0] = beam_planes[2].normal[0]; - beam_planes[3].normal[1] = beam_planes[2].normal[1]; - beam_planes[3].normal[2] = beam_planes[2].normal[2]; + VectorCopy(beam_planes[2].normal, beam_planes[3].normal); beam_planes[2].dist = (start[0] + beam_planes[2].normal[0]) * beam_planes[2].normal[0] + (start[1] + beam_planes[2].normal[1]) * beam_planes[2].normal[1] + (start[2] + beam_planes[2].normal[2]) * beam_planes[2].normal[2]; - tmp[0] = start[0] - beam_planes[2].normal[0]; - tmp[1] = start[1] - beam_planes[2].normal[1]; - tmp[2] = start[2] - beam_planes[2].normal[2]; + VectorSubtract(start, beam_planes[2].normal, tmp); beam_planes[3].dist = _DotProduct(tmp, beam_planes[2].normal); CrossProduct(beam_planes[2].normal, beam_planes[0].normal, beam_planes[4].normal); VectorNormalize(beam_planes[4].normal); - beam_planes[5].normal[0] = beam_planes[4].normal[0]; - beam_planes[5].normal[1] = beam_planes[4].normal[1]; - beam_planes[5].normal[2] = beam_planes[4].normal[2]; + VectorCopy(beam_planes[4].normal, beam_planes[5].normal); - beam_planes[4].dist = _DotProduct((vec_t *)start, beam_planes[4].normal); + beam_planes[4].dist = _DotProduct(start, beam_planes[4].normal); beam_planes[5].dist = (start[0] - beam_planes[4].normal[0]) * beam_planes[4].normal[0] + (start[1] - beam_planes[4].normal[1]) * beam_planes[4].normal[1] + (start[2] - beam_planes[4].normal[2]) * beam_planes[4].normal[2]; - beam_planes[0].dist += fabs(beam_planes[0].normal[0] * size[0]) + fabs(beam_planes[0].normal[1] * size[1]) + fabs(beam_planes[0].normal[2] * size[2]); - beam_planes[1].dist -= (fabs(beam_planes[1].normal[0] * size[0]) + fabs(beam_planes[1].normal[1] * size[1]) + fabs(beam_planes[1].normal[2] * size[2])); + beam_planes[0].dist += fabs(beam_planes[0].normal[0] * size[0]) + fabs(beam_planes[0].normal[1] * size[1]) + fabs(beam_planes[0].normal[2] * size[2]); + beam_planes[1].dist -= fabs(beam_planes[1].normal[0] * size[0]) + fabs(beam_planes[1].normal[1] * size[1]) + fabs(beam_planes[1].normal[2] * size[2]); beam_planes[2].dist += fabs(beam_planes[2].normal[0] * size[0]) + fabs(beam_planes[2].normal[1] * size[1]) + fabs(beam_planes[2].normal[2] * size[2]); - beam_planes[3].dist -= (fabs(beam_planes[3].normal[0] * size[0]) + fabs(beam_planes[3].normal[1] * size[1]) + fabs(beam_planes[3].normal[2] * size[2])); + beam_planes[3].dist -= fabs(beam_planes[3].normal[0] * size[0]) + fabs(beam_planes[3].normal[1] * size[1]) + fabs(beam_planes[3].normal[2] * size[2]); beam_planes[4].dist += fabs(beam_planes[4].normal[0] * size[0]) + fabs(beam_planes[4].normal[1] * size[1]) + fabs(beam_planes[4].normal[2] * size[2]); - beam_planes[5].dist -= (fabs(beam_planes[4].normal[0] * size[0]) + fabs(beam_planes[4].normal[1] * size[1]) + fabs(beam_planes[4].normal[2] * size[2])); + beam_planes[5].dist -= fabs(beam_planes[4].normal[0] * size[0]) + fabs(beam_planes[4].normal[1] * size[1]) + fabs(beam_planes[4].normal[2] * size[2]); return &beam_hull; } -struct hull_s *SV_HullForBsp(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset) +// Forcing to select BSP hull +hull_t *SV_HullForBsp(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset) { - model_t *model; hull_t *hull; + model_t *model; + vec3_t size; model = Mod_Handle(ent->v.modelindex); if (!model || model->type != mod_brush) - Sys_Error("%s: Hit a %s with no model (%s)", __func__, &pr_strings[ent->v.classname], &pr_strings[ent->v.model]); - - float xSize = maxs[0] - mins[0]; - if (xSize > 8.0f) { - if (xSize > 36.0f) - { - hull = &model->hulls[2]; - } - else - { - float zSize = maxs[2] - mins[2]; - if (zSize > 36.0f) - hull = &model->hulls[1]; - else - hull = &model->hulls[3]; - } -#ifdef REHLDS_FIXES - if (sv_rehlds_hull_centering.value && mins[0] + maxs[0] == 0.0f) - offset[0] = 0.0f; - else - offset[0] = hull->clip_mins[0] - mins[0]; + Sys_Error("%s: Hit a %s with no model (%s)", __func__, &pr_strings[ent->v.classname], &pr_strings[ent->v.model]); + } - if (sv_rehlds_hull_centering.value && mins[1] + maxs[1] == 0.0f) - offset[1] = 0.0f; - else - offset[1] = hull->clip_mins[1] - mins[1]; -#else // REHLDS_FIXES - offset[0] = hull->clip_mins[0] - mins[0]; - offset[1] = hull->clip_mins[1] - mins[1]; -#endif // REHLDS_FIXES - offset[2] = hull->clip_mins[2] - mins[2]; + VectorSubtract(maxs, mins, size); + + if (size[0] <= 8.0f) + { + hull = &model->hulls[0]; + VectorCopy(hull->clip_mins, offset); } else { - hull = &model->hulls[0]; - offset[0] = model->hulls[0].clip_mins[0]; - offset[1] = model->hulls[0].clip_mins[1]; - offset[2] = model->hulls[0].clip_mins[2]; + if (size[0] <= 36.0f) + { + if (size[2] <= 36.0f) + hull = &model->hulls[3]; + else + hull = &model->hulls[1]; + } + else + { + hull = &model->hulls[2]; + } + + // calculate an offset value to center the origin + VectorSubtract(hull->clip_mins, mins, offset); + +#ifdef REHLDS_FIXES + if (sv_rehlds_hull_centering.value && (mins[0] + maxs[0]) == 0.0f) + offset[0] = 0.0f; + + if (sv_rehlds_hull_centering.value && (mins[1] + maxs[1]) == 0.0f) + offset[1] = 0.0f; +#endif } - offset[0] = ent->v.origin[0] + offset[0]; - offset[1] = ent->v.origin[1] + offset[1]; - offset[2] = ent->v.origin[2] + offset[2]; + VectorAdd(offset, ent->v.origin, offset); return hull; } @@ -231,110 +228,111 @@ hull_t *SV_HullForEntity(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec vec3_t hullmins; vec3_t hullmaxs; + // decide which clipping hull to use, based on the size if (ent->v.solid == SOLID_BSP) { + // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP) + { Sys_Error("%s: SOLID_BSP without MOVETYPE_PUSH", __func__); + } return SV_HullForBsp(ent, mins, maxs, offset); } - hullmins[0] = ent->v.mins[0] - maxs[0]; - hullmins[1] = ent->v.mins[1] - maxs[1]; - hullmins[2] = ent->v.mins[2] - maxs[2]; - hullmaxs[0] = ent->v.maxs[0] - mins[0]; - hullmaxs[1] = ent->v.maxs[1] - mins[1]; - hullmaxs[2] = ent->v.maxs[2] - mins[2]; - offset[0] = ent->v.origin[0]; - offset[1] = ent->v.origin[1]; - offset[2] = ent->v.origin[2]; + // create a temp hull from bounding box sizes + VectorSubtract(ent->v.mins, maxs, hullmins); + VectorSubtract(ent->v.maxs, mins, hullmaxs); + VectorCopy(ent->v.origin, offset); + return SV_HullForBox(hullmins, hullmaxs); } +// Builds a uniformly subdivided tree for the given world size areanode_t *SV_CreateAreaNode(int depth, vec_t *mins, vec_t *maxs) { areanode_t *anode; - vec3_t mins1; - vec3_t maxs2; vec3_t size; - vec3_t maxs1; - vec3_t mins2; - float fmid; + vec3_t mins1, maxs2, maxs1, mins2; anode = &sv_areanodes[sv_numareanodes++]; + ClearLink(&anode->trigger_edicts); ClearLink(&anode->solid_edicts); - if (depth == 4) + + if (depth == AREA_DEPTH) { anode->axis = -1; - anode->children[0] = NULL; - anode->children[1] = NULL; + anode->children[0] = anode->children[1] = nullptr; return anode; } - size[0] = maxs[0] - mins[0]; - size[1] = maxs[1] - mins[1]; - anode->axis = (size[0] <= size[1]) ? 1 : 0; - mins1[0] = mins[0]; - mins1[1] = mins[1]; - mins1[2] = mins[2]; + VectorSubtract(maxs, mins, size); - mins2[0] = mins[0]; - mins2[1] = mins[1]; - mins2[2] = mins[2]; + if (size[0] > size[1]) + anode->axis = 0; + else + anode->axis = 1; - maxs1[0] = maxs[0]; - maxs1[1] = maxs[1]; - maxs1[2] = maxs[2]; + anode->dist = 0.5f * (maxs[anode->axis] + mins[anode->axis]); - maxs2[0] = maxs[0]; - maxs2[1] = maxs[1]; - maxs2[2] = maxs[2]; + VectorCopy(mins, mins1); + VectorCopy(mins, mins2); + VectorCopy(maxs, maxs1); + VectorCopy(maxs, maxs2); - fmid = 0.5f * (mins[anode->axis] + maxs[anode->axis]); - mins2[anode->axis] = fmid; - maxs1[anode->axis] = fmid; + maxs1[anode->axis] = mins2[anode->axis] = anode->dist; - anode->dist = fmid; anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1); return anode; } -void SV_ClearWorld(void) +// called after the world model has been loaded, before linking any entities +void SV_ClearWorld() { SV_InitBoxHull(); + Q_memset(sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode(0, g_psv.worldmodel->mins, g_psv.worldmodel->maxs); } +// call before removing an entity, and before trying to move one, +// so it doesn't clip against itself +// flags ent->v.modified void SV_UnlinkEdict(edict_t *ent) { - if (ent->area.prev) + if (!ent->area.prev) { - RemoveLink(&ent->area); - ent->area.next = NULL; - ent->area.prev = NULL; + // not linked in anywhere + return; } + + RemoveLink(&ent->area); + ent->area.prev = ent->area.next = nullptr; } void SV_TouchLinks(edict_t *ent, areanode_t *node) { + vec3_t localPosition, offset; edict_t *touch; + #ifndef REHLDS_FIXES link_t *touchLinksNext; #endif // REHLDS_FIXES + // touch linked edicts for (link_t *l = node->trigger_edicts.next; l != &node->trigger_edicts; l = touchLinksNext) { touchLinksNext = l->next; - touch = (edict_t *)((char *)l - offsetof(edict_t, area)); + touch = EDICT_FROM_AREA(l); + if (touch == ent) continue; - if (ent->v.groupinfo != 0 && touch->v.groupinfo != 0) + if (ent->v.groupinfo && touch->v.groupinfo) { if (g_groupop) { @@ -353,203 +351,169 @@ void SV_TouchLinks(edict_t *ent, areanode_t *node) continue; #endif // REHLDS_FIXES - if (touch->v.solid == SOLID_TRIGGER - && ent->v.absmin[0] <= touch->v.absmax[0] - && ent->v.absmin[1] <= touch->v.absmax[1] - && ent->v.absmin[2] <= touch->v.absmax[2] - && ent->v.absmax[0] >= touch->v.absmin[0] - && ent->v.absmax[1] >= touch->v.absmin[1] - && ent->v.absmax[2] >= touch->v.absmin[2]) + if (touch->v.solid != SOLID_TRIGGER) + continue; + + if (ent->v.absmin[0] > touch->v.absmax[0] + || ent->v.absmin[1] > touch->v.absmax[1] + || ent->v.absmin[2] > touch->v.absmax[2] + || ent->v.absmax[0] < touch->v.absmin[0] + || ent->v.absmax[1] < touch->v.absmin[1] + || ent->v.absmax[2] < touch->v.absmin[2]) + continue; + + // check brush triggers accuracy + if (Mod_GetType(touch->v.modelindex) == mod_brush) { - if (Mod_GetType(touch->v.modelindex) == mod_brush) - { - vec3_t offset; - hull_t *hull = SV_HullForBsp(touch, ent->v.mins, ent->v.maxs, offset); + // force to select bsp-hull + hull_t *hull = SV_HullForBsp(touch, ent->v.mins, ent->v.maxs, offset); - vec3_t localPosition; - localPosition[0] = ent->v.origin[0] - offset[0]; - localPosition[1] = ent->v.origin[1] - offset[1]; - localPosition[2] = ent->v.origin[2] - offset[2]; + // offset the test point appropriately for this hull + VectorSubtract(ent->v.origin, offset, localPosition); - int contents = SV_HullPointContents(hull, hull->firstclipnode, localPosition); - if (contents != CONTENTS_SOLID) - continue; - } - - gGlobalVariables.time = (float)g_psv.time; - gEntityInterface.pfnTouch(touch, ent); + // test hull for intersection with this model + if (SV_HullPointContents(hull, hull->firstclipnode, localPosition) != CONTENTS_SOLID) + continue; } + + gGlobalVariables.time = g_psv.time; + gEntityInterface.pfnTouch(touch, ent); } + #ifdef REHLDS_FIXES - touchLinksNext = NULL; + touchLinksNext = nullptr; #endif // REHLDS_FIXES - if (node->axis != -1) - { - if (ent->v.absmax[node->axis] > node->dist) - SV_TouchLinks(ent, node->children[0]); + // recurse down both sides + if(node->axis == -1) + return; - if (node->dist > ent->v.absmin[node->axis]) - SV_TouchLinks(ent, node->children[1]); - } + if (ent->v.absmax[node->axis] > node->dist) + SV_TouchLinks(ent, node->children[0]); + + if (node->dist > ent->v.absmin[node->axis]) + SV_TouchLinks(ent, node->children[1]); } #ifndef REHLDS_OPT_PEDANTIC + void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node, int *topnode) { - mplane_t *splitplane; - int sides; - if (node->contents == CONTENTS_SOLID) return; + // add an efrag if the node is a leaf if (node->contents < 0) { - if (ent->num_leafs < MAX_ENT_LEAFS) + if (ent->num_leafs > (MAX_ENT_LEAFS - 1)) + { + // continue counting leafs, + // so we know how many it's overrun + ent->num_leafs = (MAX_ENT_LEAFS + 1); + } + else { mleaf_t *leaf = (mleaf_t *)node; int leafnum = leaf - g_psv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; } - else - { - ent->num_leafs = MAX_ENT_LEAFS + 1; - } return; } - splitplane = node->plane; - if (splitplane->type >= 3) + // NODE_MIXED + mplane_t *splitplane = node->plane; + int sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); + + if (sides == 3 && *topnode == -1) { - sides = BoxOnPlaneSide(ent->v.absmin, ent->v.absmax, splitplane); - } - else - { - if (splitplane->dist > ent->v.absmin[splitplane->type]) - { - if (splitplane->dist < ent->v.absmax[splitplane->type]) - { - sides = 3; - } - else - { - sides = 2; - } - } - else - { - sides = 1; - } + *topnode = node - g_psv.worldmodel->nodes; } - if (sides == 3) - { - if (*topnode == -1) - *topnode = node - g_psv.worldmodel->nodes; - } - - if (sides & 1) - SV_FindTouchedLeafs(ent, node->children[0], topnode); - - if (sides & 2) - SV_FindTouchedLeafs(ent, node->children[1], topnode); + if (sides & 1) SV_FindTouchedLeafs(ent, node->children[0], topnode); + if (sides & 2) SV_FindTouchedLeafs(ent, node->children[1], topnode); } + #else // REHLDS_OPT_PEDANTIC + // unrolled some tail recursion void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node, int *topnode) { - mplane_t *splitplane; - int sides; - - while (1) + while (true) { + // if no collision model + if (!node) + return; + if (node->contents == CONTENTS_SOLID) return; + // add an efrag if the node is a leaf if (node->contents < 0) { - if (ent->num_leafs < MAX_ENT_LEAFS) + if (ent->num_leafs > (MAX_ENT_LEAFS - 1)) + { + // continue counting leafs, + // so we know how many it's overrun + ent->num_leafs = (MAX_ENT_LEAFS + 1); + } + else { mleaf_t *leaf = (mleaf_t *)node; int leafnum = leaf - g_psv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; } - else - { - ent->num_leafs = MAX_ENT_LEAFS + 1; - } + return; } - splitplane = node->plane; - if (splitplane->type >= 3) + // NODE_MIXED + mplane_t *splitplane = node->plane; + int sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); + + switch (sides) { - sides = BoxOnPlaneSide(ent->v.absmin, ent->v.absmax, splitplane); - - if (sides == 3) - { - if (*topnode == -1) - *topnode = node - g_psv.worldmodel->nodes; - } - - if (sides & 1) - SV_FindTouchedLeafs(ent, node->children[0], topnode); - - if (sides & 2) - SV_FindTouchedLeafs(ent, node->children[1], topnode); - } - else + case 1: node = node->children[0]; break; // do only SV_FindTouchedLeafs(ent, node->children[0], topnode); + case 2: node = node->children[1]; break; // do only SV_FindTouchedLeafs(ent, node->children[1], topnode); + case 3: { - if (splitplane->dist > ent->v.absmin[splitplane->type]) - { - if (splitplane->dist < ent->v.absmax[splitplane->type]) - { - // sides = 3; - // do both children nodes - if (*topnode == -1) - *topnode = node - g_psv.worldmodel->nodes; + if (*topnode == -1) + *topnode = node - g_psv.worldmodel->nodes; - SV_FindTouchedLeafs(ent, node->children[0], topnode); - node = node->children[1]; - continue; - } - else - { - // sides = 2; - // do only SV_FindTouchedLeafs(ent, node->children[1], topnode); - node = node->children[1]; - continue; - } - } - else - { - // sides = 1; - // do only SV_FindTouchedLeafs(ent, node->children[0], topnode); - node = node->children[0]; - continue; - } + // do both children nodes + SV_FindTouchedLeafs(ent, node->children[0], topnode); + node = node->children[1]; + break; + } + default: + return; } - - break; } } + #endif // REHLDS_OPT_PEDANTIC +// Needs to be called any time an entity changes origin, mins, maxs, or solid +// flags ent->v.modified +// sets ent->v.absmin and ent->v.absmax +// if touchtriggers, calls prog functions for the intersected triggers void SV_LinkEdict(edict_t *ent, qboolean touch_triggers) { - static int iTouchLinkSemaphore = 0; + static int iTouchLinkSemaphore = 0; // prevent recursion when SV_TouchLinks is active areanode_t *node; - int topnode; + // unlink from old position if (ent->area.prev) SV_UnlinkEdict(ent); + // don't add the world or free ents if (ent == &g_psv.edicts[0] || ent->free) return; + // set the abs box gEntityInterface.pfnSetAbsBox(ent); + if (ent->v.movetype == MOVETYPE_FOLLOW && ent->v.aiment) { ent->headnode = ent->v.aiment->headnode; @@ -558,20 +522,25 @@ void SV_LinkEdict(edict_t *ent, qboolean touch_triggers) } else { + int topnode = -1; + + // link to PVS leafs ent->num_leafs = 0; ent->headnode = -1; - topnode = -1; + if (ent->v.modelindex) SV_FindTouchedLeafs(ent, g_psv.worldmodel->nodes, &topnode); if (ent->num_leafs > MAX_ENT_LEAFS) { - ent->num_leafs = 0; - ent->headnode = (int)topnode; Q_memset(ent->leafnums, -1, sizeof(ent->leafnums)); + + ent->num_leafs = 0; // so we use headnode instead + ent->headnode = topnode; } } + // ignore non-solid bodies if (ent->v.solid == SOLID_NOT && ent->v.skin >= -1) return; @@ -581,67 +550,67 @@ void SV_LinkEdict(edict_t *ent, qboolean touch_triggers) return; } + // find the first node that the ent's box crosses node = sv_areanodes; while (true) { if (node->axis == -1) break; - if (ent->v.absmin[node->axis] <= node->dist) - { - if (ent->v.absmax[node->axis] >= node->dist) - break; - node = node->children[1]; - } - else - { + if (ent->v.absmin[node->axis] > node->dist) node = node->children[0]; - } + else if (ent->v.absmax[node->axis] < node->dist) + node = node->children[1]; + else break; // crosses the node } - InsertLinkBefore(&ent->area, (ent->v.solid == SOLID_TRIGGER) ? &node->trigger_edicts : &node->solid_edicts); - if (touch_triggers) + // link it in + if (ent->v.solid == SOLID_TRIGGER) + InsertLinkBefore(&ent->area, &node->trigger_edicts); + else + InsertLinkBefore(&ent->area, &node->solid_edicts); + + if (touch_triggers && !iTouchLinkSemaphore) { - if (!iTouchLinkSemaphore) - { - iTouchLinkSemaphore = 1; - SV_TouchLinks(ent, sv_areanodes); - iTouchLinkSemaphore = 0; - } + iTouchLinkSemaphore = 1; + SV_TouchLinks(ent, sv_areanodes); + iTouchLinkSemaphore = 0; } } int SV_HullPointContents(hull_t *hull, int num, const vec_t *p) { + float d; dclipnode_t *node; mplane_t *plane; - float d; - int i = num; - while (i >= 0) + while (num >= 0) { - if (hull->firstclipnode > i || hull->lastclipnode < i) + if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error("%s: bad node number", __func__); - node = &hull->clipnodes[i]; + + node = &hull->clipnodes[num]; plane = &hull->planes[node->planenum]; - if (plane->type > 2) - d = _DotProduct(plane->normal, p) - plane->dist; - else + + if (plane->type < 3) d = p[plane->type] - plane->dist; - i = node->children[(d >= 0.0f) ? 0 : 1]; + else + d = _DotProduct(plane->normal, p) - plane->dist; + + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; } - return i; + return num; } int SV_LinkContents(areanode_t *node, const vec_t *pos) { - link_t *l; - link_t *next; + vec3_t localPosition, offset; + link_t *next, *l; edict_t *touch; - hull_t *hull; - vec3_t localPosition; - vec3_t offset; #ifdef REHLDS_OPT_PEDANTIC // unroll tail recursion @@ -650,43 +619,50 @@ int SV_LinkContents(areanode_t *node, const vec_t *pos) { for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) { + touch = EDICT_FROM_AREA(l); next = l->next; - touch = (edict_t *)((char *)l - offsetof(edict_t, area)); - if (!touch->v.solid) - { - if (touch->v.groupinfo) - { - if (g_groupop) - { - if (g_groupop == GROUP_OP_NAND && (touch->v.groupinfo & g_groupmask)) - continue; - } - else - { - if (!(touch->v.groupinfo & g_groupmask)) - continue; - } - } - if (Mod_GetType(touch->v.modelindex) == mod_brush - && pos[0] <= (double)touch->v.absmax[0] - && pos[1] <= (double)touch->v.absmax[1] - && pos[2] <= (double)touch->v.absmax[2] - && pos[0] >= (double)touch->v.absmin[0] - && pos[1] >= (double)touch->v.absmin[1] - && pos[2] >= (double)touch->v.absmin[2]) + if (touch->v.solid != SOLID_NOT) + continue; + + if (touch->v.groupinfo) + { + if (g_groupop) { - int contents = touch->v.skin; - if (contents < -100 || contents > 100) - Con_DPrintf("Invalid contents on trigger field: %s\n", &pr_strings[touch->v.classname]); - hull = SV_HullForBsp(touch, vec3_origin, vec3_origin, offset); - localPosition[0] = pos[0] - offset[0]; - localPosition[1] = pos[1] - offset[1]; - localPosition[2] = pos[2] - offset[2]; - if (SV_HullPointContents(hull, hull->firstclipnode, localPosition) != CONTENTS_EMPTY) - return contents; + if (g_groupop == GROUP_OP_NAND && (touch->v.groupinfo & g_groupmask)) + continue; + } + else + { + if (!(touch->v.groupinfo & g_groupmask)) + continue; } } + + if (Mod_GetType(touch->v.modelindex) != mod_brush) + continue; + + if (pos[0] > touch->v.absmax[0] + || pos[1] > touch->v.absmax[1] + || pos[2] > touch->v.absmax[2] + || pos[0] < touch->v.absmin[0] + || pos[1] < touch->v.absmin[1] + || pos[2] < touch->v.absmin[2]) + continue; + + int contents = touch->v.skin; + if (contents < -100 || contents > 100) + Con_DPrintf("Invalid contents on trigger field: %s\n", &pr_strings[touch->v.classname]); + + // force to select bsp-hull + hull_t *hull = SV_HullForBsp(touch, vec3_origin, vec3_origin, offset); + + // offset the test point appropriately for this hull + VectorSubtract(pos, offset, localPosition); + + // test hull for intersection with this model + if (SV_HullPointContents(hull, hull->firstclipnode, localPosition) != CONTENTS_EMPTY) + return contents; } if (node->axis == -1) @@ -696,16 +672,17 @@ int SV_LinkContents(areanode_t *node, const vec_t *pos) if (pos[node->axis] > node->dist) return SV_LinkContents(node->children[0], pos); - if (pos[node->axis] < node->dist) + else if (pos[node->axis] < node->dist) return SV_LinkContents(node->children[1], pos); + #else // REHLDS_OPT_PEDANTIC + if (pos[node->axis] > node->dist) { node = node->children[0]; continue; } - - if (pos[node->axis] < node->dist) + else if (pos[node->axis] < node->dist) { node = node->children[1]; continue; @@ -718,12 +695,11 @@ int SV_LinkContents(areanode_t *node, const vec_t *pos) return CONTENTS_EMPTY; } +// Returns the CONTENTS_* value from the world at the given point. +// does not check any entities at all int SV_PointContents(const vec_t *p) { - int cont; - int entityContents; - - cont = SV_HullPointContents(g_psv.worldmodel->hulls, 0, p); + int cont = SV_HullPointContents(g_psv.worldmodel->hulls, 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) { cont = CONTENTS_WATER; @@ -734,178 +710,178 @@ int SV_PointContents(const vec_t *p) return CONTENTS_SOLID; } - entityContents = SV_LinkContents(&sv_areanodes[0], p); + int entityContents = SV_LinkContents(&sv_areanodes[0], p); return (entityContents != CONTENTS_EMPTY) ? entityContents : cont; } +// Returns true if the entity is in solid currently edict_t *SV_TestEntityPosition(edict_t *ent) { - trace_t trace; - qboolean monsterClip; + qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; + trace_t trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent, monsterClip); - monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE; - trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent, monsterClip); if (trace.startsolid) { SV_SetGlobalTrace(&trace); return trace.ent; } - return NULL; + return nullptr; } +const float DIST_EPSILON = 0.03125f; + #ifndef REHLDS_OPT_PEDANTIC qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, const vec_t *p1, const vec_t *p2, trace_t *trace) { dclipnode_t *node; mplane_t *plane; - float t2; + float t1, t2; + float frac, midf, pointf; vec3_t mid; - float frac; - float t1; - signed int side; - float midf; + int side; float pdif = p2f - p1f; - float DIST_EPSILON = 0.03125f; - - if (num >= 0) + if (num < 0) { - if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes) - Sys_Error("%s: bad node number", __func__); - - node = &hull->clipnodes[num]; - plane = &hull->planes[hull->clipnodes[num].planenum]; - if (plane->type >= 3) + if (num != CONTENTS_SOLID) { - t1 = _DotProduct(p1, plane->normal) - plane->dist; - t2 = _DotProduct(p2, plane->normal) - plane->dist; + trace->allsolid = FALSE; + + if (num == CONTENTS_EMPTY) + trace->inopen = TRUE; + + else if (num != CONTENTS_TRANSLUCENT) + trace->inwater = TRUE; } else { - t1 = p1[plane->type] - plane->dist; - t2 = p2[plane->type] - plane->dist; - } - if (t1 >= 0.0f && t2 >= 0.0f) - return SV_RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace); - - if (t1 >= 0.0f) - { - midf = t1 - DIST_EPSILON; - } - else - { - if (t2 < 0.0f) - return SV_RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace); - - midf = t1 + DIST_EPSILON; + trace->startsolid = TRUE; } - midf = midf / (t1 - t2); - if (midf >= 0.0f) - { - if (midf > 1.0f) - midf = 1.0f; - } - else - { - midf = 0.0f; - } - if (!IS_NAN(midf)) // not a number - { - frac = pdif * midf + p1f; - mid[0] = (p2[0] - p1[0]) * midf + p1[0]; - mid[1] = (p2[1] - p1[1]) * midf + p1[1]; - mid[2] = (p2[2] - p1[2]) * midf + p1[2]; - side = (t1 < 0.0f) ? 1 : 0; - if (SV_RecursiveHullCheck(hull, node->children[side], p1f, frac, p1, mid, trace)) - { - if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) - return SV_RecursiveHullCheck(hull, node->children[side ^ 1], frac, p2f, mid, p2, trace); - - if (!trace->allsolid) - { - if (side) - { - trace->plane.normal[0] = vec3_origin[0] - plane->normal[0]; - trace->plane.normal[1] = vec3_origin[1] - plane->normal[1]; - trace->plane.normal[2] = vec3_origin[2] - plane->normal[2]; - trace->plane.dist = -plane->dist; - } - else - { - trace->plane.normal[0] = plane->normal[0]; - trace->plane.normal[1] = plane->normal[1]; - trace->plane.normal[2] = plane->normal[2]; - trace->plane.dist = plane->dist; - } - - while (1) - { - if (SV_HullPointContents(hull, hull->firstclipnode, mid) != CONTENTS_SOLID) - { - trace->fraction = frac; - trace->endpos[0] = mid[0]; - trace->endpos[1] = mid[1]; - trace->endpos[2] = mid[2]; - return FALSE; - } - midf -= 0.1f; - if (midf < 0.0f) - break; - frac = pdif * midf + p1f; - mid[0] = (p2[0] - p1[0]) * midf + p1[0]; - mid[1] = (p2[1] - p1[1]) * midf + p1[1]; - mid[2] = (p2[2] - p1[2]) * midf + p1[2]; - } - trace->fraction = frac; - trace->endpos[0] = mid[0]; - trace->endpos[1] = mid[1]; - trace->endpos[2] = mid[2]; - Con_DPrintf("backup past 0\n"); - return FALSE; - } - } - } - return FALSE; + // empty + return TRUE; } - if (num == CONTENTS_SOLID) + if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes) + Sys_Error("%s: bad node number", __func__); + + // find the point distances + node = &hull->clipnodes[num]; + plane = &hull->planes[hull->clipnodes[num].planenum]; + + if (plane->type < 3) { - trace->startsolid = TRUE; + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; } else { - trace->allsolid = FALSE; - if (num == CONTENTS_EMPTY) - { - trace->inopen = TRUE; - return TRUE; - } - if (num != CONTENTS_TRANSLUCENT) - { - trace->inwater = TRUE; - return TRUE; - } + t1 = _DotProduct(plane->normal, p1) - plane->dist; + t2 = _DotProduct(plane->normal, p2) - plane->dist; } - return TRUE; + + if (t1 >= 0.0f && t2 >= 0.0f) + return SV_RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace); + + if (t1 < 0.0f && t2 < 0.0f) + return SV_RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace); + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0.0f) + { + frac = (t1 + DIST_EPSILON) / (t1 - t2); + } + else + { + frac = (t1 - DIST_EPSILON) / (t1 - t2); + } + + if (frac < 0.0f) + frac = 0.0f; + + else if (frac > 1.0f) + frac = 1.0f; + + if (IS_NAN(frac)) + { + // not a number + return FALSE; + } + + midf = p1f + pdif * frac; + + real3_t point; + VectorSubtract(p2, p1, point); + VectorMA(p1, frac, point, mid); + + side = (t1 < 0.0f) ? 1 : 0; + + // move up to the node + if (!SV_RecursiveHullCheck(hull, node->children[side], p1f, midf, p1, mid, trace)) + return FALSE; + + if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) + { + // go past the node + return SV_RecursiveHullCheck(hull, node->children[side ^ 1], midf, p2f, mid, p2, trace); + } + + if (trace->allsolid) + { + // never got out of the solid area + return FALSE; + } + + // the other side of the node is solid, this is the impact point + if (!side) + { + VectorCopy(plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + else + { + VectorNegate(plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + + while (SV_HullPointContents(hull, hull->firstclipnode, mid) == CONTENTS_SOLID) + { + // shouldn't really happen, but does occasionally + frac -= 0.1f; + if (frac < 0.0f) + { + trace->fraction = midf; + VectorCopy(mid, trace->endpos); + Con_DPrintf("backup past 0\n"); + return FALSE; + } + + midf = p1f + pdif * frac; + + real3_t point; + VectorSubtract(p2, p1, point); + VectorMA(p1, frac, point, mid); + } + + trace->fraction = midf; + VectorCopy(mid, trace->endpos); + + return FALSE; } + #else // REHLDS_OPT_PEDANTIC + // version with unrolled tail recursion qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, const vec_t *p1, const vec_t *p2, trace_t *trace) { dclipnode_t *node; mplane_t *plane; - float t2; - vec3_t mid; - float frac; - float t1; - signed int side; - float midf; + float t1, t2; + float frac, midf; + vec3_t mid, custom_p1; // for holding custom p1 value + int side; float pdif; - vec3_t custom_p1; // for holding custom p1 value - - float DIST_EPSILON = 0.03125f; while (num >= 0) { @@ -914,134 +890,135 @@ qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, cons if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes) Sys_Error("%s: bad node number", __func__); + // find the point distances node = &hull->clipnodes[num]; plane = &hull->planes[hull->clipnodes[num].planenum]; - if (plane->type >= 3) - { - t1 = _DotProduct(p1, plane->normal) - plane->dist; - t2 = _DotProduct(p2, plane->normal) - plane->dist; - } - else + + if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } + else + { + t1 = _DotProduct(plane->normal, p1) - plane->dist; + t2 = _DotProduct(plane->normal, p2) - plane->dist; + } + if (t1 >= 0.0f && t2 >= 0.0f) { num = node->children[0]; continue; } - if (t1 >= 0.0f) + if (t1 < 0.0f && t2 < 0.0f) { - midf = t1 - DIST_EPSILON; + num = node->children[1]; + continue; + } + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < 0.0f) + { + frac = (t1 + DIST_EPSILON) / (t1 - t2); } else { - if (t2 < 0.0f) - { - num = node->children[1]; - continue; - } - - midf = t1 + DIST_EPSILON; + frac = (t1 - DIST_EPSILON) / (t1 - t2); } - midf = midf / (t1 - t2); - if (midf >= 0.0f) + if (frac < 0.0f) + frac = 0.0f; + + else if (frac > 1.0f) + frac = 1.0f; + + if (IS_NAN(frac)) { - if (midf > 1.0f) - midf = 1.0f; + // not a number + return FALSE; + } + + midf = p1f + pdif * frac; + + real3_t point; + VectorSubtract(p2, p1, point); + VectorMA(p1, frac, point, mid); + + side = (t1 < 0.0f) ? 1 : 0; + + // move up to the node + if (!SV_RecursiveHullCheck(hull, node->children[side], p1f, midf, p1, mid, trace)) + return FALSE; + + if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) + { + // go past the node + num = node->children[side ^ 1]; + p1f = midf; + p1 = custom_p1; + VectorCopy(mid, custom_p1); + continue; + } + + if (trace->allsolid) + { + // never got out of the solid area + return FALSE; + } + + // the other side of the node is solid, this is the impact point + if (!side) + { + VectorCopy(plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; } else { - midf = 0.0f; + VectorNegate(plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; } - if (!IS_NAN(midf)) // not a number + + while (SV_HullPointContents(hull, hull->firstclipnode, mid) == CONTENTS_SOLID) { - frac = pdif * midf + p1f; - mid[0] = (p2[0] - p1[0]) * midf + p1[0]; - mid[1] = (p2[1] - p1[1]) * midf + p1[1]; - mid[2] = (p2[2] - p1[2]) * midf + p1[2]; - side = (t1 < 0.0f) ? 1 : 0; - if (SV_RecursiveHullCheck(hull, node->children[side], p1f, frac, p1, mid, trace)) + // shouldn't really happen, but does occasionally + frac -= 0.1f; + if (frac < 0.0f) { - if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) - { - num = node->children[side ^ 1]; - p1f = frac; - p1 = custom_p1; - custom_p1[0] = mid[0]; - custom_p1[1] = mid[1]; - custom_p1[2] = mid[2]; - continue; - } - - if (!trace->allsolid) - { - if (side) - { - trace->plane.normal[0] = vec3_origin[0] - plane->normal[0]; - trace->plane.normal[1] = vec3_origin[1] - plane->normal[1]; - trace->plane.normal[2] = vec3_origin[2] - plane->normal[2]; - trace->plane.dist = -plane->dist; - } - else - { - trace->plane.normal[0] = plane->normal[0]; - trace->plane.normal[1] = plane->normal[1]; - trace->plane.normal[2] = plane->normal[2]; - trace->plane.dist = plane->dist; - } - - while (1) - { - if (SV_HullPointContents(hull, hull->firstclipnode, mid) != CONTENTS_SOLID) - { - trace->fraction = frac; - trace->endpos[0] = mid[0]; - trace->endpos[1] = mid[1]; - trace->endpos[2] = mid[2]; - return FALSE; - } - midf -= 0.1f; - if (midf < 0.0f) - break; - frac = pdif * midf + p1f; - mid[0] = (p2[0] - p1[0]) * midf + p1[0]; - mid[1] = (p2[1] - p1[1]) * midf + p1[1]; - mid[2] = (p2[2] - p1[2]) * midf + p1[2]; - } - trace->fraction = frac; - trace->endpos[0] = mid[0]; - trace->endpos[1] = mid[1]; - trace->endpos[2] = mid[2]; - Con_DPrintf("backup past 0\n"); - return FALSE; - } + trace->fraction = midf; + VectorCopy(mid, trace->endpos); + Con_DPrintf("backup past 0\n"); + return FALSE; } + + midf = p1f + pdif * frac; + + real3_t point; + VectorSubtract(p2, p1, point); + VectorMA(p1, frac, point, mid); } + + trace->fraction = midf; + VectorCopy(mid, trace->endpos); return FALSE; } - if (num == CONTENTS_SOLID) + if (num != CONTENTS_SOLID) { - trace->startsolid = TRUE; + trace->allsolid = FALSE; + + if (num == CONTENTS_EMPTY) + trace->inopen = TRUE; + + else if (num != CONTENTS_TRANSLUCENT) + trace->inwater = TRUE; } else { - trace->allsolid = FALSE; - if (num == CONTENTS_EMPTY) - { - trace->inopen = TRUE; - return TRUE; - } - if (num != CONTENTS_TRANSLUCENT) - { - trace->inwater = TRUE; - return TRUE; - } + trace->startsolid = TRUE; } + + // empty return TRUE; } #endif // REHLDS_OPT_PEDANTIC @@ -1049,20 +1026,17 @@ qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, cons void SV_SingleClipMoveToEntity(edict_t *ent, const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, trace_t *trace) { hull_t *hull; - trace_t testtrace; vec3_t offset; - int rotated; - int closest; + bool rotated; vec3_t end_l; vec3_t start_l; int numhulls; + // fill in a default trace Q_memset(trace, 0, sizeof(trace_t)); + VectorCopy(end, trace->endpos); trace->fraction = 1.0f; trace->allsolid = TRUE; - trace->endpos[0] = end[0]; - trace->endpos[1] = end[1]; - trace->endpos[2] = end[2]; #ifdef REHLDS_FIXES if (Mod_GetType(ent->v.modelindex) == mod_studio) @@ -1070,119 +1044,127 @@ void SV_SingleClipMoveToEntity(edict_t *ent, const vec_t *start, const vec_t *mi if (g_psv.models[ent->v.modelindex]->type == mod_studio) #endif // REHLDS_FIXES { + // get the clipping hull from studio model hull = SV_HullForStudioModel(ent, mins, maxs, offset, &numhulls); } else { + // get the clipping hull hull = SV_HullForEntity(ent, mins, maxs, offset); numhulls = 1; } - start_l[0] = start[0] - offset[0]; - start_l[1] = start[1] - offset[1]; - start_l[2] = start[2] - offset[2]; - end_l[0] = end[0] - offset[0]; - end_l[1] = end[1] - offset[1]; - end_l[2] = end[2] - offset[2]; - if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] != 0.0f || ent->v.angles[1] != 0.0f || ent->v.angles[2] != 0.0f)) + + VectorSubtract(start, offset, start_l); + VectorSubtract(end, offset, end_l); + + // rotate start and end into the models frame of reference + if (ent->v.solid == SOLID_BSP && !VectorIsZero(ent->v.angles)) { - vec3_t right; - vec3_t forward; - vec3_t up; + vec3_t forward, right, up; vec3_t temp; AngleVectors(ent->v.angles, forward, right, up); - temp[0] = start_l[0]; temp[1] = start_l[1]; temp[2] = start_l[2]; - start_l[0] = _DotProduct(forward, temp); - start_l[1] = -_DotProduct(right, temp); - start_l[2] = _DotProduct(up, temp); + VectorCopy(start_l, temp); + start_l[0] = _DotProduct(temp, forward); + start_l[1] = -_DotProduct(temp, right); + start_l[2] = _DotProduct(temp, up); - temp[0] = end_l[0]; temp[1] = end_l[1]; temp[2] = end_l[2]; - end_l[0] = _DotProduct(forward, temp); - end_l[1] = -_DotProduct(right, temp); - end_l[2] = _DotProduct(up, temp); + VectorCopy(end_l, temp); + end_l[0] = _DotProduct(temp, forward); + end_l[1] = -_DotProduct(temp, right); + end_l[2] = _DotProduct(temp, up); - rotated = 1; + rotated = true; } else { - rotated = 0; + rotated = false; } + // trace a line through the appropriate clipping hull if (numhulls == 1) { SV_RecursiveHullCheck(hull, hull->firstclipnode, 0.0f, 1.0f, start_l, end_l, trace); } else { - closest = 0; + int last_hitgroup = 0; for (int i = 0; i < numhulls; i++) { - Q_memset(&testtrace, 0, sizeof(trace_t)); - testtrace.endpos[0] = end[0]; - testtrace.endpos[1] = end[1]; - testtrace.endpos[2] = end[2]; - testtrace.fraction = 1.0f; - testtrace.allsolid = TRUE; - SV_RecursiveHullCheck(&hull[i], hull[i].firstclipnode, 0.0f, 1.0f, start_l, end_l, &testtrace); - if (i == 0 || testtrace.allsolid || testtrace.startsolid || testtrace.fraction < trace->fraction) + // fill in a default trace + trace_t trace_hitbox; + Q_memset(&trace_hitbox, 0, sizeof(trace_hitbox)); + VectorCopy(end, trace_hitbox.endpos); + trace_hitbox.fraction = 1.0f; + trace_hitbox.allsolid = TRUE; + + SV_RecursiveHullCheck(&hull[i], hull[i].firstclipnode, 0.0f, 1.0f, start_l, end_l, &trace_hitbox); + + if (i == 0 || trace_hitbox.allsolid || trace_hitbox.startsolid || trace_hitbox.fraction < trace->fraction) { - int isSolid = trace->startsolid; - Q_memcpy(trace, &testtrace, sizeof(trace_t)); - if (isSolid) + if (trace->startsolid) + { + *trace = trace_hitbox; trace->startsolid = TRUE; - closest = i; + } + else + { + *trace = trace_hitbox; + } + + last_hitgroup = i; } } - trace->hitgroup = SV_HitgroupForStudioHull(closest); + trace->hitgroup = SV_HitgroupForStudioHull(last_hitgroup); } if (trace->fraction != 1.0f) { if (rotated) { - vec3_t right; - vec3_t up; - vec3_t forward; + vec3_t forward, right, up; vec3_t temp; AngleVectorsTranspose(ent->v.angles, forward, right, up); - temp[0] = trace->plane.normal[0]; - temp[1] = trace->plane.normal[1]; - temp[2] = trace->plane.normal[2]; + VectorCopy(trace->plane.normal, temp); - trace->plane.normal[0] = _DotProduct(forward, temp); - trace->plane.normal[1] = _DotProduct(right, temp); - trace->plane.normal[2] = _DotProduct(up, temp); + trace->plane.normal[0] = _DotProduct(temp, forward); + trace->plane.normal[1] = _DotProduct(temp, right); + trace->plane.normal[2] = _DotProduct(temp, up); } - trace->endpos[0] = (end[0] - start[0]) * trace->fraction + start[0]; - trace->endpos[1] = (end[1] - start[1]) * trace->fraction + start[1]; - trace->endpos[2] = (end[2] - start[2]) * trace->fraction + start[2]; + real3_t point; + VectorSubtract(end, start, point); + VectorMA(start, trace->fraction, point, trace->endpos); } + // did we clip the move? if (trace->fraction < 1.0f || trace->startsolid) trace->ent = ent; } +// Handles selection or creation of a clipping hull, and offseting (and eventually rotation) of the end points trace_t SV_ClipMoveToEntity(edict_t *ent, const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end) { - trace_t goodtrace; - SV_SingleClipMoveToEntity(ent, start, mins, maxs, end, &goodtrace); - - return goodtrace; + trace_t trace; + SV_SingleClipMoveToEntity(ent, start, mins, maxs, end, &trace); + return trace; } +// Mins and maxs enclose the entire area swept by the move void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) { - link_t *l; - link_t *next; + link_t *next, *l; + edict_t *touch; + // touch linked edicts for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) { next = l->next; - edict_t *touch = (edict_t *)((char *)l - offsetof(edict_t, area)); + touch = EDICT_FROM_AREA(l); + if (touch->v.groupinfo && clip->passedict && clip->passedict->v.groupinfo) { if (g_groupop) @@ -1197,10 +1179,7 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) } } - if (touch->v.solid == SOLID_NOT) - continue; - - if (touch == clip->passedict) + if (touch->v.solid == SOLID_NOT || touch == clip->passedict) continue; if (touch->v.solid == SOLID_TRIGGER) @@ -1214,6 +1193,7 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) return; #endif + // monsterclip filter if (touch->v.solid == SOLID_BSP) { if ((touch->v.flags & FL_MONSTERCLIP) && !clip->monsterClipBrush) @@ -1221,67 +1201,84 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) } else { - if (clip->type == 1 && touch->v.movetype != MOVETYPE_PUSHSTEP) + // ignore all monsters but pushables + if (clip->type == MOVE_NOMONSTERS && touch->v.movetype != MOVETYPE_PUSHSTEP) continue; } - if ((!clip->ignoretrans || !touch->v.rendermode || (touch->v.flags & FL_WORLDBRUSH)) - && clip->boxmins[0] <= touch->v.absmax[0] - && clip->boxmins[1] <= touch->v.absmax[1] - && clip->boxmins[2] <= touch->v.absmax[2] - && clip->boxmaxs[0] >= touch->v.absmin[0] - && clip->boxmaxs[1] >= touch->v.absmin[1] - && clip->boxmaxs[2] >= touch->v.absmin[2] -#ifdef REHLDS_FIXES - && ((touch->v.solid == SOLID_SLIDEBOX && sv_force_ent_intersection.string[0] == '0') -#else // REHLDS_FIXES - && (touch->v.solid == SOLID_SLIDEBOX -#endif // REHLDS_FIXES - || SV_CheckSphereIntersection(touch, clip->start, clip->end)) - && (!clip->passedict || clip->passedict->v.size[0] == 0.0f || touch->v.size[0] != 0.0f)) + if (clip->ignoretrans && touch->v.rendermode != kRenderNormal && !(touch->v.flags & FL_WORLDBRUSH)) + continue; + + if (clip->boxmins[0] > touch->v.absmax[0] + || clip->boxmins[1] > touch->v.absmax[1] + || clip->boxmins[2] > touch->v.absmax[2] + || clip->boxmaxs[0] < touch->v.absmin[0] + || clip->boxmaxs[1] < touch->v.absmin[1] + || clip->boxmaxs[2] < touch->v.absmin[2]) + continue; + + if (touch->v.solid != SOLID_SLIDEBOX && !SV_CheckSphereIntersection(touch, clip->start, clip->end)) + continue; + + if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) + continue; // points never interact + + // might intersect, so do an exact clip + if (clip->trace.allsolid) + return; + + if (clip->passedict) { - if (clip->trace.allsolid) - return; + if (touch->v.owner == clip->passedict) + continue; // don't clip against own missiles - if (clip->passedict && (touch->v.owner == clip->passedict || clip->passedict->v.owner == touch)) - continue; + if (clip->passedict->v.owner == touch) + continue; // don't clip against owner + } - trace_t trace; - if (touch->v.flags & FL_MONSTER) - trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end); - else - trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end); + trace_t trace; + if (touch->v.flags & FL_MONSTER) + trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end); + else + trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end); - if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) + if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) + { + trace.ent = touch; + if (clip->trace.startsolid) + { + clip->trace = trace; + clip->trace.startsolid = TRUE; + } + else { - qboolean oldStartSolid = clip->trace.startsolid; - trace.ent = touch; clip->trace = trace; - if (oldStartSolid) - clip->trace.startsolid = TRUE; } } } - if (node->axis != -1) - { - if (clip->boxmaxs[node->axis] > node->dist) - SV_ClipToLinks(node->children[0], clip); + // recurse down both sides + if (node->axis == -1) + return; - if (node->dist > clip->boxmins[node->axis]) - SV_ClipToLinks(node->children[1], clip); - } + if (clip->boxmaxs[node->axis] > node->dist) + SV_ClipToLinks(node->children[0], clip); + + if (node->dist > clip->boxmins[node->axis]) + SV_ClipToLinks(node->children[1], clip); } +// Mins and maxs enclose the entire area swept by the move void SV_ClipToWorldbrush(areanode_t *node, moveclip_t *clip) { link_t *l; link_t *next; + edict_t *touch; for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) { next = l->next; - edict_t *touch = (edict_t *)((char *)l - offsetof(edict_t, area)); + touch = EDICT_FROM_AREA(l); if (touch->v.solid != SOLID_BSP) continue; @@ -1289,36 +1286,42 @@ void SV_ClipToWorldbrush(areanode_t *node, moveclip_t *clip) if (!(touch->v.flags & FL_WORLDBRUSH)) continue; - if (clip->boxmins[0] <= touch->v.absmax[0] - && clip->boxmins[1] <= touch->v.absmax[1] - && clip->boxmins[2] <= touch->v.absmax[2] - && clip->boxmaxs[0] >= touch->v.absmin[0] - && clip->boxmaxs[1] >= touch->v.absmin[1] - && clip->boxmaxs[2] >= touch->v.absmin[2]) - { - if (clip->trace.allsolid) - return; + if (clip->boxmins[0] > touch->v.absmax[0] + || clip->boxmins[1] > touch->v.absmax[1] + || clip->boxmins[2] > touch->v.absmax[2] + || clip->boxmaxs[0] < touch->v.absmin[0] + || clip->boxmaxs[1] < touch->v.absmin[1] + || clip->boxmaxs[2] < touch->v.absmin[2]) + continue; - trace_t trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end); - if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) + if (clip->trace.allsolid) + return; + + trace_t trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end); + if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) + { + trace.ent = touch; + if (clip->trace.startsolid) + { + clip->trace = trace; + clip->trace.startsolid = TRUE; + } + else { - int oldSolid = clip->trace.startsolid; - trace.ent = touch; clip->trace = trace; - if (oldSolid) - clip->trace.startsolid = TRUE; } } } - if (node->axis != -1) - { - if (clip->boxmaxs[node->axis] > node->dist) - SV_ClipToWorldbrush(node->children[0], clip); + // recurse down both sides + if (node->axis == -1) + return; - if (node->dist > clip->boxmins[node->axis]) - SV_ClipToWorldbrush(node->children[1], clip); - } + if (clip->boxmaxs[node->axis] > node->dist) + SV_ClipToWorldbrush(node->children[0], clip); + + if (node->dist > clip->boxmins[node->axis]) + SV_ClipToWorldbrush(node->children[1], clip); } void SV_MoveBounds(const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, vec_t *boxmins, vec_t *boxmaxs) @@ -1341,71 +1344,77 @@ void SV_MoveBounds(const vec_t *start, const vec_t *mins, const vec_t *maxs, con trace_t SV_MoveNoEnts(const vec_t *start, vec_t *mins, vec_t *maxs, const vec_t *end, int type, edict_t *passedict) { moveclip_t clip; - vec3_t worldEndPoint; - float worldFraction; + vec3_t trace_endpos; + float trace_fraction; Q_memset(&clip, 0, sizeof(clip)); clip.trace = SV_ClipMoveToEntity(g_psv.edicts, start, mins, maxs, end); + if (clip.trace.fraction != 0.0f) { - worldEndPoint[0] = clip.trace.endpos[0]; - worldEndPoint[2] = clip.trace.endpos[2]; - worldEndPoint[1] = clip.trace.endpos[1]; - clip.end = worldEndPoint; + VectorCopy(clip.trace.endpos, trace_endpos); - clip.ignoretrans = type >> 8; - worldFraction = clip.trace.fraction; - clip.type = type; - clip.passedict = passedict; - - clip.mins2[0] = mins[0]; - clip.mins2[1] = mins[1]; - clip.mins2[2] = mins[2]; - clip.maxs2[0] = maxs[0]; - clip.maxs2[1] = maxs[1]; - clip.maxs2[2] = maxs[2]; + trace_fraction = clip.trace.fraction; clip.trace.fraction = 1.0f; clip.start = start; - clip.mins = mins; - clip.maxs = maxs; + clip.end = trace_endpos; + + clip.type = (type & 0xff); + clip.ignoretrans = (type >> 8); + clip.passedict = passedict; clip.monsterClipBrush = FALSE; - SV_MoveBounds(start, clip.mins2, clip.maxs2, worldEndPoint, clip.boxmins, clip.boxmaxs); + clip.mins = mins; + clip.maxs = maxs; + + VectorCopy(mins, clip.mins2); + VectorCopy(maxs, clip.maxs2); + + SV_MoveBounds(start, clip.mins2, clip.maxs2, trace_endpos, clip.boxmins, clip.boxmaxs); SV_ClipToWorldbrush(sv_areanodes, &clip); + clip.trace.fraction *= trace_fraction; gGlobalVariables.trace_ent = clip.trace.ent; - clip.trace.fraction = worldFraction * clip.trace.fraction; } return clip.trace; } +// mins and maxs are reletive +// if the entire move stays in a solid volume, trace.allsolid will be set +// if the starting point is in a solid, it will be allowed to move out to an open area +// nomonsters is used for line of sight or edge testing, +// where mosnters shouldn't be considered solid objects +// passedict is explicitly excluded from clipping checks (normally NULL) trace_t SV_Move(const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, int type, edict_t *passedict, qboolean monsterClipBrush) { moveclip_t clip; - vec3_t worldEndPoint; - float worldFraction; + vec3_t trace_endpos; + float trace_fraction; Q_memset(&clip, 0, sizeof(clip)); clip.trace = SV_ClipMoveToEntity(g_psv.edicts, start, mins, maxs, end); + if (clip.trace.fraction != 0.0f) { - worldEndPoint[0] = clip.trace.endpos[0]; - worldEndPoint[1] = clip.trace.endpos[1]; - worldEndPoint[2] = clip.trace.endpos[2]; - clip.end = worldEndPoint; - worldFraction = clip.trace.fraction; + VectorCopy(clip.trace.endpos, trace_endpos); + + trace_fraction = clip.trace.fraction; - clip.type = type & 0xFF; - clip.ignoretrans = type >> 8; clip.trace.fraction = 1.0f; clip.start = start; - clip.mins = mins; - clip.maxs = maxs; + clip.end = trace_endpos; + + clip.type = (type & 0xff); + clip.ignoretrans = (type >> 8); clip.passedict = passedict; clip.monsterClipBrush = monsterClipBrush; - if (type == 2) + + clip.mins = mins; + clip.maxs = maxs; + + if (type == MOVE_MISSILE) { for (int i = 0; i < 3; i++) { @@ -1415,36 +1424,31 @@ trace_t SV_Move(const vec_t *start, const vec_t *mins, const vec_t *maxs, const } else { - clip.mins2[0] = mins[0]; - clip.mins2[1] = mins[1]; - clip.mins2[2] = mins[2]; - clip.maxs2[0] = maxs[0]; - clip.maxs2[1] = maxs[1]; - clip.maxs2[2] = maxs[2]; + VectorCopy(mins, clip.mins2); + VectorCopy(maxs, clip.maxs2); } - SV_MoveBounds(start, clip.mins2, clip.maxs2, worldEndPoint, clip.boxmins, clip.boxmaxs); + + SV_MoveBounds(start, clip.mins2, clip.maxs2, trace_endpos, clip.boxmins, clip.boxmaxs); SV_ClipToLinks(sv_areanodes, &clip); + + clip.trace.fraction *= trace_fraction; gGlobalVariables.trace_ent = clip.trace.ent; - clip.trace.fraction = worldFraction * clip.trace.fraction; } return clip.trace; } #ifdef REHLDS_OPT_PEDANTIC + // Optimized version of SV_Move routines for moving point hull throw world void SV_SingleClipMoveToPoint(const vec_t *start, const vec_t *end, trace_t *trace) { - hull_t *hull; - Q_memset(trace, 0, sizeof(trace_t)); trace->fraction = 1.0f; trace->allsolid = TRUE; - trace->endpos[0] = end[0]; - trace->endpos[1] = end[1]; - trace->endpos[2] = end[2]; + VectorCopy(end, trace->endpos); - hull = &g_psv.models[1]->hulls[0]; // world point hull + hull_t *hull = &g_psv.models[1]->hulls[0]; // world point hull SV_RecursiveHullCheck(hull, hull->firstclipnode, 0.0f, 1.0f, start, end, trace); if (trace->fraction != 1.0f) @@ -1478,30 +1482,31 @@ void SV_MoveBounds_Point(const vec_t *start, const vec_t *end, vec_t *boxmins, v trace_t SV_Move_Point(const vec_t *start, const vec_t *end, int type, edict_t *passedict) { moveclip_t clip; - vec3_t worldEndPoint; - float worldFraction; + vec3_t trace_endpos; + float trace_fraction; Q_memset(&clip, 0, sizeof(clip)); SV_SingleClipMoveToPoint(start, end, &clip.trace); if (clip.trace.fraction != 0.0f) { - worldEndPoint[0] = clip.trace.endpos[0]; - worldEndPoint[1] = clip.trace.endpos[1]; - worldEndPoint[2] = clip.trace.endpos[2]; + VectorCopy(clip.trace.endpos, trace_endpos); - clip.end = worldEndPoint; - worldFraction = clip.trace.fraction; + trace_fraction = clip.trace.fraction; - clip.type = type & 0xFF; - clip.ignoretrans = type >> 8; clip.trace.fraction = 1.0f; clip.start = start; + clip.end = trace_endpos; + + clip.type = (type & 0xff); + clip.ignoretrans = (type >> 8); + clip.passedict = passedict; + clip.monsterClipBrush = FALSE; + clip.mins = vec3_origin; clip.maxs = vec3_origin; - clip.passedict = passedict; - clip.monsterClipBrush = 0; - if (type == 2) + + if (type == MOVE_MISSILE) { for (int i = 0; i < 3; i++) { @@ -1511,19 +1516,18 @@ trace_t SV_Move_Point(const vec_t *start, const vec_t *end, int type, edict_t *p } else { - clip.mins2[0] = 0.0; - clip.mins2[1] = 0.0; - clip.mins2[2] = 0.0; - clip.maxs2[0] = 0.0; - clip.maxs2[1] = 0.0; - clip.maxs2[2] = 0.0; + VectorClear(clip.mins2); + VectorClear(clip.maxs2); } - SV_MoveBounds_Point(start, worldEndPoint, clip.boxmins, clip.boxmaxs); + + SV_MoveBounds_Point(start, trace_endpos, clip.boxmins, clip.boxmaxs); SV_ClipToLinks(sv_areanodes, &clip); + + clip.trace.fraction *= trace_fraction; gGlobalVariables.trace_ent = clip.trace.ent; - clip.trace.fraction = worldFraction * clip.trace.fraction; } return clip.trace; } + #endif // REHLDS_OPT_PEDANTIC diff --git a/rehlds/engine/world.h b/rehlds/engine/world.h index 5b6f1e3..78115ab 100644 --- a/rehlds/engine/world.h +++ b/rehlds/engine/world.h @@ -40,6 +40,9 @@ typedef struct areanode_s link_t solid_edicts; } areanode_t; +const int AREA_DEPTH = 4; +const int AREA_NODES = 32; + typedef struct moveclip_s // TODO: Move it to world.cpp someday { vec3_t boxmins; @@ -57,6 +60,15 @@ typedef struct moveclip_s // TODO: Move it to world.cpp someday qboolean monsterClipBrush; } moveclip_t; +#define CONTENTS_NONE 0 // no custom contents specified + +#define MOVE_NORMAL 0 // normal trace +#define MOVE_NOMONSTERS 1 // ignore monsters (edicts with flags (FL_MONSTER|FL_FAKECLIENT|FL_CLIENT) set) +#define MOVE_MISSILE 2 // extra size for monsters + +#define FMOVE_IGNORE_GLASS 0x100 +#define FMOVE_SIMPLEBOX 0x200 + typedef dclipnode_t box_clipnodes_t[6]; typedef mplane_t box_planes_t[6]; typedef mplane_t beam_planes_t[6]; @@ -78,7 +90,7 @@ extern hull_t beam_hull; extern box_clipnodes_t box_clipnodes; extern box_planes_t box_planes; extern beam_planes_t beam_planes; -extern areanode_t sv_areanodes[32]; +extern areanode_t sv_areanodes[AREA_NODES]; extern int sv_numareanodes; extern cvar_t sv_force_ent_intersection; @@ -87,13 +99,13 @@ void ClearLink(link_t *l); void RemoveLink(link_t *l); void InsertLinkBefore(link_t *l, link_t *before); NOXREF void InsertLinkAfter(link_t *l, link_t *after); -void SV_InitBoxHull(void); +void SV_InitBoxHull(); hull_t *SV_HullForBox(const vec_t *mins, const vec_t *maxs); NOXREF hull_t *SV_HullForBeam(const vec_t *start, const vec_t *end, const vec_t *size); -struct hull_s *SV_HullForBsp(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset); +hull_t *SV_HullForBsp(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset); hull_t *SV_HullForEntity(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset); areanode_t *SV_CreateAreaNode(int depth, vec_t *mins, vec_t *maxs); -void SV_ClearWorld(void); +void SV_ClearWorld(); void SV_UnlinkEdict(edict_t *ent); void SV_TouchLinks(edict_t *ent, areanode_t *node); void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node, int *topnode); diff --git a/rehlds/hookers/engine/hooklist.cpp b/rehlds/hookers/engine/hooklist.cpp index 7ff8e8e..22c435e 100644 --- a/rehlds/hookers/engine/hooklist.cpp +++ b/rehlds/hookers/engine/hooklist.cpp @@ -1381,7 +1381,7 @@ FunctionHook g_FunctionHooks[] = HOOK_DEF(0x01D50460, Length), HOOK_DEF(0x01D504A0, VectorNormalize), //HOOK_DEF(0x, VectorInverse), - HOOK_DEF(0x01D50550, VectorScale), + //HOOK_DEF(0x01D50550, VectorScale), //HOOK_DEF(0x, Q_log2), //HOOK_DEF(0x, VectorMatrix), HOOK_DEF(0x01D50640, VectorAngles), @@ -2371,8 +2371,8 @@ AddressRef g_DataRefs[] = GLOBALVAR_LINK(0x01E48EA0, "sv_stopspeed", psv_stopspeed), GLOBALVAR_LINK(0x021C2B48, "g_moved_from", pg_moved_from), GLOBALVAR_LINK(0x021C2B4C, "sv_numareanodes", pg_moved_edict), - GLOBALVAR_LINK(0x021C2B50, "c_yes", pc_yes), - GLOBALVAR_LINK(0x021C2B54, "c_no", pc_no), + //GLOBALVAR_LINK(0x021C2B50, "c_yes", pc_yes), + //GLOBALVAR_LINK(0x021C2B54, "c_no", pc_no), GLOBALVAR_LINK(0x020042AC, "net_thread_initialized", pnet_thread_initialized), GLOBALVAR_LINK(0x01E3E8DC, "net_address", pnet_address), GLOBALVAR_LINK(0x01E3E900, "ipname", pipname), diff --git a/rehlds/public/rehlds/progs.h b/rehlds/public/rehlds/progs.h index 1d1f885..808ad30 100644 --- a/rehlds/public/rehlds/progs.h +++ b/rehlds/public/rehlds/progs.h @@ -60,7 +60,7 @@ struct event_state_s #include "edict.h" #endif -#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) +#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - offsetof(t, m))) #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) //============================================================================ From 5f1672ca85252d74fb501f582f6e1477700f8a9f Mon Sep 17 00:00:00 2001 From: s1lent Date: Sat, 9 Dec 2017 16:47:08 +0700 Subject: [PATCH 3/4] Move math sse to macros REHLDS_SSE --- rehlds/build.gradle | 4 +- rehlds/common/mathlib.h | 24 + rehlds/engine/mathlib.cpp | 986 +++++++++-------------------- rehlds/engine/mathlib_e.h | 61 +- rehlds/engine/mathlib_sse.cpp | 375 +++++++++++ rehlds/engine/mathlib_sse.h | 74 +++ rehlds/engine/sse_mathfun.h | 2 +- rehlds/msvc/ReHLDS.vcxproj | 6 +- rehlds/msvc/ReHLDS.vcxproj.filters | 6 + rehlds/public/rehlds/crc32c.cpp | 9 + rehlds/public/rehlds/crc32c.h | 2 + rehlds/public/rehlds/static_map.h | 7 +- 12 files changed, 812 insertions(+), 744 deletions(-) create mode 100644 rehlds/engine/mathlib_sse.cpp create mode 100644 rehlds/engine/mathlib_sse.h diff --git a/rehlds/build.gradle b/rehlds/build.gradle index b5cabf7..dafa2b9 100644 --- a/rehlds/build.gradle +++ b/rehlds/build.gradle @@ -184,11 +184,11 @@ void setupToolchain(NativeBinarySpec b) { } if (unitTestExecutable) { - cfg.singleDefines 'REHLDS_UNIT_TESTS' + cfg.singleDefines 'REHLDS_UNIT_TESTS', 'REHLDS_SSE' } if (rehldsFixes) { - cfg.singleDefines 'REHLDS_FIXES', 'REHLDS_CHECKS', 'HAVE_OPT_STRTOOLS' + cfg.singleDefines 'REHLDS_FIXES', 'REHLDS_SSE', 'REHLDS_CHECKS', 'HAVE_OPT_STRTOOLS' } ToolchainConfigUtils.apply(project, cfg, b) diff --git a/rehlds/common/mathlib.h b/rehlds/common/mathlib.h index 83affe8..545ecb3 100644 --- a/rehlds/common/mathlib.h +++ b/rehlds/common/mathlib.h @@ -82,6 +82,30 @@ inline T clamp(T a, T min, T max) return (a > max) ? max : (a < min) ? min : a; } +template +inline T M_min(T a, T b) +{ + return min(a, b); +} + +template +inline T M_max(T a, T b) +{ + return max(a, b); +} + +template +inline T M_clamp(T a, T min, T max) +{ + return clamp(a, min, max); +} + +template +inline double M_sqrt(T value) +{ + return sqrt(value); +} + template inline T bswap(T s) { diff --git a/rehlds/engine/mathlib.cpp b/rehlds/engine/mathlib.cpp index 8b81108..4647b97 100644 --- a/rehlds/engine/mathlib.cpp +++ b/rehlds/engine/mathlib.cpp @@ -28,91 +28,15 @@ #include "precompiled.h" -// Intrisics guide: https://software.intel.com/sites/landingpage/IntrinsicsGuide/ -// Shufps calculator: http://wurstcaptures.untergrund.net/assembler_tricks.html - vec3_t vec3_origin; //int nanmask; //short int new_cw; //short int old_cw; //DLONG dlong; -// aligned vec4_t -typedef ALIGN16 vec4_t avec4_t; -typedef ALIGN16 unsigned int aivec4_t[4]; - -// conversion multiplier -const avec4_t deg2rad = -{ - (float)M_PI / 180.f, - (float)M_PI / 180.f, - (float)M_PI / 180.f, - (float)M_PI / 180.f -}; - -const aivec4_t negmask[4] = -{ - 0x80000000, - 0x80000000, - 0x80000000, - 0x80000000 -}; - -const aivec4_t negmask_1001 = -{ - 0x80000000, - 0, - 0, - 0x80000000 -}; - -const aivec4_t negmask_0010 = -{ - 0, - 0, - 0x80000000, - 0 -}; - -// save 4d xmm to 3d vector. we can't optimize many simple vector3 functions because saving back to 3d is slow. -inline void xmm2vec(vec_t *v, const __m128 m) -{ - _mm_storel_pi((__m64*)v, m); - _mm_store_ss(v + 2, _mm_shuffle_ps(m, m, 0x02)); -} - -FUNC_TARGET("sse4.1") -inline __m128 dotProduct3D(__m128 v1, __m128 v2) -{ - if (cpuinfo.sse4_1) - return _mm_dp_ps(v1, v2, 0x71); - __m128 v = _mm_mul_ps(v1, v2); - return _mm_add_ps(_mm_movehl_ps(v, v), _mm_hadd_ps(v, v)); // SSE3 -} - -inline __m128 crossProduct3D(__m128 a, __m128 b) -{ - __m128 tmp1 = _mm_mul_ps(a, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1))); - __m128 tmp2 = _mm_mul_ps(b, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1))); - __m128 m = _mm_sub_ps(tmp1, tmp2); - - return _mm_shuffle_ps(m, m, _MM_SHUFFLE(3, 0, 2, 1)); -} - -inline __m128 length3D(__m128 v) -{ - return _mm_sqrt_ps(dotProduct3D(v, v)); -} - -inline __m128 length2D(__m128 v) -{ - v = _mm_mul_ps(v, v); - return _mm_sqrt_ps(_mm_hadd_ps(v, v)); // hadd = SSE3 -} - float anglemod(float a) { - return (360.0 / 65536) * ((int)(a*(65536 / 360.0)) & 65535); + return (360.0 / 65536) * ((int)(a * (65536 / 360.0)) & 65535); } void BOPS_Error(void) @@ -120,91 +44,317 @@ void BOPS_Error(void) Sys_Error("%s: Bad signbits", __func__); } -#ifdef REHLDS_OPT_PEDANTIC -int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) +NOBODY int InvertMatrix(const float *m, float *out); +//{ +// float wtmp; +// float m0; +// float m1; +// float m2; +// float m3; +// float s; +// float *r0; +// float *r1; +// float *r2; +// float *r3; +//} + +void EXT_FUNC AngleVectors_ext(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) { - double dist1, dist2; - int sides = 0; + AngleVectors(angles, forward, right, up); +} - __m128 emin = _mm_loadu_ps(emins); - __m128 emax = _mm_loadu_ps(emaxs); - avec4_t d1, d2; +NOBODY void AngleIMatrix(const vec_t *angles, float *matrix); +//{ +// float angle; +// float sr; +// float sp; +// float sy; +// float cr; +// float cp; +// float cy; +//} - // general case - switch (p->signbits) +NOBODY void NormalizeAngles(float *angles); +//{ +// int i; +//} + +NOBODY void InterpolateAngles(float *start, float *end, float *output, float frac); +//{ +// int i; +// float ang1; +// float ang2; +// float d; +// NormalizeAngles(float *angles); +// NormalizeAngles(float *angles); +// NormalizeAngles(float *angles); +//} + +void VectorTransform(const vec_t *in1, float (*in2)[4], vec_t *out) +{ + out[0] = _DotProduct(in1, in2[0]) + in2[0][3]; + out[1] = _DotProduct(in1, in2[1]) + in2[1][3]; + out[2] = _DotProduct(in1, in2[2]) + in2[2][3]; +} + +NOBODY void _VectorSubtract(vec_t *veca, vec_t *vecb, vec_t *out); +//{ +//} + +void _VectorAdd(vec_t *veca, vec_t *vecb, vec_t *out) +{ + out[0] = veca[0] + vecb[0]; + out[1] = veca[1] + vecb[1]; + out[2] = veca[2] + vecb[2]; +} + +NOBODY void _VectorCopy(vec_t *in, vec_t *out); +//{ +//} + +float VectorNormalize(vec3_t v) +{ + float length, ilength; + length = Length(v); + + const int res = clamp(0.0, 1.0, 2.0); + + if (length) { - case 0: - _mm_store_ps(d1, emax); - _mm_store_ps(d2, emin); - break; - case 1: - _mm_store_ps(d1, emax); - _mm_store_ps(d2, emin); - d1[0] = emins[0]; - d2[0] = emaxs[0]; - break; - case 2: - _mm_store_ps(d1, emax); - _mm_store_ps(d2, emin); - d1[1] = emins[1]; - d2[1] = emaxs[1]; - break; - case 3: - _mm_store_ps(d1, emin); - _mm_store_ps(d2, emax); - d1[2] = emaxs[2]; - d2[2] = emins[2]; - break; - case 4: - _mm_store_ps(d1, emax); - _mm_store_ps(d2, emin); - d1[2] = emins[2]; - d2[2] = emaxs[2]; - break; - case 5: - _mm_store_ps(d1, emin); - _mm_store_ps(d2, emax); - d1[1] = emaxs[1]; - d2[1] = emins[1]; - break; - case 6: - _mm_store_ps(d1, emin); - _mm_store_ps(d2, emax); - d1[0] = emaxs[0]; - d2[0] = emins[0]; - break; - case 7: - _mm_store_ps(d1, emin); - _mm_store_ps(d2, emax); - break; - default: - BOPS_Error(); - dist1 = dist2 = 0.0; - break; + ilength = 1 / length; + v[0] *= ilength; + v[1] *= ilength; + v[2] *= ilength; } - dist1 = _DotProduct(p->normal, d1); - dist2 = _DotProduct(p->normal, d2); - - if (dist1 >= p->dist) - sides = 1; - if (dist2 < p->dist) - sides |= 2; - - return sides; + return length; } + +NOBODY void VectorInverse(vec_t *v); +//{ +//} + +NOBODY int Q_log2(int val); +//{ +// int answer; +//} + +NOBODY void VectorMatrix(vec_t *forward, vec_t *right, vec_t *up); +//{ +// vec3_t tmp; +// CrossProduct(const vec_t *v1, const vec_t *v2, vec_t *cross) +// VectorNormalize(vec_t *v); +// CrossProduct(const vec_t *v1, const vec_t *v2, vec_t *cross); +// VectorNormalize(vec_t *v); +//} + +void VectorAngles(const vec_t *forward, vec_t *angles) +{ + float length, yaw, pitch; + + if (forward[1] == 0 && forward[0] == 0) + { + yaw = 0; + if (forward[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (atan2((double)forward[1], (double)forward[0]) * 180.0 / M_PI); + if (yaw < 0) + yaw += 360; + +#ifdef REHLDS_SSE + length = Length2D(forward); #else + length = Q_sqrt((double)(forward[0] * forward[0] + forward[1] * forward[1])); +#endif // REHLDS_SSE + + pitch = atan2((double)forward[2], (double)length) * 180.0 / M_PI; + if (pitch < 0) + pitch += 360; + } + + angles[0] = pitch; + angles[1] = yaw; + angles[2] = 0; +} + +NOBODY void R_ConcatRotations(float *in1, float *in2, float *out); +//{ +// +//} + +NOBODY void FloorDivMod(double numer, double denom, int *quotient, int *rem); +//{ +// int q; +// int r; +// double x; +// floor(double __x); +// floor(double __x); +//} + +NOBODY int GreatestCommonDivisor(int i1, int i2); +//{ +//} + +NOBODY fixed16_t Invert24To16(fixed16_t val); +//{ +//} + +#if !defined(REHLDS_SSE) +void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +{ + float sr, sp, sy, cr, cp, cy; + +#ifndef SWDS + g_engdstAddrs.pfnAngleVectors(&angles, &forward, &right, &up); +#endif // SWDS + + float angle; + angle = (float)(angles[YAW] * (M_PI * 2 / 360)); + sy = sin(angle); + cy = cos(angle); + angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); + sp = sin(angle); + cp = cos(angle); + angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); + sr = sin(angle); + cr = cos(angle); + + if (forward) + { + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + } + if (right) + { + right[0] = (-1 * sr*sp*cy + -1 * cr*-sy); + right[1] = (-1 * sr*sp*sy + -1 * cr*cy); + right[2] = -1 * sr*cp; + } + if (up) + { + up[0] = (cr*sp*cy + -sr*-sy); + up[1] = (cr*sp*sy + -sr*cy); + up[2] = cr*cp; + } +} + +void AngleVectorsTranspose(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +{ + float sr, sp, sy, cr, cp, cy; + + float angle; + angle = (float)(angles[YAW] * (M_PI * 2 / 360)); + sy = sin(angle); + cy = cos(angle); + angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); + sp = sin(angle); + cp = cos(angle); + angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); + sr = sin(angle); + cr = cos(angle); + + if (forward) + { + forward[0] = cp*cy; + forward[1] = (sr*sp*cy + cr*-sy); + forward[2] = (cr*sp*cy + -sr*-sy); + } + if (right) + { + right[0] = cp*sy; + right[1] = (sr*sp*sy + cr*cy); + right[2] = (cr*sp*sy + -sr*cy); + } + if (up) + { + up[0] = -sp; + up[1] = sr*cp; + up[2] = cr*cp; + } +} + +void AngleMatrix(const vec_t *angles, float(*matrix)[4]) +{ + float sr, sp, sy, cr, cp, cy; + + float angle; + angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); + sy = sin(angle); + cy = cos(angle); + angle = (float)(angles[YAW] * (M_PI * 2 / 360)); + sp = sin(angle); + cp = cos(angle); + angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); + sr = sin(angle); + cr = cos(angle); + + matrix[0][0] = cr * cp; + matrix[0][1] = sy * sr * cp - cy * sp; + matrix[0][2] = cy * sr * cp + sy * sp; + matrix[0][3] = 0.0f; + + matrix[1][0] = cr * sp; + matrix[1][1] = sy * sr * sp + cy * cp; + matrix[1][2] = cy * sr * sp - sy * cp; + matrix[1][3] = 0.0f; + + matrix[2][0] = -sr; + matrix[2][1] = sy * cr; + matrix[2][2] = cy * cr; + matrix[2][3] = 0.0f; +} + +void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out) +{ + out[0] = scale * vecm[0] + veca[0]; + out[1] = scale * vecm[1] + veca[1]; + out[2] = scale * vecm[2] + veca[2]; +} + +real_t _DotProduct(const vec_t *v1, const vec_t *v2) +{ + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +void CrossProduct(const vec_t *v1, const vec_t *v2, vec_t *cross) +{ + cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; + cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; + cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; +} + +void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; +} + +float Length(const vec_t *v) +{ + return Q_sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) { -#if (1) // Engine actual types - double dist1, dist2; -#else - // From sources - float dist1, dist2; -#endif - int sides = 0; + real_t dist1, dist2; + int sides = 0; // general case switch (p->signbits) @@ -249,546 +399,22 @@ int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) if (dist1 >= p->dist) sides = 1; + if (dist2 < p->dist) sides |= 2; return sides; } -#endif -NOBODY int InvertMatrix(const float *m, float *out); -//{ -// float wtmp; // 159 -// float m0; // 160 -// float m1; // 160 -// float m2; // 160 -// float m3; // 160 -// float s; // 160 -// float *r0; // 161 -// float *r1; // 161 -// float *r2; // 161 -// float *r3; // 161 -//} - -void EXT_FUNC AngleVectors_ext(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) { - AngleVectors(angles, forward, right, up); -} - -#ifdef REHLDS_FIXES -// parallel SSE version -void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +qboolean VectorCompare(const vec_t *v1, const vec_t *v2) { -#ifndef SWDS - g_engdstAddrs.pfnAngleVectors(&angles, &forward, &right, &up); -#endif // SWDS - - __m128 s, c; - sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); - - __m128 m1 = _mm_shuffle_ps(c, s, 0x90); // [cp][cp][sy][sr] - __m128 m2 = _mm_shuffle_ps(c, c, 0x09); // [cy][cr][cp][cp] - __m128 cp_mults = _mm_mul_ps(m1, m2); // [cp * cy][cp * cr][cp * sy][cp * sr]; - - m1 = _mm_shuffle_ps(c, s, 0x15); // [cy][cy][sy][sp] - m2 = _mm_shuffle_ps(s, c, 0xA0); // [sp][sp][cr][cr] - m1 = _mm_shuffle_ps(m1, m1, 0xC8); // [cy][sy][cy][sp] - - __m128 m3 = _mm_shuffle_ps(s, s, 0x4A); // [sr][sr][sp][sy]; - m3 = _mm_mul_ps(m3, _mm_mul_ps(m1, m2)); // [sp*cy*sr][sp*sy*sr][cr*cy*sp][cr*sp*sy] - - m2 = _mm_shuffle_ps(s, c, 0x65); // [sy][sy][cr][cy] - m1 = _mm_shuffle_ps(c, s, 0xA6); // [cr][cy][sr][sr] - m2 = _mm_shuffle_ps(m2, m2, 0xD8); // [sy][cr][sy][cy] - m1 = _mm_xor_ps(m1, _mm_load_ps((float *)&negmask_1001)); // [-cr][cy][sr][-sr] - m1 = _mm_mul_ps(m1, m2); // [-cr*sy][cy*cr][sr*sy][-sr*cy] - - m3 = _mm_add_ps(m3, m1); - - if (forward) - { - _mm_storel_pi((__m64 *)forward, _mm_shuffle_ps(cp_mults, cp_mults, 0x08)); - forward[2] = -_mm_cvtss_f32(s); - } - if (right) - { - __m128 r = _mm_shuffle_ps(m3, cp_mults, 0xF4); // [m3(0)][m3(1)][cp(3)][cp(3)] - xmm2vec(right, _mm_xor_ps(r, _mm_load_ps((float *)&negmask))); - } - if (up) - { - _mm_storel_pi((__m64 *)up, _mm_shuffle_ps(m3, m3, 0x0E)); - up[2] = _mm_cvtss_f32(_mm_shuffle_ps(cp_mults, cp_mults, 0x01)); - } -} -#else // REHLDS_FIXES -void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) -{ - float sr, sp, sy, cr, cp, cy; - -#ifndef SWDS - g_engdstAddrs.pfnAngleVectors(&angles, &forward, &right, &up); -#endif // SWDS - - float angle; - angle = (float)(angles[YAW] * (M_PI * 2 / 360)); - sy = sin(angle); - cy = cos(angle); - angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); - sp = sin(angle); - cp = cos(angle); - angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); - sr = sin(angle); - cr = cos(angle); - - if (forward) - { - forward[0] = cp*cy; - forward[1] = cp*sy; - forward[2] = -sp; - } - if (right) - { - right[0] = (-1 * sr*sp*cy + -1 * cr*-sy); - right[1] = (-1 * sr*sp*sy + -1 * cr*cy); - right[2] = -1 * sr*cp; - } - if (up) - { - up[0] = (cr*sp*cy + -sr*-sy); - up[1] = (cr*sp*sy + -sr*cy); - up[2] = cr*cp; - } -} -#endif // REHLDS_FIXES - -#ifdef REHLDS_FIXES -// parallel SSE version -void AngleVectorsTranspose(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) -{ - __m128 s, c; - sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); - - __m128 m1 = _mm_shuffle_ps(c, s, 0x90); // [cp][cp][sy][sr] - __m128 m2 = _mm_shuffle_ps(c, c, 0x09); // [cy][cr][cp][cp] - __m128 cp_mults = _mm_mul_ps(m1, m2); // [cp * cy][cp * cr][cp * sy][cp * sr]; - - m1 = _mm_shuffle_ps(s, s, 0x50); // [sp][sp][sy][sy] - m2 = _mm_shuffle_ps(c, s, 0x05); // [cy][cy][sp][sp] - - __m128 m3 = _mm_shuffle_ps(s, c, 0xAA); // [sr][sr][cr][cr] - m1 = _mm_mul_ps(m1, m2); - m3 = _mm_shuffle_ps(m3, m3, 0xD8); // [sr][cr][sr][cr] - m3 = _mm_mul_ps(m3, m1); // [sp*cy*sr][sp*cy*cr][sy*sp*sr][sy*sp*cr] - - m2 = _mm_shuffle_ps(c, s, 0xA6); // [cr][cy][sr][sr] - m1 = _mm_shuffle_ps(s, c, 0x65); // [sy][sy][cr][cy] - m2 = _mm_shuffle_ps(m2, m2, 0xD8); // [cr][sr][cy][sr] - m1 = _mm_xor_ps(m1, _mm_load_ps((float *)&negmask_1001)); // [-cr][cy][sr][-sr] - m1 = _mm_mul_ps(m1, m2); // [-cr*sy][sr*sy][cy*cr][-sr*cy] - - m3 = _mm_add_ps(m3, m1); - - if (forward) - { - forward[0] = _mm_cvtss_f32(cp_mults); - _mm_storel_pi((__m64*)(forward + 1), m3); // (sr*sp*cy + cr*-sy); - } - if (right) - { - right[0] = _mm_cvtss_f32(_mm_shuffle_ps(cp_mults, cp_mults, 0x02)); - _mm_storel_pi((__m64*)(right + 1), _mm_shuffle_ps(m3, m3, 0x0E)); - } - if (up) - { - up[0] = -_mm_cvtss_f32(s); - _mm_storel_pi((__m64 *)&up[1], _mm_shuffle_ps(cp_mults, cp_mults, 0x07)); - } -} -#else // REHLDS_FIXES -void AngleVectorsTranspose(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) -{ - float sr, sp, sy, cr, cp, cy; - - float angle; - angle = (float)(angles[YAW] * (M_PI * 2 / 360)); - sy = sin(angle); - cy = cos(angle); - angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); - sp = sin(angle); - cp = cos(angle); - angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); - sr = sin(angle); - cr = cos(angle); - - if (forward) - { - forward[0] = cp*cy; - forward[1] = (sr*sp*cy + cr*-sy); - forward[2] = (cr*sp*cy + -sr*-sy); - } - if (right) - { - right[0] = cp*sy; - right[1] = (sr*sp*sy + cr*cy); - right[2] = (cr*sp*sy + -sr*cy); - } - if (up) - { - up[0] = -sp; - up[1] = sr*cp; - up[2] = cr*cp; - } -} -#endif - -#ifdef REHLDS_FIXES -// parallel SSE version -void AngleMatrix(const vec_t *angles, float(*matrix)[4]) -{ - __m128 s, c; - sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); - - /* - matrix[0][1] = sr * sp * cy - cr * sy; - matrix[1][1] = sr * sp * sy + cr * cy; - matrix[0][2] = cr * sp * cy + sr * sy; - matrix[1][2] = cr * sp * sy - sr * cy; - */ - __m128 m1; - __m128 m2 = _mm_shuffle_ps(s, c, 0x00); // [sp][sp][cp][cp] - __m128 m3 = _mm_shuffle_ps(c, s, 0x55); // [cy][cy][sy][sy] - - m1 = _mm_shuffle_ps(s, c, 0xAA); // [sr][sr][cr][cr] - m2 = _mm_shuffle_ps(m2, m2, 0x00); // [sp][sp][sp][sp] - m3 = _mm_shuffle_ps(m3, m3, 0xD8); // [cy][sy][cy][sy] - - m2 = _mm_mul_ps(m2, _mm_mul_ps(m1, m3)); // m1*m2*m3 - - m1 = _mm_shuffle_ps(m1, m1, 0x1B); // [cr][cr][sr][sr] - m3 = _mm_shuffle_ps(m3, m3, 0xB1); // [sy][cy][sy][cy] - m3 = _mm_xor_ps(m3, _mm_load_ps((float *)&negmask_1001)); - m3 = _mm_mul_ps(m3, m1); - - m2 = _mm_add_ps(m2, m3); - - /* - matrix[0][0] = cp * cy; - matrix[1][0] = cp * sy; - matrix[2][1] = sr * cp; - matrix[2][2] = cr * cp; - */ - m1 = _mm_shuffle_ps(s, c, 0x29); // [sy][sr][cr][cp] - c = _mm_shuffle_ps(c, c, 0x40); // [cp][cp][cp][cy] - m1 = _mm_mul_ps(m1, c); - - // matrix[0] - m3 = _mm_shuffle_ps(m2, m2, 0xE1); - _mm_storeu_ps(&matrix[0][0], m3); - matrix[0][0] = _mm_cvtss_f32(_mm_shuffle_ps(m1, m1, 0x03)); - *(int *)&matrix[0][3] = 0; - - // matrix[1] - m2 = _mm_shuffle_ps(m2, m2, 0xB4); - _mm_storeu_ps(&matrix[1][0], m2); - matrix[1][0] = _mm_cvtss_f32(m1); - *(int *)&matrix[1][3] = 0; - - // matrix[2] - _mm_storeu_ps(&matrix[2][0], m1); - matrix[2][0] = -_mm_cvtss_f32(s); - *(int *)&matrix[2][3] = 0; -} -#else // REHLDS_FIXES -void AngleMatrix(const vec_t *angles, float(*matrix)[4]) -{ - float sr, sp, sy, cr, cp, cy; - - float angle; - angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); - sy = sin(angle); - cy = cos(angle); - angle = (float)(angles[YAW] * (M_PI * 2 / 360)); - sp = sin(angle); - cp = cos(angle); - angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); - sr = sin(angle); - cr = cos(angle); - - matrix[0][0] = cr * cp; - matrix[0][1] = sy * sr * cp - cy * sp; - matrix[0][2] = cy * sr * cp + sy * sp; - matrix[0][3] = 0.0f; - - matrix[1][0] = cr * sp; - matrix[1][1] = sy * sr * sp + cy * cp; - matrix[1][2] = cy * sr * sp - sy * cp; - matrix[1][3] = 0.0f; - - matrix[2][0] = -sr; - matrix[2][1] = sy * cr; - matrix[2][2] = cy * cr; - matrix[2][3] = 0.0f; -} -#endif // REHLDS_FIXES - -NOBODY void AngleIMatrix(const vec_t *angles, float *matrix); -//{ -// float angle; // 372 -// float sr; // 373 -// float sp; // 373 -// float sy; // 373 -// float cr; // 373 -// float cp; // 373 -// float cy; // 373 -//} - -NOBODY void NormalizeAngles(float *angles); -//{ -// int i; // 402 -//} - -NOBODY void InterpolateAngles(float *start, float *end, float *output, float frac); -//{ -// int i; // 428 -// float ang1; // 429 -// float ang2; // 429 -// float d; // 430 -// NormalizeAngles(float *angles); // 432 -// NormalizeAngles(float *angles); // 433 -// NormalizeAngles(float *angles); // 453 -//} - -void VectorTransform(const vec_t *in1, float (*in2)[4], vec_t *out) -{ - out[0] = _DotProduct(in1, in2[0]) + in2[0][3]; - out[1] = _DotProduct(in1, in2[1]) + in2[1][3]; - out[2] = _DotProduct(in1, in2[2]) + in2[2][3]; -} - -int VectorCompare(const vec_t *v1, const vec_t *v2) -{ -#ifdef REHLDS_OPT_PEDANTIC - __m128 cmp = _mm_cmpneq_ps(_mm_loadu_ps(v1), _mm_loadu_ps(v2)); - return !(_mm_movemask_ps(cmp) & (1|2|4)); -#else // REHLDS_OPT_PEDANTIC for (int i = 0; i < 3; i++) { - if (v1[i] != v2[i]) return 0; + if (v1[i] != v2[i]) + return FALSE; } - return 1; -#endif // REHLDS_OPT_PEDANTIC + return TRUE; } -#ifdef REHLDS_FIXES -void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out) -{ - xmm2vec(out, _mm_add_ps(_mm_mul_ps(_mm_set_ps1(scale), _mm_loadu_ps(vecm)), _mm_loadu_ps(veca))); -} -#else -void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out) -{ - out[0] = scale * vecm[0] + veca[0]; - out[1] = scale * vecm[1] + veca[1]; - out[2] = scale * vecm[2] + veca[2]; -} -#endif - -#ifndef REHLDS_FIXES -real_t _DotProduct(const vec_t *v1, const vec_t *v2) -{ - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; -} -#else // REHLDS_FIXES -float _DotProduct(const vec_t *v1, const vec_t *v2) -{ - // _mm_loadu_ps - load xmm from unaligned address - // _mm_cvtss_f32 - return low float value of xmm - // _mm_dp_ps - dot product - // 0x71 = 0b01110001 - mask for multiplying operands and result - // dpps isn't binary compatible with separate sse2 instructions (max difference is about 0.0002f, but usually < 0.00001f) - - return _mm_cvtss_f32(dotProduct3D(_mm_loadu_ps(v1), _mm_loadu_ps(v2))); -} -#endif // REHLDS_FIXES - -NOBODY void _VectorSubtract(vec_t *veca, vec_t *vecb, vec_t *out); -//{ -//} - -void _VectorAdd(vec_t *veca, vec_t *vecb, vec_t *out) -{ - out[0] = veca[0] + vecb[0]; - out[1] = veca[1] + vecb[1]; - out[2] = veca[2] + vecb[2]; -} - -NOBODY void _VectorCopy(vec_t *in, vec_t *out); -//{ -//} - -void CrossProduct(const vec_t *v1, const vec_t *v2, vec_t *cross) -{ -#ifdef REHLDS_FIXES - xmm2vec(cross, crossProduct3D(_mm_loadu_ps(v1), _mm_loadu_ps(v2))); -#else // REHLDS_FIXES - cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; - cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; - cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; -#endif // REHLDS_FIXES -} - -float Length(const vec_t *v) -{ -#ifdef REHLDS_FIXES - return _mm_cvtss_f32(length3D(_mm_loadu_ps(v))); -#endif // REHLDS_FIXES - - float length; - - length = 0.0f; - for (int i = 0; i < 3; i++) - { - length = v[i] * v[i] + length; - } - return Q_sqrt(length); -} - -float Length2D(const vec_t *v) -{ -#ifdef REHLDS_FIXES - return _mm_cvtss_f32(length2D(_mm_loadu_ps(v))); -#endif // REHLDS_FIXES - - return Q_sqrt(v[0] * v[0] + v[1] * v[1]); -} - -float VectorNormalize(vec3_t v) -{ - float length, ilength; - -#ifdef REHLDS_FIXES - length = Length(v); // rsqrt is very inaccurate :( -#else // REHLDS_FIXES - length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; - length = Q_sqrt(length); -#endif // REHLDS_FIXES - - if (length) - { - ilength = 1 / length; - v[0] *= ilength; - v[1] *= ilength; - v[2] *= ilength; - } - - return length; -} - -NOBODY void VectorInverse(vec_t *v); -//{ -//} - -NOBODY int Q_log2(int val); -//{ -// int answer; // 568 -//} - -NOBODY void VectorMatrix(vec_t *forward, vec_t *right, vec_t *up); -//{ -// vec3_t tmp; // 576 -// CrossProduct(const vec_t *v1, -// const vec_t *v2, -// vec_t *cross); /* size=0, low_pc=0 */ // 590 -// VectorNormalize(vec_t *v); /* size=0, low_pc=0 */ // 591 -// CrossProduct(const vec_t *v1, -// const vec_t *v2, -// vec_t *cross); /* size=0, low_pc=0 */ // 592 -// VectorNormalize(vec_t *v); /* size=0, low_pc=0 */ // 593 -//} - -void VectorAngles(const vec_t *forward, vec_t *angles) -{ - float length, yaw, pitch; - - if (forward[1] == 0 && forward[0] == 0) - { - yaw = 0; - if (forward[2] > 0) - pitch = 90; - else - pitch = 270; - } - else - { - yaw = (atan2((double)forward[1], (double)forward[0]) * 180.0 / M_PI); - if (yaw < 0) - yaw += 360; - -#ifdef REHLDS_FIXES - length = Length2D(forward); -#else // REHLDS_FIXES - length = Q_sqrt((double)(forward[0] * forward[0] + forward[1] * forward[1])); -#endif // REHLDS_FIXES - - pitch = atan2((double)forward[2], (double)length) * 180.0 / M_PI; - if (pitch < 0) - pitch += 360; - } - - angles[0] = pitch; - angles[1] = yaw; - angles[2] = 0; -} - -NOBODY void R_ConcatRotations(float *in1, float *in2, float *out); -//{ -// -//} - -#ifdef REHLDS_FIXES -void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) -{ - for (size_t i = 0; i < 3; i++) - { - __m128 a1 = _mm_mul_ps(_mm_set_ps1(in1[i][0]), _mm_loadu_ps(in2[0])); - __m128 a2 = _mm_mul_ps(_mm_set_ps1(in1[i][1]), _mm_loadu_ps(in2[1])); - __m128 a3 = _mm_mul_ps(_mm_set_ps1(in1[i][2]), _mm_loadu_ps(in2[2])); - _mm_storeu_ps(out[i], _mm_add_ps(a1, _mm_add_ps(a2, a3))); - out[i][3] += in1[i][3]; - } -} -#else // REHLDS_FIXES -void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) -{ - out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; - out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; - out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; - out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; - out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; - out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; - out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; - out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; - out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; - out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; - out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; - out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; -} -#endif // REHLDS_FIXES - -NOBODY void FloorDivMod(double numer, double denom, int *quotient, int *rem); -//{ -// int q; // 702 -// int r; // 702 -// double x; // 703 -// floor(double __x); /* size=0, low_pc=0 */ // 726 -// floor(double __x); /* size=0, low_pc=0 */ // 717 -//} - -NOBODY int GreatestCommonDivisor(int i1, int i2); -//{ -//} - -NOBODY fixed16_t Invert24To16(fixed16_t val); -//{ -//} +#endif // #if !defined(REHLDS_SSE) diff --git a/rehlds/engine/mathlib_e.h b/rehlds/engine/mathlib_e.h index 7bfcb2a..77f69ee 100644 --- a/rehlds/engine/mathlib_e.h +++ b/rehlds/engine/mathlib_e.h @@ -30,9 +30,10 @@ #include "maintypes.h" #include "model.h" +#include "mathlib_sse.h" -#ifndef REHLDS_FIXES -// NOTE: In some cases we need high precision of floating-point, +#if !defined(REHLDS_FIXES) && !defined(REHLDS_SSE) +// NOTE: In some cases we need high precision of floating-point, // so use double instead of float, otherwise unittest will fail typedef double real_t; #else @@ -77,59 +78,6 @@ static const int nanmask = 0x7F800000; #define IS_NAN(fvar) ((*reinterpret_cast(&(fvar)) & nanmask) == nanmask) -inline float M_sqrt(float value) { - return _mm_cvtss_f32(_mm_sqrt_ss(_mm_load_ss(&value))); -} - -inline double M_sqrt(double value) { - auto v = _mm_load_sd(&value); - return _mm_cvtsd_f64(_mm_sqrt_sd(v, v)); -} - -template -inline double M_sqrt(T value) { - return sqrt(value); -} - -inline float M_min(float a, float b) { - return _mm_cvtss_f32(_mm_min_ss(_mm_load_ss(&a), _mm_load_ss(&b))); -} - -inline double M_min(double a, double b) { - return _mm_cvtsd_f64(_mm_min_sd(_mm_load_sd(&a), _mm_load_sd(&b))); -} - -template -inline T M_min(T a, T b) { - return min(a, b); -} - -inline float M_max(float a, float b) { - return _mm_cvtss_f32(_mm_max_ss(_mm_load_ss(&a), _mm_load_ss(&b))); -} - -inline double M_max(double a, double b) { - return _mm_cvtsd_f64(_mm_max_sd(_mm_load_sd(&a), _mm_load_sd(&b))); -} - -template -inline T M_max(T a, T b) { - return max(a, b); -} - -inline float M_clamp(float a, float min, float max) { - return _mm_cvtss_f32(_mm_min_ss(_mm_max_ss(_mm_load_ss(&a), _mm_load_ss(&min)), _mm_load_ss(&max))); -} - -inline double M_clamp(double a, double min, double max) { - return _mm_cvtsd_f64(_mm_min_sd(_mm_max_sd(_mm_load_sd(&a), _mm_load_sd(&min)), _mm_load_sd(&max))); -} - -template -inline T M_clamp(T a, T min, T max) { - return clamp(a, min, max); -} - inline void VectorAdd(const vec_t *veca, const vec_t *vecb, vec_t *out) { out[0] = veca[0] + vecb[0]; @@ -208,7 +156,7 @@ NOBODY void AngleIMatrix(const vec_t *angles, float *matrix); NOBODY void NormalizeAngles(float *angles); NOBODY void InterpolateAngles(float *start, float *end, float *output, float frac); void VectorTransform(const vec_t *in1, float (*in2)[4], vec_t *out); -int VectorCompare(const vec_t *v1, const vec_t *v2); +qboolean VectorCompare(const vec_t *v1, const vec_t *v2); void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out); real_t _DotProduct(const vec_t *v1, const vec_t *v2); NOBODY void _VectorSubtract(vec_t *veca, vec_t *vecb, vec_t *out); @@ -219,7 +167,6 @@ float Length(const vec_t *v); float Length2D(const vec_t *v); float VectorNormalize(vec_t *v); NOBODY void VectorInverse(vec_t *v); -void VectorScale(const vec_t *in, float scale, vec_t *out); NOBODY int Q_log2(int val); NOBODY void VectorMatrix(vec_t *forward, vec_t *right, vec_t *up); void VectorAngles(const vec_t *forward, vec_t *angles); diff --git a/rehlds/engine/mathlib_sse.cpp b/rehlds/engine/mathlib_sse.cpp new file mode 100644 index 0000000..e99d85a --- /dev/null +++ b/rehlds/engine/mathlib_sse.cpp @@ -0,0 +1,375 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#if defined(REHLDS_SSE) + +// Intrisics guide: https://software.intel.com/sites/landingpage/IntrinsicsGuide/ +// Shufps calculator: http://wurstcaptures.untergrund.net/assembler_tricks.html + +// aligned vec4_t +typedef ALIGN16 vec4_t avec4_t; +typedef ALIGN16 unsigned int aivec4_t[4]; + +// conversion multiplier +const avec4_t deg2rad = +{ + (float)M_PI / 180.f, + (float)M_PI / 180.f, + (float)M_PI / 180.f, + (float)M_PI / 180.f +}; + +const aivec4_t negmask[4] = +{ + 0x80000000, + 0x80000000, + 0x80000000, + 0x80000000 +}; + +const aivec4_t negmask_1001 = +{ + 0x80000000, + 0, + 0, + 0x80000000 +}; + +const aivec4_t negmask_0010 = +{ + 0, + 0, + 0x80000000, + 0 +}; + +// save 4d xmm to 3d vector. we can't optimize many simple vector3 functions because saving back to 3d is slow. +inline void xmm2vec(vec_t *v, const __m128 m) +{ + _mm_storel_pi((__m64*)v, m); + _mm_store_ss(v + 2, _mm_shuffle_ps(m, m, 0x02)); +} + +FUNC_TARGET("sse4.1") +inline __m128 dotProduct3D(__m128 v1, __m128 v2) +{ + if (cpuinfo.sse4_1) + return _mm_dp_ps(v1, v2, 0x71); + __m128 v = _mm_mul_ps(v1, v2); + return _mm_add_ps(_mm_movehl_ps(v, v), _mm_hadd_ps(v, v)); // SSE3 +} + +inline __m128 crossProduct3D(__m128 a, __m128 b) +{ + __m128 tmp1 = _mm_mul_ps(a, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1))); + __m128 tmp2 = _mm_mul_ps(b, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1))); + __m128 m = _mm_sub_ps(tmp1, tmp2); + + return _mm_shuffle_ps(m, m, _MM_SHUFFLE(3, 0, 2, 1)); +} + +inline __m128 length3D(__m128 v) +{ + return _mm_sqrt_ps(dotProduct3D(v, v)); +} + +inline __m128 length2D(__m128 v) +{ + v = _mm_mul_ps(v, v); + return _mm_sqrt_ps(_mm_hadd_ps(v, v)); // hadd = SSE3 +} + +int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) +{ + double dist1, dist2; + int sides = 0; + + __m128 emin = _mm_loadu_ps(emins); + __m128 emax = _mm_loadu_ps(emaxs); + avec4_t d1, d2; + + // general case + switch (p->signbits) + { + case 0: + _mm_store_ps(d1, emax); + _mm_store_ps(d2, emin); + break; + case 1: + _mm_store_ps(d1, emax); + _mm_store_ps(d2, emin); + d1[0] = emins[0]; + d2[0] = emaxs[0]; + break; + case 2: + _mm_store_ps(d1, emax); + _mm_store_ps(d2, emin); + d1[1] = emins[1]; + d2[1] = emaxs[1]; + break; + case 3: + _mm_store_ps(d1, emin); + _mm_store_ps(d2, emax); + d1[2] = emaxs[2]; + d2[2] = emins[2]; + break; + case 4: + _mm_store_ps(d1, emax); + _mm_store_ps(d2, emin); + d1[2] = emins[2]; + d2[2] = emaxs[2]; + break; + case 5: + _mm_store_ps(d1, emin); + _mm_store_ps(d2, emax); + d1[1] = emaxs[1]; + d2[1] = emins[1]; + break; + case 6: + _mm_store_ps(d1, emin); + _mm_store_ps(d2, emax); + d1[0] = emaxs[0]; + d2[0] = emins[0]; + break; + case 7: + _mm_store_ps(d1, emin); + _mm_store_ps(d2, emax); + break; + default: + BOPS_Error(); + dist1 = dist2 = 0.0; + break; + } + + dist1 = _DotProduct(p->normal, d1); + dist2 = _DotProduct(p->normal, d2); + + if (dist1 >= p->dist) + sides = 1; + + if (dist2 < p->dist) + sides |= 2; + + return sides; +} + +qboolean VectorCompare(const vec_t *v1, const vec_t *v2) +{ + __m128 cmp = _mm_cmpneq_ps(_mm_loadu_ps(v1), _mm_loadu_ps(v2)); + return !(_mm_movemask_ps(cmp) & (1|2|4)); +} + +void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +{ +#ifndef SWDS + g_engdstAddrs.pfnAngleVectors(&angles, &forward, &right, &up); +#endif // SWDS + + __m128 s, c; + sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); + + __m128 m1 = _mm_shuffle_ps(c, s, 0x90); // [cp][cp][sy][sr] + __m128 m2 = _mm_shuffle_ps(c, c, 0x09); // [cy][cr][cp][cp] + __m128 cp_mults = _mm_mul_ps(m1, m2); // [cp * cy][cp * cr][cp * sy][cp * sr]; + + m1 = _mm_shuffle_ps(c, s, 0x15); // [cy][cy][sy][sp] + m2 = _mm_shuffle_ps(s, c, 0xA0); // [sp][sp][cr][cr] + m1 = _mm_shuffle_ps(m1, m1, 0xC8); // [cy][sy][cy][sp] + + __m128 m3 = _mm_shuffle_ps(s, s, 0x4A); // [sr][sr][sp][sy]; + m3 = _mm_mul_ps(m3, _mm_mul_ps(m1, m2)); // [sp*cy*sr][sp*sy*sr][cr*cy*sp][cr*sp*sy] + + m2 = _mm_shuffle_ps(s, c, 0x65); // [sy][sy][cr][cy] + m1 = _mm_shuffle_ps(c, s, 0xA6); // [cr][cy][sr][sr] + m2 = _mm_shuffle_ps(m2, m2, 0xD8); // [sy][cr][sy][cy] + m1 = _mm_xor_ps(m1, _mm_load_ps((float *)&negmask_1001)); // [-cr][cy][sr][-sr] + m1 = _mm_mul_ps(m1, m2); // [-cr*sy][cy*cr][sr*sy][-sr*cy] + + m3 = _mm_add_ps(m3, m1); + + if (forward) + { + _mm_storel_pi((__m64 *)forward, _mm_shuffle_ps(cp_mults, cp_mults, 0x08)); + forward[2] = -_mm_cvtss_f32(s); + } + if (right) + { + __m128 r = _mm_shuffle_ps(m3, cp_mults, 0xF4); // [m3(0)][m3(1)][cp(3)][cp(3)] + xmm2vec(right, _mm_xor_ps(r, _mm_load_ps((float *)&negmask))); + } + if (up) + { + _mm_storel_pi((__m64 *)up, _mm_shuffle_ps(m3, m3, 0x0E)); + up[2] = _mm_cvtss_f32(_mm_shuffle_ps(cp_mults, cp_mults, 0x01)); + } +} + +void AngleVectorsTranspose(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +{ + __m128 s, c; + sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); + + __m128 m1 = _mm_shuffle_ps(c, s, 0x90); // [cp][cp][sy][sr] + __m128 m2 = _mm_shuffle_ps(c, c, 0x09); // [cy][cr][cp][cp] + __m128 cp_mults = _mm_mul_ps(m1, m2); // [cp * cy][cp * cr][cp * sy][cp * sr]; + + m1 = _mm_shuffle_ps(s, s, 0x50); // [sp][sp][sy][sy] + m2 = _mm_shuffle_ps(c, s, 0x05); // [cy][cy][sp][sp] + + __m128 m3 = _mm_shuffle_ps(s, c, 0xAA); // [sr][sr][cr][cr] + m1 = _mm_mul_ps(m1, m2); + m3 = _mm_shuffle_ps(m3, m3, 0xD8); // [sr][cr][sr][cr] + m3 = _mm_mul_ps(m3, m1); // [sp*cy*sr][sp*cy*cr][sy*sp*sr][sy*sp*cr] + + m2 = _mm_shuffle_ps(c, s, 0xA6); // [cr][cy][sr][sr] + m1 = _mm_shuffle_ps(s, c, 0x65); // [sy][sy][cr][cy] + m2 = _mm_shuffle_ps(m2, m2, 0xD8); // [cr][sr][cy][sr] + m1 = _mm_xor_ps(m1, _mm_load_ps((float *)&negmask_1001)); // [-cr][cy][sr][-sr] + m1 = _mm_mul_ps(m1, m2); // [-cr*sy][sr*sy][cy*cr][-sr*cy] + + m3 = _mm_add_ps(m3, m1); + + if (forward) + { + forward[0] = _mm_cvtss_f32(cp_mults); + _mm_storel_pi((__m64*)(forward + 1), m3); // (sr*sp*cy + cr*-sy); + } + if (right) + { + right[0] = _mm_cvtss_f32(_mm_shuffle_ps(cp_mults, cp_mults, 0x02)); + _mm_storel_pi((__m64*)(right + 1), _mm_shuffle_ps(m3, m3, 0x0E)); + } + if (up) + { + up[0] = -_mm_cvtss_f32(s); + _mm_storel_pi((__m64 *)&up[1], _mm_shuffle_ps(cp_mults, cp_mults, 0x07)); + } +} + +void AngleMatrix(const vec_t *angles, float(*matrix)[4]) +{ + __m128 s, c; + sincos_ps(_mm_mul_ps(_mm_loadu_ps(angles), _mm_load_ps(deg2rad)), &s, &c); + + /* + matrix[0][1] = sr * sp * cy - cr * sy; + matrix[1][1] = sr * sp * sy + cr * cy; + matrix[0][2] = cr * sp * cy + sr * sy; + matrix[1][2] = cr * sp * sy - sr * cy; + */ + __m128 m1; + __m128 m2 = _mm_shuffle_ps(s, c, 0x00); // [sp][sp][cp][cp] + __m128 m3 = _mm_shuffle_ps(c, s, 0x55); // [cy][cy][sy][sy] + + m1 = _mm_shuffle_ps(s, c, 0xAA); // [sr][sr][cr][cr] + m2 = _mm_shuffle_ps(m2, m2, 0x00); // [sp][sp][sp][sp] + m3 = _mm_shuffle_ps(m3, m3, 0xD8); // [cy][sy][cy][sy] + + m2 = _mm_mul_ps(m2, _mm_mul_ps(m1, m3)); // m1*m2*m3 + + m1 = _mm_shuffle_ps(m1, m1, 0x1B); // [cr][cr][sr][sr] + m3 = _mm_shuffle_ps(m3, m3, 0xB1); // [sy][cy][sy][cy] + m3 = _mm_xor_ps(m3, _mm_load_ps((float *)&negmask_1001)); + m3 = _mm_mul_ps(m3, m1); + + m2 = _mm_add_ps(m2, m3); + + /* + matrix[0][0] = cp * cy; + matrix[1][0] = cp * sy; + matrix[2][1] = sr * cp; + matrix[2][2] = cr * cp; + */ + m1 = _mm_shuffle_ps(s, c, 0x29); // [sy][sr][cr][cp] + c = _mm_shuffle_ps(c, c, 0x40); // [cp][cp][cp][cy] + m1 = _mm_mul_ps(m1, c); + + // matrix[0] + m3 = _mm_shuffle_ps(m2, m2, 0xE1); + _mm_storeu_ps(&matrix[0][0], m3); + matrix[0][0] = _mm_cvtss_f32(_mm_shuffle_ps(m1, m1, 0x03)); + *(int *)&matrix[0][3] = 0; + + // matrix[1] + m2 = _mm_shuffle_ps(m2, m2, 0xB4); + _mm_storeu_ps(&matrix[1][0], m2); + matrix[1][0] = _mm_cvtss_f32(m1); + *(int *)&matrix[1][3] = 0; + + // matrix[2] + _mm_storeu_ps(&matrix[2][0], m1); + matrix[2][0] = -_mm_cvtss_f32(s); + *(int *)&matrix[2][3] = 0; +} + +void VectorMA(const vec_t *veca, float scale, const vec_t *vecm, vec_t *out) +{ + xmm2vec(out, _mm_add_ps(_mm_mul_ps(_mm_set_ps1(scale), _mm_loadu_ps(vecm)), _mm_loadu_ps(veca))); +} + +float _DotProduct(const vec_t *v1, const vec_t *v2) +{ + // _mm_loadu_ps - load xmm from unaligned address + // _mm_cvtss_f32 - return low float value of xmm + // _mm_dp_ps - dot product + // 0x71 = 0b01110001 - mask for multiplying operands and result + // dpps isn't binary compatible with separate sse2 instructions (max difference is about 0.0002f, but usually < 0.00001f) + + return _mm_cvtss_f32(dotProduct3D(_mm_loadu_ps(v1), _mm_loadu_ps(v2))); +} + +float Length(const vec_t *v) +{ + return _mm_cvtss_f32(length3D(_mm_loadu_ps(v))); // rsqrt is very inaccurate :( +} + +float Length2D(const vec_t *v) +{ + return _mm_cvtss_f32(length2D(_mm_loadu_ps(v))); +} + +void CrossProduct(const vec_t *v1, const vec_t *v2, vec_t *cross) +{ + xmm2vec(cross, crossProduct3D(_mm_loadu_ps(v1), _mm_loadu_ps(v2))); +} + +void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) +{ + for (size_t i = 0; i < 3; i++) + { + __m128 a1 = _mm_mul_ps(_mm_set_ps1(in1[i][0]), _mm_loadu_ps(in2[0])); + __m128 a2 = _mm_mul_ps(_mm_set_ps1(in1[i][1]), _mm_loadu_ps(in2[1])); + __m128 a3 = _mm_mul_ps(_mm_set_ps1(in1[i][2]), _mm_loadu_ps(in2[2])); + _mm_storeu_ps(out[i], _mm_add_ps(a1, _mm_add_ps(a2, a3))); + out[i][3] += in1[i][3]; + } +} + +#endif // #if defined(REHLDS_SSE) diff --git a/rehlds/engine/mathlib_sse.h b/rehlds/engine/mathlib_sse.h new file mode 100644 index 0000000..0e9550f --- /dev/null +++ b/rehlds/engine/mathlib_sse.h @@ -0,0 +1,74 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#if defined(REHLDS_SSE) + +inline float M_min(float a, float b) +{ + return _mm_cvtss_f32(_mm_min_ss(_mm_load_ss(&a), _mm_load_ss(&b))); +} + +inline double M_min(double a, double b) +{ + return _mm_cvtsd_f64(_mm_min_sd(_mm_load_sd(&a), _mm_load_sd(&b))); +} + +inline float M_max(float a, float b) +{ + return _mm_cvtss_f32(_mm_max_ss(_mm_load_ss(&a), _mm_load_ss(&b))); +} + +inline double M_max(double a, double b) +{ + return _mm_cvtsd_f64(_mm_max_sd(_mm_load_sd(&a), _mm_load_sd(&b))); +} + +inline float M_sqrt(float value) +{ + return _mm_cvtss_f32(_mm_sqrt_ss(_mm_load_ss(&value))); +} + +inline double M_sqrt(double value) +{ + auto v = _mm_load_sd(&value); + return _mm_cvtsd_f64(_mm_sqrt_sd(v, v)); +} + +inline float M_clamp(float a, float min, float max) +{ + return _mm_cvtss_f32(_mm_min_ss(_mm_max_ss(_mm_load_ss(&a), _mm_load_ss(&min)), _mm_load_ss(&max))); +} + +inline double M_clamp(double a, double min, double max) +{ + return _mm_cvtsd_f64(_mm_min_sd(_mm_max_sd(_mm_load_sd(&a), _mm_load_sd(&min)), _mm_load_sd(&max))); +} + +#endif // #if defined(REHLDS_SSE) diff --git a/rehlds/engine/sse_mathfun.h b/rehlds/engine/sse_mathfun.h index 24fd11c..2407327 100644 --- a/rehlds/engine/sse_mathfun.h +++ b/rehlds/engine/sse_mathfun.h @@ -36,7 +36,7 @@ misrepresented as being the original software. #ifdef _MSC_VER /* visual c++ */ # define ALIGN16_BEG __declspec(align(16)) -# define ALIGN16_END +# define ALIGN16_END #else /* gcc or icc */ # define ALIGN16_BEG # define ALIGN16_END __attribute__((aligned(16))) diff --git a/rehlds/msvc/ReHLDS.vcxproj b/rehlds/msvc/ReHLDS.vcxproj index 4d5147d..b587290 100644 --- a/rehlds/msvc/ReHLDS.vcxproj +++ b/rehlds/msvc/ReHLDS.vcxproj @@ -72,6 +72,7 @@ + @@ -432,6 +433,7 @@ + @@ -805,7 +807,7 @@ Level3 Disabled true - REHLDS_API;REHLDS_FLIGHT_REC;REHLDS_OPT_PEDANTIC;REHLDS_FIXES;REHLDS_SELF;REHLDS_CHECKS;HAVE_OPT_STRTOOLS;USE_BREAKPAD_HANDLER;DEDICATED;SWDS;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) + REHLDS_API;REHLDS_FLIGHT_REC;REHLDS_OPT_PEDANTIC;REHLDS_FIXES;REHLDS_SSE;REHLDS_SELF;REHLDS_CHECKS;HAVE_OPT_STRTOOLS;USE_BREAKPAD_HANDLER;DEDICATED;SWDS;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) Precise /arch:IA32 %(AdditionalOptions) MultiThreadedDebug @@ -1149,7 +1151,7 @@ true true true - REHLDS_API;REHLDS_FLIGHT_REC;REHLDS_FIXES;REHLDS_OPT_PEDANTIC;REHLDS_SELF;REHLDS_CHECKS;HAVE_OPT_STRTOOLS;USE_BREAKPAD_HANDLER;DEDICATED;SWDS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + REHLDS_API;REHLDS_FLIGHT_REC;REHLDS_FIXES;REHLDS_SSE;REHLDS_OPT_PEDANTIC;REHLDS_SELF;REHLDS_CHECKS;HAVE_OPT_STRTOOLS;USE_BREAKPAD_HANDLER;DEDICATED;SWDS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded /arch:IA32 %(AdditionalOptions) Use diff --git a/rehlds/msvc/ReHLDS.vcxproj.filters b/rehlds/msvc/ReHLDS.vcxproj.filters index e143343..fddae72 100644 --- a/rehlds/msvc/ReHLDS.vcxproj.filters +++ b/rehlds/msvc/ReHLDS.vcxproj.filters @@ -353,6 +353,9 @@ engine\common + + engine + @@ -1081,6 +1084,9 @@ public + + engine + diff --git a/rehlds/public/rehlds/crc32c.cpp b/rehlds/public/rehlds/crc32c.cpp index 669fc81..4fcde71 100644 --- a/rehlds/public/rehlds/crc32c.cpp +++ b/rehlds/public/rehlds/crc32c.cpp @@ -115,6 +115,7 @@ uint32 crc32c_t_nosse(uint32 iCRC, const uint8 *buf, int len) { return crc; } +#ifdef REHLDS_SSE FUNC_TARGET("sse4.2") uint32 crc32c_t8_sse(uint32 iCRC, uint8 u8) { return _mm_crc32_u8(iCRC, u8); @@ -140,6 +141,14 @@ uint32 crc32c_t(uint32 iCRC, const uint8 *buf, unsigned int len) { return cpuinfo.sse4_2 ? crc32c_t_sse(iCRC, buf, len) : crc32c_t_nosse(iCRC, buf, len); } +#else + +uint32 crc32c_t(uint32 iCRC, const uint8 *buf, unsigned int len) { + return crc32c_t_nosse(iCRC, buf, len); +} + +#endif // REHLDS_SSE + uint32 crc32c(const uint8 *buf, int len) { return crc32c_t(0xffffffff, buf, len); } diff --git a/rehlds/public/rehlds/crc32c.h b/rehlds/public/rehlds/crc32c.h index 55bed92..7ee27fc 100644 --- a/rehlds/public/rehlds/crc32c.h +++ b/rehlds/public/rehlds/crc32c.h @@ -11,7 +11,9 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ + #pragma once + #include "archtypes.h" extern uint32 crc32c_t8_nosse(uint32 iCRC, uint8 u8); diff --git a/rehlds/public/rehlds/static_map.h b/rehlds/public/rehlds/static_map.h index 11235d7..b1da16f 100644 --- a/rehlds/public/rehlds/static_map.h +++ b/rehlds/public/rehlds/static_map.h @@ -169,7 +169,6 @@ public: } } - Iterator(CStaticMap* m) { m_Map = m; m_RootNodes = m_Map->m_RootNodes; @@ -226,6 +225,7 @@ protected: virtual uint32 hash(const char* const &val) { uint32 cksum = 0; const char* pcc = val; +#ifdef REHLDS_SSE if (cpuinfo.sse4_2) { while (*pcc) { char cc = *(pcc++); @@ -234,7 +234,10 @@ protected: } cksum = crc32c_t8_sse(cksum, cc); } - } else { + } + else +#endif // REHLDS_SSE + { while (*pcc) { char cc = *(pcc++); if (cc >= 'A' || cc <= 'Z') { From fd64218ecdfaf232f3d12dac040299df95587a56 Mon Sep 17 00:00:00 2001 From: s1lent Date: Sat, 9 Dec 2017 21:00:18 +0700 Subject: [PATCH 4/4] Fixes #547 Refactoring minor net_ws.cpp --- rehlds/dedicated/src/sys_linux.cpp | 2 +- rehlds/engine/net_ws.cpp | 131 ++++++++++++++++++++--------- rehlds/engine/net_ws.h | 32 +++---- rehlds/engine/sys_dll.cpp | 5 +- rehlds/engine/sys_dll.h | 2 +- 5 files changed, 112 insertions(+), 60 deletions(-) diff --git a/rehlds/dedicated/src/sys_linux.cpp b/rehlds/dedicated/src/sys_linux.cpp index db6976b..111d652 100644 --- a/rehlds/dedicated/src/sys_linux.cpp +++ b/rehlds/dedicated/src/sys_linux.cpp @@ -94,7 +94,7 @@ void Sleep_Select(int msec) void Sleep_Net(int msec) { - NET_Sleep_Timeout(); + NET_Sleep_Timeout(); } // linux runs on a 100Hz scheduling clock, so the minimum latency from diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp index a18b641..7f1aeef 100644 --- a/rehlds/engine/net_ws.cpp +++ b/rehlds/engine/net_ws.cpp @@ -29,7 +29,11 @@ #include "precompiled.h" #ifdef _WIN32 + +HANDLE hThread; +DWORD ThreadId; CRITICAL_SECTION net_cs; + #endif // _WIN32 qboolean net_thread_initialized; @@ -46,8 +50,7 @@ netadr_t net_from; sizebuf_t net_message; qboolean noip; qboolean noipx; - -int use_thread; +qboolean use_thread; unsigned char net_message_buffer[NET_MAX_PAYLOAD]; unsigned char in_message_buf[NET_MAX_PAYLOAD]; @@ -141,7 +144,7 @@ cvar_t net_graphpos; #endif // HOOK_ENGINE -void NET_ThreadLock(void) +void NET_ThreadLock() { #ifdef _WIN32 if (use_thread && net_thread_initialized) @@ -151,7 +154,7 @@ void NET_ThreadLock(void) #endif // _WIN32 } -void NET_ThreadUnlock(void) +void NET_ThreadUnlock() { #ifdef _WIN32 if (use_thread && net_thread_initialized) @@ -212,7 +215,7 @@ void SockadrToNetadr(const struct sockaddr *s, netadr_t *a) else if (s->sa_family == AF_IPX) { a->type = NA_IPX; - Q_memcpy(a->ipx, s->sa_data, 10); + Q_memcpy(a->ipx, s->sa_data, sizeof(a->ipx)); a->port = *(unsigned short *)&s->sa_data[10]; } #endif // _WIN32 @@ -727,7 +730,7 @@ void NET_AddToLagged(netsrc_t sock, packetlag_t *pList, packetlag_t *pPacket, ne Q_memcpy(&pPacket->net_from_, net_from_, sizeof(netadr_t)); } -void NET_AdjustLag(void) +void NET_AdjustLag() { static double lasttime = realtime; double dt; @@ -814,9 +817,11 @@ qboolean NET_LagPacket(qboolean newdata, netsrc_t sock, netadr_t *from, sizebuf_ Cvar_SetValue("fakeloss", 0.0); } } - pNewPacketLag = (packetlag_t *)Mem_ZeroMalloc(0x28u); + + pNewPacketLag = (packetlag_t *)Mem_ZeroMalloc(sizeof(packetlag_t)); NET_AddToLagged(sock, &g_pLagData[sock], pNewPacketLag, from, *data, curtime); } + pPacket = g_pLagData[sock].pNext; while (pPacket != &g_pLagData[sock]) @@ -826,6 +831,7 @@ qboolean NET_LagPacket(qboolean newdata, netsrc_t sock, netadr_t *from, sizebuf_ pPacket = pPacket->pNext; } + if (pPacket == &g_pLagData[sock]) return FALSE; @@ -1055,7 +1061,7 @@ qboolean NET_QueuePacket(netsrc_t sock) return NET_GetLong(in_message.data, ret, &in_message.cursize); } -DLL_EXPORT int NET_Sleep_Timeout(void) +DLL_EXPORT int NET_Sleep_Timeout() { static int32 lasttime; static int numFrames; @@ -1076,7 +1082,7 @@ DLL_EXPORT int NET_Sleep_Timeout(void) { lasttime = curtime; } - + fd_set fdset; FD_ZERO(&fdset); @@ -1123,7 +1129,7 @@ DLL_EXPORT int NET_Sleep_Timeout(void) return res; } -int NET_Sleep(void) +int NET_Sleep() { fd_set fdset; struct timeval tv; @@ -1161,45 +1167,92 @@ int NET_Sleep(void) return select((int)(number + 1), &fdset, NULL, NULL, net_sleepforever == 0 ? &tv : NULL); } -void NET_StartThread(void) +#ifdef _WIN32 + +DWORD WINAPI NET_ThreadMain(LPVOID lpThreadParameter) +{ + while (true) + { + while (NET_Sleep()) + { + qboolean bret = FALSE; + for (int sock = 0; sock < NS_MAX; sock++) + { + NET_ThreadLock(); + + bret = NET_QueuePacket((netsrc_t)sock); + if (bret) + { + net_messages_t *pmsg = NET_AllocMsg(in_message.cursize); + pmsg->next = nullptr; + Q_memcpy(pmsg->buffer, in_message.data, in_message.cursize); + Q_memcpy(&pmsg->from, &in_from, sizeof(pmsg->from)); + + // add to tail of the list + net_messages_t *p = messages[sock]; + if (p) + { + while (p->next) + p = p->next; + + p->next = pmsg; + } + // add to head + else + { + messages[sock] = pmsg; + } + } + + NET_ThreadUnlock(); + } + + if (!bret) + break; + } + + Sys_Sleep(1); + } + + return 0; +} + +#endif // _WIN32 + +void NET_StartThread() { if (use_thread) { if (!net_thread_initialized) { net_thread_initialized = TRUE; - Sys_Error("%s: -netthread is not reversed yet", __func__); + #ifdef _WIN32 - /* InitializeCriticalSection(&net_cs); - hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)NET_ThreadMain, 0, 0, &ThreadId); + hThread = CreateThread(0, 0, NET_ThreadMain, 0, 0, &ThreadId); if (!hThread) { DeleteCriticalSection(&net_cs); - net_thread_initialized = 0; - use_thread = 0; + net_thread_initialized = FALSE; + use_thread = FALSE; Sys_Error("%s: Couldn't initialize network thread, run without -netthread\n", __func__); } - */ #endif // _WIN32 } } } -void NET_StopThread(void) +void NET_StopThread() { if (use_thread) { if (net_thread_initialized) { #ifdef _WIN32 - /* TerminateThread(hThread, 0); DeleteCriticalSection(&net_cs); - */ #endif // _WIN32 net_thread_initialized = FALSE; - Sys_Error("%s: -netthread is not reversed yet", __func__); } } } @@ -1214,9 +1267,9 @@ net_messages_t *NET_AllocMsg(int size) net_messages_t *pmsg; if (size <= MSG_QUEUE_SIZE && normalqueue) { - pmsg = normalqueue->next; - normalqueue->buffersize = size; - normalqueue = pmsg; + pmsg = normalqueue; + pmsg->buffersize = size; + normalqueue = pmsg->next; } else { @@ -1287,20 +1340,20 @@ qboolean NET_GetPacket(netsrc_t sock) net_from = pmsg->from; msg_readcount = 0; NET_FreeMsg(pmsg); - bret = 1; + bret = TRUE; } NET_ThreadUnlock(); return bret; } -void NET_AllocateQueues(void) +void NET_AllocateQueues() { net_messages_t *p; for (int i = 0; i < NUM_MSG_QUEUES; i++) { p = (net_messages_t *)Mem_ZeroMalloc(sizeof(net_messages_t)); p->buffer = (unsigned char *)Mem_ZeroMalloc(MSG_QUEUE_SIZE); - p->preallocated = 1; + p->preallocated = TRUE; p->next = normalqueue; normalqueue = p; } @@ -1308,8 +1361,10 @@ void NET_AllocateQueues(void) NET_StartThread(); } -void NET_FlushQueues(void) +void NET_FlushQueues() { + NET_StopThread(); + for (int i = 0; i < NS_MAX; i++) { net_messages_t *p = messages[i]; @@ -1612,7 +1667,7 @@ SOCKET NET_IPSocket(char *net_interface, int port, qboolean multicast) return newsocket; } -void NET_OpenIP(void) +void NET_OpenIP() { //cvar_t *ip;//unused? int port; @@ -1744,7 +1799,7 @@ SOCKET NET_IPXSocket(int hostshort) return newsocket; } -void NET_OpenIPX(void) +void NET_OpenIPX() { int port; int dedicated; @@ -1795,7 +1850,7 @@ void NET_OpenIPX(void) #endif // _WIN32 -void NET_GetLocalAddress(void) +void NET_GetLocalAddress() { char buff[512]; struct sockaddr_in address; @@ -1888,7 +1943,7 @@ void NET_GetLocalAddress(void) #endif //_WIN32 } -int NET_IsConfigured(void) +int NET_IsConfigured() { return net_configured; } @@ -1950,7 +2005,7 @@ void NET_Config(qboolean multiplayer) net_configured = multiplayer ? 1 : 0; } -void MaxPlayers_f(void) +void MaxPlayers_f() { if (Cmd_Argc() != 2) { @@ -1983,7 +2038,7 @@ void MaxPlayers_f(void) Cvar_Set("deathmatch", "1"); } -void NET_Init(void) +void NET_Init() { #ifdef HOOK_ENGINE Cmd_AddCommand("maxplayers", (xcommand_t)GetOriginalFuncAddrOrDefault("MaxPlayers_f", (void *)MaxPlayers_f)); @@ -2012,10 +2067,8 @@ void NET_Init(void) Cvar_RegisterVariable(&net_graphpos); if (COM_CheckParm("-netthread")) - { - use_thread = 1; - Sys_Error("%s: -netthread is not reversed yet", __func__); - } + use_thread = TRUE; + if (COM_CheckParm("-netsleep")) net_sleepforever = 0; @@ -2073,7 +2126,7 @@ void NET_ClearLagData(qboolean bClient, qboolean bServer) NET_ThreadUnlock(); } -void NET_Shutdown(void) +void NET_Shutdown() { NET_ThreadLock(); diff --git a/rehlds/engine/net_ws.h b/rehlds/engine/net_ws.h index 7c9ec5a..af4d25c 100644 --- a/rehlds/engine/net_ws.h +++ b/rehlds/engine/net_ws.h @@ -187,7 +187,7 @@ extern qboolean noipx; #endif // _WIN32 extern sizebuf_t net_message; extern cvar_t clockwindow; -extern int use_thread; +extern qboolean use_thread; extern cvar_t iphostport; extern cvar_t hostport; #ifdef _WIN32 @@ -214,8 +214,8 @@ extern net_messages_t *messages[3]; extern net_messages_t *normalqueue; -void NET_ThreadLock(void); -void NET_ThreadUnlock(void); +void NET_ThreadLock(); +void NET_ThreadUnlock(); unsigned short Q_ntohs(unsigned short netshort); void NetadrToSockadr(const netadr_t *a, struct sockaddr *s); void SockadrToNetadr(const struct sockaddr *s, netadr_t *a); @@ -237,33 +237,33 @@ void NET_RemoveFromPacketList(packetlag_t *pPacket); NOXREF int NET_CountLaggedList(packetlag_t *pList); void NET_ClearLaggedList(packetlag_t *pList); void NET_AddToLagged(netsrc_t sock, packetlag_t *pList, packetlag_t *pPacket, netadr_t *net_from_, sizebuf_t messagedata, float timestamp); -void NET_AdjustLag(void); +void NET_AdjustLag(); qboolean NET_LagPacket(qboolean newdata, netsrc_t sock, netadr_t *from, sizebuf_t *data); void NET_FlushSocket(netsrc_t sock); qboolean NET_GetLong(unsigned char *pData, int size, int *outSize); qboolean NET_QueuePacket(netsrc_t sock); -int NET_Sleep(void); -void NET_StartThread(void); -void NET_StopThread(void); +int NET_Sleep(); +void NET_StartThread(); +void NET_StopThread(); void *net_malloc(size_t size); net_messages_t *NET_AllocMsg(int size); void NET_FreeMsg(net_messages_t *pmsg); qboolean NET_GetPacket(netsrc_t sock); -void NET_AllocateQueues(void); -void NET_FlushQueues(void); +void NET_AllocateQueues(); +void NET_FlushQueues(); int NET_SendLong(netsrc_t sock, SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen); void NET_SendPacket_api(unsigned int length, void *data, const netadr_t &to); void NET_SendPacket(netsrc_t sock, int length, void *data, const netadr_t& to); SOCKET NET_IPSocket(char *net_interface, int port, qboolean multicast); -void NET_OpenIP(void); +void NET_OpenIP(); SOCKET NET_IPXSocket(int hostshort); -void NET_OpenIPX(void); -void NET_GetLocalAddress(void); -int NET_IsConfigured(void); +void NET_OpenIPX(); +void NET_GetLocalAddress(); +int NET_IsConfigured(); void NET_Config(qboolean multiplayer); -void MaxPlayers_f(void); -void NET_Init(void); +void MaxPlayers_f(); +void NET_Init(); void NET_ClearLagData(qboolean bClient, qboolean bServer); -void NET_Shutdown(void); +void NET_Shutdown(); qboolean NET_JoinGroup(netsrc_t sock, netadr_t& addr); qboolean NET_LeaveGroup(netsrc_t sock, netadr_t& addr); diff --git a/rehlds/engine/sys_dll.cpp b/rehlds/engine/sys_dll.cpp index 390f9ab..39c799b 100644 --- a/rehlds/engine/sys_dll.cpp +++ b/rehlds/engine/sys_dll.cpp @@ -428,13 +428,12 @@ NOBODY void MaskExceptions(void); NOBODY void Sys_Init(void); -NOXREF void Sys_Sleep(int msec) +void Sys_Sleep(int msec) { - NOXREFCHECK; #ifdef _WIN32 Sleep(msec); #else - usleep(1000 *msec); + usleep(1000 * msec); #endif // _WIN32 } diff --git a/rehlds/engine/sys_dll.h b/rehlds/engine/sys_dll.h index bf134d7..024ccde 100644 --- a/rehlds/engine/sys_dll.h +++ b/rehlds/engine/sys_dll.h @@ -150,7 +150,7 @@ NOBODY void Sys_PushFPCW_SetHigh(void); NOBODY void Sys_PopFPCW(void); NOBODY void MaskExceptions(void); NOBODY void Sys_Init(void); -NOXREF void Sys_Sleep(int msec); +void Sys_Sleep(int msec); NOBODY void Sys_DebugOutStraight(const char *pStr); void NORETURN Sys_Error(const char *error, ...); NOXREF void Sys_Warning(const char *pszWarning, ...);