diff options
author | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-12-02 00:29:12 +0000 |
---|---|---|
committer | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-12-02 00:29:12 +0000 |
commit | f33bab4fb3586fd06896a7730bce8913c2616b78 (patch) | |
tree | 484bc51055c9ad810fa4bfd1c0b1b0e35ae03096 | |
parent | ac0c658516d8eaef2c788624f94e974ef47acbb8 (diff) | |
download | NorthstarLauncher-f33bab4fb3586fd06896a7730bce8913c2616b78.tar.gz NorthstarLauncher-f33bab4fb3586fd06896a7730bce8913c2616b78.zip |
tier0 code cleanup and loadlibrary hook fix
-rw-r--r-- | NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters | 12 | ||||
-rw-r--r-- | NorthstarDedicatedTest/dedicated.cpp | 22 | ||||
-rw-r--r-- | NorthstarDedicatedTest/dedicatedmaterialsystem.cpp | 54 | ||||
-rw-r--r-- | NorthstarDedicatedTest/dllmain.cpp | 3 | ||||
-rw-r--r-- | NorthstarDedicatedTest/gameutils.cpp | 14 | ||||
-rw-r--r-- | NorthstarDedicatedTest/gameutils.h | 13 | ||||
-rw-r--r-- | NorthstarDedicatedTest/hooks.cpp | 7 | ||||
-rw-r--r-- | NorthstarDedicatedTest/hookutils.cpp | 9 | ||||
-rw-r--r-- | NorthstarDedicatedTest/securitypatches.cpp | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/serverauthentication.cpp | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/sourceinterface.h | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/tier0.cpp | 97 | ||||
-rw-r--r-- | NorthstarDedicatedTest/tier0.h | 32 |
14 files changed, 71 insertions, 200 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index 2fa7ff1c..716b306a 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -329,7 +329,6 @@ <ClInclude Include="sourceconsole.h" /> <ClInclude Include="sourceinterface.h" /> <ClInclude Include="squirrel.h" /> - <ClInclude Include="tier0.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="chatcommand.cpp" /> @@ -369,7 +368,6 @@ <ClCompile Include="sourceconsole.cpp" /> <ClCompile Include="sourceinterface.cpp" /> <ClCompile Include="squirrel.cpp" /> - <ClCompile Include="tier0.cpp" /> </ItemGroup> <ItemGroup> <None Include="include\spdlog\fmt\bundled\LICENSE.rst" /> diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index 9a9c6b84..ac20a53c 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -552,12 +552,6 @@ <ClInclude Include="masterserver.h"> <Filter>Header Files\Shared</Filter> </ClInclude> - <ClInclude Include="tier0.h"> - <Filter>Header Files\Shared\Game Functions</Filter> - </ClInclude> - <ClInclude Include="gameutils.h"> - <Filter>Header Files\Shared\Game Functions</Filter> - </ClInclude> <ClInclude Include="chatcommand.h"> <Filter>Header Files\Client</Filter> </ClInclude> @@ -588,6 +582,9 @@ <ClInclude Include="scriptbrowserhooks.h"> <Filter>Header Files\Client</Filter> </ClInclude> + <ClInclude Include="gameutils.h"> + <Filter>Header Files\Shared\Game Functions</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -653,9 +650,6 @@ <ClCompile Include="masterserver.cpp"> <Filter>Source Files\Shared</Filter> </ClCompile> - <ClCompile Include="tier0.cpp"> - <Filter>Source Files\Shared\Game Functions</Filter> - </ClCompile> <ClCompile Include="gameutils.cpp"> <Filter>Source Files\Shared\Game Functions</Filter> </ClCompile> diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index b3c576ac..5269c5cb 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include "dedicated.h" #include "hookutils.h" -#include "tier0.h" #include "gameutils.h" #include "serverauthentication.h" @@ -37,7 +36,6 @@ void Sys_Printf(CDedicatedExports* dedicated, char* msg) spdlog::info("[DEDICATED PRINT] {}", msg); } -typedef bool (*CEngine__FrameType)(void* engineSelf); typedef void(*CHostState__InitType)(CHostState* self); void RunServer(CDedicatedExports* dedicated) @@ -101,11 +99,11 @@ void InitialiseDedicated(HMODULE engineAddress) // force the engine into dedicated mode by changing the first comparison to IsServerOnly to an assignment char* ptr = (char*)engineAddress + 0x1C4EBD; TempReadWrite rw(ptr); - + // cmp => mov *(ptr + 1) = (char)0xC6; *(ptr + 2) = (char)0x87; - + // 00 => 01 *((char*)ptr + 7) = (char)0x01; } @@ -174,7 +172,7 @@ void InitialiseDedicated(HMODULE engineAddress) //*(ptr + 13) = (char)0x90; //*(ptr + 14) = (char)0x90; - *(ptr + 15) = (char)0x90; + * (ptr + 15) = (char)0x90; *(ptr + 16) = (char)0x90; *(ptr + 17) = (char)0x90; *(ptr + 18) = (char)0x90; @@ -344,7 +342,7 @@ void InitialiseDedicated(HMODULE engineAddress) // CEngineAPI::Init char* ptr = (char*)engineAddress + 0x1C60CE; TempReadWrite rw(ptr); - + // remove call to something or other that reads video settings *ptr = (char)0x90; *(ptr + 1) = (char)0x90; @@ -352,12 +350,12 @@ void InitialiseDedicated(HMODULE engineAddress) *(ptr + 3) = (char)0x90; *(ptr + 4) = (char)0x90; } - + { // some inputsystem bullshit char* ptr = (char*)engineAddress + 0x1CEE28; TempReadWrite rw(ptr); - + // nop an accessviolation: temp because we still create game window atm *ptr = (char)0x90; *(ptr + 1) = (char)0x90; @@ -391,7 +389,7 @@ void InitialiseDedicated(HMODULE engineAddress) // // *(ptr + 7) = (char)0xEB; // jnz => jmp //} - + // note: this is a different way of nopping window creation, i'm assuming there are like a shitload of inits here we shouldn't skip // i know at the very least it registers datatables which are important { @@ -443,7 +441,7 @@ void InitialiseDedicated(HMODULE engineAddress) HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)engineAddress + 0x1CDC80, &IsGameActiveWindowHook, reinterpret_cast<LPVOID*>(&IsGameActiveWindow)); - + // extra potential patches: // nop engine.dll+1c67d1 and +1c67d8 to skip videomode creategamewindow // also look into launcher.dll+d381, seems to cause renderthread to get made @@ -457,6 +455,8 @@ void InitialiseDedicated(HMODULE engineAddress) CommandLine()->AppendParm("-nomenuvid", 0); CommandLine()->AppendParm("-nosound", 0); CommandLine()->AppendParm("+host_preload_shaders", "0"); + CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); + CommandLine()->AppendParm("+exec", "autoexec_ns_server"); } typedef void(*Tier0_InitOriginType)(); @@ -483,7 +483,7 @@ PrintFatalSquirrelErrorType PrintFatalSquirrelError; void PrintFatalSquirrelErrorHook(void* sqvm) { PrintFatalSquirrelError(sqvm); - abort(); + //abort(); } void InitialiseDedicatedServerGameDLL(HMODULE baseAddress) diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index 3c96ef22..29ccf6f7 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -11,7 +11,7 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) //while (!IsDebuggerPresent()) // Sleep(100); - + // not using these for now since they're related to nopping renderthread/gamewindow i.e. very hard // we use -noshaderapi instead now //{ @@ -64,59 +64,43 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) *(ptr + 4) = (char)0x90; } - // these don't fully work, they cause game to hang on rpak init, needs reworking { - // materialsystem rpak type: texture - char* ptr = (char*)baseAddress + 0x2B3A; + // some renderthread stuff + char* ptr = (char*)baseAddress + 0x8C10; TempReadWrite rw(ptr); - // je=>jmp - *ptr = (char)0xE9; - *(ptr + 1) = (char)0x48; - *(ptr + 2) = (char)0x02; - *(ptr + 3) = (char)0x00; - *(ptr + 4) = (char)0x00; + // call => nop + *ptr = (char)0x90; + *(ptr + 1) = (char)0x90; } + // rpak type callbacks + // these need to be nopped for dedi { - // materialsystem rpak type: material - char* ptr = (char*)baseAddress + 0x50AD4; + // materialsystem rpak type: shader + char* ptr = (char*)baseAddress + 0x2850; TempReadWrite rw(ptr); - // je=>jmp - *ptr = (char)0xEB; + // ret + *ptr = (char)0xC3; } { - // materialsystem rpak type: shader - char* ptr = (char*)baseAddress + 0x2850; + // materialsystem rpak type: texture + char* ptr = (char*)baseAddress + 0x2B00; TempReadWrite rw(ptr); - // make it return 0 - // mov rax,0 - *ptr = 0x48; - *(ptr + 1) = (char)0xB8; - *(ptr + 2) = (char)0x00; - *(ptr + 3) = (char)0x00; - *(ptr + 4) = (char)0x00; - *(ptr + 5) = (char)0x00; - *(ptr + 6) = (char)0x00; - *(ptr + 7) = (char)0x00; - *(ptr + 8) = (char)0x00; - *(ptr + 9) = (char)0x00; - // ret - *(ptr + 10) = (char)0xC3; + *ptr = (char)0xC3; } { - // some renderthread stuff - char* ptr = (char*)baseAddress + 0x8C10; + // materialsystem rpak type: material + char* ptr = (char*)baseAddress + 0x50AA0; TempReadWrite rw(ptr); - // call => nop - *ptr = (char)0x90; - *(ptr + 1) = (char)0x90; + // ret + *ptr = (char)0xC3; } } } diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index 878800bd..bdee441f 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -2,7 +2,6 @@ #include "hooks.h" #include "main.h" #include "squirrel.h" -#include "tier0.h" #include "dedicated.h" #include "dedicatedmaterialsystem.h" #include "sourceconsole.h" @@ -66,6 +65,8 @@ void InitialiseNorthstar() InstallInitialHooks(); InitialiseInterfaceCreationHooks(); + // adding a callback to tier0 won't work for some reason + AddDllLoadCallback("launcher.dll", InitialiseTier0GameUtilFunctions); AddDllLoadCallback("engine.dll", WaitForDebugger); AddDllLoadCallback("engine.dll", InitialiseEngineGameUtilFunctions); AddDllLoadCallback("server.dll", InitialiseServerGameUtilFunctions); diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index 2ad2821c..714395c6 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -36,6 +36,10 @@ char* g_LocalPlayerOriginToken; // misc stuff ConVar* Cvar_match_defaultMap; +ConVar* Cvar_communities_hostname; +ErrorType Error; +CommandLineType CommandLine; +Plat_FloatTimeType Plat_FloatTime; void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) { @@ -57,6 +61,7 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) g_LocalPlayerOriginToken = (char*)baseAddress + 0x13979C80; Cvar_match_defaultMap = (ConVar*)((char*)baseAddress + 0x8AB530); + Cvar_communities_hostname = (ConVar*)((char*)baseAddress + 0x13157E50); } void InitialiseServerGameUtilFunctions(HMODULE baseAddress) @@ -64,4 +69,13 @@ void InitialiseServerGameUtilFunctions(HMODULE baseAddress) Server_GetEntityByIndex = (Server_GetEntityByIndexType)((char*)baseAddress + 0xFB820); Cvar_base_tickinterval_mp = (ConVar*)((char*)baseAddress + 0xBFC360); Cvar_base_tickinterval_mp = (ConVar*)((char*)baseAddress + 0xBFBEA0); +} + +void InitialiseTier0GameUtilFunctions(HMODULE baseAddress) +{ + baseAddress = GetModuleHandleA("tier0.dll"); + + Error = (ErrorType)GetProcAddress(baseAddress, "Error"); + CommandLine = (CommandLineType)GetProcAddress(baseAddress, "CommandLine"); + Plat_FloatTime = (Plat_FloatTimeType)GetProcAddress(baseAddress, "Plat_FloatTime"); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index 5b0a04a5..69da4325 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -195,6 +195,17 @@ extern char* g_LocalPlayerOriginToken; // misc stuff extern ConVar* Cvar_match_defaultMap; +extern ConVar* Cvar_communities_hostname; + +typedef void(*ErrorType)(const char* fmt, ...); +extern ErrorType Error; + +typedef CCommandLine*(*CommandLineType)(); +extern CommandLineType CommandLine; + +typedef double(*Plat_FloatTimeType)(); +extern Plat_FloatTimeType Plat_FloatTime; void InitialiseEngineGameUtilFunctions(HMODULE baseAddress); -void InitialiseServerGameUtilFunctions(HMODULE baseAddress);
\ No newline at end of file +void InitialiseServerGameUtilFunctions(HMODULE baseAddress); +void InitialiseTier0GameUtilFunctions(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index 1b4bcc28..3de8d483 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -1,8 +1,6 @@ #include "pch.h" #include "hooks.h" #include "hookutils.h" -#include "dedicated.h" -#include "tier0.h" #include <wchar.h> #include <iostream> @@ -20,7 +18,7 @@ LoadLibraryExWType LoadLibraryExWOriginal; void InstallInitialHooks() { if (MH_Initialize() != MH_OK) - Error("MH_Initialize failed"); + spdlog::error("MH_Initialize failed"); HookEnabler hook; ENABLER_CREATEHOOK(hook, &LoadLibraryExA, &LoadLibraryExAHook, reinterpret_cast<LPVOID*>(&LoadLibraryExAOriginal)); @@ -75,7 +73,8 @@ HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) { for (auto& callbackStruct : dllLoadCallbacks) { - const wchar_t* callbackDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()).c_str(); + std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); + const wchar_t* callbackDll = wcharStrDll.c_str(); if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcslen(callbackDll)), callbackDll) != nullptr) { callbackStruct->callback(moduleAddress); diff --git a/NorthstarDedicatedTest/hookutils.cpp b/NorthstarDedicatedTest/hookutils.cpp index 1623c481..e86c671c 100644 --- a/NorthstarDedicatedTest/hookutils.cpp +++ b/NorthstarDedicatedTest/hookutils.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "hookutils.h" -#include "tier0.h" #include <iostream> @@ -39,9 +38,9 @@ void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOrigina else { if (targetName != nullptr) - Error("MH_CreateHook failed for function %s", targetName); + spdlog::error("MH_CreateHook failed for function %s", targetName); else - Error("MH_CreateHook failed for unknown function"); + spdlog::error("MH_CreateHook failed for unknown function"); } } @@ -52,9 +51,9 @@ HookEnabler::~HookEnabler() if (MH_EnableHook(hook->targetAddress) != MH_OK) { if (hook->targetName != nullptr) - Error("MH_EnableHook failed for function %s", hook->targetName); + spdlog::error("MH_EnableHook failed for function %s", hook->targetName); else - Error("MH_EnableHook failed for unknown function"); + spdlog::error("MH_EnableHook failed for unknown function"); } else spdlog::info("Enabling hook {}", hook->targetName); diff --git a/NorthstarDedicatedTest/securitypatches.cpp b/NorthstarDedicatedTest/securitypatches.cpp index dacc075c..3664e96a 100644 --- a/NorthstarDedicatedTest/securitypatches.cpp +++ b/NorthstarDedicatedTest/securitypatches.cpp @@ -3,7 +3,7 @@ #include "hookutils.h" #include "concommand.h" #include "dedicated.h" -#include "tier0.h" +#include "gameutils.h" typedef bool(*IsValveModType)(); IsValveModType IsValveMod; diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index ba2e8b07..ad9670a7 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -4,7 +4,7 @@ #include "hookutils.h" #include "masterserver.h" #include "httplib.h" -#include "tier0.h" +#include "gameutils.h" #include <fstream> #include <filesystem> #include <thread> diff --git a/NorthstarDedicatedTest/sourceinterface.h b/NorthstarDedicatedTest/sourceinterface.h index 3629b9d1..829942ba 100644 --- a/NorthstarDedicatedTest/sourceinterface.h +++ b/NorthstarDedicatedTest/sourceinterface.h @@ -1,6 +1,6 @@ #pragma once #include <string> -#include "tier0.h" +#include "gameutils.h" // literally just copied from ttf2sdk definition typedef void*(*CreateInterfaceFn)(const char* pName, int* pReturnCode); diff --git a/NorthstarDedicatedTest/tier0.cpp b/NorthstarDedicatedTest/tier0.cpp deleted file mode 100644 index 74ac02d3..00000000 --- a/NorthstarDedicatedTest/tier0.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "pch.h" -#include "tier0.h" -#include <stdio.h> -#include <iostream> - -void* ResolveTier0Function(const char* name) -{ - HMODULE tier0 = GetModuleHandle(L"tier0.dll"); - - // todo: maybe cache resolved funcs? idk the performance hit of getprocaddress - //std::cout << "ResolveTier0Function " << name << " " << tier0 << "::" << GetProcAddress(tier0, name) << std::endl; - return GetProcAddress(tier0, name); -} - -// memory stuff -typedef IMemAlloc* (*Tier0CreateGlobalMemAlloc)(); -void* operator new(size_t n) -{ - // we should be trying to use tier0's g_pMemAllocSingleton, but atm i can't get it stable - // this actually seems relatively stable though, somehow??? - return malloc(n); - - //IMemAlloc** alloc = (IMemAlloc**)ResolveTier0Function("g_pMemAllocSingleton"); - //if (!alloc) - //{ - // Tier0CreateGlobalMemAlloc createAlloc = (Tier0CreateGlobalMemAlloc)ResolveTier0Function("CreateGlobalMemAlloc"); - // if (!createAlloc) - // return malloc(n); - // else - // (*alloc) = createAlloc(); - //} - // - //return (*alloc)->m_vtable->Alloc(*alloc, n); -} - -void operator delete(void* p) throw() -{ - // we should be trying to use tier0's g_pMemAllocSingleton, but atm i can't get it stable - // this actually seems relatively stable though, somehow??? - free(p); - - //IMemAlloc** alloc = (IMemAlloc**)ResolveTier0Function("g_pMemAllocSingleton"); - //if (!alloc) - //{ - // Tier0CreateGlobalMemAlloc createAlloc = (Tier0CreateGlobalMemAlloc)ResolveTier0Function("CreateGlobalMemAlloc"); - // if (!createAlloc) - // { - // free(p); - // return; - // } - // else - // (*alloc) = createAlloc(); - //} - // - //(*alloc)->m_vtable->Free(*alloc, p); -} - -// honestly this all really sucks and should be reworked - -typedef void(*Tier0Error)(const char* fmt, ...); -void Error(const char* fmt, ...) -{ - Tier0Error tier0Func = (Tier0Error)ResolveTier0Function("Error"); - - // reformat args because you can't pass varargs between funcs - char buf[1024]; - - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - - // tier0 isn't loaded yet - //if (tier0Func) - // tier0Func(buf); - //else - std::cout << "FATAL ERROR " << buf << std::endl; -} - -typedef double(*Tier0FloatTime)(); -double Plat_FloatTime() -{ - Tier0FloatTime tier0Func = (Tier0FloatTime)ResolveTier0Function("Plat_FloatTime"); - - if (tier0Func) - return tier0Func(); - else - return 0.0f; -} - -typedef CCommandLine*(*Tier0CommandLine)(); -CCommandLine* CommandLine() -{ - Tier0CommandLine tier0Func = (Tier0CommandLine)ResolveTier0Function("CommandLine"); - - return tier0Func(); -}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/tier0.h b/NorthstarDedicatedTest/tier0.h deleted file mode 100644 index 5375608c..00000000 --- a/NorthstarDedicatedTest/tier0.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "pch.h" -#include "gameutils.h" - -// get exported tier0 by name -void* ResolveTier0Function(const char* name); - -// memory stuff -class IMemAlloc -{ -public: - struct VTable - { - void* unknown[1]; - void* (*Alloc) (IMemAlloc* memAlloc, size_t nSize); - void* unknown2[3]; - void(*Free) (IMemAlloc* memAlloc, void* pMem); - }; - - VTable* m_vtable; -}; - -void* operator new(std::size_t n); -void operator delete(void* p) throw(); - -// actual function defs -// would've liked to resolve these at compile time, but we load before tier0 so not really possible -void Error(const char* fmt, ...); - -double Plat_FloatTime(); - -CCommandLine* CommandLine();
\ No newline at end of file |