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 }