You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/SystemCFG.cpp

583 lines
18 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : handles system cfg
#include "CrySystem_precompiled.h"
#include "System.h"
#include <time.h>
#include "XConsole.h"
#include "CryFile.h"
#include "CryPath.h"
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include "SystemCFG.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define SYSTEMCFG_CPP_SECTION_1 1
#define SYSTEMCFG_CPP_SECTION_2 2
#define SYSTEMCFG_CPP_SECTION_3 3
#endif
#if defined(LINUX) || defined(APPLE)
#include "ILog.h"
#endif
#ifndef EXE_VERSION_INFO_0
#define EXE_VERSION_INFO_0 1
#endif
#ifndef EXE_VERSION_INFO_1
#define EXE_VERSION_INFO_1 0
#endif
#ifndef EXE_VERSION_INFO_2
#define EXE_VERSION_INFO_2 0
#endif
#ifndef EXE_VERSION_INFO_3
#define EXE_VERSION_INFO_3 1
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMCFG_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(SystemCFG_cpp)
#endif
//////////////////////////////////////////////////////////////////////////
const SFileVersion& CSystem::GetFileVersion()
{
return m_fileVersion;
}
//////////////////////////////////////////////////////////////////////////
const SFileVersion& CSystem::GetProductVersion()
{
return m_productVersion;
}
//////////////////////////////////////////////////////////////////////////
const SFileVersion& CSystem::GetBuildVersion()
{
return m_buildVersion;
}
//////////////////////////////////////////////////////////////////////////
#ifndef _RELEASE
void CSystem::SystemVersionChanged(ICVar* pCVar)
{
if (CSystem* pThis = static_cast<CSystem*>(gEnv->pSystem))
{
pThis->SetVersionInfo(pCVar->GetString());
}
}
void CSystem::SetVersionInfo(const char* const szVersion)
{
m_fileVersion.Set(szVersion);
m_productVersion.Set(szVersion);
m_buildVersion.Set(szVersion);
CryLog("SetVersionInfo '%s'", szVersion);
CryLog("FileVersion: %d.%d.%d.%d", m_fileVersion.v[3], m_fileVersion.v[2], m_fileVersion.v[1], m_fileVersion.v[0]);
CryLog("ProductVersion: %d.%d.%d.%d", m_productVersion.v[3], m_productVersion.v[2], m_productVersion.v[1], m_productVersion.v[0]);
CryLog("BuildVersion: %d.%d.%d.%d", m_buildVersion.v[3], m_buildVersion.v[2], m_buildVersion.v[1], m_buildVersion.v[0]);
}
#endif // #ifndef _RELEASE
//////////////////////////////////////////////////////////////////////////
void CSystem::QueryVersionInfo()
{
#ifndef WIN32
//do we need some other values here?
m_fileVersion.v[0] = m_productVersion.v[0] = EXE_VERSION_INFO_3;
m_fileVersion.v[1] = m_productVersion.v[1] = EXE_VERSION_INFO_2;
m_fileVersion.v[2] = m_productVersion.v[2] = EXE_VERSION_INFO_1;
m_fileVersion.v[3] = m_productVersion.v[3] = EXE_VERSION_INFO_0;
m_buildVersion = m_fileVersion;
#else //WIN32
char moduleName[_MAX_PATH];
DWORD dwHandle;
UINT len;
char ver[1024 * 8];
GetModuleFileName(NULL, moduleName, _MAX_PATH); //retrieves the PATH for the current module
#ifdef AZ_MONOLITHIC_BUILD
GetModuleFileName(NULL, moduleName, _MAX_PATH); //retrieves the PATH for the current module
#else // AZ_MONOLITHIC_BUILD
azstrcpy(moduleName, AZ_ARRAY_SIZE(moduleName), "CrySystem.dll"); // we want to version from the system dll
#endif // AZ_MONOLITHIC_BUILD
int verSize = GetFileVersionInfoSize(moduleName, &dwHandle);
if (verSize > 0)
{
GetFileVersionInfo(moduleName, dwHandle, 1024 * 8, ver);
VS_FIXEDFILEINFO* vinfo;
VerQueryValue(ver, "\\", (void**)&vinfo, &len);
const uint32 verIndices[4] = {0, 1, 2, 3};
m_fileVersion.v[verIndices[0]] = m_productVersion.v[verIndices[0]] = vinfo->dwFileVersionLS & 0xFFFF;
m_fileVersion.v[verIndices[1]] = m_productVersion.v[verIndices[1]] = vinfo->dwFileVersionLS >> 16;
m_fileVersion.v[verIndices[2]] = m_productVersion.v[verIndices[2]] = vinfo->dwFileVersionMS & 0xFFFF;
m_fileVersion.v[verIndices[3]] = m_productVersion.v[verIndices[3]] = vinfo->dwFileVersionMS >> 16;
m_buildVersion = m_fileVersion;
struct LANGANDCODEPAGE
{
WORD wLanguage;
WORD wCodePage;
}* lpTranslate;
UINT count = 0;
char path[256];
char* version = NULL;
VerQueryValue(ver, "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &count);
if (lpTranslate != NULL)
{
azsnprintf(path, sizeof(path), "\\StringFileInfo\\%04x%04x\\InternalName", lpTranslate[0].wLanguage, lpTranslate[0].wCodePage);
VerQueryValue(ver, path, (LPVOID*)&version, &count);
if (version)
{
m_buildVersion.Set(version);
}
}
}
#endif //WIN32
}
//////////////////////////////////////////////////////////////////////////
void CSystem::LogVersion()
{
// Get time.
time_t ltime;
time(&ltime);
#ifdef AZ_COMPILER_MSVC
tm today;
localtime_s(&today, &ltime);
char s[1024];
strftime(s, 128, "%d %b %y (%H %M %S)", &today);
#else
char s[1024];
auto today = localtime(&ltime);
strftime(s, 128, "%d %b %y (%H %M %S)", today);
#endif
const SFileVersion& ver = GetFileVersion();
CryLogAlways("BackupNameAttachment=\" Build(%d) %s\" -- used by backup system\n", ver.v[0], s); // read by CreateBackupFile()
// Use strftime to build a customized time string.
#ifdef AZ_COMPILER_MSVC
strftime(s, 128, "Log Started at %c", &today);
#else
strftime(s, 128, "Log Started at %c", today);
#endif
CryLogAlways(s);
CryLogAlways("Built on " __DATE__ " " __TIME__);
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMCFG_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(SystemCFG_cpp)
#elif defined(ANDROID)
CryLogAlways("Running 32 bit Android version API VER:%d", __ANDROID_API__);
#elif defined(IOS)
CryLogAlways("Running 64 bit iOS version");
#elif defined(WIN64)
CryLogAlways("Running 64 bit Windows version");
#elif defined(WIN32)
CryLogAlways("Running 32 bit Windows version");
#elif defined(LINUX64)
CryLogAlways("Running 64 bit Linux version");
#elif defined(LINUX32)
CryLogAlways("Running 32 bit Linux version");
#elif defined(MAC)
CryLogAlways("Running 64 bit Mac version");
#endif
#if AZ_LEGACY_CRYSYSTEM_TRAIT_SYSTEMCFG_MODULENAME
GetModuleFileName(NULL, s, sizeof(s));
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
AZStd::string exeName;
if (AzFramework::StringFunc::Path::GetFullFileName(s, exeName)) {
CryLogAlways("Executable: %s", exeName.c_str());
}
#endif
CryLogAlways("FileVersion: %d.%d.%d.%d", m_fileVersion.v[3], m_fileVersion.v[2], m_fileVersion.v[1], m_fileVersion.v[0]);
#if defined(LY_BUILD)
CryLogAlways("ProductVersion: %d.%d.%d.%d - Build %d", m_productVersion.v[3], m_productVersion.v[2], m_productVersion.v[1], m_productVersion.v[0], LY_BUILD);
#else // defined(LY_BUILD)
CryLogAlways("ProductVersion: %d.%d.%d.%d", m_productVersion.v[3], m_productVersion.v[2], m_productVersion.v[1], m_productVersion.v[0]);
#endif // defined(LY_BUILD)
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMCFG_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(SystemCFG_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(_MSC_VER)
CryLogAlways("Using Microsoft (tm) C++ Standard Library implementation\n");
#elif defined(__clang__)
CryLogAlways("Using CLANG C++ Standard Library implementation\n");
#elif defined(__GNUC__)
CryLogAlways("Using GNU C++ Standard Library implementation\n");
#else
#error "Please specify C++ STL library"
#endif
}
//////////////////////////////////////////////////////////////////////////
void CSystem::LogBuildInfo()
{
auto projectName = AZ::Utils::GetProjectName();
CryLogAlways("GameName: %s", projectName.c_str());
CryLogAlways("BuildTime: " __DATE__ " " __TIME__);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class CCVarSaveDump
: public ICVarDumpSink
{
public:
CCVarSaveDump(FILE* pFile)
{
m_pFile = pFile;
}
virtual void OnElementFound(ICVar* pCVar)
{
if (!pCVar)
{
return;
}
int nFlags = pCVar->GetFlags();
if (((nFlags & VF_DUMPTODISK) && (nFlags & VF_MODIFIED)) || (nFlags & VF_WASINCONFIG))
{
string szValue = pCVar->GetString();
int pos;
pos = 1;
for (;; )
{
pos = szValue.find_first_of("\\", pos);
if (pos == string::npos)
{
break;
}
szValue.replace(pos, 1, "\\\\", 2);
pos += 2;
}
// replace " with \"
pos = 1;
for (;; )
{
pos = szValue.find_first_of("\"", pos);
if (pos == string::npos)
{
break;
}
szValue.replace(pos, 1, "\\\"", 2);
pos += 2;
}
string szLine = pCVar->GetName();
if (pCVar->GetType() == CVAR_STRING)
{
szLine += " = \"" + szValue + "\"\r\n";
}
else
{
szLine += " = " + szValue + "\r\n";
}
if (pCVar->GetFlags() & VF_WARNING_NOTUSED)
{
fputs("-- REMARK: the following was not assigned to a console variable\r\n", m_pFile);
}
fputs(szLine.c_str(), m_pFile);
}
}
private: // --------------------------------------------------------
FILE* m_pFile; //
};
//////////////////////////////////////////////////////////////////////////
void CSystem::SaveConfiguration()
{
}
//////////////////////////////////////////////////////////////////////////
// system cfg
//////////////////////////////////////////////////////////////////////////
CSystemConfiguration::CSystemConfiguration(const string& strSysConfigFilePath, CSystem* pSystem, ILoadConfigurationEntrySink* pSink, bool warnIfMissing)
: m_strSysConfigFilePath(strSysConfigFilePath)
, m_bError(false)
, m_pSink(pSink)
, m_warnIfMissing(warnIfMissing)
{
assert(pSink);
m_pSystem = pSystem;
m_bError = !ParseSystemConfig();
}
//////////////////////////////////////////////////////////////////////////
CSystemConfiguration::~CSystemConfiguration()
{
}
//////////////////////////////////////////////////////////////////////////
bool CSystemConfiguration::ParseSystemConfig()
{
string filename = m_strSysConfigFilePath;
if (strlen(PathUtil::GetExt(filename)) == 0)
{
filename = PathUtil::ReplaceExtension(filename, "cfg");
}
CCryFile file;
string filenameLog;
{
int flags = AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK;
if (filename[0] == '@')
{
// this is used when theres a very specific file to read, like @user@/game.cfg which is read
// IN ADDITION to the one in the game folder, and afterwards to override values in it.
// if the file is missing and its already prefixed with an alias, there is no need to look any further.
if (!(file.Open(filename, "rb", flags)))
{
if (m_warnIfMissing)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Config file %s not found!", filename.c_str());
}
return false;
}
}
else
{
// otherwise, if the file isn't prefixed with an alias, then its likely one of the convenience mappings
// to either root or assets/config. this is done so that code can just request a simple file name and get its data
if (
!(file.Open(filename, "rb", flags)) &&
!(file.Open(string("@root@/") + filename, "rb", flags)) &&
!(file.Open(string("@assets@/") + filename, "rb", flags)) &&
!(file.Open(string("@assets@/config/") + filename, "rb", flags)) &&
!(file.Open(string("@assets@/config/spec/") + filename, "rb", flags))
)
{
if (m_warnIfMissing)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Config file %s not found!", filename.c_str());
}
return false;
}
}
filenameLog = file.GetAdjustedFilename();
}
INDENT_LOG_DURING_SCOPE();
int nLen = file.GetLength();
if (nLen == 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Couldn't get length for Config file %s", filename.c_str());
return false;
}
char* sAllText = new char [nLen + 16];
if (file.ReadRaw(sAllText, nLen) < (size_t)nLen)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Couldn't read Config file %s", filename.c_str());
return false;
}
sAllText[nLen] = '\0';
sAllText[nLen + 1] = '\0';
string strGroup; // current group e.g. "[General]"
char* strLast = sAllText + nLen;
char* str = sAllText;
while (str < strLast)
{
char* s = str;
while (str < strLast && *str != '\n' && *str != '\r')
{
str++;
}
*str = '\0';
str++;
while (str < strLast && (*str == '\n' || *str == '\r'))
{
str++;
}
string strLine = s;
// detect groups e.g. "[General]" should set strGroup="General"
{
string strTrimmedLine(RemoveWhiteSpaces(strLine));
size_t size = strTrimmedLine.size();
if (size >= 3)
{
if (strTrimmedLine[0] == '[' && strTrimmedLine[size - 1] == ']') // currently no comments are allowed to be behind groups
{
strGroup = &strTrimmedLine[1];
strGroup.resize(size - 2); // remove [ and ]
continue; // next line
}
}
}
//trim all whitespace characters at the beginning and the end of the current line and store its size
strLine.Trim();
size_t strLineSize = strLine.size();
//skip comments, comments start with ";" or "--" but may have preceding whitespace characters
if (strLineSize > 0)
{
if (strLine[0] == ';')
{
continue;
}
else if (strLine.find("--") == 0)
{
continue;
}
}
//skip empty lines
else
{
continue;
}
//if line contains a '=' try to read and assign console variable
string::size_type posEq(strLine.find("=", 0));
if (string::npos != posEq)
{
string stemp(strLine, 0, posEq);
string strKey(RemoveWhiteSpaces(stemp));
{
// extract value
string::size_type posValueStart(strLine.find("\"", posEq + 1) + 1);
string::size_type posValueEnd(strLine.rfind('\"'));
string strValue;
if (string::npos != posValueStart && string::npos != posValueEnd)
{
strValue = string(strLine, posValueStart, posValueEnd - posValueStart);
}
else
{
string strTmp(strLine, posEq + 1, strLine.size() - (posEq + 1));
strValue = RemoveWhiteSpaces(strTmp);
}
{
// replace '\\\\' with '\\' and '\\\"' with '\"'
strValue.replace("\\\\", "\\");
strValue.replace("\\\"", "\"");
m_pSink->OnLoadConfigurationEntry(strKey, strValue, strGroup);
}
}
}
else
{
gEnv->pLog->LogWithType(ILog::eWarning, "%s -> invalid configuration line: %s", filename.c_str(), strLine.c_str());
}
}
delete []sAllText;
CryLog("Loading Config file %s (%s)", filename.c_str(), filenameLog.c_str());
m_pSink->OnLoadConfigurationEntry_End();
return true;
}
//////////////////////////////////////////////////////////////////////////
void CSystem::OnLoadConfigurationEntry(const char* szKey, const char* szValue, [[maybe_unused]] const char* szGroup)
{
bool azConsoleProcessed = false;
auto console = AZ::Interface<AZ::IConsole>::Get();
if (console)
{
AZStd::string command(AZStd::string::format("%s %s", szKey, szValue));
azConsoleProcessed = console->PerformCommand(command.c_str());
}
if (!azConsoleProcessed)
{
if (!gEnv->pConsole)
{
return;
}
if (*szKey != 0)
{
gEnv->pConsole->LoadConfigVar(szKey, szValue);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CSystem::LoadConfiguration(const char* sFilename, ILoadConfigurationEntrySink* pSink, bool warnIfMissing)
{
if (sFilename && strlen(sFilename) > 0)
{
if (!pSink)
{
pSink = this;
}
CSystemConfiguration tempConfig(sFilename, this, pSink, warnIfMissing);
}
}