SPEC-7531 Change Code/CryEngine to Code/Legacy (#1634)

* git mv Code\CryEngine Code\Legacy
* redirecting CMakeLists.txt
* fixing uic warning
* Some more CryEngine mentions
* validation scripts

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-06-29 13:51:24 -07:00
committed by GitHub
parent f570925c0d
commit 9f0bbf3b74
384 changed files with 25 additions and 31 deletions
+183
View File
@@ -0,0 +1,183 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Console/IConsole.h>
#include <CryAssert.h>
namespace AZ
{
AZ_CVAR_EXTERNED(int, bg_traceLogLevel);
}
/**
* Hook Trace bus so we can funnel AZ asserts, warnings, etc to CryEngine.
*
* Note: This is currently owned by CrySystem, because CrySystem owns
* the logging mechanism for which it is relevant.
*/
class AZCoreLogSink
: public AZ::Debug::TraceMessageBus::Handler
{
public:
inline static void Connect()
{
GetInstance().m_ignoredAsserts = new IgnoredAssertMap();
GetInstance().BusConnect();
}
inline static void Disconnect()
{
GetInstance().BusDisconnect();
delete GetInstance().m_ignoredAsserts;
}
static AZCoreLogSink& GetInstance()
{
static AZCoreLogSink s_sink;
return s_sink;
}
static bool IsCryLogReady()
{
static bool hasSetCVar = false;
bool ready = gEnv && gEnv->pSystem && gEnv->pLog;
#ifdef _RELEASE
if(!hasSetCVar && ready)
{
// AZ logging only has a concept of 3 levels (error, warning, info) but cry logging has 4 levels (..., messaging). If info level is set, we'll turn on messaging as well
int logLevel = AZ::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::bg_traceLogLevel;
gEnv->pConsole->GetCVar("log_WriteToFileVerbosity")->Set(logLevel);
hasSetCVar = true;
}
#endif
return ready;
}
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override
{
#if defined(USE_CRY_ASSERT) && AZ_LEGACY_CRYSYSTEM_TRAIT_DO_PREASSERT
AZ::Crc32 crc;
crc.Add(&line, sizeof(line));
if (fileName)
{
crc.Add(fileName, strlen(fileName));
}
bool* ignore = nullptr;
auto foundIter = m_ignoredAsserts->find(crc);
if (foundIter == m_ignoredAsserts->end())
{
ignore = &((*m_ignoredAsserts)[crc]);
*ignore = false;
}
else
{
ignore = &((*m_ignoredAsserts)[crc]);
}
if (!(*ignore))
{
using namespace AZ::Debug;
Trace::Output(nullptr, "\n==================================================================\n");
AZ::OSString outputMsg = AZ::OSString::format("Trace::Assert\n %s(%d): '%s'\n%s\n", fileName, line, func, message);
Trace::Output(nullptr, outputMsg.c_str());
// Suppress 3 in stack depth - this function, the bus broadcast that got us here, and Trace::Assert
Trace::Output(nullptr, "------------------------------------------------\n");
Trace::PrintCallstack(nullptr, 3);
Trace::Output(nullptr, "\n==================================================================\n");
AZ::EnvironmentVariable<bool> inEditorBatchMode = AZ::Environment::FindVariable<bool>("InEditorBatchMode");
if (!inEditorBatchMode.IsConstructed() || !inEditorBatchMode.Get())
{
// Note - CryAssertTrace doesn't actually print any info to logging
// it just stores the message internally for the message box in CryAssert to use
CryAssertTrace("%s", message);
if (CryAssert("Assertion failed", fileName, line, ignore) || Trace::IsDebuggerPresent())
{
Trace::Break();
}
}
}
else
{
CryLogAlways("%s", message);
}
return true; // suppress default AzCore behavior.
#else
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
AZ_UNUSED(message);
return false; // allow AZCore to do its default behavior. This usually results in an application shutdown.
#endif
}
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override
{
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
gEnv->pLog->LogError("(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
}
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override
{
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
}
bool OnOutput(const char* window, const char* message) override
{
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
if (window == AZ::Debug::Trace::GetDefaultSystemWindow())
{
CryLogAlways("%s", message);
}
else
{
CryLog("(%s) - %s", window, message);
}
return true; // suppress default AzCore behavior.
}
private:
using IgnoredAssertMap = AZStd::unordered_map<AZ::Crc32, bool, AZStd::hash<AZ::Crc32>, AZStd::equal_to<AZ::Crc32>, AZ::OSStdAllocator>;
IgnoredAssertMap* m_ignoredAsserts;
};
@@ -0,0 +1,48 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include <AZCrySystemInitLogSink.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/std/string/osstring.h>
#include <ISystem.h>
namespace AZ
{
namespace Debug
{
void CrySystemInitLogSink::SetFatalMessageBox(bool enable)
{
m_isMessageBoxFatal = enable;
}
void CrySystemInitLogSink::DisplayCollectedErrorStrings() const
{
if (m_errorStringsCollected.empty())
{
return;
}
AZ::OSString msgBoxMessage;
msgBoxMessage.append("O3DE could not initialize correctly for the following reason(s):");
for (const AZ::OSString& errMsg : m_errorStringsCollected)
{
msgBoxMessage.append("\n");
msgBoxMessage.append(errMsg.c_str());
}
Trace::Output(nullptr, "\n==================================================================\n");
Trace::Output(nullptr, msgBoxMessage.c_str());
Trace::Output(nullptr, "\n==================================================================\n");
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "O3DE Initialization Failed", msgBoxMessage.c_str(), false);
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,44 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/Logging/StartupLogSinkReporter.h>
namespace AZ
{
namespace Debug
{
/**
* A handler for the TraceMessageBus which is meant to collect errors and asserts during CrySystem::Init to display them to the user.
* It will also elevate all output to CryLogAlways while it is in scope.
* As such, it assumes that it is being used within the CrySystem library and gEnv and gEnv->pSystem are valid.
*/
class CrySystemInitLogSink
: public StartupLogSink
{
public:
CrySystemInitLogSink() = default;
/**
* Enables or disables the fatal flags to send to the platform specific message box
*/
void SetFatalMessageBox(bool enable = true);
/**
* Formats the collected error messages into a platform specific message box to display to the user.
* This expects that a valid gEnv->pSystem exists, and the IOSPlatform has been initialzied.
* Will also log output messages to whatever medium is possible for OnOutput messages on TraceMessageBus (ex. debug output, logs).
*/
void DisplayCollectedErrorStrings() const override;
protected:
bool m_isMessageBoxFatal = false;
};
} // namespace Debug
} // namespace AZ
+51
View File
@@ -0,0 +1,51 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
add_subdirectory(XML)
ly_add_target(
NAME CrySystem.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
crysystem_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::expat
3rdParty::lz4
3rdParty::md5
3rdParty::tiff
3rdParty::zlib
3rdParty::zstd
Legacy::CryCommon
Legacy::CrySystem.XMLBinary
Legacy::RemoteConsoleCore
AZ::AzFramework
)
ly_add_source_properties(
SOURCES SystemInit.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_PAL_TOOLS_DEFINES}
)
ly_add_target(
NAME CrySystem ${PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE}
NAMESPACE Legacy
FILES_CMAKE
crysystem_shared_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
Legacy::CrySystem.Static
AZ::AzCore
Legacy::CryCommon
)
+200
View File
@@ -0,0 +1,200 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "CmdLine.h"
void CCmdLine::PushCommand(const string& sCommand, const string& sParameter)
{
if (sCommand.empty())
{
return;
}
ECmdLineArgType type = eCLAT_Normal;
const char* szCommand = sCommand.c_str();
if (sCommand[0] == '-')
{
type = eCLAT_Pre;
++szCommand;
// Handle cmd line parameters that use -- properly
if (szCommand[0] == '-')
{
++szCommand;
}
}
else if (sCommand[0] == '+')
{
type = eCLAT_Post;
++szCommand;
}
m_args.push_back(CCmdLineArg(szCommand, sParameter.c_str(), type));
}
CCmdLine::CCmdLine(const char* commandLine)
{
m_sCmdLine = commandLine;
char* src = (char*)commandLine;
string command, parameter;
for (;; )
{
if (*src == '\0')
{
break;
}
string arg = Next(src);
if (m_args.empty())
{
// this is the filename, convert backslash to forward slash
arg.replace('\\', '/');
m_args.push_back(CCmdLineArg("filename", arg.c_str(), eCLAT_Executable));
}
else
{
bool bSecondCharIsNumber = false;
if (arg[0] && arg[1] >= '0' && arg[1] <= '9')
{
bSecondCharIsNumber = true;
}
if ((arg[0] == '-' && !bSecondCharIsNumber)
|| (arg[0] == '+' && !bSecondCharIsNumber)
|| command.empty()) // separator or first parameter
{
PushCommand(command, parameter);
command = arg;
parameter = "";
}
else
{
if (parameter.empty())
{
parameter = arg;
}
else
{
parameter += string(" ") + arg;
}
}
}
}
PushCommand(command, parameter);
}
CCmdLine::~CCmdLine()
{
}
const ICmdLineArg* CCmdLine::GetArg(int n) const
{
if ((n >= 0) && (n < (int)m_args.size()))
{
return &m_args[n];
}
return 0;
}
int CCmdLine::GetArgCount() const
{
return (int)m_args.size();
}
const ICmdLineArg* CCmdLine::FindArg(const ECmdLineArgType ArgType, const char* name, bool caseSensitive) const
{
if (caseSensitive)
{
for (std::vector<CCmdLineArg>::const_iterator it = m_args.begin(); it != m_args.end(); ++it)
{
if (it->GetType() == ArgType)
{
if (!strcmp(it->GetName(), name))
{
return &(*it);
}
}
}
}
else
{
for (std::vector<CCmdLineArg>::const_iterator it = m_args.begin(); it != m_args.end(); ++it)
{
if (it->GetType() == ArgType)
{
if (!_stricmp(it->GetName(), name))
{
return &(*it);
}
}
}
}
return 0;
}
string CCmdLine::Next(char*& src)
{
char ch = 0;
char* org = src;
ch = *src++;
while (ch)
{
switch (ch)
{
case '\'':
case '\"':
org = src;
while ((*src++ != ch) && *src)
{
;
}
return string(org, src - 1);
case '[':
org = src;
while ((*src++ != ']') && *src)
{
;
}
return string(org, src - 1);
case ' ':
ch = *src++;
continue;
default:
org = src - 1;
for (; *src != ' ' && *src != '\t' && *src; ++src)
{
;
}
return string(org, src);
}
ch = *src++;
}
return string();
}
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Implements the command line interface ICmdLine.
#ifndef CRYINCLUDE_CRYSYSTEM_CMDLINE_H
#define CRYINCLUDE_CRYSYSTEM_CMDLINE_H
#pragma once
#include <ICmdLine.h>
#include "CmdLineArg.h"
class CCmdLine
: public ICmdLine
{
public:
CCmdLine(const char* commandLine);
virtual ~CCmdLine();
virtual const ICmdLineArg* GetArg(int n) const;
virtual int GetArgCount() const;
virtual const ICmdLineArg* FindArg(const ECmdLineArgType ArgType, const char* name, bool caseSensitive = false) const;
virtual const char* GetCommandLine() const { return m_sCmdLine; };
private:
void PushCommand(const string& sCommand, const string& sParameter);
string Next(char*& str);
string m_sCmdLine;
std::vector<CCmdLineArg> m_args;
};
#endif // CRYINCLUDE_CRYSYSTEM_CMDLINE_H
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "CmdLineArg.h"
CCmdLineArg::CCmdLineArg(const char* name, const char* value, ECmdLineArgType type)
{
m_name = name;
m_value = value;
m_type = type;
}
CCmdLineArg::~CCmdLineArg()
{
}
const char* CCmdLineArg::GetName() const
{
return m_name.c_str();
}
const char* CCmdLineArg::GetValue() const
{
return m_value.c_str();
}
const ECmdLineArgType CCmdLineArg::GetType() const
{
return m_type;
}
const float CCmdLineArg::GetFValue() const
{
return (float)atof(m_value.c_str());
}
const int CCmdLineArg::GetIValue() const
{
return atoi(m_value.c_str());
}
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Implements the command line argument interface ICmdLineArg.
#ifndef CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
#define CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
#pragma once
#include <ICmdLine.h>
class CCmdLineArg
: public ICmdLineArg
{
public:
CCmdLineArg(const char* name, const char* value, ECmdLineArgType type);
virtual ~CCmdLineArg();
const char* GetName() const;
const char* GetValue() const;
const ECmdLineArgType GetType() const;
const float GetFValue() const;
const int GetIValue() const;
private:
ECmdLineArgType m_type;
string m_name;
string m_value;
};
#endif // CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Executes an ASCII batch file of console commands...
#include "CrySystem_precompiled.h"
#include "ConsoleBatchFile.h"
#include "IConsole.h"
#include "ISystem.h"
#include "XConsole.h"
#include <CryPath.h>
#include <stdio.h>
#include "System.h"
#include <AzCore/IO/FileIO.h>
IConsole* CConsoleBatchFile::m_pConsole = NULL;
void CConsoleBatchFile::Init()
{
m_pConsole = gEnv->pConsole;
REGISTER_COMMAND("exec", (ConsoleCommandFunc)ExecuteFileCmdFunc, 0, "executes a batch file of console commands");
}
//////////////////////////////////////////////////////////////////////////
void CConsoleBatchFile::ExecuteFileCmdFunc(IConsoleCmdArgs* args)
{
if (!m_pConsole)
{
Init();
}
if (!args->GetArg(1))
{
return;
}
ExecuteConfigFile(args->GetArg(1));
}
//////////////////////////////////////////////////////////////////////////
bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
{
if (!sFilename)
{
return false;
}
if (!m_pConsole)
{
Init();
}
string filename;
if (sFilename[0] != '@') // console config files are actually by default in @root@ instead of @assets@
{
// However, if we've passed in a relative or absolute path that matches an existing file name,
// don't change it. Only change it to "@root@/filename" and strip off any relative paths
// if the given pattern *didn't* match a file.
if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(sFilename))
{
filename = sFilename;
}
else
{
filename = PathUtil::Make("@root@", PathUtil::GetFile(sFilename));
}
}
else
{
filename = sFilename;
}
if (strlen(PathUtil::GetExt(filename)) == 0)
{
filename = PathUtil::ReplaceExtension(filename, "cfg");
}
//////////////////////////////////////////////////////////////////////////
CCryFile file;
{
const char* szLog = "Executing console batch file (try game,config,root):";
string filenameLog;
string sfn = PathUtil::GetFile(filename);
if (file.Open(filename, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("game/") + sfn;
}
else if (file.Open(string("config/") + sfn, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("game/config/") + sfn;
}
else if (file.Open(string("./") + sfn, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("./") + sfn;
}
else
{
CryLog("%s \"%s\" not found!", szLog, filename.c_str());
return false;
}
CryLog("%s \"%s\" found in %s ...", szLog, PathUtil::GetFile(filenameLog.c_str()), PathUtil::GetPath(filenameLog).c_str());
}
int nLen = file.GetLength();
char* sAllText = new char [nLen + 16];
file.ReadRaw(sAllText, nLen);
sAllText[nLen] = '\0';
sAllText[nLen + 1] = '\0';
/*
This can't work properly as ShowConsole() can be called during the execution of the scripts,
which means bConsoleStatus is outdated and must not be set at the end of the function
bool bConsoleStatus = ((CXConsole*)m_pConsole)->GetStatus();
((CXConsole*)m_pConsole)->SetStatus(false);
*/
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;
//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;
}
{
m_pConsole->ExecuteString(strLine);
}
}
// See above
// ((CXConsole*)m_pConsole)->SetStatus(bConsoleStatus);
delete []sAllText;
return true;
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Executes an ASCII batch file of console commands...
#ifndef CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
#define CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
#pragma once
struct IConsoleCmdArgs;
struct IConsole;
class CConsoleBatchFile
{
public:
static void Init();
static bool ExecuteConfigFile(const char* filename);
private:
static void ExecuteFileCmdFunc(IConsoleCmdArgs* args);
static IConsole* m_pConsole;
};
#endif // CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
+942
View File
@@ -0,0 +1,942 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#if defined(WIN32) || defined(WIN64)
#include "ConsoleHelpGen.h"
#include "System.h"
// remove bad characters, toupper, not very fast
string CConsoleHelpGen::FixAnchorName(const char* szName)
{
string ret;
const char* p = szName;
while (*p)
{
if ((*p >= 'a' && *p <= 'z')
|| (*p >= 'A' && *p <= 'Z')
|| (*p >= '0' && *p <= '9'))
{
if (*p >= 'a' && *p <= 'z')
{
ret += *p - 'a' + 'A';
}
else
{
ret += *p;
}
}
++p;
}
return ret;
}
string CConsoleHelpGen::GetCleanPrefix(const char* p)
{
string sRet;
while (*p != '_' && *p != 0)
{
sRet += *p++;
}
return sRet;
}
string CConsoleHelpGen::SplitPrefixString_Part1(const char* p)
{
string sRet;
while (*p != 10 && *p != 13 && *p != 0)
{
sRet += *p++;
}
return sRet;
}
const char* CConsoleHelpGen::SplitPrefixString_Part2(const char* p)
{
while (*p != 10 && *p != 13 && *p != 0)
{
p++;
}
while (*p == 10 || *p == 13)
{
p++;
}
return p;
}
void CConsoleHelpGen::StartPage(FILE* f, const char* szPageName, const char* szPageDescription) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<HTML><HEAD><TITLE>%s</TITLE><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">", szPageName);
fprintf(f, "<META NAME=\"DESCRIPTION\" CONTENT=\"%s\">", szPageDescription);
fprintf(f, "<META NAME=\"author\" content=\"Crytek\">");
fprintf(f, "<META NAME=\"copyright\" CONTENT=\"Crytek\">");
fprintf(f, "<META NAME=\"KEYWORDS\" CONTENT=\"CryEngine,Crytek\">");
fprintf(f, "<META NAME=\"distribution\" CONTENT=\"Crytek\">");
fprintf(f, "<META NAME=\"revisit-after\" CONTENT=\"10 days\">");
fprintf(f, "<META NAME=\"robots\" CONTENT=\"INDEX, NOFOLLOW\">");
fprintf(f, "</HEAD><BODY bgcolor=#ffffff leftmargin=0 topmargin=0 alink=#0000ff link=#0000ff vlink=#0000ff text=#000000>");
}
}
void CConsoleHelpGen::EndPage(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<P></P></BODY></HTML>");
}
}
void CConsoleHelpGen::KeyValue(FILE* f, const char* szKey, const char* szValue) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<b>%s:</b> %s<br>\n", szKey, szValue);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"*%s:* %s\n",szKey,szValue);
fprintf(f, "| *%s:* | %s |\n", szKey, szValue);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::LogVersion(FILE* f) const
{
char s[1024];
{
GetModuleFileName(NULL, s, sizeof(s));
char fdir[_MAX_PATH];
char fdrive[_MAX_PATH];
char file[_MAX_PATH];
char fext[_MAX_PATH];
_splitpath_s(s, fdrive, fdir, file, fext);
KeyValue(f, "Executable", (string(file) + fext).c_str());
}
{
time_t ltime;
time(&ltime);
tm today;
localtime_s(&today, &ltime);
strftime(s, 128, "%c", &today);
KeyValue(f, "Date(MM/DD/YY) Time", s);
}
{
const SFileVersion& ver = gEnv->pSystem->GetFileVersion();
ver.ToString(s, sizeof(s));
KeyValue(f, "FileVersion", s);
}
{
const SFileVersion& ver = gEnv->pSystem->GetProductVersion();
ver.ToString(s, sizeof(s));
KeyValue(f, "ProductVersion", s);
}
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<br>\n");
}
}
void CConsoleHelpGen::StartH1(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h1>%s</h1>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "h1. %s\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndH1(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<br>\n");
}
}
void CConsoleHelpGen::StartH3(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h3>%s</h3><ul>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "\nh3. %s\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndH3(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
}
else
{
assert(0);
}
}
void CConsoleHelpGen::StartCVar(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h3>%s</h3><ul>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"\nh3. %s\n",szName);
fprintf(f, "\n<div class=\"panel\" style=\"border-style: none;border-width: 1px;\"><div class=\"panelContent\"><p><b>%s</b><br/>\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndCVar(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "</div></div>\n\n");
}
else
{
assert(0);
}
}
void CConsoleHelpGen::SingleLinePrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const
{
// group within the list of all groups (no elements)
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s_ %s</a></li>\n", szLink, szPrefix, szPrefixDesc);
}
else if (m_eWorkMode == eWM_Confluence)
{
string sPrefix;
if (*szPrefix)
{
sPrefix = string(szPrefix) + "_";
}
// fprintf(f,"| %s | [%s|%s] |\n",sPrefix.c_str(),szPrefixDesc,szLink); // e.g. "" "CL_" "CC_" "I_" "T_"
fprintf(f, "{section:border=false}\n"
"{column:width=50px}{align:right}%s{align}{column}\n"
"{column:width=10px}{column}\n"
"{column}{align:left}[%s|%s]{align}{column}\n"
"{section}\n",
sPrefix.c_str(), szPrefixDesc, szLink); // e.g. "" "CL_" "CC_" "I_" "T_"
}
else
{
assert(0);
}
}
void CConsoleHelpGen::StartPrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const
{
// group before all the group elements
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s_ %s</li></a><ul>\n", szLink, szPrefix, szPrefixDesc);
}
else if (m_eWorkMode == eWM_Confluence)
{
if (*szPrefix)
{
fprintf(f, "* [%s %s|%s]\n", szPrefix, szPrefixDesc, szLink);
}
else
{
fprintf(f, "* [%s|%s]\n", szPrefixDesc, szLink);
}
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndPrefix(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
}
void CConsoleHelpGen::SingleLineEntry_InGlobal(FILE* f, const char* szName, const char* szLink) const
{
// element within a group
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s</a></li>\n", szLink, szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "** [%s|%s]\n", szName, szLink);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::SingleLineEntry_InGroup(FILE* f, const char* szName, const char* szLink) const
{
// element within a group
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s</a></li>\n", szLink, szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "* [%s|%s]\n", szName, szLink);
}
else
{
assert(0);
}
}
// szName without #
void CConsoleHelpGen::Anchor(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<a name=\"%s\"></a>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "{anchor:%s}", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szLocalPrefix) const
{
CXConsole::ConsoleVariablesMap::const_iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
if (_strnicmp(var->GetName(), szLocalPrefix, strlen(szLocalPrefix)) == 0)
{
setCmdAndVars.insert(var->GetName());
}
}
}
void CConsoleHelpGen::InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szLocalPrefix) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
if (_strnicmp(cmd.m_sName, szLocalPrefix, strlen(szLocalPrefix)) == 0)
{
setCmdAndVars.insert(cmd.m_sName.c_str());
}
}
}
void CConsoleHelpGen::InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const
{
CXConsole::ConsoleVariablesMap::const_iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
bool bInsert = true;
{
std::map<string, const char*>::const_iterator it2, end = mapPrefix.end();
for (it2 = mapPrefix.begin(); it2 != end; ++it2)
{
if (it2->first != "___" && _strnicmp(var->GetName(), it2->first, it2->first.size()) == 0)
{
bInsert = false;
break;
}
}
}
if (bInsert)
{
setCmdAndVars.insert(var->GetName());
}
}
}
void CConsoleHelpGen::InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
bool bInsert = true;
{
std::map<string, const char*>::const_iterator it2, end = mapPrefix.end();
for (it2 = mapPrefix.begin(); it2 != end; ++it2)
{
if (it2->first != "___" && _strnicmp(cmd.m_sName, it2->first, it2->first.size()) == 0)
{
bInsert = false;
break;
}
}
}
if (bInsert)
{
setCmdAndVars.insert(cmd.m_sName.c_str());
}
}
}
void CConsoleHelpGen::CreateSingleEntryFile(const char* szName) const
{
assert(m_eWorkMode == eWM_Confluence); // only needed for confluence
FILE* f3 = nullptr;
azfopen(&f3, (string(GetFolderName()) + GetFileExtension() + "/" + FixAnchorName(szName)).c_str(), "w");
if (!f3)
{
assert(0);
return; // error
}
// StartPage(f3,szName,""); // HTML style
IncludeSingleEntry(f3, szName);
// EndPage(f3); // HTML style
fclose(f3);
}
const CConsoleCommand* CConsoleHelpGen::FindConsoleCommand(const char* szName) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
if (strcmp(cmd.m_sName.c_str(), szName) == 0)
{
return &cmd;
}
}
return 0;
}
void CConsoleHelpGen::IncludeSingleEntry(FILE* f, const char* szName) const
{
StartCVar(f, szName);
uint32 dwFlags = 0;
const char* szHelp = "";
// slow but good for simpler code
if (ICVar* pVar = gEnv->pConsole->GetCVar(szName))
{
dwFlags = pVar->GetFlags();
szHelp = pVar->GetHelp();
}
else if (const CConsoleCommand* pCommand = FindConsoleCommand(szName))
{
dwFlags = pCommand->m_nFlags;
szHelp = pCommand->m_sHelp.c_str();
}
else
{
assert(0); // internal error
}
const char* szFlags = CXConsole::GetFlagsString(dwFlags);
if (*szFlags)
{
// fprintf(f3,"%%GRAY%% %s %%ENDCOLOR%%<br>\n",szFlags); // twiki style
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "%s<br>\n", szFlags); // simple HTML style
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "<font color=\"#808080\">%s</font></p>\n", szFlags);
}
else
{
assert(0);
}
}
if (*szHelp == 0)
{
// fprintf(f,"%%TODO%%\n"); // wiki style, in our wiki %TODO% is defined as <img src=\"%%ICONURL{todo}%%\" width=\"37\" height=\"16\" alt=\"TODO\" border=\"0\" />
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote></b>*TODO*</b></blockquote>\n"); // HTML style
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "{warning}TODO{warning}\n"); // Confluence style
}
else
{
assert(0);
}
}
else
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote><pre><verbatim><tt>\n%s\n</tt></verbatim></pre></blockquote>\n", szHelp); // HTML style, <tt> to get fixed with font (layout in code is often making the assumption the forn is fixed width)
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "<pre>\n");
string sHelp = szHelp;
// currently not required as the {noformat} is used
// sHelp.replace("[","\\["); sHelp.replace("]","\\]");
// sHelp.replace("{","\\{"); sHelp.replace("}","\\}");
// sHelp.replace("(","\\("); sHelp.replace(")","\\)");
fprintf(f, "%s\n", sHelp.c_str()); // Confluence style
fprintf(f, "</pre>");
}
else
{
assert(0);
}
}
EndCVar(f);
}
void CConsoleHelpGen::Work()
{
// string sEngineFolder = string("@user@/")+GetFolderName();
// gEnv->pCryPak->RemoveDir(sEngineFolder.c_str()); // todo: check if that works
// gEnv->pFileIO->CreatePath(sEngineFolder.c_str());
m_eWorkMode = eWM_HTML;
CreateMainPages();
m_eWorkMode = eWM_Confluence;
CreateMainPages();
CreateFileForEachEntry();
m_eWorkMode = eWM_None;
}
void CConsoleHelpGen::CreateFileForEachEntry()
{
assert(m_eWorkMode == eWM_Confluence); // only needed for confluence
// generate a single file for each console command
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
CreateSingleEntryFile(cmd.m_sName.c_str());
}
}
// generate a single file for each console variable
{
CXConsole::ConsoleVariablesMap::iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
CreateSingleEntryFile(var->GetName());
}
}
}
void CConsoleHelpGen::CreateMainPages()
{
gEnv->pFileIO->CreatePath(GetFolderName());
std::map<string, const char*> mapPrefix;
// order here doesn't matter, after the name some help can be added (after the first return)
mapPrefix[ "AI_"] = "Artificial Intelligence";
mapPrefix[ "NET_"] = "Network";
mapPrefix[ "ED_"] = "Editor";
mapPrefix[ "ES_"] = "Entity System";
mapPrefix[ "CON_"] = "Console";
mapPrefix[ "AG_"] = "Animation Graph" "\n" "High level animation logic, describes animation selection and flow, matches animation state to current game logical state.";
mapPrefix[ "AC_"] = "Animated Character" "\n" "Better name would be 'Character Movement'.\nBridges game controlled movement and animation controlled movement.";
mapPrefix[ "CA_"] = "Character Animation" "\n" "Motion synthesize and playback, parameterization through blending and inversed kinematics.";
mapPrefix[ "E_"] = "3DEngine";
mapPrefix[ "I_"] = "Input";
mapPrefix[ "FG_"] = "Flow Graph" "\n" "hyper graph: game logic";
mapPrefix[ "P_"] = "Physics";
mapPrefix[ "R_"] = "Renderer";
mapPrefix[ "S_"] = "Sound";
mapPrefix[ "G_"] = "Game" "\n" "game specific, not part of CryEngine";
mapPrefix[ "SYS_"] = "System";
mapPrefix[ "V_"] = "Vehicle";
mapPrefix[ "FT_"] = "Feature Test";
mapPrefix[ "DEMO_"] = "Time Demo";
mapPrefix[ "FT_"] = "Feature Test";
mapPrefix[ "GL_"] = "Game Lobby";
mapPrefix[ "HUD_"] = "Heads Up Display";
mapPrefix[ "KC_"] = "Kill Cam";
mapPrefix[ "PL_"] = "Player";
mapPrefix[ "PP_"] = "Player Progression";
mapPrefix[ "AIM_"] = "Aiming";
mapPrefix["CAPTURE_"] = "Capture";
mapPrefix[ "DS_"] = "Dialog Scripts";
mapPrefix[ "GT_"] = "Game Token";
mapPrefix[ "LOG_"] = "Logging";
mapPrefix[ "MOV_"] = "Movie Sequences";
mapPrefix[ "OSM_"] = "Overload Scene Manager";
mapPrefix["PROFILE_"] = "Profiling";
mapPrefix[ "STAP_"] = "Screen-space Torso Aim Pose";
mapPrefix[ "LUA_"] = "Lua" "\n" "scripting system";
mapPrefix[ "SV_"] = "Server";
mapPrefix[ "MFX_"] = "Material Effects";
mapPrefix[ "M_"] = "Multi threading";
mapPrefix[ "CC_"] = "Character Customization";
mapPrefix[ "CL_"] = "Client";
mapPrefix[ "Q_"] = "Quality" "\n" "usually shader quality";
mapPrefix[ "T_"] = "Timer";
mapPrefix[ "___"] = "Remaining"; // key defined to get it sorted in the end
FILE* f1 = nullptr;
azfopen(&f1, (string(GetFolderName()) + "/index" + GetFileExtension()).c_str(), "w");
if (!f1)
{
return;
}
StartPage(f1, "CryEngine ConsoleHTMLHelp", "main page");
StartH1(f1, "Console Commands and Variables");
LogVersion(f1);
if (m_eWorkMode == eWM_HTML)
{
fprintf(f1, "This list was exported from the engine by using the <b>DumpCommandsVars</b> console command.<br>\n\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f1, "This list was exported from the engine by using the *DumpCommandsVars* console command.\n\n");
}
else
{
assert(0);
}
// show all registered Prefix with one line
{
std::map<string, const char*>::const_iterator it, end = mapPrefix.end();
StartH3(f1, "Registered Prefixes");
for (it = mapPrefix.begin(); it != end; ++it)
{
const char* szLocalPrefix = it->first.c_str(); // can be 0 for remaining ones
string sCleanPrefix = GetCleanPrefix(szLocalPrefix);
string sPrefixName = SplitPrefixString_Part1(it->second);
SingleLinePrefix(f1, sCleanPrefix.c_str(), sPrefixName.c_str(), (string("CONSOLEPREFIX") + FixAnchorName(sCleanPrefix.c_str()) + GetFileExtension()).c_str());
// fprintf(f1," * [[CONSOLEPREFIX%s][%s_ %s]]\n",FixAnchorName(sCleanPrefix.c_str()).c_str(),sCleanPrefix.c_str(),sPrefixName.c_str());
}
EndH3(f1); // Registered prefixes
}
{
std::map<string, const char*>::const_iterator it, it2, end = mapPrefix.end();
StartH3(f1, "Console Commands and Variables Sorted by Prefix");
for (it = mapPrefix.begin(); it != end; ++it)
{
const char* szLocalPrefix = it->first.c_str(); // can be 0 for remaining ones
string sCleanPrefix = GetCleanPrefix(szLocalPrefix);
string sPrefixName = SplitPrefixString_Part1(it->second);
std::set<const char*, string_nocase_lt> setCmdAndVars; // to get console variables and commands sorted together
if (strcmp("___", szLocalPrefix) != 0)
{
// insert all starting with the prefix
InsertConsoleVars(setCmdAndVars, szLocalPrefix);
InsertConsoleCommands(setCmdAndVars, szLocalPrefix);
}
else
{
// insert all not starting with any of the prefix
InsertConsoleVars(setCmdAndVars, mapPrefix);
InsertConsoleCommands(setCmdAndVars, mapPrefix);
}
// -------------------------------
string sSubName = string("CONSOLEPREFIX") + sCleanPrefix;
StartPrefix(f1, sCleanPrefix.c_str(), sPrefixName.c_str(), (sSubName + GetFileExtension()).c_str());
string sFileOut = string(GetFolderName()) + "/" + sSubName + GetFileExtension();
FILE* f2 = nullptr;
azfopen(&f2, sFileOut.c_str(), "w");
if (!f2)
{
fclose(f1);
return;
}
// headline
{
string sHeadline;
if (sCleanPrefix.empty())
{
sHeadline = "Console Commands and Variables Without Special Prefix";
}
else
{
sHeadline = string("Console Commands and Variables with Prefix ") + sCleanPrefix + "_";
}
StartH1(f2, sHeadline.c_str());
}
Explanation(f2, SplitPrefixString_Part2(it->second));
KeyValue(f2, "Possible Flags", CXConsole::GetFlagsString(0xffffffff));
// fprintf(f2,"<b>Possible Flags:</b><br>\n");
// fprintf(f2," <blockquote>%s</blockquote>",CXConsole::GetFlagsString(0xffffffff));
// log console variables and commands
{
StartH3(f2, "Alphabetically Sorted");
std::set<const char*, string_nocase_lt>::const_iterator itI, endI = setCmdAndVars.end();
for (itI = setCmdAndVars.begin(); itI != endI; ++itI)
{
SingleLineEntry_InGlobal(f1, *itI, (sSubName + GetFileExtension() + "#Anchor" + FixAnchorName(*itI)).c_str());
SingleLineEntry_InGroup(f2, *itI, (string("#Anchor") + FixAnchorName(*itI)).c_str());
}
EndH3(f2);
}
{
StartH3(f2, "Console Variables and Commands");
bool bFirst = true;
{
std::set<const char*, string_nocase_lt>::const_iterator itI, endI = setCmdAndVars.end();
for (itI = setCmdAndVars.begin(); itI != endI; ++itI)
{
if (!bFirst)
{
Separator(f2);
}
bFirst = false;
Anchor(f2, (string("Anchor") + FixAnchorName(*itI)).c_str()); // anchor
IncludeSingleEntry(f2, *itI);
}
}
EndH3(f2);
}
EndH1(f2); // Console Commands and Variables ...
fclose(f2);
f2 = 0;
EndPrefix(f1);
}
EndH3(f1); // Console commands and variables sorted by prefix
}
EndH1(f1);
EndPage(f1);
fclose(f1);
m_rParent.ConsoleLogInputResponse("successfully wrote directory %s", GetFolderName());
}
void CConsoleHelpGen::Separator(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<hr>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"----\n");
}
else
{
assert(0);
}
}
void CConsoleHelpGen::Explanation(FILE* f, const char* szText) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote>%s</blockquote><br>\n<br>\n", szText);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"{quote}%s{quote}\n\n",szText);
fprintf(f, "%s\n\n", szText);
}
else
{
assert(0);
}
}
const char* CConsoleHelpGen::GetFileExtension() const
{
if (m_eWorkMode == eWM_HTML)
{
return ".html";
}
else if (m_eWorkMode == eWM_Confluence)
{
}
else
{
assert(0);
}
return "";
}
const char* CConsoleHelpGen::GetFolderName() const
{
if (m_eWorkMode == eWM_HTML)
{
return "ConsoleHTMLHelp";
}
else if (m_eWorkMode == eWM_Confluence)
{
return "ConsoleHTMLHelp/CRYAUTOGEN";
}
else
{
assert(0);
}
return 0;
}
#endif // defined(WIN32) || defined(WIN64)
+122
View File
@@ -0,0 +1,122 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#ifndef CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
#define CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
#if defined(WIN32) || defined(WIN64)
#include "XConsole.h" // CXConsole, struct string_nocase_lt
// extract consoel variable and command help
// in some HTML pages and many small files that can be included in Confluence wiki
// pages (so maintain the documentation only in one place)
//
// Possible improvements/Known issues:
// - Nicer HTML layout (CSS?)
// - Searching in the content of the cvars is tricky (main page doesn't have help content)
// - %TODO% (was wiki image, should look good in confluence and HTML)
// - many small files should be stored in some extra folder for clearity
// - before generating the data the older directoy should be cleaned
// - file should be generated in the user folder
// - "wb" should be used instead of "w", to get the same result in unix
class CConsoleHelpGen
{
public:
CConsoleHelpGen(CXConsole& rParent)
: m_rParent(rParent)
, m_eWorkMode(eWM_None)
{
}
void Work();
private: // --------------------------------------------------------
enum EWorkMode
{
eWM_None,
eWM_HTML,
eWM_Confluence
};
//
void CreateMainPages();
// to create one file for for each cvar/command in confluence style
void CreateFileForEachEntry();
// insert if the name starts with the with prefix
void InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szPrefix) const;
// insert if the name starts with the with prefix
void InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szPrefix) const;
// insert if the name does not start with any of the prefix in the map
void InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const;
// insert if the name does not start with any of the prefix in the map
void InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const;
// a single file for the entry is generate
void CreateSingleEntryFile(const char* szName) const;
void IncludeSingleEntry(FILE* f, const char* szName) const;
static string FixAnchorName(const char* szName);
static string GetCleanPrefix(const char* p);
// split before "|" (to get the prefix itself)
static string SplitPrefixString_Part1(const char* p);
// split string after "|" (to get the optional help)
static const char* SplitPrefixString_Part2(const char* p);
void StartPage(FILE* f, const char* szPageName, const char* szPageDescription) const;
void EndPage(FILE* f) const;
void StartH1(FILE* f, const char* szName) const;
void EndH1(FILE* f) const;
void StartH3(FILE* f, const char* szName) const;
void EndH3(FILE* f) const;
void StartCVar(FILE* f, const char* szName) const;
void EndCVar(FILE* f) const;
void SingleLinePrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const;
void StartPrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const;
void EndPrefix(FILE* f) const;
void SingleLineEntry_InGlobal(FILE* f, const char* szName, const char* szLink) const;
void SingleLineEntry_InGroup(FILE* f, const char* szName, const char* szLink) const;
void Anchor(FILE* f, const char* szName) const;
// to log some inital stats like date or application name
void KeyValue(FILE* f, const char* szKey, const char* szValue) const;
// prefix explanation and indention for following text
void Explanation(FILE* f, const char* szText) const;
void Separator(FILE* f) const;
void LogVersion(FILE* f) const;
// case senstive
// Returns
// 0 if not found
const CConsoleCommand* FindConsoleCommand(const char* szName) const;
// ----------------------------------------------
//
const char* GetFolderName() const;
//
const char* GetFileExtension() const;
// ----------------------------------------------
CXConsole& m_rParent;
EWorkMode m_eWorkMode; // during Work() thise true:HTML and false:Confluence at some point
};
#endif // defined(WIN32) || defined(WIN64)
#endif // CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
@@ -0,0 +1,130 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Precompiled Header.
#pragma once
#include <AzCore/PlatformIncl.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define STDAFX_H_SECTION_1 1
#define STDAFX_H_SECTION_2 2
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION STDAFX_H_SECTION_1
#include AZ_RESTRICTED_FILE(CrySystem_precompiled_h)
#elif defined(LINUX) // Scrubber friendly negated define pattern
#elif !defined(APPLE)
#include <memory.h>
#include <malloc.h>
#endif
//on mac the precompiled header is auto included in every .c and .cpp file, no include line necessary.
//.c files don't like the cpp things in here
#ifdef __cplusplus
#include <vector>
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION STDAFX_H_SECTION_2
#include AZ_RESTRICTED_FILE(CrySystem_precompiled_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(APPLE) // Scrubber friendly negated define pattern
#elif defined(ANDROID) // Scrubber friendly negated define pattern
#elif defined(LINUX)
# include <sys/io.h>
#else
# include <io.h>
#endif
//#define DEFINE_MODULE_NAME "CrySystem"
#define CRYSYSTEM_EXPORTS
#include <platform.h>
#if defined(WIN32) || defined(WIN64) || defined(APPLE) || defined(LINUX)
#if defined(DEDICATED_SERVER)
// enable/disable map load slicing functionality from the build
#define MAP_LOADING_SLICING
#endif
#endif
#ifdef WIN32
#include <CryWindows.h>
#include <tlhelp32.h>
#undef GetCharWidth
#undef GetUserName
#endif
/////////////////////////////////////////////////////////////////////////////
// CRY Stuff ////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
#include "Cry_Math.h"
#include <Cry_Camera.h>
#include <smartptr.h>
#include <Range.h>
#include <CrySizer.h>
#include <StlUtils.h>
inline int RoundToClosestMB(size_t memSize)
{
// add half a MB and shift down to get closest MB
return((int) ((memSize + (1 << 19)) >> 20));
}
//////////////////////////////////////////////////////////////////////////
// For faster compilation
//////////////////////////////////////////////////////////////////////////
#include <IRenderer.h>
#include <CryFile.h>
#include <ISystem.h>
#include <ITimer.h>
#include <IPhysics.h>
#include <IXml.h>
#include <ICmdLine.h>
#include <IConsole.h>
#include <ILog.h>
/////////////////////////////////////////////////////////////////////////////
//forward declarations for common Interfaces.
/////////////////////////////////////////////////////////////////////////////
class ITexture;
struct IRenderer;
struct ISystem;
struct ITimer;
struct IFFont;
struct ICVar;
struct IConsole;
struct IProcess;
namespace AZ::IO
{
struct IArchive;
}
struct ICryFont;
struct IMovieSystem;
struct IAudioSystem;
struct IPhysicalWorld;
#endif //__cplusplus
+895
View File
@@ -0,0 +1,895 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "DebugCallStack.h"
#if defined(WIN32) || defined(WIN64)
#include <IConsole.h>
#include <CryPath.h>
#include "System.h"
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#define VS_VERSION_INFO 1
#define IDD_CRITICAL_ERROR 101
#define IDB_CONFIRM_SAVE 102
#define IDB_DONT_SAVE 103
#define IDD_CONFIRM_SAVE_LEVEL 127
#define IDB_CRASH_FACE 128
#define IDD_EXCEPTION 245
#define IDC_CALLSTACK 1001
#define IDC_EXCEPTION_CODE 1002
#define IDC_EXCEPTION_ADDRESS 1003
#define IDC_EXCEPTION_MODULE 1004
#define IDC_EXCEPTION_DESC 1005
#define IDB_EXIT 1008
#define IDB_IGNORE 1010
__pragma(comment(lib, "version.lib"))
//! Needs one external of DLL handle.
extern HMODULE gDLLHandle;
#include <DbgHelp.h>
#define MAX_PATH_LENGTH 1024
#define MAX_SYMBOL_LENGTH 512
static HWND hwndException = 0;
static bool g_bUserDialog = true; // true=on crash show dialog box, false=supress user interaction
static int PrintException(EXCEPTION_POINTERS* pex);
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex);
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
extern LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE mdumpValue);
//=============================================================================
CONTEXT CaptureCurrentContext()
{
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
return context;
}
LONG __stdcall CryUnhandledExceptionHandler(EXCEPTION_POINTERS* pex)
{
return DebugCallStack::instance()->handleException(pex);
}
BOOL CALLBACK EnumModules(
PCSTR ModuleName,
DWORD64 BaseOfDll,
PVOID UserContext)
{
DebugCallStack::TModules& modules = *static_cast<DebugCallStack::TModules*>(UserContext);
modules[(void*)BaseOfDll] = ModuleName;
return TRUE;
}
//=============================================================================
// Class Statics
//=============================================================================
// Return single instance of class.
IDebugCallStack* IDebugCallStack::instance()
{
static DebugCallStack sInstance;
return &sInstance;
}
//------------------------------------------------------------------------------------------------------------------------
// Sets up the symbols for functions in the debug file.
//------------------------------------------------------------------------------------------------------------------------
DebugCallStack::DebugCallStack()
: prevExceptionHandler(0)
, m_pSystem(0)
, m_nSkipNumFunctions(0)
, m_bCrash(false)
, m_szBugMessage(NULL)
{
}
DebugCallStack::~DebugCallStack()
{
}
void DebugCallStack::RemoveOldFiles()
{
RemoveFile("error.log");
RemoveFile("error.bmp");
RemoveFile("error.dmp");
}
void DebugCallStack::RemoveFile(const char* szFileName)
{
FILE* pFile = nullptr;
azfopen(&pFile, szFileName, "r");
const bool bFileExists = (pFile != NULL);
if (bFileExists)
{
fclose(pFile);
WriteLineToLog("Removing file \"%s\"...", szFileName);
if (remove(szFileName) == 0)
{
WriteLineToLog("File successfully removed.");
}
else
{
WriteLineToLog("Couldn't remove file!");
}
}
}
void DebugCallStack::installErrorHandler(ISystem* pSystem)
{
m_pSystem = pSystem;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::SetUserDialogEnable(const bool bUserDialogEnable)
{
g_bUserDialog = bUserDialogEnable;
}
DWORD g_idDebugThreads[10];
const char* g_nameDebugThreads[10];
int g_nDebugThreads = 0;
volatile int g_lockThreadDumpList = 0;
void MarkThisThreadForDebugging(const char* name)
{
EBUS_EVENT(AZ::Debug::EventTraceDrillerSetupBus, SetThreadName, AZStd::this_thread::get_id(), name);
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
if (g_nDebugThreads == sizeof(g_idDebugThreads) / sizeof(g_idDebugThreads[0]))
{
return;
}
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] == id)
{
return;
}
}
g_nameDebugThreads[g_nDebugThreads] = name;
g_idDebugThreads[g_nDebugThreads++] = id;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
}
void UnmarkThisThreadFromDebugging()
{
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
for (int i = g_nDebugThreads - 1; i >= 0; i--)
{
if (g_idDebugThreads[i] == id)
{
memmove(g_idDebugThreads + i, g_idDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_idDebugThreads[0]));
memmove(g_nameDebugThreads + i, g_nameDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_nameDebugThreads[0]));
--g_nDebugThreads;
}
}
}
extern int prev_sys_float_exceptions;
void UpdateFPExceptionsMaskForThreads()
{
int mask = -iszero(g_cvars.sys_float_exceptions);
CONTEXT ctx;
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
ctx.ContextFlags = CONTEXT_ALL;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
#ifndef WIN64
(ctx.FloatSave.ControlWord |= 7) &= ~5 | mask;
(*(WORD*)(ctx.ExtendedRegisters + 24) |= 0x280) &= ~0x280 | mask;
#else
(ctx.FltSave.ControlWord |= 7) &= ~5 | mask;
(ctx.FltSave.MxCsr |= 0x280) &= ~0x280 | mask;
#endif
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
}
}
//////////////////////////////////////////////////////////////////////////
int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
{
if (gEnv == NULL)
{
return EXCEPTION_EXECUTE_HANDLER;
}
ResetFPU(exception_pointer);
prev_sys_float_exceptions = 0;
const int cached_sys_float_exceptions = g_cvars.sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(0);
if (g_cvars.sys_WER)
{
gEnv->pLog->FlushAndClose();
return CryEngineExceptionFilterWER(exception_pointer);
}
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
m_bCrash = true;
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
static bool firstTime = true;
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
SuspendThread(OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]));
}
}
}
// uninstall our exception handler.
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)prevExceptionHandler);
if (!firstTime)
{
WriteLineToLog("Critical Exception! Called Multiple Times!");
gEnv->pLog->FlushAndClose();
// Exception called more then once.
return EXCEPTION_EXECUTE_HANDLER;
}
// Print exception info:
{
char excCode[80];
char excAddr[80];
WriteLineToLog("<CRITICAL EXCEPTION>");
sprintf_s(excAddr, "0x%04X:0x%p", exception_pointer->ContextRecord->SegCs, exception_pointer->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
}
firstTime = false;
const int ret = SubmitBug(exception_pointer);
if (ret != IDB_IGNORE)
{
CryEngineExceptionFilterWER(exception_pointer);
}
gEnv->pLog->FlushAndClose();
if (exception_pointer->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// This is non continuable exception. abort application now.
exit(exception_pointer->ExceptionRecord->ExceptionCode);
}
//typedef long (__stdcall *ExceptionFunc)(EXCEPTION_POINTERS*);
//ExceptionFunc prevFunc = (ExceptionFunc)prevExceptionHandler;
//return prevFunc( (EXCEPTION_POINTERS*)exception_pointer );
if (ret == IDB_EXIT)
{
// Immediate exit.
// on windows, exit() and _exit() do all sorts of things, unfortuantely
// TerminateProcess is the only way to die.
TerminateProcess(GetCurrentProcess(), exception_pointer->ExceptionRecord->ExceptionCode); // we crashed, so don't return a zero exit code!
// on linux based systems, _exit will not call ATEXIT and other things, which makes it more suitable for termination in an emergency such
// as an unhandled exception.
// however, this function is a windows exception handler.
}
else if (ret == IDB_IGNORE)
{
#ifndef WIN64
exception_pointer->ContextRecord->FloatSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FloatSave.ControlWord |= 7;
(*(WORD*)(exception_pointer->ContextRecord->ExtendedRegisters + 24) &= 31) |= 0x1F80;
#else
exception_pointer->ContextRecord->FltSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FltSave.ControlWord |= 7;
(exception_pointer->ContextRecord->FltSave.MxCsr &= 31) |= 0x1F80;
#endif
firstTime = true;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
g_cvars.sys_float_exceptions = cached_sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
return EXCEPTION_CONTINUE_EXECUTION;
}
// Continue;
return EXCEPTION_EXECUTE_HANDLER;
}
void DebugCallStack::ReportBug(const char* szErrorMessage)
{
WriteLineToLog("Reporting bug: %s", szErrorMessage);
m_szBugMessage = szErrorMessage;
m_context = CaptureCurrentContext();
SubmitBug(NULL);
m_szBugMessage = NULL;
}
void DebugCallStack::dumpCallStack(std::vector<string>& funcs)
{
WriteLineToLog("=============================================================================");
int len = (int)funcs.size();
for (int i = 0; i < len; i++)
{
const char* str = funcs[i].c_str();
WriteLineToLog("%2d) %s", len - i, str);
}
WriteLineToLog("=============================================================================");
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
{
string path("");
if ((gEnv) && (gEnv->pFileIO))
{
const char* logAlias = gEnv->pFileIO->GetAlias("@log@");
if (!logAlias)
{
logAlias = gEnv->pFileIO->GetAlias("@root@");
}
if (logAlias)
{
path = logAlias;
path += "/";
}
}
string fileName = path;
fileName += "error.log";
struct stat fileInfo;
string timeStamp;
string backupPath;
if (gEnv->IsDedicated())
{
backupPath = PathUtil::ToUnixPath(PathUtil::AddSlash(path + "DumpBackups"));
gEnv->pFileIO->CreatePath(backupPath.c_str());
if (stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup log
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
string backupFileName = backupPath + timeStamp + " error.log";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
}
FILE* f = nullptr;
azfopen(&f, fileName.c_str(), "wt");
static char errorString[s_iCallStackSize];
errorString[0] = 0;
// Time and Version.
char versionbuf[1024];
azstrcpy(versionbuf, AZ_ARRAY_SIZE(versionbuf), "");
PutVersion(versionbuf, AZ_ARRAY_SIZE(versionbuf));
cry_strcat(errorString, versionbuf);
cry_strcat(errorString, "\n");
char excCode[MAX_WARNING_LENGTH];
char excAddr[80];
char desc[1024];
char excDesc[MAX_WARNING_LENGTH];
// make sure the mouse cursor is visible
ShowCursor(TRUE);
const char* excName;
if (m_bIsFatalError || !pex)
{
const char* const szMessage = m_bIsFatalError ? s_szFatalErrorCode : m_szBugMessage;
excName = szMessage;
cry_strcpy(excCode, szMessage);
cry_strcpy(excAddr, "");
cry_strcpy(desc, "");
cry_strcpy(m_excModule, "");
cry_strcpy(excDesc, szMessage);
}
else
{
sprintf_s(excAddr, "0x%04X:0x%p", pex->ContextRecord->SegCs, pex->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", pex->ExceptionRecord->ExceptionCode);
excName = TranslateExceptionCode(pex->ExceptionRecord->ExceptionCode);
cry_strcpy(desc, "");
sprintf_s(excDesc, "%s\r\n%s", excName, desc);
if (pex->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
if (pex->ExceptionRecord->NumberParameters > 1)
{
ULONG_PTR iswrite = pex->ExceptionRecord->ExceptionInformation[0];
DWORD64 accessAddr = pex->ExceptionRecord->ExceptionInformation[1];
if (iswrite)
{
sprintf_s(desc, "Attempt to write data to address 0x%08llu\r\nThe memory could not be \"written\"", accessAddr);
}
else
{
sprintf_s(desc, "Attempt to read from address 0x%08llu\r\nThe memory could not be \"read\"", accessAddr);
}
}
}
}
WriteLineToLog("Exception Code: %s", excCode);
WriteLineToLog("Exception Addr: %s", excAddr);
WriteLineToLog("Exception Module: %s", m_excModule);
WriteLineToLog("Exception Name : %s", excName);
WriteLineToLog("Exception Description: %s", desc);
cry_strcpy(m_excDesc, excDesc);
cry_strcpy(m_excAddr, excAddr);
cry_strcpy(m_excCode, excCode);
char errs[32768];
sprintf_s(errs, "Exception Code: %s\nException Addr: %s\nException Module: %s\nException Description: %s, %s\n",
excCode, excAddr, m_excModule, excName, desc);
cry_strcat(errs, "\nCall Stack Trace:\n");
std::vector<string> funcs;
{
AZ::Debug::StackFrame frames[25];
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 3);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i = 0; i < numFrames; i++)
{
funcs.push_back(lines[i]);
}
}
dumpCallStack(funcs);
// Fill call stack.
char str[s_iCallStackSize];
cry_strcpy(str, "");
for (unsigned int i = 0; i < funcs.size(); i++)
{
char temp[s_iCallStackSize];
sprintf_s(temp, "%2zd) %s", funcs.size() - i, (const char*)funcs[i].c_str());
cry_strcat(str, temp);
cry_strcat(str, "\r\n");
cry_strcat(errs, temp);
cry_strcat(errs, "\n");
}
cry_strcpy(m_excCallstack, str);
}
cry_strcat(errorString, errs);
if (f)
{
fwrite(errorString, strlen(errorString), 1, f);
{
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
fprintf(f, "\n\nSuspended thread (%s):\n", g_nameDebugThreads[i]);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
// mirrors the AZ::Debug::Trace::PrintCallstack() functionality, but prints to a file
{
AZ::Debug::StackFrame frames[10];
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
// which causes the stack tracing to fail.
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 0, hThread);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
{
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
}
}
}
ResumeThread(hThread);
}
}
}
}
fflush(f);
fclose(f);
}
if (pex)
{
MINIDUMP_TYPE mdumpValue = MiniDumpNormal;
bool bDump = true;
switch (g_cvars.sys_dump_type)
{
case 0:
bDump = false;
break;
case 1:
mdumpValue = MiniDumpNormal;
break;
case 2:
mdumpValue = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithDataSegs);
break;
case 3:
mdumpValue = MiniDumpWithFullMemory;
break;
default:
mdumpValue = (MINIDUMP_TYPE)g_cvars.sys_dump_type;
break;
}
if (bDump)
{
fileName = path + "error.dmp";
if (gEnv->IsDedicated() && stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup dump (use timestamp from error.log if available)
if (timeStamp.empty())
{
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
}
string backupFileName = backupPath + timeStamp + " error.dmp";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
CryEngineExceptionFilterMiniDump(pex, fileName.c_str(), mdumpValue);
}
}
//if no crash dialog don't even submit the bug
if (m_postBackupProcess && g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog)
{
m_postBackupProcess();
}
else
{
// lawsonn: Disabling the JIRA-based crash reporter for now
// we'll need to deal with it our own way, pending QA.
// if you're customizing the engine this is also your opportunity to deal with it.
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// ------------ place custom crash handler here ---------------------
// it should launch an executable!
/// by this time, error.bmp will be in the engine root folder
// error.log and error.dmp will also be present in the engine root folder
// if your error dumper wants those, it should zip them up and send them or offer to do so.
// ------------------------------------------------------------------
}
}
const bool bQuitting = !gEnv || !gEnv->pSystem || gEnv->pSystem->IsQuitting();
//[AlexMcC|16.04.10] When the engine is shutting down, MessageBox doesn't display a box
// and immediately returns IDYES. Avoid this by just not trying to save if we're quitting.
// Don't ask to save if this isn't a real crash (a real crash has exception pointers)
if (g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog && gEnv->IsEditor() && !bQuitting && pex)
{
BackupCurrentLevel();
const INT_PTR res = DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CONFIRM_SAVE_LEVEL), NULL, DebugCallStack::ConfirmSaveDialogProc, NULL);
if (res == IDB_CONFIRM_SAVE)
{
if (SaveCurrentLevel())
{
MessageBox(NULL, "Level has been successfully saved!\r\nPress Ok to terminate Editor.", "Save", MB_OK);
}
else
{
MessageBox(NULL, "Error saving level.\r\nPress Ok to terminate Editor.", "Save", MB_OK | MB_ICONWARNING);
}
}
}
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// terminate immediately - since we're in a crash, there is no point unwinding stack, we've already done access violation or worse.
// calling exit will only cause further death down the line...
TerminateProcess(GetCurrentProcess(), pex->ExceptionRecord->ExceptionCode);
}
}
INT_PTR CALLBACK DebugCallStack::ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static EXCEPTION_POINTERS* pex;
static char errorString[32768] = "";
switch (message)
{
case WM_INITDIALOG:
{
pex = (EXCEPTION_POINTERS*)lParam;
HWND h;
if (pex->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// Disable continue button for non continuable exceptions.
//h = GetDlgItem( hwndDlg,IDB_CONTINUE );
//if (h) EnableWindow( h,FALSE );
}
DebugCallStack* pDCS = static_cast<DebugCallStack*>(DebugCallStack::instance());
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_DESC);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excDesc);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_CODE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excCode);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_MODULE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excModule);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_ADDRESS);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excAddr);
}
// Fill call stack.
HWND callStack = GetDlgItem(hwndDlg, IDC_CALLSTACK);
if (callStack)
{
SendMessage(callStack, WM_SETTEXT, FALSE, (LPARAM)pDCS->m_excCallstack);
}
if (hwndException)
{
DestroyWindow(hwndException);
hwndException = 0;
}
if (IsFloatingPointException(pex))
{
EnableWindow(GetDlgItem(hwndDlg, IDB_IGNORE), TRUE);
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDB_EXIT:
case IDB_IGNORE:
// Fall through.
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
return FALSE;
}
INT_PTR CALLBACK DebugCallStack::ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// The user might be holding down the spacebar while the engine crashes.
// If we don't remove keyboard focus from this dialog, the keypress will
// press the default button before the dialog actually appears, even if
// the user has already released the key, which is bad.
SetFocus(NULL);
} break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDB_CONFIRM_SAVE: // Fall through
case IDB_DONT_SAVE:
{
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
} break;
}
return FALSE;
}
bool DebugCallStack::BackupCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnBackupDocument();
}
return false;
}
bool DebugCallStack::SaveCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnSaveDocument();
}
return false;
}
int DebugCallStack::SubmitBug(EXCEPTION_POINTERS* exception_pointer)
{
int ret = IDB_EXIT;
assert(!hwndException);
RemoveOldFiles();
AZ::Debug::Trace::PrintCallstack("", 2);
LogExceptionInfo(exception_pointer);
if (IsFloatingPointException(exception_pointer))
{
//! Print exception dialog.
ret = PrintException(exception_pointer);
}
return ret;
}
void DebugCallStack::ResetFPU(EXCEPTION_POINTERS* pex)
{
if (IsFloatingPointException(pex))
{
// How to reset FPU: http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_10310953.html
_clearfp();
#ifndef WIN64
pex->ContextRecord->FloatSave.ControlWord |= 0x2F;
pex->ContextRecord->FloatSave.StatusWord &= ~0x8080;
#endif
}
}
string DebugCallStack::GetModuleNameForAddr(void* addr)
{
if (m_modules.empty())
{
return "[unknown]";
}
if (addr < m_modules.begin()->first)
{
return "[unknown]";
}
TModules::const_iterator it = m_modules.begin();
TModules::const_iterator end = m_modules.end();
for (; ++it != end; )
{
if (addr < it->first)
{
return (--it)->second;
}
}
//if address is higher than the last module, we simply assume it is in the last module.
return m_modules.rbegin()->second;
}
void DebugCallStack::GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
AZ::Debug::SymbolStorage::StackLine func, file, module;
AZ::Debug::SymbolStorage::FindFunctionFromIP(addr, &func, &file, &module, line, baseAddr);
procName = func;
filename = file;
}
string DebugCallStack::GetCurrentFilename()
{
char fullpath[MAX_PATH_LENGTH + 1];
GetModuleFileName(NULL, fullpath, MAX_PATH_LENGTH);
return fullpath;
}
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex)
{
if (!pex)
{
return false;
}
DWORD exceptionCode = pex->ExceptionRecord->ExceptionCode;
switch (exceptionCode)
{
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case STATUS_FLOAT_MULTIPLE_FAULTS:
case STATUS_FLOAT_MULTIPLE_TRAPS:
return true;
default:
return false;
}
}
int DebugCallStack::PrintException(EXCEPTION_POINTERS* exception_pointer)
{
return (int)DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CRITICAL_ERROR), NULL, DebugCallStack::ExceptionDialogProc, (LPARAM)exception_pointer);
}
#else
void MarkThisThreadForDebugging(const char*) {}
void UnmarkThisThreadFromDebugging() {}
void UpdateFPExceptionsMaskForThreads() {}
#endif //WIN32
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#pragma once
#include "IDebugCallStack.h"
#if defined (WIN32) || defined (WIN64)
//! Limits the maximal number of functions in call stack.
const int MAX_DEBUG_STACK_ENTRIES_FILE_DUMP = 12;
struct ISystem;
//!============================================================================
//!
//! DebugCallStack class, capture call stack information from symbol files.
//!
//!============================================================================
class DebugCallStack
: public IDebugCallStack
{
public:
DebugCallStack();
virtual ~DebugCallStack();
ISystem* GetSystem() { return m_pSystem; };
virtual string GetModuleNameForAddr(void* addr);
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line);
virtual string GetCurrentFilename();
void installErrorHandler(ISystem* pSystem);
virtual int handleException(EXCEPTION_POINTERS* exception_pointer);
virtual void ReportBug(const char*);
void dumpCallStack(std::vector<string>& functions);
void SetUserDialogEnable(const bool bUserDialogEnable);
typedef std::map<void*, string> TModules;
protected:
static void RemoveOldFiles();
static void RemoveFile(const char* szFileName);
static int PrintException(EXCEPTION_POINTERS* exception_pointer);
static INT_PTR CALLBACK ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
void LogExceptionInfo(EXCEPTION_POINTERS* exception_pointer);
bool BackupCurrentLevel();
bool SaveCurrentLevel();
int SubmitBug(EXCEPTION_POINTERS* exception_pointer);
void ResetFPU(EXCEPTION_POINTERS* pex);
static const int s_iCallStackSize = 32768;
char m_excLine[256];
char m_excModule[128];
char m_excDesc[MAX_WARNING_LENGTH];
char m_excCode[MAX_WARNING_LENGTH];
char m_excAddr[80];
char m_excCallstack[s_iCallStackSize];
void* prevExceptionHandler;
bool m_bCrash;
const char* m_szBugMessage;
ISystem* m_pSystem;
int m_nSkipNumFunctions;
CONTEXT m_context;
TModules m_modules;
};
#endif //WIN32
#endif // CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "System.h"
#include <AZCrySystemInitLogSink.h>
#include "DebugCallStack.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define DLLMAIN_CPP_SECTION_1 1
#define DLLMAIN_CPP_SECTION_2 2
#define DLLMAIN_CPP_SECTION_3 3
#define DLLMAIN_CPP_SECTION_4 4
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
// For lua debugger
//#include <malloc.h>
HMODULE gDLLHandle = NULL;
#if !defined(AZ_MONOLITHIC_BUILD) && defined(AZ_HAS_DLL_SUPPORT) && AZ_TRAIT_LEGACY_CRYSYSTEM_DEFINE_DLLMAIN
AZ_PUSH_DISABLE_WARNING(4447, "-Wunknown-warning-option")
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
[[maybe_unused]] LPVOID lpReserved
)
AZ_POP_DISABLE_WARNING
{
PREVENT_MODULE_AND_ENVIRONMENT_SYMBOL_STRIPPING
gDLLHandle = (HMODULE)hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
// int sbh = _set_sbh_threshold(1016);
return TRUE;
}
#endif
extern "C"
{
CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupParams)
{
CSystem* pSystem = NULL;
// We must attach to the environment prior to allocating CSystem, as opposed to waiting
// for ModuleInitISystem(), because the log message sink uses buses.
// Environment should have been attached via InjectEnvironment
AZ_Assert(AZ::Environment::IsReady(), "Environment is not attached, must be attached before CreateSystemInterface can be called");
pSystem = new CSystem(startupParams.pSharedEnvironment);
ModuleInitISystem(pSystem, "CrySystem");
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
// the earliest point the system exists - w2e tell the callback
if (startupParams.pUserCallback)
{
startupParams.pUserCallback->OnSystemConnect(pSystem);
}
#if defined(WIN32)
// Environment Variable to signal we don't want to override our exception handler - our crash report system will set this
auto envVar = AZ::Environment::FindVariable<bool>("ExceptionHandlerIsSet");
const bool handlerIsSet = (envVar && *envVar);
if (!handlerIsSet)
{
((DebugCallStack*)IDebugCallStack::instance())->installErrorHandler(pSystem);
}
#endif
bool retVal = false;
{
AZ::Debug::StartupLogSinkReporter<AZ::Debug::CrySystemInitLogSink> initLogSink;
retVal = pSystem->Init(startupParams);
if (!retVal)
{
initLogSink.GetContainedLogSink().SetFatalMessageBox();
}
}
if (!retVal)
{
delete pSystem;
gEnv = nullptr;
return 0;
}
return pSystem;
}
};
+462
View File
@@ -0,0 +1,462 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "Huffman.h"
void HuffmanCoder::BitStreamBuilder::AddBits(uint32 value, uint32 numBits)
{
if (numBits > 24)
{
AddBits(static_cast<uint8>((value >> 24) & 0x000000ff), numBits - 24);
numBits = 24;
}
if (numBits > 16)
{
AddBits(static_cast<uint8>((value >> 16) & 0x000000ff), numBits - 16);
numBits = 16;
}
if (numBits > 8)
{
AddBits(static_cast<uint8>((value >> 8) & 0x000000ff), numBits - 8);
numBits = 8;
}
AddBits(static_cast<uint8>(value & 0x000000ff), numBits);
}
void HuffmanCoder::BitStreamBuilder::AddBits(uint8 value, uint32 numBits)
{
if (m_mode != eM_WRITE)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to write to a read only BitStreamBuilder");
return;
}
uint8 mask;
mask = (uint8)(1 << (numBits - 1));
while (mask != 0)
{
//CryLogAlways("mask is %u", mask);
if (mask & value)
{
//CryLogAlways("Buffer value was %u", *m_pBufferCursor.ptr);
*(m_pBufferCursor.ptr) |= m_mask;
//CryLogAlways("Buffer value now %u", *m_pBufferCursor.ptr);
}
//CryLogAlways("m_mask was %u", m_mask);
m_mask = m_mask >> 1;
//CryLogAlways("m_mask now %u", m_mask);
if (m_mask == 0)
{
if (m_pBufferCursor.ptr == m_pBufferEnd.ptr)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Bit Stream has consumed the last byte of the buffer and is requesting another. This stream will be truncated here.");
return;
}
//CryLogAlways("Buffer cursor was %u (%p)", *m_pBufferCursor.ptr, m_pBufferCursor.ptr);
m_pBufferCursor.ptr++;
//CryLogAlways("Buffer cursor now %u (%p)", *m_pBufferCursor.ptr, m_pBufferCursor.ptr);
m_mask = 0x80;
}
mask = mask >> 1L;
}
}
//Returns 1 or 0 for valid values. Returns 2 if the buffer has run out or is the wrong type of builder.
uint8 HuffmanCoder::BitStreamBuilder::GetBit()
{
if (m_mode != eM_READ)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to read from a write only BitStreamBuilder");
return 2;
}
uint8 value = 0;
if (m_mask == 0)
{
if (m_pBufferCursor.const_ptr == m_pBufferEnd.const_ptr)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Bit Stream has consumed the last byte of the buffer and is requesting another. This stream will be truncated here.");
return 2;
}
//CryLogAlways("Buffer cursor was %u (%p)", *m_pBufferCursor.const_ptr, m_pBufferCursor.const_ptr);
m_pBufferCursor.const_ptr++;
//CryLogAlways("Buffer cursor now %u (%p)", *m_pBufferCursor.const_ptr, m_pBufferCursor.const_ptr);
m_mask = 0x80;
}
if (m_mask & *(m_pBufferCursor.const_ptr))
{
value = 1;
}
//CryLogAlways("m_mask was %u", m_mask);
m_mask = m_mask >> 1;
//CryLogAlways("m_mask now %u", m_mask);
return value;
}
void HuffmanCoder::Init()
{
SAFE_DELETE_ARRAY(m_TreeNodes);
SAFE_DELETE_ARRAY(m_Codes);
m_Counts = new uint32[MAX_NUM_SYMBOLS];
memset(m_Counts, 0, sizeof(uint32) * MAX_NUM_SYMBOLS);
m_State = eHCS_OPEN;
}
//Adds the values of an array of chars to the counts
void HuffmanCoder::Update(const uint8* const pSource, const size_t numBytes)
{
if (m_State != eHCS_OPEN)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to update a Huffman Coder that has not been initialized, or has been finalized");
return;
}
size_t i;
for (i = 0; i < numBytes; i++)
{
const int symbol = pSource[i];
m_Counts[symbol]++;
}
}
void HuffmanCoder::Finalize()
{
if (m_State != eHCS_OPEN)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to finalize a Huffman Coder that has not been initialized, or has been finalized");
return;
}
//Construct the tree
m_TreeNodes = new HuffmanTreeNode[MAX_NUM_NODES];
memset(m_TreeNodes, 0, sizeof(HuffmanTreeNode) * MAX_NUM_NODES);
m_Codes = new HuffmanSymbolCode[MAX_NUM_CODES];
memset(m_Codes, 0, sizeof(HuffmanSymbolCode) * MAX_NUM_CODES);
ScaleCountsAndUpdateNodes();
m_RootNode = BuildTree();
ConvertTreeToCode(m_TreeNodes, m_Codes, 0, 0, m_RootNode);
//Finalize the coder so that it won't accept any more strings
m_State = eHCS_FINAL;
//Counts are no longer needed
SAFE_DELETE_ARRAY(m_Counts);
}
void HuffmanCoder::CompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, size_t* const outputSize)
{
BitStreamBuilder streamBuilder(pOutput, pOutput + (*outputSize));
for (size_t i = 0; i < numBytes; i++)
{
const int symbol = pInput[i];
const uint32 value = m_Codes[symbol].value;
const uint32 numBits = m_Codes[symbol].numBits;
/*char szBits[33];
memset(szBits, '0', 33);
for( uint32 j = 0; j < numBits; j++ )
{
if( (value & (uint32)(1<<j)) != 0 )
{
szBits[31-j] = '1';
}
else
{
szBits[31-j] = '0';
}
}
szBits[32] = 0;
CryLogAlways("%c - %s (%u)", value, szBits, numBits);*/
streamBuilder.AddBits(value, numBits);
}
streamBuilder.AddBits(m_Codes[END_OF_STREAM].value, m_Codes[END_OF_STREAM].numBits);
*outputSize = (streamBuilder.m_pBufferCursor.ptr - streamBuilder.m_pBufferStart.ptr) + 1;
}
size_t HuffmanCoder::UncompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, const size_t maxOutputSize)
{
size_t numOutputBytes = 0;
BitStreamBuilder streamBuilder(pInput, pInput + numBytes);
while (1)
{
int code;
int node = m_RootNode;
do
{
uint8 bitValue = streamBuilder.GetBit();
#if 0
CryLogAlways("bit=%ld\n", bitValue);
#endif
if (bitValue == 0)
{
node = m_TreeNodes[node].child0;
}
else
{
node = m_TreeNodes[node].child1;
}
} while (node > END_OF_STREAM);
if (node == END_OF_STREAM)
{
pOutput[numOutputBytes] = '\0';
break;
}
code = node;
#if 0
{
CryLogAlways("%c", code);
if (code == '\0')
{
CryLogAlways("EOM");
}
}
#endif
pOutput[numOutputBytes] = (char)code;
numOutputBytes++;
if (numOutputBytes >= maxOutputSize)
{
pOutput[maxOutputSize - 1] = '\0';
break;
}
}
return numOutputBytes;
}
//Private functions
void HuffmanCoder::ScaleCountsAndUpdateNodes()
{
unsigned long maxCount = 0;
int i;
for (i = 0; i < MAX_NUM_SYMBOLS; i++)
{
const unsigned long count = m_Counts[i];
if (count > maxCount)
{
maxCount = count;
}
}
if (maxCount == 0)
{
m_Counts[0] = 1;
maxCount = 1;
}
maxCount = maxCount / MAX_NUM_SYMBOLS;
maxCount = maxCount + 1;
for (i = 0; i < MAX_NUM_SYMBOLS; i++)
{
const unsigned long count = m_Counts[i];
unsigned int scaledCount = (unsigned int)(count / maxCount);
if ((scaledCount == 0) && (count != 0))
{
scaledCount = 1;
}
m_TreeNodes[i].count = scaledCount;
m_TreeNodes[i].child0 = END_OF_STREAM;
m_TreeNodes[i].child1 = END_OF_STREAM;
}
m_TreeNodes[END_OF_STREAM].count = 1;
m_TreeNodes[END_OF_STREAM].child0 = END_OF_STREAM;
m_TreeNodes[END_OF_STREAM].child1 = END_OF_STREAM;
}
//Jake's file IO code. Kept in case we make the compression and table generation an offline task
/* Format is: startSymbol, stopSymbol, count0, count1, count2, ... countN, ..., 0 */
/* When finding the start, stop symbols only break out if find more than 3 0's in the counts */
/*static void outputCounts(FILE* const pFile, const HuffmanTreeNode* const pNodes)
{
int first = 0;
int last;
int next;
while ((first < MAX_NUM_SYMBOLS) && (pNodes[first].count == 0))
{
first++;
}
last = first;
next = first;
for (; first < MAX_NUM_SYMBOLS; first = next)
{
int i;
last = first+1;
while (1)
{
for (; last < MAX_NUM_SYMBOLS; last++)
{
if (pNodes[last].count == 0)
{
break;
}
}
last--;
for (next = last+1; next < MAX_NUM_SYMBOLS; next++)
{
if (pNodes[next].count != 0)
{
break;
}
}
if (next == MAX_NUM_SYMBOLS)
{
break;
}
if ((next-last) > 3)
{
break;
}
last = next;
}
putc(first, pFile);
putc(last, pFile);
for (i = first; i <= last; i++)
{
const unsigned int count = pNodes[i].count;
putc((int)count, pFile);
}
}
putc(0xFF, pFile);
putc(0xFF, pFile);
putc((int)(pNodes[0xFF].count), pFile);
}*/
/*static void inputCounts(FILE* const pFile, HuffmanTreeNode* const pNodes)
{
while (1)
{
int i;
const int first = getc(pFile);
const int last = getc(pFile);
for (i = first; i <= last; i++)
{
const int count = getc(pFile);
pNodes[i].count = (size_t)count;
pNodes[i].child0 = END_OF_STREAM;
pNodes[i].child1 = END_OF_STREAM;
}
if ((first == last) && (first == 0xFF))
{
break;
}
}
pNodes[END_OF_STREAM].count = 1;
pNodes[END_OF_STREAM].child0 = END_OF_STREAM;
pNodes[END_OF_STREAM].child1 = END_OF_STREAM;
}*/
int HuffmanCoder::BuildTree()
{
int min1;
int min2;
int nextFree;
m_TreeNodes[MAX_NODE].count = 0xFFFFFFF;
for (nextFree = END_OF_STREAM + 1;; nextFree++)
{
int i;
min1 = MAX_NODE;
min2 = MAX_NODE;
for (i = 0; i < nextFree; i++)
{
const unsigned int count = m_TreeNodes[i].count;
if (count != 0)
{
if (count < m_TreeNodes[min1].count)
{
min2 = min1;
min1 = i;
}
else if (count < m_TreeNodes[min2].count)
{
min2 = i;
}
}
}
if (min2 == MAX_NODE)
{
break;
}
m_TreeNodes[nextFree].count = m_TreeNodes[min1].count + m_TreeNodes[min2].count;
m_TreeNodes[min1].savedCount = m_TreeNodes[min1].count;
m_TreeNodes[min1].count = 0;
m_TreeNodes[min2].savedCount = m_TreeNodes[min2].count;
m_TreeNodes[min2].count = 0;
m_TreeNodes[nextFree].child0 = min1;
m_TreeNodes[nextFree].child1 = min2;
m_TreeNodes[nextFree].savedCount = 0;
}
nextFree--;
m_TreeNodes[nextFree].savedCount = m_TreeNodes[nextFree].count;
return nextFree;
}
void HuffmanCoder::ConvertTreeToCode(const HuffmanTreeNode* const pNodes, HuffmanSymbolCode* const pCodes,
const unsigned int value, const unsigned int numBits, const int node)
{
unsigned int nextValue;
unsigned int nextNumBits;
if (node <= END_OF_STREAM)
{
pCodes[node].value = value;
pCodes[node].numBits = numBits;
return;
}
nextValue = value << 1;
nextNumBits = numBits + 1;
ConvertTreeToCode(pNodes, pCodes, nextValue, nextNumBits, pNodes[node].child0);
nextValue = nextValue | 0x1;
ConvertTreeToCode(pNodes, pCodes, nextValue, nextNumBits, pNodes[node].child1);
}
/*static void printChar(const int c)
{
if (c >= ' ' && c < 127)
{
printf("'%c'", c);
}
else
{
printf("0x%03X", c);
}
}
static void printModel(const HuffmanTreeNode* const pNodes, const HuffmanSymbolCode* const pCodes)
{
int i;
for (i = 0; i < MAX_NODE; i++)
{
const unsigned int count = pNodes[i].savedCount;
if (count != 0)
{
printf("node=");
printChar(i);
printf(" count=%3d", count);
printf(" child0=");
printChar(pNodes[i].child0);
printf(" child1=");
printChar(pNodes[i].child1);
if (pCodes && (i <= END_OF_STREAM))
{
printf(" Huffman code=");
binaryFilePrint(stdout, pCodes[i].value, pCodes[i].numBits);
}
printf("\n");
}
}
}*/
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
#define CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
#pragma once
class HuffmanCoder
{
private:
struct HuffmanTreeNode
{
uint32 count;
uint32 savedCount;
int child0;
int child1;
};
struct HuffmanSymbolCode
{
uint32 value;
uint32 numBits;
};
struct BitStreamBuilder
{
enum EModes
{
eM_WRITE,
eM_READ
};
union buf_ptr
{
uint8* ptr;
const uint8* const_ptr;
};
EModes m_mode;
uint8 m_mask;
buf_ptr m_pBufferStart;
buf_ptr m_pBufferCursor;
buf_ptr m_pBufferEnd; //Pointer to the last byte in the buffer
BitStreamBuilder(uint8* pBufferStart, uint8* pBufferEnd)
: m_mode(eM_WRITE)
, m_mask(0x80)
{
m_pBufferStart.ptr = pBufferStart;
m_pBufferCursor.ptr = pBufferStart;
m_pBufferEnd.ptr = pBufferEnd;
}
BitStreamBuilder(const uint8* pBufferStart, const uint8* pBufferEnd)
: m_mode(eM_READ)
, m_mask(0x80)
{
m_pBufferStart.const_ptr = pBufferStart;
m_pBufferCursor.const_ptr = pBufferStart;
m_pBufferEnd.const_ptr = pBufferEnd;
}
void AddBits(uint32 value, uint32 numBits);
void AddBits(uint8 value, uint32 numBits);
//Returns 1 or 0 for valid values. Returns 2 if the buffer has run out or is the wrong type of builder.
uint8 GetBit();
};
const static int MAX_SYMBOL_VALUE = (255);
const static int MAX_NUM_SYMBOLS = (MAX_SYMBOL_VALUE + 1);
const static int END_OF_STREAM = (MAX_NUM_SYMBOLS);
const static int MAX_NUM_CODES = (MAX_NUM_SYMBOLS + 1);
const static int MAX_NUM_NODES = (MAX_NUM_CODES * 2);
const static int MAX_NODE = (MAX_NUM_NODES - 1);
enum EHuffmanCoderState
{
eHCS_NEW, //Has been created, Init not called
eHCS_OPEN, //Init has been called, tree not yet constructed. Can accept new data.
eHCS_FINAL //Finalize has been called. Can no longer accept data, but can encode/decode.
};
HuffmanTreeNode* m_TreeNodes;
HuffmanSymbolCode* m_Codes;
uint32* m_Counts;
int m_RootNode;
uint32 m_RefCount;
EHuffmanCoderState m_State;
public:
HuffmanCoder()
: m_TreeNodes(NULL)
, m_Codes(NULL)
, m_Counts(NULL)
, m_State(eHCS_NEW)
, m_RootNode(0)
, m_RefCount(0) {}
~HuffmanCoder()
{
SAFE_DELETE_ARRAY(m_TreeNodes);
SAFE_DELETE_ARRAY(m_Codes);
SAFE_DELETE_ARRAY(m_Counts);
}
//A bit like an MD5 generator, has three phases.
//Clears the existing data
void Init();
//Adds the values of an array of chars to the counts
void Update(const uint8* const pSource, const size_t numBytes);
//Construct the coding tree using the counts
void Finalize();
//We typically create a Huffman Coder per localized string table loaded. Since we can and do unload strings at runtime, it's useful to keep a ref count of each coder.
inline void AddRef() { m_RefCount++; }
inline void DecRef() { m_RefCount = m_RefCount > 0 ? m_RefCount - 1 : 0; }
inline uint32 RefCount() { return m_RefCount; }
void CompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, size_t* const outputSize);
size_t UncompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, const size_t maxOutputSize);
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
if (m_Counts != NULL)
{
pSizer->AddObject(m_Counts, sizeof(uint32), MAX_NUM_SYMBOLS);
}
if (m_TreeNodes != NULL)
{
pSizer->AddObject(m_TreeNodes, sizeof(HuffmanTreeNode), MAX_NUM_NODES);
}
if (m_Codes != NULL)
{
pSizer->AddObject(m_Codes, sizeof(HuffmanSymbolCode), MAX_NUM_CODES);
}
}
private:
void ScaleCountsAndUpdateNodes();
int BuildTree();
void ConvertTreeToCode(const HuffmanTreeNode* const pNodes, HuffmanSymbolCode* const pCodes, const unsigned int value, const unsigned int numBits, const int node);
};
#endif // CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
+270
View File
@@ -0,0 +1,270 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : A multiplatform base class for handling errors and collecting call stacks
#include "CrySystem_precompiled.h"
#include "IDebugCallStack.h"
#include "System.h"
#include <AzFramework/IO/FileOperations.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
//#if !defined(LINUX)
#include <ISystem.h>
const char* const IDebugCallStack::s_szFatalErrorCode = "FATAL_ERROR";
IDebugCallStack::IDebugCallStack()
: m_bIsFatalError(false)
, m_postBackupProcess(0)
, m_memAllocFileHandle(AZ::IO::InvalidHandle)
{
}
IDebugCallStack::~IDebugCallStack()
{
StopMemLog();
}
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON
IDebugCallStack* IDebugCallStack::instance()
{
static IDebugCallStack sInstance;
return &sInstance;
}
#endif
void IDebugCallStack::FileCreationCallback(void (* postBackupProcess)())
{
m_postBackupProcess = postBackupProcess;
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::LogCallstack()
{
AZ::Debug::Trace::PrintCallstack("", 2);
}
const char* IDebugCallStack::TranslateExceptionCode(DWORD dwExcept)
{
switch (dwExcept)
{
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
break;
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
break;
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
break;
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
break;
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
break;
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
break;
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
break;
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
break;
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
break;
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
break;
case EXCEPTION_GUARD_PAGE:
return "EXCEPTION_GUARD_PAGE";
break;
case EXCEPTION_INVALID_HANDLE:
return "EXCEPTION_INVALID_HANDLE";
break;
//case EXCEPTION_POSSIBLE_DEADLOCK: return "EXCEPTION_POSSIBLE_DEADLOCK"; break ;
case STATUS_FLOAT_MULTIPLE_FAULTS:
return "STATUS_FLOAT_MULTIPLE_FAULTS";
break;
case STATUS_FLOAT_MULTIPLE_TRAPS:
return "STATUS_FLOAT_MULTIPLE_TRAPS";
break;
#endif
default:
return "Unknown";
break;
}
}
void IDebugCallStack::PutVersion(char* str, size_t length)
{
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
if (!gEnv || !gEnv->pSystem)
{
return;
}
char sFileVersion[128];
gEnv->pSystem->GetFileVersion().ToString(sFileVersion, sizeof(sFileVersion));
char sProductVersion[128];
gEnv->pSystem->GetProductVersion().ToString(sProductVersion, sizeof(sFileVersion));
//! Get time.
time_t ltime;
time(&ltime);
tm* today = localtime(&ltime);
char s[1024];
//! Use strftime to build a customized time string.
strftime(s, 128, "Logged at %#c\n", today);
azstrcat(str, length, s);
sprintf_s(s, "FileVersion: %s\n", sFileVersion);
azstrcat(str, length, s);
sprintf_s(s, "ProductVersion: %s\n", sProductVersion);
azstrcat(str, length, s);
if (gEnv->pLog)
{
const char* logfile = gEnv->pLog->GetFileName();
if (logfile)
{
sprintf (s, "LogFile: %s\n", logfile);
azstrcat(str, length, s);
}
}
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
azstrcat(str, length, "ProjectDir: ");
azstrcat(str, length, projectPath.c_str());
azstrcat(str, length, "\n");
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
GetModuleFileNameA(NULL, s, sizeof(s));
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
AZStd::string exeName;
if (AZ::StringFunc::Path::GetFullFileName(s, exeName))
{
azstrcat(str, length, "Executable: ");
azstrcat(str, length, exeName.c_str());
# ifdef AZ_DEBUG_BUILD
azstrcat(str, length, " (debug: yes");
# else
azstrcat(str, length, " (debug: no");
# endif
}
#endif
AZ_POP_DISABLE_WARNING
}
//Crash the application, in this way the debug callstack routine will be called and it will create all the necessary files (error.log, dump, and eventually screenshot)
void IDebugCallStack::FatalError(const char* description)
{
m_bIsFatalError = true;
WriteLineToLog(description);
#ifndef _RELEASE
bool bShowDebugScreen = g_cvars.sys_no_crash_dialog == 0;
// showing the debug screen is not safe when not called from mainthread
// it normally leads to a infinity recursion followed by a stack overflow, preventing
// useful call stacks, thus they are disabled
bShowDebugScreen = bShowDebugScreen && gEnv->mMainThreadId == CryGetCurrentThreadId();
if (bShowDebugScreen)
{
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Open 3D Engine Fatal Error", description, false);
}
#endif
#if defined(WIN32) || !defined(_RELEASE)
int* p = 0x0;
PREFAST_SUPPRESS_WARNING(6011) * p = 1; // we're intentionally crashing here
#endif
}
void IDebugCallStack::WriteLineToLog(const char* format, ...)
{
va_list ArgList;
char szBuffer[MAX_WARNING_LENGTH];
va_start(ArgList, format);
vsnprintf_s(szBuffer, sizeof(szBuffer), sizeof(szBuffer) - 1, format, ArgList);
cry_strcat(szBuffer, "\n");
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(ArgList);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
AZ::IO::FileIOBase::GetDirectInstance()->Flush(fileHandle);
AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle);
}
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StartMemLog()
{
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StopMemLog()
{
if (m_memAllocFileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_memAllocFileHandle);
m_memAllocFileHandle = AZ::IO::InvalidHandle;
}
}
//#endif //!defined(LINUX)
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : A multiplatform base class for handling errors and collecting call stacks
#ifndef CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#pragma once
#include "System.h"
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FORWARD_EXCEPTION_POINTERS
struct EXCEPTION_POINTERS;
#endif
//! Limits the maximal number of functions in call stack.
enum
{
MAX_DEBUG_STACK_ENTRIES = 80
};
class IDebugCallStack
{
public:
// Returns single instance of DebugStack
static IDebugCallStack* instance();
virtual int handleException([[maybe_unused]] EXCEPTION_POINTERS* exception_pointer){return 0; }
// returns the module name of a given address
virtual string GetModuleNameForAddr([[maybe_unused]] void* addr) { return "[unknown]"; }
// returns the function name of a given address together with source file and line number (if available) of a given address
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
filename = "[unknown]";
line = 0;
baseAddr = addr;
#if defined(PLATFORM_64BIT)
procName.Format("[%016llX]", addr);
#else
procName.Format("[%08X]", addr);
#endif
}
// returns current filename
virtual string GetCurrentFilename() { return "[unknown]"; }
//! Dumps Current Call Stack to log.
virtual void LogCallstack();
//triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
void FatalError(const char*);
//Reports a bug and continues execution
virtual void ReportBug(const char*) {}
virtual void FileCreationCallback(void (* postBackupProcess)());
static void WriteLineToLog(const char* format, ...);
virtual void StartMemLog();
virtual void StopMemLog();
protected:
IDebugCallStack();
virtual ~IDebugCallStack();
static const char* TranslateExceptionCode(DWORD dwExcept);
static void PutVersion(char* str, size_t length);
bool m_bIsFatalError;
static const char* const s_szFatalErrorCode;
void (* m_postBackupProcess)();
AZ::IO::HandleType m_memAllocFileHandle;
};
#endif // CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
@@ -0,0 +1,992 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// [LYN-2376] Remove the entire file once legacy slice support is removed
#include "CrySystem_precompiled.h"
#include "LevelSystem.h"
#include <IAudioSystem.h>
#include "IMovieSystem.h"
#include <ILocalizationManager.h>
#include "CryPath.h"
#include <LoadScreenBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/IO/FileOperations.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include "MainThreadRenderRequestBus.h"
#include <LyShine/ILyShine.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Script/ScriptSystemBus.h>
#ifdef WIN32
#include <CryWindows.h>
#endif
namespace LegacyLevelSystem
{
static constexpr const char* ArchiveExtension = ".pak";
void CLevelInfo::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(m_levelName);
pSizer->AddObject(m_levelPath);
}
//////////////////////////////////////////////////////////////////////////
bool CLevelInfo::OpenLevelPak()
{
LOADING_TIME_PROFILE_SECTION;
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
// The prefab system doesn't use level.pak
if (usePrefabSystemForLevels)
{
return false;
}
AZStd::string levelpak(m_levelPath);
levelpak += "/level.pak";
AZStd::fixed_string<AZ::IO::IArchive::MaxPath> fullLevelPakPath;
bool bOk = gEnv->pCryPak->OpenPack(
levelpak.c_str(), m_isPak ? AZ::IO::IArchive::FLAGS_LEVEL_PAK_INSIDE_PAK : (unsigned)0, NULL, &fullLevelPakPath, false);
m_levelPakFullPath.assign(fullLevelPakPath.c_str());
return bOk;
}
//////////////////////////////////////////////////////////////////////////
void CLevelInfo::CloseLevelPak()
{
LOADING_TIME_PROFILE_SECTION;
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
// The prefab system doesn't use level.pak
if (usePrefabSystemForLevels)
{
return;
}
if (!m_levelPakFullPath.empty())
{
gEnv->pCryPak->ClosePack(m_levelPakFullPath.c_str(), AZ::IO::IArchive::FLAGS_PATH_REAL);
m_levelPakFullPath.clear();
}
}
//////////////////////////////////////////////////////////////////////////
bool CLevelInfo::ReadInfo()
{
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
// Set up a default game type for legacy code.
m_defaultGameTypeName = "Mission0";
if (usePrefabSystemForLevels)
{
return true;
}
AZStd::string levelPath(m_levelPath);
AZStd::string xmlFile(levelPath);
xmlFile += "/LevelInfo.xml";
XmlNodeRef rootNode = GetISystem()->LoadXmlFromFile(xmlFile.c_str());
if (rootNode)
{
AZStd::string dataFile(levelPath);
dataFile += "/LevelDataAction.xml";
XmlNodeRef dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
if (!dataNode)
{
dataFile = levelPath + "/LevelData.xml";
dataNode = GetISystem()->LoadXmlFromFile(dataFile.c_str());
}
if (dataNode)
{
XmlNodeRef gameTypesNode = dataNode->findChild("Missions");
if ((gameTypesNode != 0) && (gameTypesNode->getChildCount() > 0))
{
m_defaultGameTypeName.clear();
for (int i = 0; i < gameTypesNode->getChildCount(); i++)
{
XmlNodeRef gameTypeNode = gameTypesNode->getChild(i);
if (gameTypeNode->isTag("Mission"))
{
const char* gameTypeName = gameTypeNode->getAttr("Name");
if (gameTypeName)
{
m_defaultGameTypeName = gameTypeName;
break;
}
}
}
}
}
}
return rootNode != 0;
}
//////////////////////////////////////////////////////////////////////////
/// Used by console auto completion.
struct SLevelNameAutoComplete
: public IConsoleArgumentAutoComplete
{
AZStd::vector<AZStd::string> levels;
virtual int GetCount() const { return levels.size(); };
virtual const char* GetValue(int nIndex) const { return levels[nIndex].c_str(); };
};
// definition and declaration must be separated for devirtualization
static StaticInstance<SLevelNameAutoComplete, AZStd::no_destruct<SLevelNameAutoComplete>> g_LevelNameAutoComplete;
//------------------------------------------------------------------------
static void LoadMap(IConsoleCmdArgs* args)
{
if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
if (args->GetArgCount() > 1)
{
gEnv->pSystem->GetILevelSystem()->UnloadLevel();
gEnv->pSystem->GetILevelSystem()->LoadLevel(args->GetArg(1));
}
}
}
//------------------------------------------------------------------------
static void UnloadMap([[maybe_unused]] IConsoleCmdArgs* args)
{
if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
gEnv->pSystem->GetILevelSystem()->UnloadLevel();
}
}
//------------------------------------------------------------------------
CLevelSystem::CLevelSystem(ISystem* pSystem, const char* levelsFolder)
: m_pSystem(pSystem)
, m_pCurrentLevel(0)
, m_pLoadingLevelInfo(0)
{
LOADING_TIME_PROFILE_SECTION;
CRY_ASSERT(pSystem);
//if (!gEnv->IsEditor())
Rescan(levelsFolder);
m_fLastLevelLoadTime = 0;
m_fLastTime = 0;
m_bLevelLoaded = false;
m_levelLoadStartTime.SetValue(0);
m_nLoadedLevelsCount = 0;
REGISTER_COMMAND("map", LoadMap, VF_BLOCKFRAME, "Load a map");
REGISTER_COMMAND("unload", UnloadMap, 0, "Unload current map");
gEnv->pConsole->RegisterAutoComplete("map", &(*g_LevelNameAutoComplete));
AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and CryPak must be initialized for loading levels.");
if (!gEnv || !gEnv->pCryPak)
{
return;
}
auto pPak = gEnv->pCryPak;
if (AZ::IO::IArchive::LevelPackOpenEvent* levelPakOpenEvent = pPak->GetLevelPackOpenEvent())
{
m_levelPackOpenHandler = AZ::IO::IArchive::LevelPackOpenEvent::Handler([this](const AZStd::vector<AZStd::string>& levelDirs)
{
for (AZStd::string dir : levelDirs)
{
AZ::StringFunc::Path::StripComponent(dir, true);
AZStd::string searchPattern = dir + AZ_FILESYSTEM_SEPARATOR_WILDCARD;
bool modFolder = false;
PopulateLevels(searchPattern, dir, gEnv->pCryPak, modFolder, false);
}
});
m_levelPackOpenHandler.Connect(*levelPakOpenEvent);
}
if (AZ::IO::IArchive::LevelPackCloseEvent* levelPakCloseEvent = pPak->GetLevelPackCloseEvent())
{
m_levelPackCloseHandler = AZ::IO::IArchive::LevelPackCloseEvent::Handler([this](AZStd::string_view)
{
Rescan(ILevelSystem::LevelsDirectoryName);
});
m_levelPackCloseHandler.Connect(*levelPakCloseEvent);
}
}
//------------------------------------------------------------------------
CLevelSystem::~CLevelSystem()
{
}
//------------------------------------------------------------------------
void CLevelSystem::Rescan(const char* levelsFolder)
{
if (levelsFolder)
{
m_levelsFolder = levelsFolder;
}
CRY_ASSERT(!m_levelsFolder.empty());
m_levelInfos.clear();
m_levelInfos.reserve(64);
ScanFolder(0, false);
g_LevelNameAutoComplete->levels.clear();
for (int i = 0; i < (int)m_levelInfos.size(); i++)
{
g_LevelNameAutoComplete->levels.push_back(AZStd::string(PathUtil::GetFileName(m_levelInfos[i].GetName()).c_str()));
}
}
//-----------------------------------------------------------------------
void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
{
AZStd::string folder;
if (subfolder && subfolder[0])
{
folder = subfolder;
}
AZStd::string search(m_levelsFolder);
if (!folder.empty())
{
if (AZ::StringFunc::StartsWith(folder.c_str(), m_levelsFolder.c_str()))
{
search = folder;
}
else
{
search += "/" + folder;
}
}
search += "/*";
AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and must be initialized for loading levels.");
if (!gEnv || !gEnv->pCryPak)
{
return;
}
auto pPak = gEnv->pCryPak;
AZStd::unordered_set<AZStd::string> pakList;
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
if (handle)
{
do
{
AZStd::string extension;
AZStd::string levelName;
AZ::StringFunc::Path::Split(handle.m_filename.data(), nullptr, nullptr, &levelName, &extension);
if (extension == ArchiveExtension)
{
if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName))
{
// level folder contain pak files like 'level.pak'
// which we only want to load during level loading.
continue;
}
AZStd::string levelContainerPakPath;
AZ::StringFunc::Path::Join("@assets@", m_levelsFolder.c_str(), levelContainerPakPath);
if (subfolder && subfolder[0])
{
AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), subfolder, levelContainerPakPath);
}
AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), handle.m_filename.data(), levelContainerPakPath);
pakList.emplace(levelContainerPakPath);
continue;
}
} while (handle = pPak->FindNext(handle));
pPak->FindClose(handle);
}
// Open all the available paks found in the levels folder
for (auto iter = pakList.begin(); iter != pakList.end(); iter++)
{
AZStd::fixed_string<AZ::IO::IArchive::MaxPath> fullLevelPakPath;
gEnv->pCryPak->OpenPack(iter->c_str(), (unsigned)0, nullptr, &fullLevelPakPath, false);
}
// Levels in bundles now take priority over levels outside of bundles.
PopulateLevels(search, folder, pPak, modFolder, false);
// Load levels outside of the bundles to maintain backward compatibility.
PopulateLevels(search, folder, pPak, modFolder, true);
}
void CLevelSystem::PopulateLevels(
AZStd::string searchPattern, AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly)
{
{
// allow this find first to actually touch the file system
// (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
if (handle)
{
do
{
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory ||
handle.m_filename == "." || handle.m_filename == "..")
{
continue;
}
AZStd::string levelFolder;
if (fromFileSystemOnly)
{
levelFolder =
(folder.empty() ? "" : (folder + "/")) + AZStd::string(handle.m_filename.data(), handle.m_filename.size());
}
else
{
AZStd::string levelName(AZ::IO::PathView(handle.m_filename).Filename().Native());
levelFolder = (folder.empty() ? "" : (folder + "/")) + levelName;
}
AZStd::string levelPath;
if (AZ::StringFunc::StartsWith(levelFolder.c_str(), m_levelsFolder.c_str()))
{
levelPath = levelFolder;
}
else
{
levelPath = m_levelsFolder + "/" + levelFolder;
}
const AZStd::string levelPakName = levelPath + "/" + LevelPakName;
const AZStd::string levelInfoName = levelPath + "/levelinfo.xml";
if (!pPak->IsFileExist(
levelPakName.c_str(),
fromFileSystemOnly ? AZ::IO::IArchive::eFileLocation_OnDisk : AZ::IO::IArchive::eFileLocation_InPak) &&
!pPak->IsFileExist(
levelInfoName.c_str(),
fromFileSystemOnly ? AZ::IO::IArchive::eFileLocation_OnDisk : AZ::IO::IArchive::eFileLocation_InPak))
{
ScanFolder(levelFolder.c_str(), modFolder);
continue;
}
// With the level.pak workflow, levelPath and levelName will point to a directory.
// levelPath: levels/mylevel
// levelName: mylevel
CLevelInfo levelInfo;
levelInfo.m_levelPath = levelPath;
levelInfo.m_levelName = levelFolder;
levelInfo.m_isPak = !fromFileSystemOnly;
CLevelInfo* pExistingInfo = GetLevelInfoInternal(levelInfo.m_levelName);
// Don't add the level if it is already in the list
if (pExistingInfo == NULL)
{
m_levelInfos.push_back(levelInfo);
}
else
{
// Levels in bundles take priority over levels outside bundles.
if (!pExistingInfo->m_isPak && levelInfo.m_isPak)
{
*pExistingInfo = levelInfo;
}
}
} while (handle = pPak->FindNext(handle));
pPak->FindClose(handle);
}
}
}
//------------------------------------------------------------------------
int CLevelSystem::GetLevelCount()
{
return (int)m_levelInfos.size();
}
//------------------------------------------------------------------------
ILevelInfo* CLevelSystem::GetLevelInfo(int level)
{
return GetLevelInfoInternal(level);
}
//------------------------------------------------------------------------
CLevelInfo* CLevelSystem::GetLevelInfoInternal(int level)
{
if ((level >= 0) && (level < GetLevelCount()))
{
return &m_levelInfos[level];
}
return 0;
}
//------------------------------------------------------------------------
ILevelInfo* CLevelSystem::GetLevelInfo(const char* levelName)
{
return GetLevelInfoInternal(levelName);
}
//------------------------------------------------------------------------
CLevelInfo* CLevelSystem::GetLevelInfoInternal(const AZStd::string& levelName)
{
// If level not found by full name try comparing with only filename
for (AZStd::vector<CLevelInfo>::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it)
{
if (!azstricmp(it->GetName(), levelName.c_str()))
{
return &(*it);
}
}
//////////////////////////////////////////////////////////////////////////
for (AZStd::vector<CLevelInfo>::iterator it = m_levelInfos.begin(); it != m_levelInfos.end(); ++it)
{
{
if (!azstricmp(PathUtil::GetFileName(it->GetName()), levelName.c_str()))
{
return &(*it);
}
}
}
// Try stripping out the folder to find the raw filename
AZStd::string sLevelName(levelName);
size_t lastSlash = sLevelName.find_last_of('\\');
if (lastSlash == AZStd::string::npos)
{
lastSlash = sLevelName.find_last_of('/');
}
if (lastSlash != AZStd::string::npos)
{
sLevelName = sLevelName.substr(lastSlash + 1, sLevelName.size() - lastSlash - 1);
return GetLevelInfoInternal(sLevelName.c_str());
}
return 0;
}
//------------------------------------------------------------------------
void CLevelSystem::AddListener(ILevelSystemListener* pListener)
{
AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
if (it == m_listeners.end())
{
m_listeners.reserve(12);
m_listeners.push_back(pListener);
}
}
//------------------------------------------------------------------------
void CLevelSystem::RemoveListener(ILevelSystemListener* pListener)
{
AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
if (it != m_listeners.end())
{
m_listeners.erase(it);
if (m_listeners.empty())
{
m_listeners.shrink_to_fit();
}
}
}
//------------------------------------------------------------------------
bool CLevelSystem::LoadLevel(const char* _levelName)
{
if (gEnv->IsEditor())
{
AZ_TracePrintf("CrySystem::CLevelSystem", "LoadLevel for %s was called in the editor - not actually loading.\n", _levelName);
return false;
}
// If a level is currently loaded, unload it before loading the next one.
if (IsLevelLoaded())
{
UnloadLevel();
}
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_PREPARE, 0, 0);
PrepareNextLevel(_levelName);
ILevel* level = LoadLevelInternal(_levelName);
if (level)
{
OnLoadingComplete(_levelName);
}
return (level != nullptr);
}
//------------------------------------------------------------------------
ILevel* CLevelSystem::LoadLevelInternal(const char* _levelName)
{
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START);
AZ_ASSET_NAMED_SCOPE("Level: %s", _levelName);
CryLog ("Level system is loading \"%s\"", _levelName);
INDENT_LOG_DURING_SCOPE();
char levelName[256];
cry_strcpy(levelName, _levelName);
// Not remove a scope!!!
{
LOADING_TIME_PROFILE_SECTION;
//m_levelLoadStartTime = gEnv->pTimer->GetAsyncTime();
CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName);
if (!pLevelInfo)
{
// alert the listener
OnLevelNotFound(levelName);
return 0;
}
m_bLevelLoaded = false;
const bool bLoadingSameLevel = azstricmp(m_lastLevelName.c_str(), levelName) == 0;
m_lastLevelName = levelName;
delete m_pCurrentLevel;
CLevel* pLevel = new CLevel();
pLevel->m_levelInfo = *pLevelInfo;
m_pCurrentLevel = pLevel;
//////////////////////////////////////////////////////////////////////////
// Read main level info.
if (!pLevelInfo->ReadInfo())
{
OnLoadingError(levelName, "Failed to read level info (level.pak might be corrupted)!");
return 0;
}
//[AlexMcC|19.04.10]: Update the level's LevelInfo
pLevel->m_levelInfo = *pLevelInfo;
//////////////////////////////////////////////////////////////////////////
gEnv->pConsole->SetScrollMax(600);
ICVar* con_showonload = gEnv->pConsole->GetCVar("con_showonload");
if (con_showonload && con_showonload->GetIVal() != 0)
{
gEnv->pConsole->ShowConsole(true);
ICVar* g_enableloadingscreen = gEnv->pConsole->GetCVar("g_enableloadingscreen");
if (g_enableloadingscreen)
{
g_enableloadingscreen->Set(0);
}
}
// Reset the camera to (1,1,1) (not (0,0,0) which is the invalid/uninitialised state,
// to avoid the hack in the renderer to not show anything if the camera is at the origin).
CCamera defaultCam;
defaultCam.SetPosition(Vec3(1.0f));
m_pSystem->SetViewCamera(defaultCam);
m_pLoadingLevelInfo = pLevelInfo;
OnLoadingStart(levelName);
auto pPak = gEnv->pCryPak;
AZStd::string levelPath(pLevelInfo->GetPath());
ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
float spamDelay = 0.0f;
if (pSpamDelay)
{
spamDelay = pSpamDelay->GetFVal();
pSpamDelay->Set(0.0f);
}
// Parse level specific config data.
AZStd::string const sLevelNameOnly(PathUtil::GetFileName(levelName));
if (!sLevelNameOnly.empty())
{
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
if (controlsPath)
{
AZStd::string sAudioLevelPath(controlsPath);
sAudioLevelPath.append("levels/");
sAudioLevelPath += sLevelNameOnly;
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oAMData(sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC);
Audio::SAudioRequest oAudioRequestData;
oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING); // Needs to be blocking so data is available for next preloading request!
oAudioRequestData.pData = &oAMData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_PRELOADS_DATA> oAMData2(sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData2;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::TAudioPreloadRequestID nPreloadRequestID = INVALID_AUDIO_PRELOAD_REQUEST_ID;
Audio::AudioSystemRequestBus::BroadcastResult(nPreloadRequestID, &Audio::AudioSystemRequestBus::Events::GetAudioPreloadRequestID, sLevelNameOnly.c_str());
if (nPreloadRequestID != INVALID_AUDIO_PRELOAD_REQUEST_ID)
{
Audio::SAudioManagerRequestData<Audio::eAMRT_PRELOAD_SINGLE_REQUEST> requestData(nPreloadRequestID, true);
oAudioRequestData.pData = &requestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
}
}
}
{
AZStd::string missionXml("Mission_");
missionXml += pLevelInfo->m_defaultGameTypeName;
missionXml += ".xml";
AZStd::string xmlFile(pLevelInfo->GetPath());
xmlFile += "/";
xmlFile += missionXml;
if (!gEnv->IsEditor())
{
AZStd::string entitiesFilename =
AZStd::string::format("%s/%s.entities_xml", pLevelInfo->GetPath(), pLevelInfo->m_defaultGameTypeName.c_str());
AZStd::vector<char> fileBuffer;
CCryFile entitiesFile;
if (entitiesFile.Open(entitiesFilename.c_str(), "rt"))
{
fileBuffer.resize(entitiesFile.GetLength());
if (fileBuffer.size() == entitiesFile.ReadRaw(fileBuffer.begin(), fileBuffer.size()))
{
AZ::IO::ByteContainerStream<AZStd::vector<char>> fileStream(&fileBuffer);
EBUS_EVENT(AzFramework::GameEntityContextRequestBus, LoadFromStream, fileStream, false);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
// Movie system must be reset after entities.
//////////////////////////////////////////////////////////////////////////
IMovieSystem* movieSys = gEnv->pMovieSystem;
if (movieSys != NULL)
{
// bSeekAllToStart needs to be false here as it's only of interest in the editor
movieSys->Reset(true, false);
}
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PRECACHE);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
gEnv->pConsole->SetScrollMax(600 / 2);
pPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Clear();
if (pSpamDelay)
{
pSpamDelay->Set(spamDelay);
}
m_bLevelLoaded = true;
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_END);
}
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
if (auto cvar = gEnv->pConsole->GetCVar("sv_map"); cvar)
{
cvar->Set(levelName);
}
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
return m_pCurrentLevel;
}
//------------------------------------------------------------------------
void CLevelSystem::PrepareNextLevel(const char* levelName)
{
CLevelInfo* pLevelInfo = GetLevelInfoInternal(levelName);
if (!pLevelInfo)
{
// alert the listener
OnLevelNotFound(levelName);
return;
}
// This work not required in-editor.
if (!gEnv || !gEnv->IsEditor())
{
m_levelLoadStartTime = gEnv->pTimer->GetAsyncTime();
// Open pak file for a new level.
pLevelInfo->OpenLevelPak();
// switched to level heap, so now imm start the loading screen (renderer will be reinitialized in the levelheap)
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN, 0, 0);
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PREPARE);
}
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnPrepareNextLevel(pLevelInfo->GetName());
}
}
//------------------------------------------------------------------------
void CLevelSystem::OnLevelNotFound(const char* levelName)
{
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnLevelNotFound(levelName);
}
}
//------------------------------------------------------------------------
void CLevelSystem::OnLoadingStart(const char* levelName)
{
if (gEnv->pCryPak->GetRecordFileOpenList() == AZ::IO::IArchive::RFOM_EngineStartup)
{
gEnv->pCryPak->RecordFileOpen(AZ::IO::IArchive::RFOM_Level);
}
m_fLastTime = gEnv->pTimer->GetAsyncCurTime();
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START, 0, 0);
LOADING_TIME_PROFILE_SECTION(gEnv->pSystem);
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnLoadingStart(levelName);
}
}
//------------------------------------------------------------------------
void CLevelSystem::OnLoadingError(const char* levelName, const char* error)
{
ILevelInfo* pLevelInfo = m_pLoadingLevelInfo;
if (!pLevelInfo)
{
CRY_ASSERT(false);
return;
}
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnLoadingError(levelName, error);
}
((CLevelInfo*)pLevelInfo)->CloseLevelPak();
}
//------------------------------------------------------------------------
void CLevelSystem::OnLoadingComplete(const char* levelName)
{
CTimeValue t = gEnv->pTimer->GetAsyncTime();
m_fLastLevelLoadTime = (t - m_levelLoadStartTime).GetSeconds();
LogLoadingTime();
m_nLoadedLevelsCount++;
// Hide console after loading.
gEnv->pConsole->ShowConsole(false);
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnLoadingComplete(levelName);
}
#if AZ_LOADSCREENCOMPONENT_ENABLED
EBUS_EVENT(LoadScreenBus, Stop);
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
}
//------------------------------------------------------------------------
void CLevelSystem::OnLoadingProgress(const char* levelName, int progressAmount)
{
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnLoadingProgress(levelName, progressAmount);
}
}
//------------------------------------------------------------------------
void CLevelSystem::OnUnloadComplete(const char* levelName)
{
for (AZStd::vector<ILevelSystemListener*>::const_iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
{
(*it)->OnUnloadComplete(levelName);
}
}
//////////////////////////////////////////////////////////////////////////
void CLevelSystem::LogLoadingTime()
{
if (gEnv->IsEditor())
{
return;
}
if (!GetISystem()->IsDevMode())
{
return;
}
char vers[128];
GetISystem()->GetFileVersion().ToString(vers, sizeof(vers));
const char* sChain = "";
if (m_nLoadedLevelsCount > 0)
{
sChain = " (Chained)";
}
AZStd::string text;
text.format("Game Level Load Time: [%s] Level %s loaded in %.2f seconds%s", vers, m_lastLevelName.c_str(), m_fLastLevelLoadTime, sChain);
gEnv->pLog->Log(text.c_str());
}
void CLevelSystem::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_levelInfos);
pSizer->AddObject(m_levelsFolder);
pSizer->AddObject(m_listeners);
}
//////////////////////////////////////////////////////////////////////////
void CLevelSystem::UnloadLevel()
{
if (gEnv->IsEditor())
{
return;
}
if (!m_pLoadingLevelInfo)
{
return;
}
CryLog("UnloadLevel Start");
INDENT_LOG_DURING_SCOPE();
// Flush core buses. We're about to unload Cry modules and need to ensure we don't have module-owned functions left behind.
AZ::Data::AssetBus::ExecuteQueuedEvents();
AZ::TickBus::ExecuteQueuedEvents();
AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
if (gEnv && gEnv->pSystem)
{
// clear all error messages to prevent stalling due to runtime file access check during chainloading
gEnv->pSystem->ClearErrorMessages();
}
if (gEnv && gEnv->pCryPak)
{
gEnv->pCryPak->DisableRuntimeFileAccess(false);
}
CTimeValue tBegin = gEnv->pTimer->GetAsyncTime();
// Clear level entities and prefab instances.
EBUS_EVENT(AzFramework::GameEntityContextRequestBus, ResetGameContext);
if (gEnv->pMovieSystem)
{
gEnv->pMovieSystem->Reset(false, false);
gEnv->pMovieSystem->RemoveAllSequences();
}
// Unload level specific audio binary data.
Audio::SAudioManagerRequestData<Audio::eAMRT_UNLOAD_AFCM_DATA_BY_SCOPE> oAMData(Audio::eADS_LEVEL_SPECIFIC);
Audio::SAudioRequest oAudioRequestData;
oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING);
oAudioRequestData.pData = &oAMData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
// Now unload level specific audio config data.
Audio::SAudioManagerRequestData<Audio::eAMRT_CLEAR_CONTROLS_DATA> oAMData2(Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData2;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::SAudioManagerRequestData<Audio::eAMRT_CLEAR_PRELOADS_DATA> oAMData3(Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData3;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
// Reset the camera to (0,0,0) which is the invalid/uninitialised state
CCamera defaultCam;
m_pSystem->SetViewCamera(defaultCam);
OnUnloadComplete(m_lastLevelName.c_str());
// -- kenzo: this will close all pack files for this level
// (even the ones which were not added through here, if this is not desired,
// then change code to close only level.pak)
if (m_pLoadingLevelInfo)
{
((CLevelInfo*)m_pLoadingLevelInfo)->CloseLevelPak();
m_pLoadingLevelInfo = NULL;
}
m_lastLevelName.clear();
SAFE_RELEASE(m_pCurrentLevel);
// Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
// Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);
// Perform level unload procedures for the LyShine UI system
if (gEnv && gEnv->pLyShine)
{
gEnv->pLyShine->OnLevelUnload();
}
m_bLevelLoaded = false;
CTimeValue tUnloadTime = gEnv->pTimer->GetAsyncTime() - tBegin;
CryLog("UnloadLevel End: %.1f sec", tUnloadTime.GetSeconds());
// Must be sent last.
// Cleanup all containers
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0);
AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
}
} // namespace LegacyLevelSystem
@@ -0,0 +1,169 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "ILevelSystem.h"
#include <AzFramework/Archive/IArchive.h>
// [LYN-2376] Remove the entire file once legacy slice support is removed
namespace LegacyLevelSystem
{
class CLevelInfo
: public ILevelInfo
{
friend class CLevelSystem;
public:
CLevelInfo() = default;
// ILevelInfo
virtual const char* GetName() const { return m_levelName.c_str(); }
virtual const char* GetPath() const { return m_levelPath.c_str(); }
virtual const char* GetAssetName() const { return m_levelAssetName.c_str(); }
// ~ILevelInfo
void GetMemoryUsage(ICrySizer*) const;
private:
bool ReadInfo();
bool OpenLevelPak();
void CloseLevelPak();
AZStd::string m_defaultGameTypeName;
AZStd::string m_levelName;
AZStd::string m_levelPath;
AZStd::string m_levelAssetName;
AZStd::string m_levelPakFullPath;
bool m_isPak = false;
};
struct ILevel
{
virtual ~ILevel() = default;
virtual void Release() = 0;
virtual ILevelInfo* GetLevelInfo() = 0;
};
class CLevel
: public ILevel
{
friend class CLevelSystem;
public:
CLevel() {}
virtual ~CLevel() = default;
virtual void Release() { delete this; }
virtual ILevelInfo* GetLevelInfo() { return &m_levelInfo; }
private:
CLevelInfo m_levelInfo;
};
class CLevelSystem
: public ILevelSystem
{
public:
CLevelSystem(ISystem* pSystem, const char* levelsFolder);
virtual ~CLevelSystem();
void Release() { delete this; };
// ILevelSystem
virtual void Rescan(const char* levelsFolder);
virtual int GetLevelCount();
virtual ILevelInfo* GetLevelInfo(int level);
virtual ILevelInfo* GetLevelInfo(const char* levelName);
virtual void AddListener(ILevelSystemListener* pListener);
virtual void RemoveListener(ILevelSystemListener* pListener);
virtual bool LoadLevel(const char* levelName);
virtual void UnloadLevel();
virtual bool IsLevelLoaded() { return m_bLevelLoaded; }
const char* GetCurrentLevelName() const override
{
if (m_pCurrentLevel && m_pCurrentLevel->GetLevelInfo())
{
return m_pCurrentLevel->GetLevelInfo()->GetName();
}
else
{
return "";
}
}
// If the level load failed then we need to have a different shutdown procedure vs when a level is naturally unloaded
virtual void SetLevelLoadFailed(bool loadFailed) { m_levelLoadFailed = loadFailed; }
virtual bool GetLevelLoadFailed() { return m_levelLoadFailed; }
// Unsupported by legacy level system.
virtual AZ::Data::AssetType GetLevelAssetType() const { return {}; }
// ~ILevelSystem
void GetMemoryUsage(ICrySizer* s) const;
private:
float GetLastLevelLoadTime() { return m_fLastLevelLoadTime; }
void ScanFolder(const char* subfolder, bool modFolder);
void PopulateLevels(
AZStd::string searchPattern, AZStd::string& folder, AZ::IO::IArchive* pPak, bool& modFolder, bool fromFileSystemOnly);
void PrepareNextLevel(const char* levelName);
ILevel* LoadLevelInternal(const char* _levelName);
// Methods to notify ILevelSystemListener
void OnLevelNotFound(const char* levelName);
void OnLoadingStart(const char* levelName);
void OnLoadingComplete(const char* levelName);
void OnLoadingError(const char* levelName, const char* error);
void OnLoadingProgress(const char* levelName, int progressAmount);
void OnUnloadComplete(const char* levelName);
void LogLoadingTime();
bool LoadLevelInfo(CLevelInfo& levelInfo);
// internal get functions for the level infos ... they preserve the type and don't
// directly cast to the interface
CLevelInfo* GetLevelInfoInternal(int level);
CLevelInfo* GetLevelInfoInternal(const AZStd::string& levelName);
ISystem* m_pSystem;
AZStd::vector<CLevelInfo> m_levelInfos;
AZStd::string m_levelsFolder;
ILevel* m_pCurrentLevel;
ILevelInfo* m_pLoadingLevelInfo;
AZStd::string m_lastLevelName;
float m_fLastLevelLoadTime;
float m_fLastTime;
bool m_bLevelLoaded;
bool m_levelLoadFailed = false;
int m_nLoadedLevelsCount;
CTimeValue m_levelLoadStartTime;
AZStd::vector<ILevelSystemListener*> m_listeners;
AZ::IO::IArchive::LevelPackOpenEvent::Handler m_levelPackOpenHandler;
AZ::IO::IArchive::LevelPackCloseEvent::Handler m_levelPackCloseHandler;
static constexpr const char* LevelPakName = "level.pak";
};
} // namespace LegacyLevelSystem
@@ -0,0 +1,595 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "SpawnableLevelSystem.h"
#include <IAudioSystem.h>
#include "IMovieSystem.h"
#include <LoadScreenBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/IO/FileOperations.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include "MainThreadRenderRequestBus.h"
#include <LyShine/ILyShine.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Script/ScriptSystemBus.h>
namespace LegacyLevelSystem
{
//------------------------------------------------------------------------
static void LoadLevel(const AZ::ConsoleCommandContainer& arguments)
{
AZ_Error("SpawnableLevelSystem", !arguments.empty(), "LoadLevel requires a level file name to be provided.");
AZ_Error("SpawnableLevelSystem", arguments.size() == 1, "LoadLevel requires a single level file name to be provided.");
if (!arguments.empty() && gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
gEnv->pSystem->GetILevelSystem()->LoadLevel(arguments[0].data());
}
}
//------------------------------------------------------------------------
static void UnloadLevel([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
AZ_Warning("SpawnableLevelSystem", !arguments.empty(), "UnloadLevel doesn't use any arguments.");
if (gEnv->pSystem && gEnv->pSystem->GetILevelSystem() && !gEnv->IsEditor())
{
gEnv->pSystem->GetILevelSystem()->UnloadLevel();
}
}
AZ_CONSOLEFREEFUNC(LoadLevel, AZ::ConsoleFunctorFlags::Null, "Unloads the current level and loads a new one with the given asset name");
AZ_CONSOLEFREEFUNC(UnloadLevel, AZ::ConsoleFunctorFlags::Null, "Unloads the current level");
//------------------------------------------------------------------------
SpawnableLevelSystem::SpawnableLevelSystem(ISystem* pSystem)
: m_pSystem(pSystem)
{
LOADING_TIME_PROFILE_SECTION;
CRY_ASSERT(pSystem);
m_fLastLevelLoadTime = 0;
m_fLastTime = 0;
m_bLevelLoaded = false;
m_levelLoadStartTime.SetValue(0);
m_nLoadedLevelsCount = 0;
AZ_Assert(gEnv && gEnv->pCryPak, "gEnv and CryPak must be initialized for loading levels.");
if (!gEnv || !gEnv->pCryPak)
{
return;
}
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
}
//------------------------------------------------------------------------
SpawnableLevelSystem::~SpawnableLevelSystem()
{
AzFramework::RootSpawnableNotificationBus::Handler::BusDisconnect();
}
void SpawnableLevelSystem::Release()
{
delete this;
}
bool SpawnableLevelSystem::IsLevelLoaded()
{
return m_bLevelLoaded;
}
const char* SpawnableLevelSystem::GetCurrentLevelName() const
{
return m_bLevelLoaded ? m_lastLevelName.c_str() : "";
}
// If the level load failed then we need to have a different shutdown procedure vs when a level is naturally unloaded
void SpawnableLevelSystem::SetLevelLoadFailed(bool loadFailed)
{
m_levelLoadFailed = loadFailed;
}
bool SpawnableLevelSystem::GetLevelLoadFailed()
{
return m_levelLoadFailed;
}
AZ::Data::AssetType SpawnableLevelSystem::GetLevelAssetType() const
{
return azrtti_typeid<AzFramework::Spawnable>();
}
// The following methods are deprecated from ILevelSystem and will be removed once slice support is removed.
// [LYN-2376] Remove once legacy slice support is removed
void SpawnableLevelSystem::Rescan([[maybe_unused]] const char* levelsFolder)
{
AZ_Assert(false, "Rescan - No longer supported.");
}
// [LYN-2376] Remove once legacy slice support is removed
int SpawnableLevelSystem::GetLevelCount()
{
AZ_Assert(false, "GetLevelCount - No longer supported.");
return 0;
}
// [LYN-2376] Remove once legacy slice support is removed
ILevelInfo* SpawnableLevelSystem::GetLevelInfo([[maybe_unused]] int level)
{
AZ_Assert(false, "GetLevelInfo - No longer supported.");
return nullptr;
}
// [LYN-2376] Remove once legacy slice support is removed
ILevelInfo* SpawnableLevelSystem::GetLevelInfo([[maybe_unused]] const char* levelName)
{
AZ_Assert(false, "GetLevelInfo - No longer supported.");
return nullptr;
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::AddListener(ILevelSystemListener* pListener)
{
AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
if (it == m_listeners.end())
{
m_listeners.push_back(pListener);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::RemoveListener(ILevelSystemListener* pListener)
{
AZStd::vector<ILevelSystemListener*>::iterator it = AZStd::find(m_listeners.begin(), m_listeners.end(), pListener);
if (it != m_listeners.end())
{
m_listeners.erase(it);
}
}
//------------------------------------------------------------------------
bool SpawnableLevelSystem::LoadLevel(const char* levelName)
{
if (gEnv->IsEditor())
{
AZ_TracePrintf("CrySystem::CLevelSystem", "LoadLevel for %s was called in the editor - not actually loading.\n", levelName);
return false;
}
// If a level is currently loaded, unload it before loading the next one.
if (IsLevelLoaded())
{
UnloadLevel();
}
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_PREPARE, 0, 0);
PrepareNextLevel(levelName);
bool result = LoadLevelInternal(levelName);
if (result)
{
OnLoadingComplete(levelName);
}
return result;
}
//------------------------------------------------------------------------
bool SpawnableLevelSystem::LoadLevelInternal(const char* levelName)
{
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START);
AZ_ASSET_NAMED_SCOPE("Level: %s", levelName);
INDENT_LOG_DURING_SCOPE();
AZ::Data::AssetId rootSpawnableAssetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, nullptr, false);
if (!rootSpawnableAssetId.IsValid())
{
OnLoadingError(levelName, "AssetCatalog has no entry for the requested level.");
return false;
}
// This scope is specifically used for marking a loading time profile section
{
LOADING_TIME_PROFILE_SECTION;
m_bLevelLoaded = false;
m_lastLevelName = levelName;
gEnv->pConsole->SetScrollMax(600);
ICVar* con_showonload = gEnv->pConsole->GetCVar("con_showonload");
if (con_showonload && con_showonload->GetIVal() != 0)
{
gEnv->pConsole->ShowConsole(true);
ICVar* g_enableloadingscreen = gEnv->pConsole->GetCVar("g_enableloadingscreen");
if (g_enableloadingscreen)
{
g_enableloadingscreen->Set(0);
}
}
// This is a workaround until the replacement for GameEntityContext is done
AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
// Reset the camera to (1,1,1) (not (0,0,0) which is the invalid/uninitialised state,
// to avoid the hack in the renderer to not show anything if the camera is at the origin).
CCamera defaultCam;
defaultCam.SetPosition(Vec3(1.0f));
m_pSystem->SetViewCamera(defaultCam);
OnLoadingStart(levelName);
auto pPak = gEnv->pCryPak;
ICVar* pSpamDelay = gEnv->pConsole->GetCVar("log_SpamDelay");
float spamDelay = 0.0f;
if (pSpamDelay)
{
spamDelay = pSpamDelay->GetFVal();
pSpamDelay->Set(0.0f);
}
// Parse level specific config data.
AZStd::string const sLevelNameOnly(PathUtil::GetFileName(levelName));
if (!sLevelNameOnly.empty())
{
const char* controlsPath = nullptr;
Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath);
if (controlsPath)
{
AZStd::string sAudioLevelPath(controlsPath);
sAudioLevelPath.append("levels/");
sAudioLevelPath += sLevelNameOnly;
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_CONTROLS_DATA> oAMData(
sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC);
Audio::SAudioRequest oAudioRequestData;
oAudioRequestData.nFlags =
(Audio::eARF_PRIORITY_HIGH |
Audio::eARF_EXECUTE_BLOCKING); // Needs to be blocking so data is available for next preloading request!
oAudioRequestData.pData = &oAMData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::SAudioManagerRequestData<Audio::eAMRT_PARSE_PRELOADS_DATA> oAMData2(
sAudioLevelPath.c_str(), Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData2;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::TAudioPreloadRequestID nPreloadRequestID = INVALID_AUDIO_PRELOAD_REQUEST_ID;
Audio::AudioSystemRequestBus::BroadcastResult(
nPreloadRequestID, &Audio::AudioSystemRequestBus::Events::GetAudioPreloadRequestID, sLevelNameOnly.c_str());
if (nPreloadRequestID != INVALID_AUDIO_PRELOAD_REQUEST_ID)
{
Audio::SAudioManagerRequestData<Audio::eAMRT_PRELOAD_SINGLE_REQUEST> requestData(nPreloadRequestID, true);
oAudioRequestData.pData = &requestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
}
}
}
AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable(
rootSpawnableAssetId, azrtti_typeid<AzFramework::Spawnable>(), levelName);
m_rootSpawnableId = rootSpawnableAssetId;
m_rootSpawnableGeneration = AzFramework::RootSpawnableInterface::Get()->AssignRootSpawnable(rootSpawnable);
// This is a workaround until the replacement for GameEntityContext is done
AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
//////////////////////////////////////////////////////////////////////////
// Movie system must be reset after entities.
//////////////////////////////////////////////////////////////////////////
IMovieSystem* movieSys = gEnv->pMovieSystem;
if (movieSys != NULL)
{
// bSeekAllToStart needs to be false here as it's only of interest in the editor
movieSys->Reset(true, false);
}
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PRECACHE);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
gEnv->pConsole->SetScrollMax(600 / 2);
pPak->GetResourceList(AZ::IO::IArchive::RFOM_NextLevel)->Clear();
if (pSpamDelay)
{
pSpamDelay->Set(spamDelay);
}
m_bLevelLoaded = true;
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_END);
}
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_END, 0, 0);
if (auto cvar = gEnv->pConsole->GetCVar("sv_map"); cvar)
{
cvar->Set(levelName);
}
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_PRECACHE_START, 0, 0);
return true;
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::PrepareNextLevel(const char* levelName)
{
AZ::Data::AssetId rootSpawnableAssetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
rootSpawnableAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, levelName, nullptr, false);
if (!rootSpawnableAssetId.IsValid())
{
// alert the listener
OnLevelNotFound(levelName);
return;
}
// This work not required in-editor.
if (!gEnv || !gEnv->IsEditor())
{
m_levelLoadStartTime = gEnv->pTimer->GetAsyncTime();
// switched to level heap, so now imm start the loading screen (renderer will be reinitialized in the levelheap)
gEnv->pSystem->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN, 0, 0);
gEnv->pSystem->SetSystemGlobalState(ESYSTEM_GLOBAL_STATE_LEVEL_LOAD_START_PREPARE);
}
OnPrepareNextLevel(levelName);
}
void SpawnableLevelSystem::OnPrepareNextLevel(const char* levelName)
{
AZ_TracePrintf("LevelSystem", "Level system is preparing to load '%s'\n", levelName);
for (auto& listener : m_listeners)
{
listener->OnPrepareNextLevel(levelName);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnLevelNotFound(const char* levelName)
{
AZ_Error("LevelSystem", false, "Requested level not found: '%s'\n", levelName);
for (auto& listener : m_listeners)
{
listener->OnLevelNotFound(levelName);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnLoadingStart(const char* levelName)
{
AZ_TracePrintf("LevelSystem", "Level system is loading '%s'\n", levelName);
if (gEnv->pCryPak->GetRecordFileOpenList() == AZ::IO::IArchive::RFOM_EngineStartup)
{
gEnv->pCryPak->RecordFileOpen(AZ::IO::IArchive::RFOM_Level);
}
m_fLastTime = gEnv->pTimer->GetAsyncCurTime();
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_LOAD_START, 0, 0);
LOADING_TIME_PROFILE_SECTION(gEnv->pSystem);
for (auto& listener : m_listeners)
{
listener->OnLoadingStart(levelName);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnLoadingError(const char* levelName, const char* error)
{
AZ_Error("LevelSystem", false, "Error loading level '%s': %s\n", levelName, error);
for (auto& listener : m_listeners)
{
listener->OnLoadingError(levelName, error);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnLoadingComplete(const char* levelName)
{
CTimeValue t = gEnv->pTimer->GetAsyncTime();
m_fLastLevelLoadTime = (t - m_levelLoadStartTime).GetSeconds();
LogLoadingTime();
m_nLoadedLevelsCount++;
// Hide console after loading.
gEnv->pConsole->ShowConsole(false);
for (auto& listener : m_listeners)
{
listener->OnLoadingComplete(levelName);
}
#if AZ_LOADSCREENCOMPONENT_ENABLED
EBUS_EVENT(LoadScreenBus, Stop);
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
AZ_TracePrintf("LevelSystem", "Level load complete: '%s'\n", levelName);
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnLoadingProgress(const char* levelName, int progressAmount)
{
for (auto& listener : m_listeners)
{
listener->OnLoadingProgress(levelName, progressAmount);
}
}
//------------------------------------------------------------------------
void SpawnableLevelSystem::OnUnloadComplete(const char* levelName)
{
for (auto& listener : m_listeners)
{
listener->OnUnloadComplete(levelName);
}
AZ_TracePrintf("LevelSystem", "Level unload complete: '%s'\n", levelName);
}
//////////////////////////////////////////////////////////////////////////
void SpawnableLevelSystem::LogLoadingTime()
{
if (gEnv->IsEditor())
{
return;
}
if (!GetISystem()->IsDevMode())
{
return;
}
char vers[128];
GetISystem()->GetFileVersion().ToString(vers, sizeof(vers));
const char* sChain = "";
if (m_nLoadedLevelsCount > 0)
{
sChain = " (Chained)";
}
AZStd::string text;
text.format(
"Game Level Load Time: [%s] Level %s loaded in %.2f seconds%s", vers, m_lastLevelName.c_str(), m_fLastLevelLoadTime, sChain);
gEnv->pLog->Log(text.c_str());
}
//////////////////////////////////////////////////////////////////////////
void SpawnableLevelSystem::UnloadLevel()
{
if (gEnv->IsEditor())
{
return;
}
if (m_lastLevelName.empty())
{
return;
}
AZ_TracePrintf("LevelSystem", "UnloadLevel Start\n");
INDENT_LOG_DURING_SCOPE();
// Flush core buses. We're about to unload Cry modules and need to ensure we don't have module-owned functions left behind.
AZ::Data::AssetBus::ExecuteQueuedEvents();
AZ::TickBus::ExecuteQueuedEvents();
AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
if (gEnv && gEnv->pSystem)
{
// clear all error messages to prevent stalling due to runtime file access check during chainloading
gEnv->pSystem->ClearErrorMessages();
}
if (gEnv && gEnv->pCryPak)
{
gEnv->pCryPak->DisableRuntimeFileAccess(false);
}
CTimeValue tBegin = gEnv->pTimer->GetAsyncTime();
// Clear level entities and prefab instances.
EBUS_EVENT(AzFramework::GameEntityContextRequestBus, ResetGameContext);
if (gEnv->pMovieSystem)
{
gEnv->pMovieSystem->Reset(false, false);
gEnv->pMovieSystem->RemoveAllSequences();
}
// Unload level specific audio binary data.
Audio::SAudioManagerRequestData<Audio::eAMRT_UNLOAD_AFCM_DATA_BY_SCOPE> oAMData(Audio::eADS_LEVEL_SPECIFIC);
Audio::SAudioRequest oAudioRequestData;
oAudioRequestData.nFlags = (Audio::eARF_PRIORITY_HIGH | Audio::eARF_EXECUTE_BLOCKING);
oAudioRequestData.pData = &oAMData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
// Now unload level specific audio config data.
Audio::SAudioManagerRequestData<Audio::eAMRT_CLEAR_CONTROLS_DATA> oAMData2(Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData2;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
Audio::SAudioManagerRequestData<Audio::eAMRT_CLEAR_PRELOADS_DATA> oAMData3(Audio::eADS_LEVEL_SPECIFIC);
oAudioRequestData.pData = &oAMData3;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequestBlocking, oAudioRequestData);
// Reset the camera to (0,0,0) which is the invalid/uninitialised state
CCamera defaultCam;
m_pSystem->SetViewCamera(defaultCam);
OnUnloadComplete(m_lastLevelName.c_str());
AzFramework::RootSpawnableInterface::Get()->ReleaseRootSpawnable();
m_lastLevelName.clear();
// Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
// Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);
// Perform level unload procedures for the LyShine UI system
if (gEnv && gEnv->pLyShine)
{
gEnv->pLyShine->OnLevelUnload();
}
m_bLevelLoaded = false;
CTimeValue tUnloadTime = gEnv->pTimer->GetAsyncTime() - tBegin;
AZ_TracePrintf("LevelSystem", "UnloadLevel End: %.1f sec\n", tUnloadTime.GetSeconds());
// Must be sent last.
// Cleanup all containers
GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_LEVEL_POST_UNLOAD, 0, 0);
AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesReset);
}
void SpawnableLevelSystem::OnRootSpawnableAssigned(
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
{
}
void SpawnableLevelSystem::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
{
}
} // namespace LegacyLevelSystem
@@ -0,0 +1,88 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "ILevelSystem.h"
#include <AzCore/Console/IConsole.h>
#include <AzFramework/Archive/IArchive.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
namespace LegacyLevelSystem
{
class SpawnableLevelSystem
: public ILevelSystem
, public AzFramework::RootSpawnableNotificationBus::Handler
{
public:
explicit SpawnableLevelSystem(ISystem* pSystem);
~SpawnableLevelSystem() override;
// ILevelSystem
void Release() override;
void AddListener(ILevelSystemListener* pListener) override;
void RemoveListener(ILevelSystemListener* pListener) override;
bool LoadLevel(const char* levelName) override;
void UnloadLevel() override;
bool IsLevelLoaded() override;
const char* GetCurrentLevelName() const override;
// If the level load failed then we need to have a different shutdown procedure vs when a level is naturally unloaded
void SetLevelLoadFailed(bool loadFailed) override;
bool GetLevelLoadFailed() override;
AZ::Data::AssetType GetLevelAssetType() const override;
// The following methods are deprecated from ILevelSystem and will be removed once slice support is removed.
// [LYN-2376] Remove once legacy slice support is removed
void Rescan([[maybe_unused]] const char* levelsFolder) override;
int GetLevelCount() override;
ILevelInfo* GetLevelInfo([[maybe_unused]] int level) override;
ILevelInfo* GetLevelInfo([[maybe_unused]] const char* levelName) override;
private:
void OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, uint32_t generation) override;
void OnRootSpawnableReleased(uint32_t generation) override;
void PrepareNextLevel(const char* levelName);
bool LoadLevelInternal(const char* levelName);
// Methods to notify ILevelSystemListener
void OnPrepareNextLevel(const char* levelName);
void OnLevelNotFound(const char* levelName);
void OnLoadingStart(const char* levelName);
void OnLoadingComplete(const char* levelName);
void OnLoadingError(const char* levelName, const char* error);
void OnLoadingProgress(const char* levelName, int progressAmount);
void OnUnloadComplete(const char* levelName);
void LogLoadingTime();
ISystem* m_pSystem{nullptr};
AZStd::string m_lastLevelName;
float m_fLastLevelLoadTime{0.0f};
float m_fLastTime{0.0f};
bool m_bLevelLoaded{false};
bool m_levelLoadFailed{false};
int m_nLoadedLevelsCount{0};
CTimeValue m_levelLoadStartTime;
AZStd::vector<ILevelSystemListener*> m_listeners;
// Information about the currently-loaded root spawnable, used for tracking loads and unloads.
uint64_t m_rootSpawnableGeneration{0};
AZ::Data::AssetId m_rootSpawnableId{};
};
} // namespace LegacyLevelSystem
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,315 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ILocalizationManager.h>
#include <StlUtils.h>
#include <VectorMap.h>
#include <AzCore/std/containers/map.h>
#include "Huffman.h"
//////////////////////////////////////////////////////////////////////////
/*
Manage Localization Data
*/
class CLocalizedStringsManager
: public ILocalizationManager
, public ISystemEventListener
{
public:
typedef std::vector<string> TLocalizationTagVec;
constexpr const static size_t LOADING_FIXED_STRING_LENGTH = 2048;
constexpr const static size_t COMPRESSION_FIXED_BUFFER_LENGTH = 6144;
CLocalizedStringsManager(ISystem* pSystem);
virtual ~CLocalizedStringsManager();
// ILocalizationManager
const char* LangNameFromPILID(const ILocalizationManager::EPlatformIndependentLanguageID id);
ILocalizationManager::EPlatformIndependentLanguageID PILIDFromLangName(AZStd::string langName) override;
ILocalizationManager::EPlatformIndependentLanguageID GetSystemLanguage() override;
ILocalizationManager::TLocalizationBitfield MaskSystemLanguagesFromSupportedLocalizations(const ILocalizationManager::TLocalizationBitfield systemLanguages);
ILocalizationManager::TLocalizationBitfield IsLanguageSupported(const ILocalizationManager::EPlatformIndependentLanguageID id);
const char* GetLanguage() override;
bool SetLanguage(const char* sLanguage) override;
int GetLocalizationFormat() const override;
virtual AZStd::string GetLocalizedSubtitleFilePath(const AZStd::string& localVideoPath, const AZStd::string& subtitleFileExtension) const override;
virtual AZStd::string GetLocalizedLocXMLFilePath(const AZStd::string& localXmlPath) const override;
bool InitLocalizationData(const char* sFileName, bool bReload = false);
bool RequestLoadLocalizationDataByTag(const char* sTag);
bool LoadLocalizationDataByTag(const char* sTag, bool bReload = false);
bool ReleaseLocalizationDataByTag(const char* sTag);
bool LoadAllLocalizationData(bool bReload = false) override;
bool LoadExcelXmlSpreadsheet(const char* sFileName, bool bReload = false) override;
void ReloadData() override;
void FreeData();
bool LocalizeString_s(const string& sString, string& outLocalizedString, bool bEnglish = false) override;
bool LocalizeString_ch(const char* sString, string& outLocalizedString, bool bEnglish = false) override;
void LocalizeAndSubstituteInternal(AZStd::string& locString, const AZStd::vector<AZStd::string>& keys, const AZStd::vector<AZStd::string>& values) override;
bool LocalizeLabel(const char* sLabel, string& outLocalizedString, bool bEnglish = false) override;
bool IsLocalizedInfoFound(const char* sKey);
bool GetLocalizedInfoByKey(const char* sKey, SLocalizedInfoGame& outGameInfo);
bool GetLocalizedInfoByKey(const char* sKey, SLocalizedSoundInfoGame* pOutSoundInfoGame);
int GetLocalizedStringCount();
bool GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoGame& outGameInfo);
bool GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoEditor& outEditorInfo);
bool GetEnglishString(const char* sKey, string& sLocalizedString) override;
bool GetSubtitle(const char* sKeyOrLabel, string& outSubtitle, bool bForceSubtitle = false) override;
void FormatStringMessage_List(string& outString, const string& sString, const char** sParams, int nParams) override;
void FormatStringMessage(string& outString, const string& sString, const char* param1, const char* param2 = 0, const char* param3 = 0, const char* param4 = 0) override;
void LocalizeTime(time_t t, bool bMakeLocalTime, bool bShowSeconds, string& outTimeString) override;
void LocalizeDate(time_t t, bool bMakeLocalTime, bool bShort, bool bIncludeWeekday, string& outDateString) override;
void LocalizeDuration(int seconds, string& outDurationString) override;
void LocalizeNumber(int number, string& outNumberString) override;
void LocalizeNumber_Decimal(float number, int decimals, string& outNumberString) override;
bool ProjectUsesLocalization() const override;
// ~ILocalizationManager
// ISystemEventManager
void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
// ~ISystemEventManager
int GetMemoryUsage(ICrySizer* pSizer);
void GetLoadedTags(TLocalizationTagVec& tagVec);
void FreeLocalizationData();
private:
void SetAvailableLocalizationsBitfield(const ILocalizationManager::TLocalizationBitfield availableLocalizations);
bool LocalizeStringInternal(const char* pStr, size_t len, string& outLocalizedString, bool bEnglish);
bool DoLoadExcelXmlSpreadsheet(const char* sFileName, uint8 tagID, bool bReload);
typedef bool(CLocalizedStringsManager::*LoadFunc)(const char*, uint8, bool);
bool DoLoadAGSXmlDocument(const char* sFileName, uint8 tagID, bool bReload);
LoadFunc GetLoadFunction() const;
struct SLocalizedStringEntryEditorExtension
{
string sKey; // Map key text equivalent (without @)
string sOriginalActorLine; // english text
string sUtf8TranslatedActorLine; // localized text
string sOriginalText; // subtitle. if empty, uses English text
string sOriginalCharacterName; // english character name speaking via XML asset
unsigned int nRow; // Number of row in XML file
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sKey);
pSizer->AddObject(sOriginalActorLine);
pSizer->AddObject(sUtf8TranslatedActorLine);
pSizer->AddObject(sOriginalText);
pSizer->AddObject(sOriginalCharacterName);
}
};
struct SLanguage;
//#define LOG_DECOMP_TIMES //If defined, will log decompression times to a file
struct SLocalizedStringEntry
{
//Flags
enum
{
USE_SUBTITLE = BIT(0), //should a subtitle displayed for this key?
IS_DIRECTED_RADIO = BIT(1), //should the radio receiving hud be displayed?
IS_INTERCEPTED = BIT(2), //should the radio receiving hud show the interception display?
IS_COMPRESSED = BIT(3), //Translated text is compressed
};
union trans_text
{
string* psUtf8Uncompressed;
uint8* szCompressed; // Note that no size information is stored. This is for struct size optimization and unfortunately renders the size info inaccurate.
};
string sCharacterName; // character name speaking via XML asset
trans_text TranslatedText; // Subtitle of this line
// audio specific part
string sPrototypeSoundEvent; // associated sound event prototype (radio, ...)
CryHalf fVolume;
CryHalf fRadioRatio;
// SoundMoods
DynArray<SLocalizedAdvancesSoundEntry> SoundMoods;
// EventParameters
DynArray<SLocalizedAdvancesSoundEntry> EventParameters;
// ~audio specific part
// subtitle & radio flags
uint8 flags;
// Index of Huffman tree for translated text. -1 = no tree assigned (error)
int8 huffmanTreeIndex;
uint8 nTagID;
// bool bDependentTranslation; // if the english/localized text contains other localization labels
//Additional information for Sandbox. Null in game
SLocalizedStringEntryEditorExtension* pEditorExtension;
SLocalizedStringEntry()
: flags(0)
, huffmanTreeIndex(-1)
, pEditorExtension(NULL)
{
TranslatedText.psUtf8Uncompressed = NULL;
};
~SLocalizedStringEntry()
{
SAFE_DELETE(pEditorExtension);
if ((flags & IS_COMPRESSED) == 0)
{
SAFE_DELETE(TranslatedText.psUtf8Uncompressed);
}
else
{
SAFE_DELETE_ARRAY(TranslatedText.szCompressed);
}
};
string GetTranslatedText(const SLanguage* pLanguage) const;
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sCharacterName);
if ((flags & IS_COMPRESSED) == 0 && TranslatedText.psUtf8Uncompressed != NULL) //Number of bytes stored for compressed text is unknown, which throws this GetMemoryUsage off
{
pSizer->AddObject(*TranslatedText.psUtf8Uncompressed);
}
pSizer->AddObject(sPrototypeSoundEvent);
pSizer->AddObject(SoundMoods);
pSizer->AddObject(EventParameters);
if (pEditorExtension != NULL)
{
pEditorExtension->GetMemoryUsage(pSizer);
}
}
};
//Keys as CRC32. Strings previously, but these proved too large
typedef VectorMap<uint32, SLocalizedStringEntry*> StringsKeyMap;
struct SLanguage
{
typedef std::vector<SLocalizedStringEntry*> TLocalizedStringEntries;
typedef std::vector<HuffmanCoder*> THuffmanCoders;
string sLanguage;
StringsKeyMap m_keysMap;
TLocalizedStringEntries m_vLocalizedStrings;
THuffmanCoders m_vEncoders;
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sLanguage);
pSizer->AddObject(m_vLocalizedStrings);
pSizer->AddObject(m_keysMap);
pSizer->AddObject(m_vEncoders);
}
};
struct SFileInfo
{
bool bDataStripping;
uint8 nTagID;
};
#ifndef _RELEASE
std::map<string, bool> m_warnedAboutLabels;
bool m_haveWarnedAboutAtLeastOneLabel;
void LocalizedStringsManagerWarning(const char* label, const char* message);
void ListAndClearProblemLabels();
#else
inline void LocalizedStringsManagerWarning(...) {};
inline void ListAndClearProblemLabels() {};
#endif
void AddLocalizedString(SLanguage* pLanguage, SLocalizedStringEntry* pEntry, const uint32 keyCRC32);
void AddControl(int nKey);
//////////////////////////////////////////////////////////////////////////
void ParseFirstLine(IXmlTableReader* pXmlTableReader, char* nCellIndexToType, std::map<int, string>& SoundMoodIndex, std::map<int, string>& EventParameterIndex);
void InternalSetCurrentLanguage(SLanguage* pLanguage);
ISystem* m_pSystem;
// Pointer to the current language.
SLanguage* m_pLanguage;
// all loaded Localization Files
typedef std::pair<string, SFileInfo> pairFileName;
typedef std::map<string, SFileInfo> tmapFilenames;
tmapFilenames m_loadedTables;
// filenames per tag
typedef std::vector<string> TStringVec;
struct STag
{
TStringVec filenames;
uint8 id;
bool loaded;
};
typedef std::map<string, STag> TTagFileNames;
TTagFileNames m_tagFileNames;
TStringVec m_tagLoadRequests;
// Array of loaded languages.
std::vector<SLanguage*> m_languages;
typedef std::set<string> PrototypeSoundEvents;
PrototypeSoundEvents m_prototypeEvents; // this set is purely used for clever string/string assigning to save memory
struct less_strcmp
{
bool operator()(const string& left, const string& right) const
{
return strcmp(left.c_str(), right.c_str()) < 0;
}
};
typedef std::set<string, less_strcmp> CharacterNameSet;
CharacterNameSet m_characterNameSet; // this set is purely used for clever string/string assigning to save memory
// CVARs
int m_cvarLocalizationDebug;
int m_cvarLocalizationEncode; //Encode/Compress translated text to save memory
int m_cvarLocalizationFormat;
//The localizations that are available for this SKU. Used for determining what to show on a language select screen or whether to show one at all
TLocalizationBitfield m_availableLocalizations;
//Lock for
mutable CryCriticalSection m_cs;
typedef CryAutoCriticalSection AutoLock;
};
File diff suppressed because it is too large Load Diff
+227
View File
@@ -0,0 +1,227 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ILog.h>
#include <CryThread.h>
#include <MultiThread.h>
#include <MultiThread_Containers.h>
//////////////////////////////////////////////////////////////////////
#if defined(ANDROID) || defined(AZ_PLATFORM_MAC)
#define MAX_TEMP_LENGTH_SIZE 4098
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(Log_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
#define MAX_TEMP_LENGTH_SIZE 8196
#endif
#define MAX_FILENAME_SIZE 256
#define KEEP_LOG_FILE_OPEN
//////////////////////////////////////////////////////////////////////
class CLog
: public ILog
{
public:
typedef std::list<ILogCallback*> Callbacks;
typedef CryStackStringT<char, MAX_TEMP_LENGTH_SIZE> LogStringType;
// constructor
CLog(ISystem* pSystem);
// destructor
~CLog();
// interface ILog, IMiniLog -------------------------------------------------
virtual void Release() { delete this; };
virtual bool SetFileName(const char* fileNameOrAbsolutePath, bool backupLogs);
virtual const char* GetFileName();
virtual const char* GetBackupFileName();
#if !defined(EXCLUDE_NORMAL_LOG)
virtual void Log(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogAlways(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogWarning(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogError(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogPlus(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToFile (const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToFilePlus(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToConsole(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToConsolePlus(const char* command, ...) PRINTF_PARAMS(2, 3);
#else
virtual void Log(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogAlways(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogWarning(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogError(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogPlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToFile (const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToFilePlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToConsole(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToConsolePlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
#endif // !defined(EXCLUDE_NORMAL_LOG)
virtual void UpdateLoadingScreen(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void SetVerbosity(int verbosity);
virtual int GetVerbosityLevel();
virtual void RegisterConsoleVariables();
virtual void UnregisterConsoleVariables();
virtual void AddCallback(ILogCallback* pCallback);
virtual void RemoveCallback(ILogCallback* pCallback);
virtual void LogV(ELogType ineType, int flags, const char* szFormat, va_list args);
virtual void LogV(ELogType ineType, const char* szFormat, va_list args);
virtual void Update();
virtual const char* GetModuleFilter();
virtual void FlushAndClose();
private: // -------------------------------------------------------------------
struct SLogMsg
{
enum class Destination
{
Default, //LogString, sends OnWrite to anycallback registered with AddCallback
Console,
File
};
char msg[512];
ELogType logType;
bool bAdd;
Destination destination;
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const {}
};
void CheckAndPruneBackupLogs() const;
bool IsError(ELogType logType) const { return logType == ELogType::eError || logType == ELogType::eErrorAlways || logType == ELogType::eWarning || logType == ELogType::eWarningAlways; }
//helper function to pass calls to LogString... to the main thread, returns false if you are on the main thread already, in which case just process the work.
bool LogToMainThread(const char* szString, ELogType logType, bool bAdd, SLogMsg::Destination destination);
enum class MessageQueueState
{
NotQueued,
Queued
};
#if !defined(EXCLUDE_NORMAL_LOG)
void LogString(const char* szString, ELogType logType);
void LogStringToFile(const char* szString, ELogType logType, bool bAdd, MessageQueueState queueState);
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd);
#else
void LogString(const char* szString, ELogType logType) {}
void LogStringToFile(const char* szString, ELogType logType, bool bAdd, MessageQueueState queueState) {}
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd) {}
#endif // !defined(EXCLUDE_NORMAL_LOG)
bool OpenLogFile(const char* filename, int mode);
void CloseLogFile();
// will format the message into m_szTemp
void FormatMessage(const char* szCommand, ...) PRINTF_PARAMS(2, 3);
#if defined(SUPPORT_LOG_IDENTER)
void Indent(CLogIndenter* indenter);
void Unindent(CLogIndenter* indenter);
void BuildIndentString();
virtual void PushAssetScopeName(const char* sAssetType, const char* sName);
virtual void PopAssetScopeName();
virtual const char* GetAssetScopeString();
#endif
ISystem* m_pSystem; //
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
AZ::IO::SystemFile m_logFileHandle;
bool m_backupLogs;
#if defined(SUPPORT_LOG_IDENTER)
uint8 m_indentation;
LogStringType m_indentWithString;
class CLogIndenter* m_topIndenter;
struct SAssetScopeInfo
{
string sType;
string sName;
};
std::vector<SAssetScopeInfo> m_assetScopeQueue;
CryCriticalSection m_assetScopeQueueLock;
string m_assetScopeString;
#endif
ICVar* m_pLogIncludeTime; //
IConsole* m_pConsole; //
CryCriticalSection m_logCriticalSection;
struct SLogHistoryItem
{
char str[MAX_WARNING_LENGTH];
const char* ptr;
ELogType type;
float time;
};
SLogHistoryItem m_history[16];
int m_iLastHistoryItem;
static bool CheckLogFormatter(const char* formatter);
#if defined(KEEP_LOG_FILE_OPEN)
static void LogFlushFile(IConsoleCmdArgs* pArgs);
bool m_bFirstLine;
#endif
public: // -------------------------------------------------------------------
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_pLogVerbosity);
pSizer->AddObject(m_pLogWriteToFile);
pSizer->AddObject(m_pLogWriteToFileVerbosity);
pSizer->AddObject(m_pLogVerbosityOverridesWriteToFile);
pSizer->AddObject(m_pLogSpamDelay);
pSizer->AddObject(m_threadSafeMsgQueue);
}
// checks the verbosity of the message and returns NULL if the message must NOT be
// logged, or the pointer to the part of the message that should be logged
const char* CheckAgainstVerbosity(const char* pText, bool& logtofile, bool& logtoconsole, const uint8 DefaultVerbosity = 2);
// create backup of log file, useful behavior - only on development platform
void CreateBackupFile() const;
ICVar* m_pLogVerbosity; //
ICVar* m_pLogWriteToFile; //
ICVar* m_pLogWriteToFileVerbosity; //
ICVar* m_pLogVerbosityOverridesWriteToFile; //
ICVar* m_pLogSpamDelay; //
ICVar* m_pLogModule; // Module filter for log
Callbacks m_callbacks; //
threadID m_nMainThreadId;
CryMT::queue<SLogMsg> m_threadSafeMsgQueue;
};
@@ -0,0 +1,16 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "RemoteConsole.h"
#ifdef USE_REMOTE_CONSOLE
#include "RemoteConsole_impl.inl"
#else
#include "RemoteConsole_none.inl"
#endif
@@ -0,0 +1,73 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_REMOTECONSOLE_REMOTECONSOLE_H
#define CRYINCLUDE_CRYSYSTEM_REMOTECONSOLE_REMOTECONSOLE_H
#pragma once
#include <IConsole.h>
#include <CryListenerSet.h>
#if !defined(RELEASE) || defined(RELEASE_LOGGING) || defined(ENABLE_PROFILING_CODE)
#define USE_REMOTE_CONSOLE
struct SRemoteServer;
#endif
/////////////////////////////////////////////////////////////////////////////////////////////
// CRemoteConsole
//
// IRemoteConsole implementation
//
/////////////////////////////////////////////////////////////////////////////////////////////
class CRemoteConsole
: public IRemoteConsole
{
public:
static CRemoteConsole* GetInst()
{
static StaticInstance<CRemoteConsole, AZStd::no_destruct<CRemoteConsole>> inst;
return &inst;
}
virtual void RegisterConsoleVariables();
virtual void UnregisterConsoleVariables();
virtual void Start();
virtual void Stop();
virtual bool IsStarted() const { return m_running; }
virtual void AddLogMessage(const char* log);
virtual void AddLogWarning(const char* log);
virtual void AddLogError(const char* log);
virtual void Update();
virtual void RegisterListener(IRemoteConsoleListener* pListener, const char* name);
virtual void UnregisterListener(IRemoteConsoleListener* pListener);
typedef CListenerSet<IRemoteConsoleListener*> TListener;
CRemoteConsole();
virtual ~CRemoteConsole();
TListener m_listener;
int m_lastPortValue = 0;
volatile bool m_running;
#if defined(USE_REMOTE_CONSOLE)
SRemoteServer* m_pServer;
ICVar* m_pLogEnableRemoteConsole = nullptr;
ICVar* m_remoteConsoleAllowedHostList = nullptr;
ICVar* m_remoteConsolePort = nullptr;
#endif
};
#endif // CRYINCLUDE_CRYSYSTEM_REMOTECONSOLE_REMOTECONSOLE_H
@@ -0,0 +1,180 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <RemoteConsoleCore.h>
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
CRemoteConsole::CRemoteConsole()
: m_listener(1)
, m_lastPortValue(0)
, m_running(false)
, m_pServer(nullptr)
, m_pLogEnableRemoteConsole(nullptr)
, m_remoteConsoleAllowedHostList(nullptr)
, m_remoteConsolePort(nullptr)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
CRemoteConsole::~CRemoteConsole()
{
Stop();
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::RegisterConsoleVariables()
{
m_pLogEnableRemoteConsole = REGISTER_INT("log_EnableRemoteConsole", 1, VF_DUMPTODISK, "enables/disables the remote console");
m_remoteConsoleAllowedHostList = REGISTER_STRING("log_RemoteConsoleAllowedAddresses", "", VF_DUMPTODISK, "COMMA separated list of allowed hosts or IP addresses which can connect");
m_remoteConsolePort = REGISTER_INT("log_RemoteConsolePort", defaultRemoteConsolePort, VF_DUMPTODISK, "Base port (4600 for example) for remote console to listen on. It will start there and continue upwards until an unused one is found.");
m_lastPortValue = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::UnregisterConsoleVariables()
{
m_pLogEnableRemoteConsole = nullptr;
m_remoteConsoleAllowedHostList = nullptr;
m_remoteConsolePort = nullptr;
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Start()
{
if (!IsStarted())
{
m_pServer = new SRemoteServer;
m_pServer->StartServer();
m_running = true;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Stop()
{
// make sure we don't stop if we never started the remote console in the first place
if (IsStarted())
{
m_running = false;
m_pServer->StopServer();
m_pServer->WaitForThread();
delete m_pServer;
m_pServer = nullptr;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogMessage(const char* log)
{
if (!IsStarted())
{
return;
}
IRemoteEvent* pEvent = new SStringEvent<eCET_LogMessage>(log);
m_pServer->AddEvent(pEvent);
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogWarning(const char* log)
{
if (!IsStarted())
{
return;
}
IRemoteEvent* pEvent = new SStringEvent<eCET_LogWarning>(log);
m_pServer->AddEvent(pEvent);
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogError(const char* log)
{
if (!IsStarted())
{
return;
}
IRemoteEvent* pEvent = new SStringEvent<eCET_LogError>(log);
m_pServer->AddEvent(pEvent);
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Update()
{
if (m_pLogEnableRemoteConsole)
{
// we disable the remote console in the editor, since there is no reason to remote into it and we don't want it eating up that port
// number anyway and preventing the game from using it.
bool isEditor = gEnv && gEnv->IsEditor();
bool isEnabled = (!isEditor) && (m_pLogEnableRemoteConsole->GetIVal());
bool isStarted = IsStarted();
int newPortValue = m_remoteConsolePort->GetIVal();
// the editor never allows remote control.
if ((isEnabled) && (!isStarted))
{
Start();
}
else if (isStarted)
{
if ((!isEnabled) || (newPortValue != m_lastPortValue))
{
Stop();
}
}
m_lastPortValue = newPortValue;
}
if (m_pServer)
{
TEventBuffer events;
m_pServer->GetEvents(events);
for (TEventBuffer::iterator it = events.begin(), end = events.end(); it != end; ++it)
{
IRemoteEvent* pEvent = *it;
switch (pEvent->GetType())
{
case eCET_ConsoleCommand:
for (TListener::Notifier notifier(m_listener); notifier.IsValid(); notifier.Next())
{
notifier->OnConsoleCommand(((SStringEvent<eCET_ConsoleCommand>*)pEvent)->GetData());
}
break;
case eCET_GameplayEvent:
for (TListener::Notifier notifier(m_listener); notifier.IsValid(); notifier.Next())
{
notifier->OnGameplayCommand(((SStringEvent<eCET_GameplayEvent>*)pEvent)->GetData());
}
break;
}
delete *it;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::RegisterListener(IRemoteConsoleListener* pListener, const char* name)
{
m_listener.Add(pListener, name);
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::UnregisterListener(IRemoteConsoleListener* pListener)
{
m_listener.Remove(pListener);
}
@@ -0,0 +1,70 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
/////////////////////////////////////////////////////////////////////////////////////////////
CRemoteConsole::CRemoteConsole()
: m_listener(1)
, m_lastPortValue(0)
, m_running(false)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
CRemoteConsole::~CRemoteConsole()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::RegisterConsoleVariables()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::UnregisterConsoleVariables()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Start()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Stop()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogMessage(const char* log)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogWarning(const char* log)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::AddLogError(const char* log)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::Update()
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::RegisterListener(IRemoteConsoleListener* pListener, const char* name)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////
void CRemoteConsole::UnregisterListener(IRemoteConsoleListener* pListener)
{
}
+373
View File
@@ -0,0 +1,373 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_SIMPLESTRINGPOOL_H
#define CRYINCLUDE_CRYSYSTEM_SIMPLESTRINGPOOL_H
#pragma once
#include "ISystem.h"
#include <StlUtils.h>
//TODO: Pull most of this into a cpp file!
struct SStringData
{
SStringData(const char* szString, int nStrLen)
: m_szString(szString)
, m_nStrLen(nStrLen)
{
}
const char* m_szString;
int m_nStrLen;
bool operator==(const SStringData& other) const
{
if (m_nStrLen != other.m_nStrLen)
{
return false;
}
return strcmp(m_szString, other.m_szString) == 0;
}
private:
};
template<>
inline const char* stl::constchar_cast(const SStringData& in)
{
return in.m_szString;
}
/////////////////////////////////////////////////////////////////////
// String pool implementation.
// Inspired by expat implementation.
/////////////////////////////////////////////////////////////////////
class CSimpleStringPool
{
public:
enum
{
STD_BLOCK_SIZE = 1u << 16
};
struct BLOCK
{
BLOCK* next;
int size;
char s[1];
};
unsigned int m_blockSize;
BLOCK* m_blocks;
BLOCK* m_free_blocks;
const char* m_end;
char* m_ptr;
char* m_start;
int nUsedSpace;
int nUsedBlocks;
bool m_reuseStrings;
typedef AZStd::unordered_map<SStringData, char*, stl::hash_string<SStringData> > TStringToExistingStringMap;
TStringToExistingStringMap m_stringToExistingStringMap;
static size_t g_nTotalAllocInXmlStringPools;
CSimpleStringPool()
{
m_blockSize = STD_BLOCK_SIZE - offsetof(BLOCK, s);
m_blocks = 0;
m_start = 0;
m_ptr = 0;
m_end = 0;
nUsedSpace = 0;
nUsedBlocks = 0;
m_free_blocks = 0;
m_reuseStrings = false;
}
explicit CSimpleStringPool(bool reuseStrings)
: m_blockSize(STD_BLOCK_SIZE - offsetof(BLOCK, s))
, m_blocks(NULL)
, m_free_blocks(NULL)
, m_end(0)
, m_ptr(0)
, m_start(0)
, nUsedSpace(0)
, nUsedBlocks(0)
, m_reuseStrings(reuseStrings)
{
}
~CSimpleStringPool()
{
BLOCK* pBlock = m_blocks;
while (pBlock)
{
BLOCK* temp = pBlock->next;
g_nTotalAllocInXmlStringPools -= (offsetof(BLOCK, s) + pBlock->size * sizeof(char));
CryModuleFree(pBlock);
pBlock = temp;
}
pBlock = m_free_blocks;
while (pBlock)
{
BLOCK* temp = pBlock->next;
g_nTotalAllocInXmlStringPools -= (offsetof(BLOCK, s) + pBlock->size * sizeof(char));
CryModuleFree(pBlock);
pBlock = temp;
}
m_blocks = 0;
m_ptr = 0;
m_start = 0;
m_end = 0;
}
void SetBlockSize(unsigned int nBlockSize)
{
if (nBlockSize > 1024 * 1024)
{
nBlockSize = 1024 * 1024;
}
unsigned int size = 512;
while (size < nBlockSize)
{
size *= 2;
}
m_blockSize = size - offsetof(BLOCK, s);
}
void Clear()
{
BLOCK* pLast = m_free_blocks;
if (pLast)
{
while (pLast->next)
{
pLast = pLast->next;
}
pLast->next = m_blocks;
}
else
{
m_free_blocks = m_blocks;
}
m_blocks = 0;
m_start = 0;
m_ptr = 0;
m_end = 0;
nUsedSpace = 0;
if (m_reuseStrings)
{
m_stringToExistingStringMap.clear();
}
}
char* Append(const char* ptr, int nStrLen)
{
// If a string does not fit within the remainder of the string pool, a new pool will be allocated with at least
// nStrLen + 1 size, which means this code does take care of incredibly large strings.
if (m_reuseStrings)
{
if (char* existingString = FindExistingString(ptr, nStrLen))
{
return existingString;
}
}
char* ret = m_ptr;
if (m_ptr && nStrLen + 1 < (m_end - m_ptr))
{
memcpy(m_ptr, ptr, nStrLen);
m_ptr = m_ptr + nStrLen;
*m_ptr++ = 0; // add null termination.
}
else
{
int nNewBlockSize = std::max(nStrLen + 1, (int)m_blockSize);
AllocBlock(nNewBlockSize, nStrLen + 1);
PREFAST_ASSUME(m_ptr);
memcpy(m_ptr, ptr, nStrLen);
m_ptr = m_ptr + nStrLen;
*m_ptr++ = 0; // add null termination.
ret = m_start;
}
if (m_reuseStrings)
{
assert(!FindExistingString(ptr, nStrLen));
m_stringToExistingStringMap[SStringData(ret, nStrLen)] = ret;
}
nUsedSpace += nStrLen;
return ret;
}
char* ReplaceString(const char* str1, const char* str2)
{
if (m_reuseStrings)
{
CryFatalError("Can't replace strings in an xml node that reuses strings");
}
int nStrLen1 = strlen(str1);
int nStrLen2 = strlen(str2);
// undo ptr1 add.
if (m_ptr != m_start)
{
m_ptr = m_ptr - nStrLen1 - 1;
}
assert(m_ptr == str1);
int nStrLen = nStrLen1 + nStrLen2;
char* ret = m_ptr;
if (m_ptr && nStrLen + 1 < (m_end - m_ptr))
{
if (m_ptr != str1)
{
memcpy(m_ptr, str1, nStrLen1);
}
memcpy(m_ptr + nStrLen1, str2, nStrLen2);
m_ptr = m_ptr + nStrLen;
*m_ptr++ = 0; // add null termination.
}
else
{
int nNewBlockSize = std::max(nStrLen + 1, (int)m_blockSize);
if (m_ptr == m_start)
{
ReallocBlock(nNewBlockSize * 2); // Reallocate current block.
PREFAST_ASSUME(m_ptr);
memcpy(m_ptr + nStrLen1, str2, nStrLen2);
}
else
{
AllocBlock(nNewBlockSize, nStrLen + 1);
PREFAST_ASSUME(m_ptr);
memcpy(m_ptr, str1, nStrLen1);
memcpy(m_ptr + nStrLen1, str2, nStrLen2);
}
m_ptr = m_ptr + nStrLen;
*m_ptr++ = 0; // add null termination.
ret = m_start;
}
nUsedSpace += nStrLen;
return ret;
}
void GetMemoryUsage(ICrySizer* pSizer) const
{
BLOCK* pBlock = m_blocks;
while (pBlock)
{
pSizer->AddObject(pBlock, offsetof(BLOCK, s) + pBlock->size * sizeof(char));
pBlock = pBlock->next;
}
pBlock = m_free_blocks;
while (pBlock)
{
pSizer->AddObject(pBlock, offsetof(BLOCK, s) + pBlock->size * sizeof(char));
pBlock = pBlock->next;
}
}
private:
CSimpleStringPool(const CSimpleStringPool&);
CSimpleStringPool& operator = (const CSimpleStringPool&);
private:
void AllocBlock(int blockSize, int nMinBlockSize)
{
if (m_free_blocks)
{
BLOCK* pBlock = m_free_blocks;
BLOCK* pPrev = 0;
while (pBlock)
{
if (pBlock->size >= nMinBlockSize)
{
// Reuse free block
if (pPrev)
{
pPrev->next = pBlock->next;
}
else
{
m_free_blocks = pBlock->next;
}
pBlock->next = m_blocks;
m_blocks = pBlock;
m_ptr = pBlock->s;
m_start = pBlock->s;
m_end = pBlock->s + pBlock->size;
return;
}
pPrev = pBlock;
pBlock = pBlock->next;
}
}
size_t nMallocSize = offsetof(BLOCK, s) + blockSize * sizeof(char);
g_nTotalAllocInXmlStringPools += nMallocSize;
BLOCK* pBlock = (BLOCK*)CryModuleMalloc(nMallocSize);
;
assert(pBlock);
pBlock->size = blockSize;
pBlock->next = m_blocks;
m_blocks = pBlock;
m_ptr = pBlock->s;
m_start = pBlock->s;
m_end = pBlock->s + blockSize;
nUsedBlocks++;
}
void ReallocBlock(int blockSize)
{
if (m_reuseStrings)
{
CryFatalError("Can't replace strings in an xml node that reuses strings");
}
BLOCK* pThisBlock = m_blocks;
BLOCK* pPrevBlock = m_blocks->next;
m_blocks = pPrevBlock;
size_t nMallocSize = offsetof(BLOCK, s) + blockSize * sizeof(char);
if (pThisBlock)
{
g_nTotalAllocInXmlStringPools -= (offsetof(BLOCK, s) + pThisBlock->size * sizeof(char));
}
g_nTotalAllocInXmlStringPools += nMallocSize;
BLOCK* pBlock = (BLOCK*)CryModuleRealloc(pThisBlock, nMallocSize);
assert(pBlock);
pBlock->size = blockSize;
pBlock->next = m_blocks;
m_blocks = pBlock;
m_ptr = pBlock->s;
m_start = pBlock->s;
m_end = pBlock->s + blockSize;
}
char* FindExistingString(const char* szString, int nStrLen)
{
SStringData testData(szString, nStrLen);
char* szResult = stl::find_in_map(m_stringToExistingStringMap, testData, NULL);
assert(!szResult || !_stricmp(szResult, szString));
return szResult;
}
};
#endif // CRYINCLUDE_CRYSYSTEM_SIMPLESTRINGPOOL_H
File diff suppressed because it is too large Load Diff
+743
View File
@@ -0,0 +1,743 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ISystem.h>
#include <IRenderer.h>
#include <IPhysics.h>
#include <IWindowMessageHandler.h>
#include "Timer.h"
#include <CryVersion.h>
#include "CmdLine.h"
#include "CryName.h"
#include <AzFramework/Archive/ArchiveVars.h>
#include "RenderBus.h"
#include <LoadScreenBus.h>
#include <AzCore/Module/DynamicModuleHandle.h>
namespace AzFramework
{
class MissingAssetLogger;
}
struct IConsoleCmdArgs;
class CWatchdogThread;
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define SYSTEM_H_SECTION_1 1
#define SYSTEM_H_SECTION_2 2
#define SYSTEM_H_SECTION_3 3
#define SYSTEM_H_SECTION_4 4
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_1
#include AZ_RESTRICTED_FILE(System_h)
#else
#if defined(WIN32) || defined(LINUX) || defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_ALLOW_CREATE_BACKUP_LOG_FILE 1
#endif
#if defined(WIN32) || (defined(LINUX) && !defined(ANDROID)) || defined(MAC)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_DEFINE_DETECT_PROCESSOR 1
#endif
//////////////////////////////////////////////////////////////////////////
#if defined(WIN32) || defined(APPLE) || defined(LINUX)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_DO_PREASSERT 1
#endif
#if defined(MAC) || (defined(LINUX) && !defined(ANDROID))
#define AZ_LEGACY_CRYSYSTEM_TRAIT_ASM_VOLATILE_CPUID 1
#endif
#if (defined(WIN32) && !defined(WIN64)) || (defined(LINUX) && !defined(ANDROID) && !defined(LINUX64))
#define AZ_LEGACY_CRYSYSTEM_TRAIT_HAS64BITEXT 1
#endif
#if defined(WIN32) || (defined(LINUX) && !defined(ANDROID)) || defined(MAC)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_HTSUPPORTED 1
#endif
#if defined(WIN32) || (defined(LINUX) && !defined(ANDROID)) || defined(MAC)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_HASCPUID 1
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_HASAFFINITYMASK 1
#endif
#if defined(LINUX) || defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_CRYPAK_POSIX 1
#endif
#if defined(WIN64)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_BIT64 1
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_PACKED_PEHEADER 1
#endif
#if defined(WIN32) || defined(LINUX)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_RENDERMEMORY_INFO 1
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_HANDLER_SYNC_AFFINITY 1
#endif
#if defined(LINUX) || defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_FORWARD_EXCEPTION_POINTERS 1
#endif
#if !defined(_WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON 1
#endif
#if !defined(LINUX) && !defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE 1
#endif
#if !defined(LINUX) && !defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME 1
#endif
#if !(defined(ANDROID) || defined(IOS) || defined(LINUX))
#define AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO 1
#endif
#if 1
#define AZ_LEGACY_CRYSYSTEM_TRAIT_JOBMANAGER_SIXWORKERTHREADS 0
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_MEMADDRESSRANGE_WINDOWS_STYLE 1
#endif
#if 1
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_EXCLUDEUPDATE_ON_CONSOLE 0
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_MESSAGE_HANDLER 1
#endif
#if defined(WIN64) || defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_CAPTURESTACK 1
#endif
#if !defined(LINUX) && !defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_SYSTEMCFG_MODULENAME 1
#endif
#if defined(WIN32) || defined(WIN64)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_THREADINFO_WINDOWS_STYLE 1
#endif
#if defined(WIN32)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_THREADTASK_EXCEPTIONS 1
#endif
//////////////////////////////////////////////////////////////////////////
#if defined(APPLE) || defined(LINUX)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_FACTORY_REGISTRY_USE_PRINTF_FOR_FATAL 1
#endif
#if defined(LINUX) || defined(APPLE)
#define AZ_LEGACY_CRYSYSTEM_TRAIT_USE_FTELL_NOT_FTELLI64 1
#endif
#endif
#if defined(USE_UNIXCONSOLE) || defined(USE_ANDROIDCONSOLE) || defined(USE_WINDOWSCONSOLE) || defined(USE_IOSCONSOLE) || defined(USE_NULLCONSOLE)
#define USE_DEDICATED_SERVER_CONSOLE
#endif
#if defined(LINUX)
#include "CryLibrary.h"
#endif
#ifdef WIN32
typedef void* WIN_HMODULE;
#else
typedef void* WIN_HMODULE;
#endif
//forward declarations
namespace Audio
{
struct IAudioSystem;
struct IMusicSystem;
} // namespace Audio
struct IDataProbe;
#define PHSYICS_OBJECT_ENTITY 0
typedef void (__cdecl * VTuneFunction)(void);
extern VTuneFunction VTResume;
extern VTuneFunction VTPause;
#define MAX_STREAMING_POOL_INDEX 6
#define MAX_THREAD_POOL_INDEX 6
struct SSystemCVars
{
int sys_streaming_requests_grouping_time_period;
int sys_streaming_sleep;
int sys_streaming_memory_budget;
int sys_streaming_max_finalize_per_frame;
float sys_streaming_max_bandwidth;
int sys_streaming_cpu;
int sys_streaming_cpu_worker;
int sys_streaming_debug;
int sys_streaming_resetstats;
int sys_streaming_debug_filter;
float sys_streaming_debug_filter_min_time;
int sys_streaming_use_optical_drive_thread;
ICVar* sys_streaming_debug_filter_file_name;
ICVar* sys_localization_folder;
int sys_streaming_in_blocks;
int sys_float_exceptions;
int sys_no_crash_dialog;
int sys_no_error_report_window;
int sys_dump_aux_threads;
int sys_WER;
int sys_dump_type;
int sys_ai;
int sys_entitysystem;
int sys_trackview;
int sys_vtune;
float sys_update_profile_time;
int sys_limit_phys_thread_count;
int sys_MaxFPS;
float sys_maxTimeStepForMovieSystem;
int sys_force_installtohdd_mode;
int sys_report_files_not_found_in_paks = 0;
#ifdef USE_HTTP_WEBSOCKETS
int sys_simple_http_base_port;
#endif
int sys_asserts;
int sys_error_debugbreak;
int sys_FilesystemCaseSensitivity;
AZ::IO::ArchiveVars archiveVars;
#if defined(WIN32)
int sys_display_threads;
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_2
#include AZ_RESTRICTED_FILE(System_h)
#endif
};
extern SSystemCVars g_cvars;
class CSystem;
struct CProfilingSystem
: public IProfilingSystem
{
//////////////////////////////////////////////////////////////////////////
// VTune Profiling interface.
// Summary:
// Resumes vtune data collection.
virtual void VTuneResume();
// Summary:
// Pauses vtune data collection.
virtual void VTunePause();
//////////////////////////////////////////////////////////////////////////
};
class AssetSystem;
/*
===========================================
The System interface Class
===========================================
*/
class CXConsole;
//////////////////////////////////////////////////////////////////////
//! ISystem implementation
class CSystem
: public ISystem
, public ILoadConfigurationEntrySink
, public ISystemEventListener
, public IWindowMessageHandler
, public AZ::RenderNotificationsBus::Handler
, public CrySystemRequestBus::Handler
{
public:
CSystem(SharedEnvironmentInstance* pSharedEnvironment);
~CSystem();
static void OnLanguageCVarChanged(ICVar* language);
static void OnLanguageAudioCVarChanged(ICVar* language);
static void OnLocalizationFolderCVarChanged(ICVar* const pLocalizationFolder);
// adding CVAR to toggle assert verbosity level
static void OnAssertLevelCvarChanged(ICVar* pArgs);
static void SetAssertLevel(int _assertlevel);
static void OnLogLevelCvarChanged(ICVar* pArgs);
static void SetLogLevel(int _logLevel);
// interface ILoadConfigurationEntrySink ----------------------------------
virtual void OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup);
// ISystemEventListener
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
///////////////////////////////////////////////////////////////////////////
//! @name ISystem implementation
//@{
virtual bool Init(const SSystemInitParams& startupParams);
virtual void Release();
virtual SSystemGlobalEnvironment* GetGlobalEnvironment() { return &m_env; }
virtual bool UpdatePreTickBus(int updateFlags = 0, int nPauseMode = 0);
virtual bool UpdatePostTickBus(int updateFlags = 0, int nPauseMode = 0);
virtual bool UpdateLoadtime();
////////////////////////////////////////////////////////////////////////
// CrySystemRequestBus interface implementation
ISystem* GetCrySystem() override;
////////////////////////////////////////////////////////////////////////
void Relaunch(bool bRelaunch);
bool IsRelaunch() const { return m_bRelaunch; };
void SerializingFile(int mode) { m_iLoadingMode = mode; }
int IsSerializingFile() const { return m_iLoadingMode; }
void Quit();
bool IsQuitting() const;
void ShutdownFileSystem(); // used to cleanup any file resources, such as cache handle.
void SetAffinity();
virtual const char* GetUserName();
virtual int GetApplicationInstance();
int GetApplicationLogInstance(const char* logFilePath) override;
ITimer* GetITimer(){ return m_env.pTimer; }
AZ::IO::IArchive* GetIPak() { return m_env.pCryPak; };
IConsole* GetIConsole() { return m_env.pConsole; };
IRemoteConsole* GetIRemoteConsole();
IMovieSystem* GetIMovieSystem() { return m_env.pMovieSystem; };
ICryFont* GetICryFont(){ return m_env.pCryFont; }
ILog* GetILog(){ return m_env.pLog; }
ICmdLine* GetICmdLine(){ return m_pCmdLine; }
INameTable* GetINameTable() { return m_env.pNameTable; };
IViewSystem* GetIViewSystem();
ILevelSystem* GetILevelSystem();
ISystemEventDispatcher* GetISystemEventDispatcher() { return m_pSystemEventDispatcher; }
IProfilingSystem* GetIProfilingSystem() { return &m_ProfilingSystem; }
//////////////////////////////////////////////////////////////////////////
// retrieves the perlin noise singleton instance
CPNoise3* GetNoiseGen();
virtual uint64 GetUpdateCounter() { return m_nUpdateCounter; };
void DetectGameFolderAccessRights();
virtual void ExecuteCommandLine(bool deferred=true);
virtual void GetUpdateStats(SSystemUpdateStats& stats);
//////////////////////////////////////////////////////////////////////////
virtual XmlNodeRef CreateXmlNode(const char* sNodeName = "", bool bReuseStrings = false, bool bIsProcessingInstruction = false);
virtual XmlNodeRef LoadXmlFromFile(const char* sFilename, bool bReuseStrings = false);
virtual XmlNodeRef LoadXmlFromBuffer(const char* buffer, size_t size, bool bReuseStrings = false, bool bSuppressWarnings = false);
virtual IXmlUtils* GetXmlUtils();
//////////////////////////////////////////////////////////////////////////
void SetViewCamera(CCamera& Camera){ m_ViewCamera = Camera; }
CCamera& GetViewCamera() { return m_ViewCamera; }
void IgnoreUpdates(bool bIgnore) { m_bIgnoreUpdates = bIgnore; };
void SetIProcess(IProcess* process);
IProcess* GetIProcess(){ return m_pProcess; }
bool IsTestMode() const { return m_bTestMode; }
//@}
void SleepIfNeeded();
virtual void FatalError(const char* format, ...) PRINTF_PARAMS(2, 3);
virtual void ReportBug(const char* format, ...) PRINTF_PARAMS(2, 3);
// Validator Warning.
void WarningV(EValidatorModule module, EValidatorSeverity severity, int flags, const char* file, const char* format, va_list args);
void Warning(EValidatorModule module, EValidatorSeverity severity, int flags, const char* file, const char* format, ...);
virtual int ShowMessage(const char* text, const char* caption, unsigned int uType);
bool CheckLogVerbosity(int verbosity);
//! Return pointer to user defined callback.
ISystemUserCallback* GetUserCallback() const { return m_pUserCallback; };
//////////////////////////////////////////////////////////////////////////
virtual void SaveConfiguration();
virtual void LoadConfiguration(const char* sFilename, ILoadConfigurationEntrySink* pSink = 0, bool warnIfMissing = true);
virtual ESystemConfigSpec GetMaxConfigSpec() const;
virtual ESystemConfigPlatform GetConfigPlatform() const;
virtual void SetConfigPlatform(ESystemConfigPlatform platform);
//////////////////////////////////////////////////////////////////////////
virtual bool IsPaused() const { return m_bPaused; };
virtual ILocalizationManager* GetLocalizationManager();
virtual void debug_GetCallStack(const char** pFunctions, int& nCount);
virtual void debug_LogCallStack(int nMaxFuncs = 32, int nFlags = 0);
// Get the current callstack in raw address form (more lightweight than the above functions)
// static as memReplay needs it before CSystem has been setup - expose a ISystem interface to this function if you need it outside CrySystem
static void debug_GetCallStackRaw(void** callstack, uint32& callstackLength);
public:
#if !defined(RELEASE)
void SetVersionInfo(const char* const szVersion);
#endif
void ShutdownModuleLibraries();
#if defined(WIN32)
friend LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
#endif
virtual void* GetRootWindowMessageHandler();
virtual void RegisterWindowMessageHandler(IWindowMessageHandler* pHandler);
virtual void UnregisterWindowMessageHandler(IWindowMessageHandler* pHandler);
// IWindowMessageHandler
#if defined(WIN32)
virtual bool HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
#endif
// ~IWindowMessageHandler
private:
// Release all resources.
void ShutDown();
bool LoadEngineDLLs();
//! @name Initialization routines
//@{
bool InitConsole();
bool InitFileSystem();
bool InitFileSystem_LoadEngineFolders(const SSystemInitParams& initParams);
bool InitAudioSystem(const SSystemInitParams& initParams);
//@}
//////////////////////////////////////////////////////////////////////////
// Helper functions.
//////////////////////////////////////////////////////////////////////////
void CreateSystemVars();
void CreateAudioVars();
AZStd::unique_ptr<AZ::DynamicModuleHandle> LoadDLL(const char* dllName);
void FreeLib(AZStd::unique_ptr<AZ::DynamicModuleHandle>& hLibModule);
bool UnloadDLL(const char* dllName);
void QueryVersionInfo();
void LogVersion();
void LogBuildInfo();
void SetDevMode(bool bEnable);
#ifndef _RELEASE
static void SystemVersionChanged(ICVar* pCVar);
#endif // #ifndef _RELEASE
bool ReLaunchMediaCenter();
void UpdateAudioSystems();
void AddCVarGroupDirectory(const string& sPath);
AZStd::unique_ptr<AZ::DynamicModuleHandle> LoadDynamiclibrary(const char* dllName) const;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_3
#include AZ_RESTRICTED_FILE(System_h)
#elif defined(WIN32)
bool GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize);
#endif
public:
void EnableFloatExceptions(int type);
// interface ISystem -------------------------------------------
virtual IDataProbe* GetIDataProbe() { return m_pDataProbe; };
virtual void SetForceNonDevMode(bool bValue);
virtual bool GetForceNonDevMode() const;
virtual bool WasInDevMode() const { return m_bWasInDevMode; };
virtual bool IsDevMode() const { return m_bInDevMode && !GetForceNonDevMode(); }
virtual void SetConsoleDrawEnabled(bool enabled) { m_bDrawConsole = enabled; }
virtual void SetUIDrawEnabled(bool enabled) { m_bDrawUI = enabled; }
// -------------------------------------------------------------
//! attaches the given variable to the given container;
//! recreates the variable if necessary
ICVar* attachVariable (const char* szVarName, int* pContainer, const char* szComment, int dwFlags = 0);
const CTimeValue& GetLastTickTime(void) const { return m_lastTickTime; }
const ICVar* GetDedicatedMaxRate(void) const { return m_svDedicatedMaxRate; }
std::shared_ptr<AZ::IO::FileIOBase> CreateLocalFileIO();
private: // ------------------------------------------------------
// System environment.
SSystemGlobalEnvironment m_env;
CTimer m_Time; //!<
CCamera m_ViewCamera; //!<
bool m_bInitializedSuccessfully; //!< true if the system completed all initialization steps
bool m_bRelaunch; //!< relaunching the app or not (true beforerelaunch)
int m_iLoadingMode; //!< Game is loading w/o changing context (0 not, 1 quickloading, 2 full loading)
bool m_bTestMode; //!< If running in testing mode.
bool m_bEditor; //!< If running in Editor.
bool m_bNoCrashDialog;
bool m_bNoErrorReportWindow;
bool m_bPreviewMode; //!< If running in Preview mode.
bool m_bDedicatedServer; //!< If running as Dedicated server.
bool m_bIgnoreUpdates; //!< When set to true will ignore Update and Render calls,
bool m_bForceNonDevMode; //!< true when running on a cheat protected server or a client that is connected to it (not used in singlplayer)
bool m_bWasInDevMode; //!< Set to true if was in dev mode.
bool m_bInDevMode; //!< Set to true if was in dev mode.
bool m_bGameFolderWritable;//!< True when verified that current game folder have write access.
int m_ttMemStatSS; //!< Time to memstat screenshot
bool m_bDrawConsole; //!< Set to true if OK to draw the console.
bool m_bDrawUI; //!< Set to true if OK to draw UI.
std::map<CCryNameCRC, AZStd::unique_ptr<AZ::DynamicModuleHandle> > m_moduleDLLHandles;
//! current active process
IProcess* m_pProcess;
CCamera m_PhysRendererCamera;
ICVar* m_p_draw_helpers_str;
int m_iJumpToPhysProfileEnt;
CTimeValue m_lastTickTime;
//! system event dispatcher
ISystemEventDispatcher* m_pSystemEventDispatcher;
//! The default mono-spaced font for internal usage (profiling, debug info, etc.)
IFFont* m_pIFont;
//! The default font for end-user UI interfaces
IFFont* m_pIFontUi;
//! System to manage levels.
ILevelSystem* m_pLevelSystem;
//! System to manage views.
IViewSystem* m_pViewSystem;
// XML Utils interface.
class CXmlUtils* m_pXMLUtils;
int m_iApplicationInstance;
//! to hold the values stored in system.cfg
//! because editor uses it's own values,
//! and then saves them to file, overwriting the user's resolution.
int m_iHeight;
int m_iWidth;
int m_iColorBits;
// System console variables.
//////////////////////////////////////////////////////////////////////////
// DLL names
ICVar* m_sys_dll_response_system;
#if !defined(_RELEASE)
ICVar* m_sys_resource_cache_folder;
#endif
#if AZ_LOADSCREENCOMPONENT_ENABLED
ICVar* m_game_load_screen_uicanvas_path;
ICVar* m_level_load_screen_uicanvas_path;
ICVar* m_game_load_screen_sequence_to_auto_play;
ICVar* m_level_load_screen_sequence_to_auto_play;
ICVar* m_game_load_screen_sequence_fixed_fps;
ICVar* m_level_load_screen_sequence_fixed_fps;
ICVar* m_game_load_screen_max_fps;
ICVar* m_level_load_screen_max_fps;
ICVar* m_game_load_screen_minimum_time{};
ICVar* m_level_load_screen_minimum_time{};
#endif // if AZ_LOADSCREENCOMPONENT_ENABLED
ICVar* m_sys_initpreloadpacks;
ICVar* m_sys_menupreloadpacks;
ICVar* m_cvAIUpdate;
ICVar* m_rWidth;
ICVar* m_rHeight;
ICVar* m_rWidthAndHeightAsFractionOfScreenSize;
ICVar* m_rTabletWidthAndHeightAsFractionOfScreenSize;
ICVar* m_rHDRDolby;
ICVar* m_rMaxWidth;
ICVar* m_rMaxHeight;
ICVar* m_rColorBits;
ICVar* m_rDepthBits;
ICVar* m_rStencilBits;
ICVar* m_rFullscreen;
ICVar* m_rFullscreenWindow;
ICVar* m_rFullscreenNativeRes;
ICVar* m_rDisplayInfo;
ICVar* m_rOverscanBordersDrawDebugView;
ICVar* m_sysNoUpdate;
ICVar* m_cvEntitySuppressionLevel;
ICVar* m_pCVarQuit;
ICVar* m_cvMemStats;
ICVar* m_cvMemStatsThreshold;
ICVar* m_cvMemStatsMaxDepth;
ICVar* m_sysKeyboard;
ICVar* m_sysWarnings; //!< might be 0, "sys_warnings" - Treat warning as errors.
ICVar* m_cvSSInfo; //!< might be 0, "sys_SSInfo" 0/1 - get file sourcesafe info
ICVar* m_svDedicatedMaxRate;
ICVar* m_sys_firstlaunch;
ICVar* m_sys_asset_processor;
ICVar* m_sys_load_files_to_memory;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEM_H_SECTION_4
#include AZ_RESTRICTED_FILE(System_h)
#endif
ICVar* m_sys_audio_disable;
ICVar* m_sys_min_step;
ICVar* m_sys_max_step;
ICVar* m_sys_enable_budgetmonitoring;
ICVar* m_sys_memory_debug;
ICVar* m_sys_preload;
// ICVar *m_sys_filecache;
ICVar* m_gpu_particle_physics;
string m_sSavedRDriver; //!< to restore the driver when quitting the dedicated server
//////////////////////////////////////////////////////////////////////////
//! User define callback for system events.
ISystemUserCallback* m_pUserCallback;
SFileVersion m_fileVersion;
SFileVersion m_productVersion;
SFileVersion m_buildVersion;
IDataProbe* m_pDataProbe;
class CLocalizedStringsManager* m_pLocalizationManager;
// Name table.
CNameTable m_nameTable;
ESystemConfigSpec m_nServerConfigSpec;
ESystemConfigSpec m_nMaxConfigSpec;
ESystemConfigPlatform m_ConfigPlatform;
CProfilingSystem m_ProfilingSystem;
// Pause mode.
bool m_bPaused;
bool m_bNoUpdate;
uint64 m_nUpdateCounter;
bool m_executedCommandLine = false;
AZStd::unique_ptr<AzFramework::MissingAssetLogger> m_missingAssetLogger;
public:
ICVar* m_sys_main_CPU;
ICVar* m_sys_streaming_CPU;
ICVar* m_sys_TaskThread_CPU[MAX_THREAD_POOL_INDEX];
//////////////////////////////////////////////////////////////////////////
// File version.
//////////////////////////////////////////////////////////////////////////
virtual const SFileVersion& GetFileVersion();
virtual const SFileVersion& GetProductVersion();
virtual const SFileVersion& GetBuildVersion();
bool InitVTuneProfiler();
void OpenBasicPaks();
void OpenLanguagePak(const char* sLanguage);
void OpenLanguageAudioPak(const char* sLanguage);
void GetLocalizedPath(const char* sLanguage, string& sLocalizedPath);
void GetLocalizedAudioPath(const char* sLanguage, string& sLocalizedPath);
void CloseLanguagePak(const char* sLanguage);
void CloseLanguageAudioPak(const char* sLanguage);
void UpdateMovieSystem(const int updateFlags, const float fFrameTime, const bool bPreUpdate);
//////////////////////////////////////////////////////////////////////////
// CryAssert and error related.
virtual bool RegisterErrorObserver(IErrorObserver* errorObserver);
bool UnregisterErrorObserver(IErrorObserver* errorObserver);
virtual void OnAssert(const char* condition, const char* message, const char* fileName, unsigned int fileLineNumber);
void OnFatalError(const char* message);
bool IsAssertDialogVisible() const;
void SetAssertVisible(bool bAssertVisble);
//////////////////////////////////////////////////////////////////////////
virtual void ClearErrorMessages()
{
m_ErrorMessages.clear();
}
bool IsLoading()
{
return m_eRuntimeState == ESYSTEM_EVENT_LEVEL_LOAD_START_LOADINGSCREEN;
}
virtual ESystemGlobalState GetSystemGlobalState(void);
virtual void SetSystemGlobalState(ESystemGlobalState systemGlobalState);
#if !defined(_RELEASE)
virtual bool IsSavingResourceList() const { return (g_cvars.archiveVars.nSaveLevelResourceList != 0); }
#endif
private:
std::vector<IErrorObserver*> m_errorObservers;
ESystemGlobalState m_systemGlobalState;
static const char* GetSystemGlobalStateName(const ESystemGlobalState systemGlobalState);
public:
void InitLocalization();
protected: // -------------------------------------------------------------
CCmdLine* m_pCmdLine;
string m_currentLanguageAudio;
string m_systemConfigName; // computed from system_(hardwareplatform)_(assetsPlatform) - eg, system_android_android.cfg or system_windows_pc.cfg
std::vector< std::pair<CTimeValue, float> > m_updateTimes;
struct SErrorMessage
{
string m_Message;
float m_fTimeToShow;
float m_Color[4];
bool m_HardFailure;
};
typedef std::list<SErrorMessage> TErrorMessages;
TErrorMessages m_ErrorMessages;
bool m_bHasRenderedErrorMessage;
ESystemEvent m_eRuntimeState;
bool m_bIsAsserting;
std::vector<IWindowMessageHandler*> m_windowMessageHandlers;
bool m_initedOSAllocator = false;
bool m_initedSysAllocator = false;
};
+577
View File
@@ -0,0 +1,577 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// 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);
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_SYSTEMCFG_H
#define CRYINCLUDE_CRYSYSTEM_SYSTEMCFG_H
#pragma once
#include <math.h>
#include <map>
typedef string SysConfigKey;
typedef string SysConfigValue;
//////////////////////////////////////////////////////////////////////////
class CSystemConfiguration
{
public:
CSystemConfiguration(const string& strSysConfigFilePath, CSystem* pSystem, ILoadConfigurationEntrySink* pSink, bool warnIfMissing = true);
~CSystemConfiguration();
string RemoveWhiteSpaces(string& s)
{
s.Trim();
return s;
}
bool IsError() const { return m_bError; }
private: // ----------------------------------------
// Returns:
// success
bool ParseSystemConfig();
CSystem* m_pSystem;
string m_strSysConfigFilePath;
bool m_bError;
ILoadConfigurationEntrySink* m_pSink; // never 0
bool m_warnIfMissing;
};
#endif // CRYINCLUDE_CRYSYSTEM_SYSTEMCFG_H
@@ -0,0 +1,85 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "SystemEventDispatcher.h"
CSystemEventDispatcher::CSystemEventDispatcher()
: m_listeners(0)
{
}
bool CSystemEventDispatcher::RegisterListener(ISystemEventListener* pListener)
{
m_listenerRegistrationLock.Lock();
bool ret = m_listeners.Add(pListener);
m_listenerRegistrationLock.Unlock();
return ret;
}
bool CSystemEventDispatcher::RemoveListener(ISystemEventListener* pListener)
{
m_listenerRegistrationLock.Lock();
m_listeners.Remove(pListener);
m_listenerRegistrationLock.Unlock();
return true;
}
//////////////////////////////////////////////////////////////////////////
void CSystemEventDispatcher::OnSystemEventAnyThread(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
m_listenerRegistrationLock.Lock();
for (TSystemEventListeners::Notifier notifier(m_listeners); notifier.IsValid(); notifier.Next())
{
notifier->OnSystemEventAnyThread(event, wparam, lparam);
}
m_listenerRegistrationLock.Unlock();
}
//////////////////////////////////////////////////////////////////////////
void CSystemEventDispatcher::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
if (gEnv && gEnv->mMainThreadId == CryGetCurrentThreadId())
{
for (TSystemEventListeners::Notifier notifier(m_listeners); notifier.IsValid(); notifier.Next())
{
notifier->OnSystemEvent(event, wparam, lparam);
}
}
else
{
SEventParams params;
params.event = event;
params.wparam = wparam;
params.lparam = lparam;
m_systemEventQueue.push(params);
}
// Also dispatch the event on this thread. This technically means the event
// will be sent twice (thru different OnSystemEventXX functions), therefore it is up to listeners which one they react to.
OnSystemEventAnyThread(event, wparam, lparam);
}
//////////////////////////////////////////////////////////////////////////
void CSystemEventDispatcher::Update()
{
AZ_TRACE_METHOD();
assert(gEnv && gEnv->mMainThreadId == CryGetCurrentThreadId());
SEventParams params;
while (m_systemEventQueue.try_pop(params))
{
OnSystemEvent(params.event, params.wparam, params.lparam);
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_SYSTEMEVENTDISPATCHER_H
#define CRYINCLUDE_CRYSYSTEM_SYSTEMEVENTDISPATCHER_H
#pragma once
#include <ISystem.h>
#include <CryListenerSet.h>
class CSystemEventDispatcher
: public ISystemEventDispatcher
{
public:
CSystemEventDispatcher();
virtual ~CSystemEventDispatcher(){}
// ISystemEventDispatcher
virtual bool RegisterListener(ISystemEventListener* pListener);
virtual bool RemoveListener(ISystemEventListener* pListener);
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
virtual void Update();
// ~ISystemEventDispatcher
private:
void OnSystemEventAnyThread(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
typedef CListenerSet<ISystemEventListener*> TSystemEventListeners;
TSystemEventListeners m_listeners;
// for the events coming from other threads
struct SEventParams
{
ESystemEvent event;
UINT_PTR wparam;
UINT_PTR lparam;
};
typedef CryMT::queue<SEventParams> TSystemEventQueue;
TSystemEventQueue m_systemEventQueue;
CryCriticalSection m_listenerRegistrationLock;
};
#endif // CRYINCLUDE_CRYSYSTEM_SYSTEMEVENTDISPATCHER_H
File diff suppressed because it is too large Load Diff
+653
View File
@@ -0,0 +1,653 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "System.h"
#include <time.h>
#include <IRenderer.h>
#include <IMovieSystem.h>
#include <ILog.h>
#include <CryLibrary.h>
#include <StringUtils.h>
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
#include <AzCore/std/allocator_stack.h>
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define SYSTEMWIN32_CPP_SECTION_1 1
#define SYSTEMWIN32_CPP_SECTION_2 2
#define SYSTEMWIN32_CPP_SECTION_3 3
#endif
#if defined(LINUX) || defined(APPLE)
#include <unistd.h>
#endif
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <float.h>
#include <shellapi.h> // Needed for ShellExecute.
#include <Psapi.h>
#include <Aclapi.h>
#include <shlobj.h>
#endif
#include "IDebugCallStack.h"
#if defined(APPLE) || defined(LINUX)
#include <pwd.h>
#endif
#include "XConsole.h"
#include "LocalizedStringManager.h"
#include "XML/XmlUtils.h"
#if defined(WIN32)
__pragma(comment(lib, "wininet.lib"))
__pragma(comment(lib, "Winmm.lib"))
#endif
#if defined(APPLE)
#include <AzFramework/Utils/SystemUtilsApple.h>
#endif
// this is the list of modules that can be loaded into the game process
// Each array element contains 2 strings: the name of the module (case-insensitive)
// and the name of the group the module belongs to
//////////////////////////////////////////////////////////////////////////
const char g_szGroupCore[] = "CryEngine";
const char* g_szModuleGroups[][2] = {
{"Editor.exe", g_szGroupCore},
{"CrySystem.dll", g_szGroupCore}
};
//////////////////////////////////////////////////////////////////////////
void CSystem::SetAffinity()
{
// the following code is only for Windows
#ifdef WIN32
// set the process affinity
ICVar* pcvAffinityMask = GetIConsole()->GetCVar("sys_affinity");
if (!pcvAffinityMask)
{
pcvAffinityMask = REGISTER_INT("sys_affinity", 0, VF_NULL, "");
}
if (pcvAffinityMask)
{
unsigned nAffinity = pcvAffinityMask->GetIVal();
if (nAffinity)
{
typedef BOOL (WINAPI * FnSetProcessAffinityMask)(IN HANDLE hProcess, IN DWORD_PTR dwProcessAffinityMask);
HMODULE hKernel = CryLoadLibrary ("kernel32.dll");
if (hKernel)
{
FnSetProcessAffinityMask SetProcessAffinityMask = (FnSetProcessAffinityMask)GetProcAddress(hKernel, "SetProcessAffinityMask");
if (SetProcessAffinityMask && !SetProcessAffinityMask(GetCurrentProcess(), nAffinity))
{
GetILog()->LogError("Error: Cannot set affinity mask %d, error code %d", nAffinity, GetLastError());
}
FreeLibrary (hKernel);
}
}
}
#endif
}
#if defined(WIN32)
#pragma pack(push,1)
struct PEHeader_DLL
{
DWORD signature;
IMAGE_FILE_HEADER _head;
IMAGE_OPTIONAL_HEADER opt_head;
IMAGE_SECTION_HEADER* section_header; // actual number in NumberOfSections
};
#pragma pack(pop)
#endif
//////////////////////////////////////////////////////////////////////////
const char* CSystem::GetUserName()
{
#if defined(WIN32) || defined(WIN64)
static const int iNameBufferSize = 1024;
static char szNameBuffer[iNameBufferSize];
memset(szNameBuffer, 0, iNameBufferSize);
DWORD dwSize = iNameBufferSize;
wchar_t nameW[iNameBufferSize];
::GetUserNameW(nameW, &dwSize);
cry_strcpy(szNameBuffer, CryStringUtils::WStrToUTF8(nameW));
return szNameBuffer;
#else
#if defined(LINUX)
static uid_t uid = geteuid ();
static struct passwd* pw = getpwuid (uid);
if (pw)
{
return (pw->pw_name);
}
else
{
return NULL;
}
#elif defined(APPLE)
static const int iNameBufferSize = 1024;
static char szNameBuffer[iNameBufferSize];
if(SystemUtilsApple::GetUserName(szNameBuffer, iNameBufferSize))
{
return szNameBuffer;
}
else
{
return "";
}
#else
return "";
#endif
#endif
}
//////////////////////////////////////////////////////////////////////////
int CSystem::GetApplicationInstance()
{
#ifdef WIN32
// tools that declare themselves as in "tool mode" may not access @user@ and may also not lock it
if (gEnv->IsInToolMode())
{
return 0;
}
// this code below essentially "locks" an instance of the USER folder to a specific running application
if (m_iApplicationInstance == -1)
{
string suffix;
for (int instance = 0;; ++instance)
{
suffix.Format("(%d)", instance);
CreateMutex(NULL, TRUE, "LumberyardApplication" + suffix);
// search for duplicates
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
m_iApplicationInstance = instance;
break;
}
}
}
return m_iApplicationInstance;
#else
return 0;
#endif
}
int CSystem::GetApplicationLogInstance([[maybe_unused]] const char* logFilePath)
{
#if AZ_TRAIT_OS_USE_WINDOWS_MUTEX
string suffix;
int instance = 0;
for (;; ++instance)
{
suffix.Format("(%d)", instance);
CreateMutex(NULL, TRUE, logFilePath + suffix);
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
break;
}
}
return instance;
#else
return 0;
#endif
}
//////////////////////////////////////////////////////////////////////////
struct CryDbgModule
{
HANDLE heap;
WIN_HMODULE handle;
string name;
DWORD dwSize;
};
#ifdef WIN32
//////////////////////////////////////////////////////////////////////////
class CStringOrder
{
public:
bool operator () (const char* szLeft, const char* szRight) const {return azstricmp(szLeft, szRight) < 0; }
};
typedef std::map<const char*, unsigned, CStringOrder> StringToSizeMap;
void AddSize (StringToSizeMap& mapSS, const char* szString, unsigned nSize)
{
StringToSizeMap::iterator it = mapSS.find (szString);
if (it == mapSS.end())
{
mapSS.insert (StringToSizeMap::value_type(szString, nSize));
}
else
{
it->second += nSize;
}
}
//////////////////////////////////////////////////////////////////////////
const char* GetModuleGroup (const char* szString)
{
for (unsigned i = 0; i < sizeof(g_szModuleGroups) / sizeof(g_szModuleGroups[0]); ++i)
{
if (azstricmp(szString, g_szModuleGroups[i][0]) == 0)
{
return g_szModuleGroups[i][1];
}
}
return "Other";
}
#endif
// Make system error message string
//////////////////////////////////////////////////////////////////////////
//! \return pointer to the null terminated error string or 0
static const char* GetLastSystemErrorMessage()
{
#ifdef WIN32
DWORD dwError = GetLastError();
static char szBuffer[512]; // function will return pointer to this buffer
if (dwError)
{
LPVOID lpMsgBuf = 0;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL))
{
cry_strcpy(szBuffer, (char*)lpMsgBuf);
LocalFree(lpMsgBuf);
}
else
{
return 0;
}
return szBuffer;
}
#else
return 0;
#endif //WIN32
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CSystem::FatalError(const char* format, ...)
{
// Guard against reentrancy - out of memory fatal errors can become reentrant since logging can try to alloc.
static bool currentlyReportingError = false;
if (currentlyReportingError == true)
{
return;
}
currentlyReportingError = true;
// format message
va_list ArgList;
char szBuffer[MAX_WARNING_LENGTH];
const char* sPrefix = "";
azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
va_start(ArgList, format);
azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
va_end(ArgList);
// get system error message before any attempt to write into log
const char* szSysErrorMessage = GetLastSystemErrorMessage();
CryLogAlways("=============================================================================");
CryLogAlways("*ERROR");
CryLogAlways("=============================================================================");
// write both messages into log
CryLogAlways("%s", szBuffer);
if (szSysErrorMessage)
{
CryLogAlways("Last System Error: %s", szSysErrorMessage);
}
if (GetUserCallback())
{
GetUserCallback()->OnError(szBuffer);
}
assert(szBuffer[0] >= ' ');
// strcpy(szBuffer,szBuffer+1); // remove verbosity tag since it is not supported by ::MessageBox
OutputDebugString(szBuffer);
#ifdef WIN32
OnFatalError(szBuffer);
if (!g_cvars.sys_no_crash_dialog)
{
::MessageBox(NULL, szBuffer, "Open 3D Engine Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
}
// Dump callstack.
IDebugCallStack::instance()->FatalError(szBuffer);
#endif
CryDebugBreak();
// app can not continue
#ifdef _DEBUG
#if defined(WIN32) && !defined(WIN64)
DEBUG_BREAK;
#endif
#else
#if defined(WIN32) || defined(WIN64)
_flushall();
// on windows, _exit does all sorts of things which can cause cleanup to fail during a crash, we need to terminate instead.
TerminateProcess(GetCurrentProcess(), 1);
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(SystemWin32_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
_exit(1);
#endif
#endif
}
void CSystem::ReportBug([[maybe_unused]] const char* format, ...)
{
#if defined (WIN32)
va_list ArgList;
char szBuffer[MAX_WARNING_LENGTH];
const char* sPrefix = "";
azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
va_start(ArgList, format);
azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
va_end(ArgList);
IDebugCallStack::instance()->ReportBug(szBuffer);
#endif
}
//////////////////////////////////////////////////////////////////////////
void CSystem::debug_GetCallStack(const char** pFunctions, int& nCount)
{
#if defined(WIN32)
using namespace AZ::Debug;
int nMaxCount = nCount;
StackFrame* frames = (StackFrame*)AZ_ALLOCA(sizeof(StackFrame)*nMaxCount);
unsigned int numFrames = StackRecorder::Record(frames, nMaxCount, 1);
SymbolStorage::StackLine* textLines = (SymbolStorage::StackLine*)AZ_ALLOCA(sizeof(SymbolStorage::StackLine)*nMaxCount);
SymbolStorage::DecodeFrames(frames, numFrames, textLines);
for (int i = 0; i < numFrames; i++)
{
pFunctions[i] = textLines[i];
}
nCount = numFrames;
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(SystemWin32_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
AZ_UNUSED(pFunctions);
nCount = 0;
#endif
}
//////////////////////////////////////////////////////////////////////////
void CSystem::debug_LogCallStack(int nMaxFuncs, [[maybe_unused]] int nFlags)
{
if (nMaxFuncs > 32)
{
nMaxFuncs = 32;
}
// Print call stack for each find.
const char* funcs[32];
int nCount = nMaxFuncs;
GetISystem()->debug_GetCallStack(funcs, nCount);
for (int i = 1; i < nCount; i++) // start from 1 to skip this function.
{
CryLogAlways(" %02d) %s", i, funcs[i]);
}
}
//////////////////////////////////////////////////////////////////////////
// Support relaunching for windows media center edition.
//////////////////////////////////////////////////////////////////////////
#if defined(WIN32)
#if (_WIN32_WINNT < 0x0501)
#define SM_MEDIACENTER 87
#endif
bool CSystem::ReLaunchMediaCenter()
{
// Skip if not running on a Media Center
if (GetSystemMetrics(SM_MEDIACENTER) == 0)
{
return false;
}
// Get the path to Media Center
char szExpandedPath[AZ_MAX_PATH_LEN];
if (!ExpandEnvironmentStrings("%SystemRoot%\\ehome\\ehshell.exe", szExpandedPath, AZ_MAX_PATH_LEN))
{
return false;
}
// Skip if ehshell.exe doesn't exist
if (GetFileAttributes(szExpandedPath) == 0xFFFFFFFF)
{
return false;
}
// Launch ehshell.exe
INT_PTR result = (INT_PTR)ShellExecute(NULL, TEXT("open"), szExpandedPath, NULL, NULL, SW_SHOWNORMAL);
return (result > 32);
}
#else
bool CSystem::ReLaunchMediaCenter()
{
return false;
}
#endif //defined(WIN32)
#if (defined(WIN32) || defined(WIN64))
//////////////////////////////////////////////////////////////////////////
bool CSystem::GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize)
{
bool bSucceeded = false;
// check Vista and later OS first
HMODULE shell32 = LoadLibraryA("Shell32.dll");
if (shell32)
{
typedef long (__stdcall * T_SHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, unsigned long dwFlags, void* hToken, wchar_t** ppszPath);
T_SHGetKnownFolderPath SHGetKnownFolderPath = (T_SHGetKnownFolderPath)GetProcAddress(shell32, "SHGetKnownFolderPath");
if (SHGetKnownFolderPath)
{
// We must be running Vista or newer
wchar_t* wMyDocumentsPath;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_DONT_UNEXPAND, NULL, &wMyDocumentsPath);
bSucceeded = SUCCEEDED(hr);
if (bSucceeded)
{
// Convert from UNICODE to UTF-8
cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath));
CoTaskMemFree(wMyDocumentsPath);
}
}
FreeLibrary(shell32);
}
if (!bSucceeded)
{
// check pre-vista OS if not succeeded before
wchar_t wMyDocumentsPath[AZ_MAX_PATH_LEN];
bSucceeded = SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, wMyDocumentsPath));
if (bSucceeded)
{
cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath));
}
}
return bSucceeded;
}
#endif
//////////////////////////////////////////////////////////////////////////
void CSystem::DetectGameFolderAccessRights()
{
// This code is trying to figure out if the current folder we are now running under have write access.
// By default assume folder is not writable.
// If folder is writable game.log is saved there, otherwise it is saved in user documents folder.
#if defined(WIN32)
DWORD DesiredAccess = FILE_GENERIC_WRITE;
DWORD GrantedAccess = 0;
DWORD dwRes = 0;
PACL pDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
HANDLE hClientToken = 0;
PRIVILEGE_SET PrivilegeSet;
DWORD PrivilegeSetLength = sizeof(PrivilegeSet);
BOOL bAccessStatus = FALSE;
// Get a pointer to the existing DACL.
dwRes = GetNamedSecurityInfo(".", SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
NULL, NULL, &pDACL, NULL, &pSD);
if (ERROR_SUCCESS != dwRes)
{
//
assert(0);
}
if (!ImpersonateSelf(SecurityIdentification))
{
return;
}
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hClientToken) && hClientToken != 0)
{
return;
}
GENERIC_MAPPING GenMap;
GenMap.GenericRead = FILE_GENERIC_READ;
GenMap.GenericWrite = FILE_GENERIC_WRITE;
GenMap.GenericExecute = FILE_GENERIC_EXECUTE;
GenMap.GenericAll = FILE_ALL_ACCESS;
MapGenericMask(&DesiredAccess, &GenMap);
if (!AccessCheck(pSD, hClientToken, DesiredAccess, &GenMap, &PrivilegeSet, &PrivilegeSetLength, &GrantedAccess, &bAccessStatus))
{
RevertToSelf();
CloseHandle(hClientToken);
return;
}
CloseHandle(hClientToken);
RevertToSelf();
if (bAccessStatus)
{
m_bGameFolderWritable = true;
}
#elif defined(MOBILE)
char cwd[AZ_MAX_PATH_LEN];
if (getcwd(cwd, AZ_MAX_PATH_LEN) != NULL)
{
if (0 == access(cwd, W_OK))
{
m_bGameFolderWritable = true;
}
}
#endif //WIN32
}
/////////////////////////////////`/////////////////////////////////////////
void CSystem::EnableFloatExceptions([[maybe_unused]] int type)
{
#ifndef _RELEASE
#if defined(WIN32)
#if defined(WIN32) && !defined(WIN64)
// Optimization
// Enable DAZ/FZ
// Denormals Are Zeros
// Flush-to-Zero
_controlfp(_DN_FLUSH, _MCW_DN);
#endif //#if defined(WIN32) && !defined(WIN64)
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
_controlfp(_DN_FLUSH, _MCW_DN);
if (type == 0)
{
// mask all floating exceptions off.
_controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE, _MCW_EM);
}
else
{
// Clear pending exceptions
_fpreset();
if (type == 1)
{
// enable just the most important fp-exceptions.
_controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM); // Enable floating point exceptions.
}
if (type == 2)
{
// enable ALL floating point exceptions.
_controlfp(_EM_INEXACT, _MCW_EM);
}
}
AZ_POP_DISABLE_WARNING
#endif //#if defined(WIN32) && !defined(WIN64)
#ifdef WIN32
_mm_setcsr(_mm_getcsr() & ~0x280 | (type > 0 ? 0 : 0x280));
#endif
#endif //_RELEASE
}
+724
View File
@@ -0,0 +1,724 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "Timer.h"
#include <time.h>
#include <ISystem.h>
#include <IConsole.h>
#include <ILog.h>
#include <ISerialize.h>
/////////////////////////////////////////////////////
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include "Mmsystem.h"
#endif
//#define PROFILING 1
#ifdef PROFILING
static int64 g_lCurrentTime = 0;
#endif
//! Profile smoothing time in seconds (original default was .8 / log(10) ~= .35 s)
static const float fDEFAULT_PROFILE_SMOOTHING = 1.0f;
#define DEFAULT_FRAME_SMOOTHING 1
/////////////////////////////////////////////////////
CTimer::CTimer()
{
// Default CVar values
m_fixed_time_step = 0;
m_max_time_step = 0.25f;
m_cvar_time_scale = 1.0f;
m_TimeSmoothing = DEFAULT_FRAME_SMOOTHING; // note: frame numbers (old version - commented out) are not used but is based on time
m_TimeDebug = 0;
m_profile_smooth_time = fDEFAULT_PROFILE_SMOOTHING;
m_profile_weighting = 1;
// Persistant state
m_bEnabled = true;
//m_fixedTimeModeEnabled = false;
m_nFrameCounter = 0;
m_lTicksPerSec = CryGetTicksPerSec();
m_fSecsPerTick = 1.0 / m_lTicksPerSec;
m_fAverageFrameTime = 1.0f / 30.0f;
for (int i = 0; i < MAX_FRAME_AVERAGE; i++)
{
m_arrFrameTimes[i] = m_fAverageFrameTime;
}
m_fAvgFrameTime = 0.0f;
m_fProfileBlend = 1.0f;
m_fSmoothTime = 0;
m_totalTimeScale = 1.0f;
ClearTimeScales();
ResetTimer();
}
/////////////////////////////////////////////////////
bool CTimer::Init()
{
// if game code was accessing them by name there was something wrong anyway
REGISTER_CVAR2("t_Smoothing", &m_TimeSmoothing, DEFAULT_FRAME_SMOOTHING, 0,
"time smoothing\n"
"0=off, 1=on");
REGISTER_CVAR2("t_FixedStep", &m_fixed_time_step, 0, VF_NET_SYNCED | VF_DEV_ONLY,
"Game updated with this fixed frame time\n"
"0=off, number specifies the frame time in seconds\n"
"e.g. 0.033333(30 fps), 0.1(10 fps), 0.01(100 fps)");
REGISTER_CVAR2("t_MaxStep", &m_max_time_step, 0.25f, 0,
"Game systems clamped to this frame time");
// todo: reconsider exposing that as cvar (negative time, same value is used by Trackview, better would be another value multipled with the internal one)
REGISTER_CVAR2("t_Scale", &m_cvar_time_scale, 1.0f, VF_NET_SYNCED | VF_DEV_ONLY,
"Game time scaled by this - for variable slow motion");
REGISTER_CVAR2("t_Debug", &m_TimeDebug, 0, 0, "Timer debug: 0 = off, 1 = events, 2 = verbose");
// -----------------
REGISTER_CVAR2("profile_smooth", &m_profile_smooth_time, fDEFAULT_PROFILE_SMOOTHING, 0,
"Profiler exponential smoothing interval (seconds)");
REGISTER_CVAR2("profile_weighting", &m_profile_weighting, 1, 0,
"Profiler smoothing mode: 0 = legacy, 1 = average, 2 = peak weighted, 3 = peak hold");
return true;
}
/////////////////////////////////////////////////////
float CTimer::GetFrameTime(ETimer which) const
{
float result = 0.0f;
if (m_bEnabled)
{
if (which != ETIMER_GAME || !m_bGameTimerPaused)
{
if (which == ETIMER_UI)
{
result = m_fRealFrameTime;
}
else
{
result = m_fFrameTime;
}
}
}
return result;
}
/////////////////////////////////////////////////////
float CTimer::GetCurrTime(ETimer which) const
{
assert(which >= 0 && which < ETIMER_LAST && "Bad timer index");
return m_CurrTime[which].GetSeconds();
}
/////////////////////////////////////////////////////
float CTimer::GetRealFrameTime() const
{
return m_bEnabled ? m_fRealFrameTime : 0.0f;
}
/////////////////////////////////////////////////////
float CTimer::GetTimeScale() const
{
return m_cvar_time_scale * m_totalTimeScale;
}
/////////////////////////////////////////////////////
float CTimer::GetTimeScale(uint32 channel) const
{
assert(channel < NUM_TIME_SCALE_CHANNELS);
if (channel >= NUM_TIME_SCALE_CHANNELS)
{
return GetTimeScale();
}
return m_cvar_time_scale * m_timeScaleChannels[channel];
}
/////////////////////////////////////////////////////
void CTimer::SetTimeScale(float scale, uint32 channel /* = 0 */)
{
assert(channel < NUM_TIME_SCALE_CHANNELS);
if (channel >= NUM_TIME_SCALE_CHANNELS)
{
return;
}
const float currentScale = m_timeScaleChannels[channel];
if (scale != currentScale)
{
// Need to adjust previous frame times for time scale to have immediate effect
const float adjustFactor = scale / currentScale;
for (uint32 i = 0; i < MAX_FRAME_AVERAGE; ++i)
{
m_arrFrameTimes[i] *= adjustFactor;
}
// Update total time scale immediately
m_totalTimeScale *= adjustFactor;
}
m_timeScaleChannels[channel] = scale;
}
/////////////////////////////////////////////////////
void CTimer::ClearTimeScales()
{
if (m_totalTimeScale != 1.0f)
{
// Need to adjust previous frame times for time scale to have immediate effect
const float adjustFactor = 1.0f / m_totalTimeScale;
for (uint32 i = 0; i < MAX_FRAME_AVERAGE; ++i)
{
m_arrFrameTimes[i] *= adjustFactor;
}
}
for (int i = 0; i < NUM_TIME_SCALE_CHANNELS; ++i)
{
m_timeScaleChannels[i] = 1.0f;
}
m_totalTimeScale = 1.0f;
}
/////////////////////////////////////////////////////
float CTimer::GetAsyncCurTime()
{
//int64 llNow = CryGetTicks() - m_lBaseTime_Async;
int64 llNow = CryGetTicks() - m_lBaseTime;
return TicksToSeconds(llNow);
}
/////////////////////////////////////////////////////
float CTimer::GetFrameRate()
{
// Use real frame time.
if (m_fRealFrameTime != 0.f)
{
return 1.f / m_fRealFrameTime;
}
return 0.f;
}
void CTimer::UpdateBlending()
{
// Accumulate smoothing time up to specified max.
float fFrameTime = m_fRealFrameTime;
m_fSmoothTime = min(m_fSmoothTime + fFrameTime, m_profile_smooth_time);
if (m_fSmoothTime <= fFrameTime)
{
m_fAvgFrameTime = fFrameTime;
m_fProfileBlend = 1.f;
return;
}
if (m_profile_weighting <= 2)
{
// Update average frame time.
if (m_fSmoothTime < m_fAvgFrameTime)
{
m_fAvgFrameTime = m_fSmoothTime;
}
m_fAvgFrameTime *= m_fSmoothTime / (m_fSmoothTime - fFrameTime + m_fAvgFrameTime);
if (m_profile_weighting == 1)
{
// Weight all frames equally.
m_fProfileBlend = m_fAvgFrameTime / m_fSmoothTime;
}
else
{
// Weight frames by time.
m_fProfileBlend = fFrameTime / m_fSmoothTime;
}
}
else
{
// Decay avg frame time, set as new peak.
m_fAvgFrameTime *= 1.f - fFrameTime / m_fSmoothTime;
if (fFrameTime > m_fAvgFrameTime)
{
m_fAvgFrameTime = fFrameTime;
m_fProfileBlend = 1.f;
}
else
{
m_fProfileBlend = 0.f;
}
}
}
float CTimer::GetProfileFrameBlending(float* pfBlendTime, int* piBlendMode)
{
if (piBlendMode)
{
*piBlendMode = m_profile_weighting;
}
if (pfBlendTime)
{
*pfBlendTime = m_fSmoothTime;
}
return m_fProfileBlend;
}
/////////////////////////////////////////////////////
void CTimer::RefreshGameTime(int64 curTime)
{
assert(curTime + m_lOffsetTime >= 0);
m_CurrTime[ETIMER_GAME].SetSeconds(TicksToSeconds(curTime + m_lOffsetTime));
}
/////////////////////////////////////////////////////
void CTimer::RefreshUITime(int64 curTime)
{
assert(curTime >= 0);
m_CurrTime[ETIMER_UI].SetSeconds(TicksToSeconds(curTime));
}
/////////////////////////////////////////////////////
void CTimer::UpdateOnFrameStart()
{
if (!m_bEnabled)
{
return;
}
//int64 now;
//if (m_fixedTimeModeEnabled)
//{
// m_nFrameCounter++;
// m_fRealFrameTime = m_fFrameTime = m_fixedTimeModeStep;
// m_lCurrentTime += m_fixedTimeModeStep*m_lTicksPerSec;
// now = m_lCurrentTime;
//}
//else
//{
// On Windows before Vista, frequency can change (even though it should be impossible),
// See also: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
// Win2000, WinXP: Uses RDTSC, which may not be monotonic across all cores (a bug), costs in the order of 10~100 cycles (cheap).
// WinVista: Uses HPET or ACPI timer (a kernel call, and much more expensive than RDTSC, but it's not bugged).
// Win7+: RDTSC if the CPU feature bit for monotonic is set, HPET or ACPI otherwise (not bugged).
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
if ((m_nFrameCounter & 127) == 0)
{
// every bunch of frames, check frequency to adapt to
// CPU power management clock rate changes
LARGE_INTEGER TTicksPerSec;
if (QueryPerformanceFrequency(&TTicksPerSec))
{
// if returns false, no performance counter is available
m_lTicksPerSec = TTicksPerSec.QuadPart;
m_fSecsPerTick = 1.0 / m_lTicksPerSec;
}
}
m_nFrameCounter++;
#endif
//}
#ifdef PROFILING
m_fRealFrameTime = m_fFrameTime = 0.020f; // 20ms = 50fps
g_lCurrentTime += (int)(m_fFrameTime * (float)(CTimeValue::TIMEVALUE_PRECISION));
m_lLastTime = g_lCurrentTime;
RefreshGameTime(m_lLastTime);
RefreshUITime(m_lLastTime);
return;
#endif
if (m_fixed_time_step < 0.0f)
{
// Enforce real framerate by sleeping.
const int64 elapsedTicks = CryGetTicks() - m_lBaseTime - m_lLastTime;
const int64 minTicks = SecondsToTicks(-m_fixed_time_step);
if (elapsedTicks < minTicks)
{
const int64 ms = (minTicks - elapsedTicks) * 1000 / m_lTicksPerSec;
CrySleep((unsigned int)ms);
}
}
const int64 now = CryGetTicks();
assert(now + 1 >= m_lBaseTime && "Invalid base time"); //+1 margin because QPC may be one off across cores
m_fRealFrameTime = TicksToSeconds(now - m_lBaseTime - m_lLastTime);
if (0.0f != m_fixed_time_step)
{
// Apply fixed_time_step
m_fFrameTime = abs(m_fixed_time_step);
}
else
{
// Clamp to max_time_step
m_fFrameTime = min(m_fRealFrameTime, m_max_time_step);
}
// Dilate time.
m_fFrameTime *= GetTimeScale();
if (m_TimeSmoothing > 0)
{
m_fFrameTime = GetAverageFrameTime();
}
// Time can only go forward.
if (m_fFrameTime < 0.0f)
{
m_fFrameTime = 0.0f;
}
if (m_fRealFrameTime < 0.0f)
{
m_fRealFrameTime = 0.0;
}
// Adjust the base time so that time actually seems to have moved forward m_fFrameTime
const int64 frameTicks = SecondsToTicks(m_fFrameTime);
const int64 realTicks = SecondsToTicks(m_fRealFrameTime);
m_lBaseTime += realTicks - frameTicks;
if (m_lBaseTime > now)
{
// Guard against rounding errors due to float <-> int64 precision
assert(m_lBaseTime - now <= 10 && "Bad base time or adjustment, too much difference for a rounding error");
m_lBaseTime = now;
}
const int64 currentTime = now - m_lBaseTime;
assert(fabsf(TicksToSeconds(currentTime - m_lLastTime) - m_fFrameTime) < 0.01f && "Bad calculation");
assert(currentTime >= m_lLastTime && "Bad adjustment in previous frame");
assert(currentTime + m_lOffsetTime >= 0 && "Sum of game time is negative");
// Update timers
RefreshUITime(currentTime);
if (!m_bGameTimerPaused)
{
RefreshGameTime(currentTime);
}
m_lLastTime = currentTime;
UpdateBlending();
if (m_TimeDebug > 1)
{
CryLogAlways("[CTimer]: Cur=%lld Now=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)currentTime, (long long)now, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
}
}
//------------------------------------------------------------------------
//-- average frame-times to avoid stalls and peaks in framerate
//-- note that is is time-base averaging and not frame-based
//------------------------------------------------------------------------
float CTimer::GetAverageFrameTime()
{
f32 LastAverageFrameTime = m_fAverageFrameTime;
f32 FrameTime = m_fFrameTime;
uint32 numFT = MAX_FRAME_AVERAGE;
for (int32 i = (numFT - 2); i > -1; i--)
{
m_arrFrameTimes[i + 1] = m_arrFrameTimes[i];
}
if (FrameTime > 0.4f)
{
FrameTime = 0.4f;
}
if (FrameTime < 0.0f)
{
FrameTime = 0.0f;
}
m_arrFrameTimes[0] = FrameTime;
//get smoothed frame
uint32 avrg_ftime = 1;
if (LastAverageFrameTime)
{
avrg_ftime = uint32(0.25f / LastAverageFrameTime + 0.5f); //average the frame-times for a certain time-period (sec)
if (avrg_ftime > numFT)
{
avrg_ftime = numFT;
}
if (avrg_ftime < 1)
{
avrg_ftime = 1;
}
}
f32 AverageFrameTime = 0;
for (uint32 i = 0; i < avrg_ftime; i++)
{
AverageFrameTime += m_arrFrameTimes[i];
}
AverageFrameTime /= avrg_ftime;
//don't smooth if we pause the game
if (FrameTime < 0.0001f)
{
AverageFrameTime = FrameTime;
}
m_fAverageFrameTime = AverageFrameTime;
return AverageFrameTime;
}
/////////////////////////////////////////////////////
void CTimer::ResetTimer()
{
m_lBaseTime = CryGetTicks();
//m_lBaseTime_Async = CryGetTicks();
m_lLastTime = 0;
m_lOffsetTime = 0;
m_fFrameTime = 0.0f;
m_fRealFrameTime = 0.0f;
RefreshGameTime(0);
RefreshUITime(0);
m_bGameTimerPaused = false;
m_lGameTimerPausedTime = 0;
}
/////////////////////////////////////////////////////
void CTimer::EnableTimer(bool bEnable)
{
m_bEnabled = bEnable;
}
bool CTimer::IsTimerEnabled() const
{
return m_bEnabled;
}
/////////////////////////////////////////////////////
CTimeValue CTimer::GetAsyncTime() const
{
int64 llNow = CryGetTicks();
double fConvert = CTimeValue::TIMEVALUE_PRECISION * m_fSecsPerTick;
return CTimeValue(int64(llNow * fConvert));
}
/////////////////////////////////////////////////////
void CTimer::Serialize(TSerialize ser)
{
// cannot change m_lBaseTime, as this is used for async time (which shouldn't be affected by save games)
if (ser.IsWriting())
{
int64 currentGameTime = m_lLastTime + m_lOffsetTime;
ser.Value("curTime", currentGameTime);
ser.Value("ticksPerSecond", m_lTicksPerSec);
}
else
{
int64 ticksPerSecond = 1, curTime = 1;
ser.Value("curTime", curTime);
ser.Value("ticksPerSecond", ticksPerSecond);
// Adjust curTime for ticksPerSecond on this machine.
// Some precision will be lost if the frequencies are not identical.
const double multiplier = (double)m_lTicksPerSec / (double)ticksPerSecond;
curTime = (int64)((double)curTime * multiplier);
SetOffsetToMatchGameTime(curTime);
if (m_TimeDebug)
{
const int64 now = CryGetTicks();
CryLogAlways("[CTimer]: Serialize: Last=%lld Now=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)now, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
}
}
}
//! try to pause/unpause a timer
// returns true if successfully paused/unpaused, false otherwise
bool CTimer::PauseTimer(ETimer which, bool bPause)
{
if (which != ETIMER_GAME)
{
return false;
}
if (m_bGameTimerPaused == bPause)
{
return false;
}
m_bGameTimerPaused = bPause;
if (bPause)
{
m_lGameTimerPausedTime = m_lLastTime + m_lOffsetTime;
if (m_TimeDebug)
{
CryLogAlways("[CTimer]: Pausing ON: Last=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
}
}
else
{
SetOffsetToMatchGameTime(m_lGameTimerPausedTime);
m_lGameTimerPausedTime = 0;
if (m_TimeDebug)
{
CryLogAlways("[CTimer]: Pausing OFF: Last=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
}
}
return true;
}
//! determine if a timer is paused
// returns true if paused, false otherwise
bool CTimer::IsTimerPaused(ETimer which)
{
if (which != ETIMER_GAME)
{
return false;
}
return m_bGameTimerPaused;
}
//! try to set a timer
// return true if successful, false otherwise
bool CTimer::SetTimer(ETimer which, float timeInSeconds)
{
if (which != ETIMER_GAME)
{
return false;
}
SetOffsetToMatchGameTime(SecondsToTicks(timeInSeconds));
return true;
}
ITimer* CTimer::CreateNewTimer()
{
return new CTimer();
}
void CTimer::SecondsToDateUTC(time_t inTime, struct tm& outDateUTC)
{
#ifdef AZ_COMPILER_MSVC
gmtime_s(&outDateUTC, &inTime);
#else
outDateUTC = *gmtime(&inTime);
#endif
}
#if defined (WIN32) || defined(WIN64)
time_t gmt_to_local_win32(void)
{
TIME_ZONE_INFORMATION tzinfo;
DWORD dwStandardDaylight;
long bias;
dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
bias = tzinfo.Bias;
if (dwStandardDaylight == TIME_ZONE_ID_STANDARD)
{
bias += tzinfo.StandardBias;
}
if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT)
{
bias += tzinfo.DaylightBias;
}
return (-bias * 60);
}
#endif
time_t CTimer::DateToSecondsUTC(struct tm& inDate)
{
#if defined (WIN32)
return mktime(&inDate) + gmt_to_local_win32();
#elif defined (LINUX)
#if defined (HAVE_TIMEGM)
// return timegm(&inDate);
#else
// craig: temp disabled the +tm.tm_gmtoff because i can't see the intention here
// and it doesn't compile anymore
// alexl: tm_gmtoff is the offset to greenwhich mean time, whereas mktime uses localtime
// but not all linux distributions have it...
return mktime(&inDate) /*+ tm.tm_gmtoff*/;
#endif
#else
return mktime(&inDate);
#endif
}
void CTimer::EnableFixedTimeMode([[maybe_unused]] bool enable, [[maybe_unused]] float timeStep)
{
//if (enable)
//{
// m_fixedTimeModeEnabled = true;
// m_fixedTimeModeStep = timeStep;
// m_lBaseTime =0;
// m_lBaseTime_Async = 0;
// m_lLastTime = m_lCurrentTime = 0;
// m_fRealFrameTime = m_fFrameTime = timeStep;
// RefreshGameTime(m_lCurrentTime);
// RefreshUITime(m_lCurrentTime);
// m_lForcedGameTime = -1;
// m_bGameTimerPaused = false;
// m_lGameTimerPausedTime = 0;
//}
//else
//{
// m_fixedTimeModeEnabled = false;
// ResetTimer();
//}
}
void CTimer::SetOffsetToMatchGameTime(int64 ticks)
{
const int64 previousOffset = m_lOffsetTime;
const float previousGameTime = GetCurrTime(ETIMER_GAME);
m_lOffsetTime = ticks - m_lLastTime;
RefreshGameTime(m_lLastTime);
if (m_bGameTimerPaused)
{
// On un-pause, we will restore the specified time.
// If we don't do this, the un-pause will over-write the offset again.
m_lGameTimerPausedTime = ticks;
}
if (m_TimeDebug)
{
CryLogAlways("[CTimer] SetOffset: Offset %lld -> %lld, GameTime %f -> %f", (long long)previousOffset, (long long)m_lOffsetTime, GetCurrTime(ETIMER_GAME), previousGameTime);
}
}
int64 CTimer::SecondsToTicks(double seconds) const
{
return (int64)(seconds * (double)m_lTicksPerSec);
}
+165
View File
@@ -0,0 +1,165 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_TIMER_H
#define CRYINCLUDE_CRYSYSTEM_TIMER_H
# pragma once
#include <ITimer.h>
// Implements all common timing routines
class CTimer
: public ITimer
{
public:
// constructor
CTimer();
// destructor
~CTimer() {};
bool Init();
// interface ITimer ----------------------------------------------------------
// TODO: Review m_time usage in System.cpp
// if it wants Game Time / UI Time or a new Render Time?
virtual void ResetTimer();
virtual void UpdateOnFrameStart();
virtual float GetCurrTime(ETimer which = ETIMER_GAME) const;
virtual CTimeValue GetAsyncTime() const;
virtual float GetAsyncCurTime(); // retrieve the actual wall clock time passed since the game started, in seconds
virtual float GetFrameTime(ETimer which = ETIMER_GAME) const;
virtual float GetRealFrameTime() const;
virtual float GetTimeScale() const;
virtual float GetTimeScale(uint32 channel) const;
virtual void SetTimeScale(float scale, uint32 channel = 0);
virtual void ClearTimeScales();
virtual void EnableTimer(bool bEnable);
virtual float GetFrameRate();
virtual float GetProfileFrameBlending(float* pfBlendTime = 0, int* piBlendMode = 0);
virtual void Serialize(TSerialize ser);
virtual bool IsTimerEnabled() const;
//! try to pause/unpause a timer
// returns true if successfully paused/unpaused, false otherwise
virtual bool PauseTimer(ETimer which, bool bPause);
//! determine if a timer is paused
// returns true if paused, false otherwise
virtual bool IsTimerPaused(ETimer which);
//! try to set a timer
// return true if successful, false otherwise
virtual bool SetTimer(ETimer which, float timeInSeconds);
//! make a tm struct from a time_t in UTC (like gmtime)
virtual void SecondsToDateUTC(time_t time, struct tm& outDateUTC);
//! make a UTC time from a tm (like timegm, but not available on all platforms)
virtual time_t DateToSecondsUTC(struct tm& timePtr);
//! Convert from Tics to Seconds
virtual float TicksToSeconds(int64 ticks)
{
return float((double)ticks * m_fSecsPerTick);
}
//! Get number of ticks per second
virtual int64 GetTicksPerSecond()
{
return m_lTicksPerSec;
}
virtual const CTimeValue& GetFrameStartTime(ETimer which = ETIMER_GAME) const { return m_CurrTime[(int)which]; }
virtual ITimer* CreateNewTimer();
virtual void EnableFixedTimeMode(bool enable, float timeStep) override;
private: // ---------------------------------------------------------------------
// ---------------------------------------------------------------------------
// updates m_CurrTime (either pass m_lCurrentTime or custom curTime)
void RefreshGameTime(int64 curTime);
void RefreshUITime(int64 curTime);
void UpdateBlending();
float GetAverageFrameTime();
// Updates the game-time offset to match the the specified time.
// The argument is the new number of ticks since the last Reset().
void SetOffsetToMatchGameTime(int64 ticks);
// Convert seconds to ticks using the timer frequency.
// Note: Loss of precision may occur, especially if magnitude of argument or timer frequency is large.
int64 SecondsToTicks(double seconds) const;
enum
{
MAX_FRAME_AVERAGE = 100,
NUM_TIME_SCALE_CHANNELS = 8,
};
//////////////////////////////////////////////////////////////////////////
// Dynamic state, reset by ResetTimer()
//////////////////////////////////////////////////////////////////////////
CTimeValue m_CurrTime[ETIMER_LAST]; // Time since last Reset(), cached during Update()
int64 m_lBaseTime; // Ticks elapsed since system boot, all other tick-unit variables are relative to this.
int64 m_lLastTime; // Ticks since last Reset(). This is the base for UI time. UI time is monotonic, it always moves forward at a constant rate until the timer is Reset()).
int64 m_lOffsetTime; // Additional ticks for Game time (relative to UI time). Game time can be affected by loading, pausing, time smoothing and time clamping, as well as SetTimer().
//// the GetcurAsyncTime function appears to want to return the actual wall clock time delta
//// but its using the base time (above) which is adjusted when there is a frame skip.
//int64 m_lBaseTime_Async;
float m_fFrameTime; // In seconds since the last Update(), clamped/smoothed etc.
float m_fRealFrameTime; // In real seconds since the last Update(), non-clamped/un-smoothed etc.
bool m_bGameTimerPaused; // Set if the game is paused. GetFrameTime() will return 0, GetCurrTime(ETIMER_GAME) will not progress.
int64 m_lGameTimerPausedTime; // The UI time when the game timer was paused. On un-pause, offset will be adjusted to match.
//////////////////////////////////////////////////////////////////////////
// Persistant state, kept by ResetTimer()
//////////////////////////////////////////////////////////////////////////
bool m_bEnabled;
unsigned int m_nFrameCounter;
int64 m_lTicksPerSec; // Ticks per second
double m_fSecsPerTick; // Seconds per tick
// smoothing
float m_arrFrameTimes[MAX_FRAME_AVERAGE];
float m_fAverageFrameTime; // used for smoothing (AverageFrameTime())
float m_fAvgFrameTime; // used for blend weighting (UpdateBlending())
float m_fProfileBlend; // current blending amount for profile.
float m_fSmoothTime; // smoothing interval (up to m_profile_smooth_time).
// time scale
float m_timeScaleChannels[NUM_TIME_SCALE_CHANNELS];
float m_totalTimeScale;
//////////////////////////////////////////////////////////////////////////
// Console vars, always have default value on secondary CTimer instances
//////////////////////////////////////////////////////////////////////////
float m_fixed_time_step; // in seconds
float m_max_time_step; // in seconds
float m_cvar_time_scale; // slow down time cvar
int m_TimeSmoothing; // Console Variable, 0=off, otherwise on
int m_TimeDebug; // Console Variable, 0=off, otherwise on
// Profile averaging help.
float m_profile_smooth_time; // seconds to exponentially smooth profile results.
int m_profile_weighting; // weighting mode (see RegisterVar desc).
//bool m_fixedTimeModeEnabled;
//float m_fixedTimeModeStep;
};
#endif // CRYINCLUDE_CRYSYSTEM_TIMER_H
@@ -0,0 +1,345 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "DebugCamera.h"
#include "ISystem.h"
#include "Cry_Camera.h"
#include "IViewSystem.h"
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
using namespace AzFramework;
namespace LegacyViewSystem
{
const float g_moveScaleIncrement = 0.1f;
const float g_moveScaleMin = 0.01f;
const float g_moveScaleMax = 10.0f;
const float g_mouseMoveScale = 0.1f;
const float g_gamepadRotationSpeed = 5.0f;
const float g_mouseMaxRotationSpeed = 270.0f;
const float g_moveSpeed = 10.0f;
const float g_maxPitch = 85.0f;
const float g_boostMultiplier = 10.0f;
const float g_minRotationSpeed = 15.0f;
const float g_maxRotationSpeed = 70.0f;
///////////////////////////////////////////////////////////////////////////////
DebugCamera::DebugCamera()
: m_mouseMoveMode(0)
, m_isYInverted(0)
, m_cameraMode(DebugCamera::ModeOff)
, m_cameraYawInput(0.0f)
, m_cameraPitchInput(0.0f)
, m_cameraYaw(0.0f)
, m_cameraPitch(0.0f)
, m_moveInput(ZERO)
, m_moveScale(1.0f)
, m_oldMoveScale(1.0f)
, m_position(ZERO)
, m_view(IDENTITY)
{
InputChannelEventListener::Connect();
}
///////////////////////////////////////////////////////////////////////////////
DebugCamera::~DebugCamera()
{
InputChannelEventListener::Disconnect();
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::OnEnable()
{
m_position = gEnv->pSystem->GetViewCamera().GetPosition();
m_moveInput = Vec3_Zero;
Ang3 cameraAngles = Ang3(gEnv->pSystem->GetViewCamera().GetMatrix());
m_cameraYaw = RAD2DEG(cameraAngles.z);
m_cameraPitch = RAD2DEG(cameraAngles.x);
m_view = Matrix33(Ang3(DEG2RAD(m_cameraPitch), 0.0f, DEG2RAD(m_cameraYaw)));
m_cameraYawInput = 0.0f;
m_cameraPitchInput = 0.0f;
m_mouseMoveMode = 0;
m_cameraMode = DebugCamera::ModeFree;
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::OnDisable()
{
m_mouseMoveMode = 0;
m_cameraMode = DebugCamera::ModeOff;
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::OnInvertY()
{
m_isYInverted = !m_isYInverted;
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::OnNextMode()
{
if (m_cameraMode == DebugCamera::ModeFree)
{
m_cameraMode = DebugCamera::ModeFixed;
}
// ...
else if (m_cameraMode == DebugCamera::ModeFixed)
{
// this is the last mode, go to disabled.
OnDisable();
}
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::Update()
{
if (m_cameraMode == DebugCamera::ModeOff)
{
return;
}
float rotationSpeed = clamp_tpl(m_moveScale, g_minRotationSpeed, g_maxRotationSpeed);
UpdateYaw(m_cameraYawInput * rotationSpeed * gEnv->pTimer->GetFrameTime());
UpdatePitch(m_cameraPitchInput * rotationSpeed * gEnv->pTimer->GetFrameTime());
m_view = Matrix33(Ang3(DEG2RAD(m_cameraPitch), 0.0f, DEG2RAD(m_cameraYaw)));
UpdatePosition(m_moveInput);
// update the listener of the active view
if (IView* view = gEnv->pSystem->GetIViewSystem()->GetActiveView())
{
view->UpdateAudioListener(Matrix34(m_view, m_position));
}
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::PostUpdate()
{
if (m_cameraMode == DebugCamera::ModeOff)
{
return;
}
CCamera& camera = gEnv->pSystem->GetViewCamera();
camera.SetMatrix(Matrix34(m_view, m_position));
}
///////////////////////////////////////////////////////////////////////////////
bool DebugCamera::OnInputChannelEventFiltered(const InputChannel& inputChannel)
{
if (!IsEnabled() || m_cameraMode == DebugCamera::ModeFixed || gEnv->pConsole->IsOpened())
{
return false;
}
const InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
const InputChannelId& channelId = inputChannel.GetInputChannelId();
const float eventValue = inputChannel.GetValue();
if (InputDeviceKeyboard::IsKeyboardDevice(deviceId))
{
if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
{
m_moveInput.y = eventValue;
}
else if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
{
m_moveInput.y = -eventValue;
}
else if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
{
m_moveInput.x = -eventValue;
}
else if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
{
m_moveInput.x = eventValue;
}
else if (channelId == InputDeviceKeyboard::Key::ModifierShiftL)
{
if (inputChannel.IsStateEnded())
{
m_moveScale = m_oldMoveScale;
}
else if (inputChannel.IsStateBegan())
{
m_oldMoveScale = m_moveScale;
m_moveScale = clamp_tpl(m_moveScale * g_boostMultiplier, g_moveScaleMin, g_moveScaleMax);
}
}
}
else if (InputDeviceMouse::IsMouseDevice(deviceId))
{
if (channelId == InputDeviceMouse::Movement::Z)
{
if (inputChannel.GetValue() > 0)
{
m_moveScale = clamp_tpl(m_moveScale + g_moveScaleIncrement, g_moveScaleMin, g_moveScaleMax);
}
else
{
m_moveScale = clamp_tpl(m_moveScale - g_moveScaleIncrement, g_moveScaleMin, g_moveScaleMax);
}
}
else if (channelId == InputDeviceMouse::Movement::X)
{
//KC: If both left and right mouse buttons are pressed then use
//the mouse movement for horizontal movement.
if (2 != m_mouseMoveMode)
{
UpdateYaw(fsgnf(-eventValue) * clamp_tpl(fabs_tpl(eventValue) * m_moveScale, 0.0f, g_mouseMaxRotationSpeed) * gEnv->pTimer->GetFrameTime());
}
else
{
UpdatePosition(Vec3(eventValue * g_mouseMoveScale, 0.0f, 0.0f));
}
}
else if (channelId == InputDeviceMouse::Movement::Y)
{
//KC: If both left and right mouse buttons are pressed then use
//the mouse movement for vertical movement.
if (2 != m_mouseMoveMode)
{
UpdatePitch(fsgnf(-eventValue) * clamp_tpl(fabs_tpl(eventValue) * m_moveScale, 0.0f, g_mouseMaxRotationSpeed) * gEnv->pTimer->GetFrameTime());
}
else
{
UpdatePosition(Vec3(0.0f, 0.0f, -eventValue * g_mouseMoveScale));
}
}
else if (channelId == InputDeviceMouse::Button::Left)
{
if (inputChannel.IsStateEnded())
{
m_mouseMoveMode = clamp_tpl(m_mouseMoveMode - 1, 0, 2);
}
else
{
m_mouseMoveMode = clamp_tpl(m_mouseMoveMode + 1, 0, 2);
}
}
else if (channelId == InputDeviceMouse::Button::Right)
{
if (inputChannel.IsStateEnded())
{
m_mouseMoveMode = clamp_tpl(m_mouseMoveMode - 1, 0, 2);
}
else
{
m_mouseMoveMode = clamp_tpl(m_mouseMoveMode + 1, 0, 2);
}
}
}
else if (InputDeviceGamepad::IsGamepadDevice(deviceId))
{
if (channelId == InputDeviceGamepad::Button::DU)
{
m_moveScale = clamp_tpl(m_moveScale + g_moveScaleIncrement, g_moveScaleMin, g_moveScaleMax);
}
else if (channelId == InputDeviceGamepad::Button::DD)
{
m_moveScale = clamp_tpl(m_moveScale - g_moveScaleIncrement, g_moveScaleMin, g_moveScaleMax);
}
else if (channelId == InputDeviceGamepad::Trigger::L2)
{
m_moveInput.z = -eventValue;
}
else if (channelId == InputDeviceGamepad::Trigger::R2)
{
m_moveInput.z = eventValue;
}
else if (channelId == InputDeviceGamepad::ThumbStickAxis1D::LX)
{
m_moveInput.x = eventValue;
}
else if (channelId == InputDeviceGamepad::ThumbStickAxis1D::LY)
{
m_moveInput.y = eventValue;
}
else if (channelId == InputDeviceGamepad::ThumbStickAxis1D::RX)
{
m_cameraYawInput = -eventValue * g_gamepadRotationSpeed;
}
else if (channelId == InputDeviceGamepad::ThumbStickAxis1D::RY)
{
m_cameraPitchInput = eventValue * g_gamepadRotationSpeed;
}
//KC: Use the shoulder buttons to temporarily boost or reduce the scale.
else if (channelId == InputDeviceGamepad::Button::L1)
{
if (inputChannel.IsStateEnded())
{
m_moveScale = m_oldMoveScale;
}
else if (inputChannel.IsStateBegan())
{
m_oldMoveScale = m_moveScale;
m_moveScale = clamp_tpl(m_moveScale / g_boostMultiplier, g_moveScaleMin, g_moveScaleMax);
}
}
else if (channelId == InputDeviceGamepad::Button::R1)
{
if (inputChannel.IsStateEnded())
{
m_moveScale = m_oldMoveScale;
}
else if (inputChannel.IsStateBegan())
{
m_oldMoveScale = m_moveScale;
m_moveScale = clamp_tpl(m_moveScale * g_boostMultiplier, g_moveScaleMin, g_moveScaleMax);
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::UpdatePitch(float amount)
{
if (m_isYInverted)
{
amount = -amount;
}
m_cameraPitch += amount;
m_cameraPitch = clamp_tpl(m_cameraPitch, -g_maxPitch, g_maxPitch);
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::UpdateYaw(float amount)
{
m_cameraYaw += amount;
if (m_cameraYaw < 0.0f)
{
m_cameraYaw += 360.0f;
}
else if (m_cameraYaw >= 360.0f)
{
m_cameraYaw -= 360.0f;
}
}
///////////////////////////////////////////////////////////////////////////////
void DebugCamera::UpdatePosition(const Vec3& amount)
{
Vec3 diff = amount * g_moveSpeed * m_moveScale * gEnv->pTimer->GetFrameTime();
MovePosition(diff);
}
void DebugCamera::MovePosition(const Vec3& offset)
{
m_position += m_view.GetColumn0() * offset.x;
m_position += m_view.GetColumn1() * offset.y;
m_position += m_view.GetColumn2() * offset.z;
}
} // namespace LegacyViewSystem
@@ -0,0 +1,78 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/Input/Events/InputChannelEventListener.h>
namespace LegacyViewSystem
{
///////////////////////////////////////////////////////////////////////////////
class DebugCamera
: public AzFramework::InputChannelEventListener
{
public:
enum Mode
{
ModeOff, // no debug cam
ModeFree, // free-fly
ModeFixed, // fixed cam, control goes back to game
};
DebugCamera();
~DebugCamera() override;
void Update();
void PostUpdate();
bool IsEnabled();
bool IsFixed();
bool IsFree();
// AzFramework::InputChannelEventListener
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
void OnEnable();
void OnDisable();
void OnInvertY();
void OnNextMode();
void UpdatePitch(float amount);
void UpdateYaw(float amount);
void UpdatePosition(const Vec3& amount);
void MovePosition(const Vec3& offset);
protected:
int m_mouseMoveMode;
int m_isYInverted;
int m_cameraMode;
float m_cameraYawInput;
float m_cameraPitchInput;
float m_cameraYaw;
float m_cameraPitch;
Vec3 m_moveInput;
float m_moveScale;
float m_oldMoveScale;
Vec3 m_position;
Matrix33 m_view;
};
inline bool DebugCamera::IsEnabled()
{
return m_cameraMode != DebugCamera::ModeOff;
}
inline bool DebugCamera::IsFixed()
{
return m_cameraMode == DebugCamera::ModeFixed;
}
inline bool DebugCamera::IsFree()
{
return m_cameraMode == DebugCamera::ModeFree;
}
} // namespace LegacyViewSystem
+635
View File
@@ -0,0 +1,635 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include <Cry_Camera.h>
#include <HMDBus.h>
#include "View.h"
#include <AzCore/Math/MathUtils.h>
#include <IStereoRenderer.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/Entity.h>
#include <Random.h>
#include <MathConversion.h>
namespace LegacyViewSystem
{
static ICVar* pCamShakeMult = 0;
static ICVar* pHmdReferencePoint = 0;
//------------------------------------------------------------------------
CView::CView(ISystem* pSystem)
: m_pSystem(pSystem)
, m_linkedTo(0)
, m_frameAdditiveAngles(0.0f, 0.0f, 0.0f)
, m_scale(1.0f)
, m_zoomedScale(1.0f)
{
if (!pCamShakeMult)
{
pCamShakeMult = gEnv->pConsole->GetCVar("c_shakeMult");
}
if (!pHmdReferencePoint)
{
pHmdReferencePoint = gEnv->pConsole->GetCVar("hmd_reference_point");
}
}
//------------------------------------------------------------------------
CView::~CView()
{
}
//-----------------------------------------------------------------------
void CView::Release()
{
delete this;
}
//------------------------------------------------------------------------
void CView::Update(float frameTime, bool isActive)
{
//FIXME:some cameras may need to be updated always
if (!isActive)
{
return;
}
if (m_azEntity)
{
m_viewParams.SaveLast();
CCamera* pSysCam = &m_pSystem->GetViewCamera();
//process screen shaking
ProcessShaking(frameTime);
//FIXME:to let the updateView implementation use the correct shakeVector
m_viewParams.currentShakeShift = m_viewParams.rotation * m_viewParams.currentShakeShift;
m_viewParams.frameTime = frameTime;
//update view position/rotation
if (m_azEntity != nullptr)
{
auto entityTransform = m_azEntity->GetTransform();
if (entityTransform != nullptr)
{
AZ::Transform transform = entityTransform->GetWorldTM();
m_viewParams.position = AZVec3ToLYVec3(transform.GetTranslation());
m_viewParams.rotation = AZQuaternionToLYQuaternion(transform.GetRotation());
}
}
ApplyFrameAdditiveAngles(m_viewParams.rotation);
const float fNearZ = gEnv->pSystem->GetIViewSystem()->GetDefaultZNear();
//see if the view have to use a custom near clipping plane
const float nearPlane = (m_viewParams.nearplane >= CAMERA_MIN_NEAR) ? (m_viewParams.nearplane) : fNearZ;
const float farPlane = (m_viewParams.farplane > 0.f) ? m_viewParams.farplane : DEFAULT_FAR;
float fov = (m_viewParams.fov < 0.001f) ? DEFAULT_FOV : m_viewParams.fov;
m_camera.SetFrustum(pSysCam->GetViewSurfaceX(), pSysCam->GetViewSurfaceZ(), fov, nearPlane, farPlane, pSysCam->GetPixelAspectRatio());
//apply shake & set the view matrix
m_viewParams.rotation *= m_viewParams.currentShakeQuat;
m_viewParams.rotation.NormalizeSafe();
m_viewParams.position += m_viewParams.currentShakeShift;
// Blending between cameras needs to happen after Camera space rendering calculations have been applied
// so that the m_viewParams.position is in World Space again
m_viewParams.UpdateBlending(frameTime);
// [VR] specific
// Add HMD's pose tracking on top of current camera pose
// Each game-title can decide whether to keep this functionality here or (most likely)
// move it somewhere else.
Quat q = m_viewParams.rotation;
Vec3 pos = m_viewParams.position;
Vec3 p = Vec3(ZERO);
Matrix34 viewMtx(q);
viewMtx.SetTranslation(pos + p);
m_camera.SetMatrix(viewMtx);
m_camera.SetEntityRotation(m_viewParams.rotation);
m_camera.SetEntityPos(pos);
}
else
{
m_linkedTo = AZ::EntityId(0);
}
}
//-----------------------------------------------------------------------
void CView::ApplyFrameAdditiveAngles(Quat& cameraOrientation)
{
if ((m_frameAdditiveAngles.x != 0.f) || (m_frameAdditiveAngles.y != 0.f) || (m_frameAdditiveAngles.z != 0.f))
{
Ang3 cameraAngles(cameraOrientation);
cameraAngles += m_frameAdditiveAngles;
cameraOrientation.SetRotationXYZ(cameraAngles);
m_frameAdditiveAngles.Set(0.0f, 0.0f, 0.0f);
}
}
//------------------------------------------------------------------------
void CView::SetViewShake(Ang3 shakeAngle, Vec3 shakeShift, float duration, float frequency, float randomness, int shakeID, bool bFlipVec, bool bUpdateOnly, bool bGroundOnly)
{
SShakeParams params;
params.shakeAngle = shakeAngle;
params.shakeShift = shakeShift;
params.frequency = frequency;
params.randomness = randomness;
params.shakeID = shakeID;
params.bFlipVec = bFlipVec;
params.bUpdateOnly = bUpdateOnly;
params.bGroundOnly = bGroundOnly;
params.fadeInDuration = 0; //
params.fadeOutDuration = duration; // originally it was faded out from start. that is why the values are set this way here, to preserve compatibility.
params.sustainDuration = 0; //
SetViewShakeEx(params);
}
//------------------------------------------------------------------------
void CView::SetViewShakeEx(const SShakeParams& params)
{
float shakeMult = GetScale();
if (shakeMult < 0.001f)
{
return;
}
int shakes(m_shakes.size());
SShake* pSetShake(NULL);
for (int i = 0; i < shakes; ++i)
{
SShake* pShake = &m_shakes[i];
if (pShake->ID == params.shakeID)
{
pSetShake = pShake;
break;
}
}
if (!pSetShake)
{
m_shakes.push_back(SShake(params.shakeID));
pSetShake = &m_shakes.back();
}
if (pSetShake)
{
// this can be set dynamically
pSetShake->frequency = max(0.00001f, params.frequency);
// the following are set on a 'new' shake as well
if (params.bUpdateOnly == false)
{
pSetShake->amount = params.shakeAngle * shakeMult;
pSetShake->amountVector = params.shakeShift * shakeMult;
pSetShake->randomness = params.randomness;
pSetShake->doFlip = params.bFlipVec;
pSetShake->groundOnly = params.bGroundOnly;
pSetShake->isSmooth = params.isSmooth;
pSetShake->permanent = params.bPermanent;
pSetShake->fadeInDuration = params.fadeInDuration;
pSetShake->sustainDuration = params.sustainDuration;
pSetShake->fadeOutDuration = params.fadeOutDuration;
pSetShake->timeDone = 0;
pSetShake->updating = true;
pSetShake->interrupted = false;
pSetShake->goalShake = Quat(ZERO);
pSetShake->goalShakeSpeed = Quat(ZERO);
pSetShake->goalShakeVector = Vec3(ZERO);
pSetShake->goalShakeVectorSpeed = Vec3(ZERO);
pSetShake->nextShake = 0.0f;
}
}
}
//------------------------------------------------------------------------
void CView::SetScale(const float scale)
{
CRY_ASSERT_MESSAGE(scale == 1.0f || m_scale == 1.0f, "Attempting to CView::SetScale but has already been set!");
m_scale = scale;
}
void CView::SetZoomedScale(const float scale)
{
CRY_ASSERT_MESSAGE(scale == 1.0f || m_zoomedScale == 1.0f, "Attempting to CView::SetZoomedScale but has already been set!");
m_zoomedScale = scale;
}
//------------------------------------------------------------------------
const float CView::GetScale()
{
float shakeMult(pCamShakeMult->GetFVal());
return m_scale * shakeMult * m_zoomedScale;
}
//------------------------------------------------------------------------
void CView::ProcessShaking(float frameTime)
{
m_viewParams.currentShakeQuat.SetIdentity();
m_viewParams.currentShakeShift.zero();
m_viewParams.shakingRatio = 0;
m_viewParams.groundOnly = false;
int shakes(m_shakes.size());
for (int i = 0; i < shakes; ++i)
{
ProcessShake(&m_shakes[i], frameTime);
}
}
//------------------------------------------------------------------------
void CView::ProcessShake(SShake* pShake, float frameTime)
{
if (!pShake->updating)
{
return;
}
pShake->timeDone += frameTime;
if (pShake->isSmooth)
{
ProcessShakeSmooth(pShake, frameTime);
}
else
{
ProcessShakeNormal(pShake, frameTime);
}
}
//------------------------------------------------------------------------
void CView::ProcessShakeNormal(SShake* pShake, float frameTime)
{
float endSustain = pShake->fadeInDuration + pShake->sustainDuration;
float totalDuration = endSustain + pShake->fadeOutDuration;
bool finalDamping = (!pShake->permanent && pShake->timeDone > totalDuration) || (pShake->interrupted && pShake->ratio < 0.05f);
if (finalDamping)
{
ProcessShakeNormal_FinalDamping(pShake, frameTime);
}
else
{
ProcessShakeNormal_CalcRatio(pShake, frameTime, endSustain);
ProcessShakeNormal_DoShaking(pShake, frameTime);
//for the global shaking ratio keep the biggest
if (pShake->groundOnly)
{
m_viewParams.groundOnly = true;
}
m_viewParams.shakingRatio = max(m_viewParams.shakingRatio, pShake->ratio);
m_viewParams.currentShakeQuat *= pShake->shakeQuat;
m_viewParams.currentShakeShift += pShake->shakeVector;
}
}
//////////////////////////////////////////////////////////////////////////
void CView::ProcessShakeSmooth(SShake* pShake, float frameTime)
{
assert(pShake->timeDone >= 0);
float endTimeFadeIn = pShake->fadeInDuration;
float endTimeSustain = pShake->sustainDuration + endTimeFadeIn;
float totalTime = endTimeSustain + pShake->fadeOutDuration;
if (pShake->interrupted && endTimeFadeIn <= pShake->timeDone && pShake->timeDone < endTimeSustain)
{
pShake->timeDone = endTimeSustain;
}
float damping = 1.f;
if (pShake->timeDone < endTimeFadeIn)
{
damping = pShake->timeDone / endTimeFadeIn;
}
else if (endTimeSustain < pShake->timeDone && pShake->timeDone < totalTime)
{
damping = (totalTime - pShake->timeDone) / (totalTime - endTimeSustain);
}
else if (totalTime <= pShake->timeDone)
{
pShake->shakeQuat.SetIdentity();
pShake->shakeVector.zero();
pShake->ratio = 0.0f;
pShake->nextShake = 0.0f;
pShake->flip = false;
pShake->updating = false;
return;
}
ProcessShakeSmooth_DoShaking(pShake, frameTime);
if (pShake->groundOnly)
{
m_viewParams.groundOnly = true;
}
pShake->ratio = (3.f - 2.f * damping) * damping * damping; // smooth ration change
m_viewParams.shakingRatio = max(m_viewParams.shakingRatio, pShake->ratio);
m_viewParams.currentShakeQuat *= Quat::CreateSlerp(IDENTITY, pShake->shakeQuat, pShake->ratio);
m_viewParams.currentShakeShift += Vec3::CreateLerp(ZERO, pShake->shakeVector, pShake->ratio);
}
//////////////////////////////////////////////////////////////////////////
void CView::GetRandomQuat(Quat& quat, SShake* pShake)
{
quat.SetRotationXYZ(pShake->amount);
float randomAmt(pShake->randomness);
float len(fabs(pShake->amount.x) + fabs(pShake->amount.y) + fabs(pShake->amount.z));
len /= 3.f;
float r = len * randomAmt;
quat *= Quat::CreateRotationXYZ(Ang3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r)));
}
//////////////////////////////////////////////////////////////////////////
void CView::GetRandomVector(Vec3& vec, SShake* pShake)
{
vec = pShake->amountVector;
float randomAmt(pShake->randomness);
float len = fabs(pShake->amountVector.x) + fabs(pShake->amountVector.y) + fabs(pShake->amountVector.z);
len /= 3.f;
float r = len * randomAmt;
vec += Vec3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r));
}
//////////////////////////////////////////////////////////////////////////
void CView::CubeInterpolateQuat(float t, SShake* pShake)
{
Quat p0 = pShake->startShake;
Quat p1 = pShake->goalShake;
Quat v0 = pShake->startShakeSpeed * 0.5f;
Quat v1 = pShake->goalShakeSpeed * 0.5f;
pShake->shakeQuat = (((p0 * 2.f + p1 * -2.f + v0 + v1) * t
+ (p0 * -3.f + p1 * 3.f + v0 * -2.f - v1)) * t
+ (v0)) * t
+ p0;
pShake->shakeQuat.Normalize();
}
//////////////////////////////////////////////////////////////////////////
void CView::CubeInterpolateVector(float t, SShake* pShake)
{
Vec3 p0 = pShake->startShakeVector;
Vec3 p1 = pShake->goalShakeVector;
Vec3 v0 = pShake->startShakeVectorSpeed * 0.8f;
Vec3 v1 = pShake->goalShakeVectorSpeed * 0.8f;
pShake->shakeVector = (((p0 * 2.f + p1 * -2.f + v0 + v1) * t
+ (p0 * -3.f + p1 * 3.f + v0 * -2.f - v1)) * t
+ (v0)) * t
+ p0;
}
//////////////////////////////////////////////////////////////////////////
void CView::ProcessShakeSmooth_DoShaking(SShake* pShake, float frameTime)
{
if (pShake->nextShake <= 0.0f)
{
pShake->nextShake = pShake->frequency;
pShake->startShake = pShake->goalShake;
pShake->startShakeSpeed = pShake->goalShakeSpeed;
pShake->startShakeVector = pShake->goalShakeVector;
pShake->startShakeVectorSpeed = pShake->goalShakeVectorSpeed;
GetRandomQuat(pShake->goalShake, pShake);
GetRandomQuat(pShake->goalShakeSpeed, pShake);
GetRandomVector(pShake->goalShakeVector, pShake);
GetRandomVector(pShake->goalShakeVectorSpeed, pShake);
if (pShake->flip)
{
pShake->goalShake.Invert();
pShake->goalShakeSpeed.Invert();
pShake->goalShakeVector = -pShake->goalShakeVector;
pShake->goalShakeVectorSpeed = -pShake->goalShakeVectorSpeed;
}
if (pShake->doFlip)
{
pShake->flip = !pShake->flip;
}
}
pShake->nextShake -= frameTime;
float t = (pShake->frequency - pShake->nextShake) / pShake->frequency;
CubeInterpolateQuat(t, pShake);
CubeInterpolateVector(t, pShake);
}
//////////////////////////////////////////////////////////////////////////
void CView::ProcessShakeNormal_FinalDamping(SShake* pShake, float frameTime)
{
pShake->shakeQuat = Quat::CreateSlerp(pShake->shakeQuat, IDENTITY, frameTime * 5.0f);
m_viewParams.currentShakeQuat *= pShake->shakeQuat;
pShake->shakeVector = Vec3::CreateLerp(pShake->shakeVector, ZERO, frameTime * 5.0f);
m_viewParams.currentShakeShift += pShake->shakeVector;
float svlen2(pShake->shakeVector.len2());
bool quatIsIdentity(Quat::IsEquivalent(IDENTITY, pShake->shakeQuat, 0.0001f));
if (quatIsIdentity && svlen2 < 0.01f)
{
pShake->shakeQuat.SetIdentity();
pShake->shakeVector.zero();
pShake->ratio = 0.0f;
pShake->nextShake = 0.0f;
pShake->flip = false;
pShake->updating = false;
}
}
// "ratio" is the amplitude of the shaking
void CView::ProcessShakeNormal_CalcRatio(SShake* pShake, float frameTime, float endSustain)
{
const float FADEOUT_TIME_WHEN_INTERRUPTED = 0.5f;
if (pShake->interrupted)
{
pShake->ratio = max(0.f, pShake->ratio - (frameTime / FADEOUT_TIME_WHEN_INTERRUPTED)); // fadeout after interrupted
}
else
if (pShake->timeDone >= endSustain && pShake->fadeOutDuration > 0)
{
float timeFading = pShake->timeDone - endSustain;
pShake->ratio = clamp_tpl(1.f - timeFading / pShake->fadeOutDuration, 0.f, 1.f); // fadeOut
}
else
if (pShake->timeDone >= pShake->fadeInDuration)
{
pShake->ratio = 1.f; // sustain
}
else
{
pShake->ratio = min(1.f, pShake->timeDone / pShake->fadeInDuration); // fadeIn
}
if (pShake->permanent && pShake->timeDone >= pShake->fadeInDuration && !pShake->interrupted)
{
pShake->ratio = 1.f; // permanent standing
}
}
//////////////////////////////////////////////////////////////////////////
void CView::ProcessShakeNormal_DoShaking(SShake* pShake, float frameTime)
{
float t;
if (pShake->nextShake <= 0.0f)
{
//angular
pShake->goalShake.SetRotationXYZ(pShake->amount);
if (pShake->flip)
{
pShake->goalShake.Invert();
}
//translational
pShake->goalShakeVector = pShake->amountVector;
if (pShake->flip)
{
pShake->goalShakeVector = -pShake->goalShakeVector;
}
if (pShake->doFlip)
{
pShake->flip = !pShake->flip;
}
//randomize it a little
float randomAmt(pShake->randomness);
float len(fabs(pShake->amount.x) + fabs(pShake->amount.y) + fabs(pShake->amount.z));
len /= 3.0f;
float r = len * randomAmt;
pShake->goalShake *= Quat::CreateRotationXYZ(Ang3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r)));
//translational randomization
len = fabs(pShake->amountVector.x) + fabs(pShake->amountVector.y) + fabs(pShake->amountVector.z);
len /= 3.0f;
r = len * randomAmt;
pShake->goalShakeVector += Vec3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r));
//damp & bounce it in a non linear fashion
t = 1.0f - (pShake->ratio * pShake->ratio);
pShake->goalShake = Quat::CreateSlerp(pShake->goalShake, IDENTITY, t);
pShake->goalShakeVector = Vec3::CreateLerp(pShake->goalShakeVector, ZERO, t);
pShake->nextShake = pShake->frequency;
}
pShake->nextShake = max(0.0f, pShake->nextShake - frameTime);
t = min(1.0f, frameTime * (1.0f / pShake->frequency));
pShake->shakeQuat = Quat::CreateSlerp(pShake->shakeQuat, pShake->goalShake, t);
pShake->shakeQuat.Normalize();
pShake->shakeVector = Vec3::CreateLerp(pShake->shakeVector, pShake->goalShakeVector, t);
}
//------------------------------------------------------------------------
void CView::StopShake(int shakeID)
{
uint32 num = m_shakes.size();
for (uint32 i = 0; i < num; ++i)
{
if (m_shakes[i].ID == shakeID && m_shakes[i].updating)
{
m_shakes[i].interrupted = true;
}
}
}
//------------------------------------------------------------------------
void CView::ResetShaking()
{
// disable shakes
std::vector<SShake>::iterator iter = m_shakes.begin();
std::vector<SShake>::iterator iterEnd = m_shakes.end();
while (iter != iterEnd)
{
SShake& shake = *iter;
shake.updating = false;
shake.timeDone = 0;
++iter;
}
}
//------------------------------------------------------------------------
void CView::LinkTo(AZ::Entity* follow)
{
CRY_ASSERT(follow);
m_azEntity = follow;
m_linkedTo = follow->GetId();
m_viewParams.targetPos = Vec3();// This should be quickly overwritten by the camera's acutal position from its matrix
}
//------------------------------------------------------------------------
void CView::Unlink()
{
m_azEntity = nullptr;
m_linkedTo.SetInvalid();
m_viewParams.targetPos = Vec3();
}
//------------------------------------------------------------------------
void CView::SetFrameAdditiveCameraAngles(const Ang3& addFrameAngles)
{
m_frameAdditiveAngles = addFrameAngles;
}
void CView::GetMemoryUsage(ICrySizer* s) const
{
s->AddObject(this, sizeof(*this));
s->AddObject(m_shakes);
}
void CView::Serialize(TSerialize ser)
{
if (ser.IsReading())
{
ResetShaking();
}
}
void CView::PostSerialize()
{
}
//////////////////////////////////////////////////////////////////////////
void CView::UpdateAudioListener([[maybe_unused]] Matrix34 const& rMatrix)
{
}
//////////////////////////////////////////////////////////////////////////
void CView::SetActive([[maybe_unused]] bool const bActive)
{
}
} // namespace LegacyViewSystem
+156
View File
@@ -0,0 +1,156 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : View System interfaces.
# pragma once
#include "IViewSystem.h"
#include <Cry_Camera.h>
class CGameObject;
namespace LegacyViewSystem
{
class CView
: public IView
{
public:
CView(ISystem* pSystem);
virtual ~CView();
//shaking
struct SShake
{
bool updating;
bool flip;
bool doFlip;
bool groundOnly;
bool permanent;
bool interrupted; // when forcefully stopped
bool isSmooth;
int ID;
float nextShake;
float timeDone;
float sustainDuration;
float fadeInDuration;
float fadeOutDuration;
float frequency;
float ratio;
float randomness;
Quat startShake;
Quat startShakeSpeed;
Vec3 startShakeVector;
Vec3 startShakeVectorSpeed;
Quat goalShake;
Quat goalShakeSpeed;
Vec3 goalShakeVector;
Vec3 goalShakeVectorSpeed;
Ang3 amount;
Vec3 amountVector;
Quat shakeQuat;
Vec3 shakeVector;
SShake(int shakeID)
{
memset(this, 0, sizeof(SShake));
startShake.SetIdentity();
startShakeSpeed.SetIdentity();
goalShake.SetIdentity();
shakeQuat.SetIdentity();
randomness = 0.5f;
ID = shakeID;
}
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const { /*nothing*/}
};
// IView
virtual void Release();
virtual void Update(float frameTime, bool isActive);
virtual void ProcessShaking(float frameTime);
virtual void ProcessShake(SShake* pShake, float frameTime);
virtual void ResetShaking();
virtual void ResetBlending() { m_viewParams.ResetBlending(); }
virtual void LinkTo(AZ::Entity* follow);
virtual void Unlink();
virtual AZ::EntityId GetLinkedId() {return m_linkedTo; };
virtual void SetCurrentParams(SViewParams& params) { m_viewParams = params; };
virtual const SViewParams* GetCurrentParams() {return &m_viewParams; }
virtual void SetViewShake(Ang3 shakeAngle, Vec3 shakeShift, float duration, float frequency, float randomness, int shakeID, bool bFlipVec = true, bool bUpdateOnly = false, bool bGroundOnly = false);
virtual void SetViewShakeEx(const SShakeParams& params);
virtual void StopShake(int shakeID);
virtual void SetFrameAdditiveCameraAngles(const Ang3& addFrameAngles);
virtual void SetScale(const float scale);
virtual void SetZoomedScale(const float scale);
virtual void SetActive(const bool bActive);
// ~IView
void Serialize(TSerialize ser) override;
void PostSerialize() override;
CCamera& GetCamera() override { return m_camera; }
const CCamera& GetCamera() const override { return m_camera; }
void UpdateAudioListener(const Matrix34& rMatrix) override;
void GetMemoryUsage(ICrySizer* s) const;
protected:
void ProcessShakeNormal(SShake* pShake, float frameTime);
void ProcessShakeNormal_FinalDamping(SShake* pShake, float frameTime);
void ProcessShakeNormal_CalcRatio(SShake* pShake, float frameTime, float endSustain);
void ProcessShakeNormal_DoShaking(SShake* pShake, float frameTime);
void ProcessShakeSmooth(SShake* pShake, float frameTime);
void ProcessShakeSmooth_DoShaking(SShake* pShake, float frameTime);
void ApplyFrameAdditiveAngles(Quat& cameraOrientation);
const float GetScale();
private:
void GetRandomQuat(Quat& quat, SShake* pShake);
void GetRandomVector(Vec3& vec3, SShake* pShake);
void CubeInterpolateQuat(float t, SShake* pShake);
void CubeInterpolateVector(float t, SShake* pShake);
protected:
bool m_active;
AZ::EntityId m_linkedTo;
AZ::Entity* m_azEntity = nullptr;
SViewParams m_viewParams;
CCamera m_camera;
ISystem* m_pSystem;
std::vector<SShake> m_shakes;
Ang3 m_frameAdditiveAngles; // Used mainly for cinematics, where the game can slightly override camera orientation
float m_scale;
float m_zoomedScale;
};
} // namespace LegacyViewSystem
@@ -0,0 +1,684 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <Cry_Camera.h>
#include <ILevelSystem.h>
#include "ViewSystem.h"
#include "PNoise3.h"
#include "DebugCamera.h"
#include <MathConversion.h>
#include <AzCore/Casting/lossy_cast.h>
#define VS_CALL_LISTENERS(func) \
{ \
size_t count = m_listeners.size(); \
if (count > 0) \
{ \
const size_t memSize = count * sizeof(IViewSystemListener*); \
PREFAST_SUPPRESS_WARNING(6255) IViewSystemListener * *pArray = (IViewSystemListener**) alloca(memSize); \
memcpy(pArray, &*m_listeners.begin(), memSize); \
while (count--) \
{ \
(*pArray)->func; ++pArray; \
} \
} \
}
namespace LegacyViewSystem
{
void ToggleDebugCamera([[maybe_unused]] IConsoleCmdArgs* pArgs)
{
#if !defined(_RELEASE)
if (!gEnv->IsDedicated())
{
DebugCamera* debugCamera = CViewSystem::s_debugCamera;
if (debugCamera)
{
if (!debugCamera->IsEnabled())
{
debugCamera->OnEnable();
}
else
{
debugCamera->OnNextMode();
}
}
}
#endif
}
void ToggleDebugCameraInvertY([[maybe_unused]] IConsoleCmdArgs* pArgs)
{
#if !defined(_RELEASE)
if (!gEnv->IsDedicated())
{
DebugCamera* debugCamera = CViewSystem::s_debugCamera;
if (debugCamera)
{
debugCamera->OnInvertY();
}
}
#endif
}
void DebugCameraMove([[maybe_unused]] IConsoleCmdArgs* pArgs)
{
#if !defined(_RELEASE)
if (!gEnv->IsDedicated())
{
if (pArgs->GetArgCount() != 4)
{
CryLogAlways("debugCameraMove requires 3 args, not %d.", pArgs->GetArgCount() - 1);
return;
}
DebugCamera* debugCamera = CViewSystem::s_debugCamera;
if (debugCamera && debugCamera->IsFree())
{
Vec3::value_type x = azlossy_cast<float>(atof(pArgs->GetArg(1)));
Vec3::value_type y = azlossy_cast<float>(atof(pArgs->GetArg(2)));
Vec3::value_type z = azlossy_cast<float>(atof(pArgs->GetArg(3)));
Vec3 newPos(x, y, z);
debugCamera->MovePosition(newPos);
}
}
#endif
}
DebugCamera* CViewSystem::s_debugCamera = nullptr;
//------------------------------------------------------------------------
CViewSystem::CViewSystem(ISystem* pSystem)
: m_pSystem(pSystem)
, m_activeViewId(0)
, m_nextViewIdToAssign(1000)
, m_preSequenceViewId(0)
, m_cutsceneViewId(0)
, m_cutsceneCount(0)
, m_bOverridenCameraRotation(false)
, m_bActiveViewFromSequence(false)
, m_fBlendInPosSpeed(0.0f)
, m_fBlendInRotSpeed(0.0f)
, m_bPerformBlendOut(false)
, m_useDeferredViewSystemUpdate(false)
, m_bControlsAudioListeners(true)
{
#if !defined(_RELEASE) && !defined(DEDICATED_SERVER)
if (!s_debugCamera)
{
s_debugCamera = new DebugCamera;
}
REGISTER_COMMAND("debugCameraToggle", ToggleDebugCamera, VF_DEV_ONLY, "Toggle the debug camera.\n");
REGISTER_COMMAND("debugCameraInvertY", ToggleDebugCameraInvertY, VF_DEV_ONLY, "Toggle debug camera Y-axis inversion.\n");
REGISTER_COMMAND("debugCameraMove", DebugCameraMove, VF_DEV_ONLY, "Move the debug camera the specified distance (x y z).\n");
gEnv->pConsole->CreateKeyBind("ctrl_keyboard_key_punctuation_backslash", "debugCameraToggle");
gEnv->pConsole->CreateKeyBind("alt_keyboard_key_punctuation_backslash", "debugCameraInvertY");
#endif
REGISTER_CVAR2("cl_camera_noise", &m_fCameraNoise, -1, 0,
"Adds hand-held like camera noise to the camera view. \n The higher the value, the higher the noise.\n A value <= 0 disables it.");
REGISTER_CVAR2("cl_camera_noise_freq", &m_fCameraNoiseFrequency, 2.5326173f, 0,
"Defines camera noise frequency for the camera view. \n The higher the value, the higher the noise.");
REGISTER_CVAR2("cl_ViewSystemDebug", &m_nViewSystemDebug, 0, VF_CHEAT,
"Sets Debug information of the ViewSystem.");
REGISTER_CVAR2("cl_DefaultNearPlane", &m_fDefaultCameraNearZ, DEFAULT_NEAR, VF_CHEAT,
"The default camera near plane. ");
//Register as level system listener
if (m_pSystem->GetILevelSystem())
{
m_pSystem->GetILevelSystem()->AddListener(this);
}
Camera::CameraSystemRequestBus::Handler::BusConnect();
}
//------------------------------------------------------------------------
CViewSystem::~CViewSystem()
{
Camera::CameraSystemRequestBus::Handler::BusDisconnect();
ClearAllViews();
IConsole* pConsole = gEnv->pConsole;
CRY_ASSERT(pConsole);
pConsole->UnregisterVariable("cl_camera_noise", true);
pConsole->UnregisterVariable("cl_camera_noise_freq", true);
pConsole->UnregisterVariable("cl_ViewSystemDebug", true);
pConsole->UnregisterVariable("cl_DefaultNearPlane", true);
//Remove as level system listener
if (m_pSystem->GetILevelSystem())
{
m_pSystem->GetILevelSystem()->RemoveListener(this);
}
}
//------------------------------------------------------------------------
void CViewSystem::Update(float frameTime)
{
FUNCTION_PROFILER(GetISystem(), PROFILE_ACTION);
if (gEnv->IsDedicated())
{
return;
}
if (s_debugCamera)
{
s_debugCamera->Update();
}
CView* const pActiveView = static_cast<CView*>(GetActiveView());
TViewMap::const_iterator Iter(m_views.begin());
TViewMap::const_iterator const IterEnd(m_views.end());
for (; Iter != IterEnd; ++Iter)
{
IView* const pView = Iter->second;
bool const bIsActive = (pView == pActiveView);
pView->Update(frameTime, bIsActive);
if (bIsActive)
{
CCamera& rCamera = pView->GetCamera();
if (!s_debugCamera || !s_debugCamera->IsEnabled())
{
pView->UpdateAudioListener(rCamera.GetMatrix());
}
if (const SViewParams* currentParams = pView->GetCurrentParams())
{
SViewParams copyCurrentParams = *currentParams;
rCamera.SetJustActivated(copyCurrentParams.justActivated);
copyCurrentParams.justActivated = false;
pView->SetCurrentParams(copyCurrentParams);
}
if (m_bOverridenCameraRotation)
{
// When camera rotation is overridden.
Vec3 pos = rCamera.GetMatrix().GetTranslation();
Matrix34 camTM(m_overridenCameraRotation);
camTM.SetTranslation(pos);
rCamera.SetMatrix(camTM);
}
else
{
// Normal setting of the camera
if (m_fCameraNoise > 0)
{
Matrix33 m = Matrix33(rCamera.GetMatrix());
m.OrthonormalizeFast();
Ang3 aAng1 = Ang3::GetAnglesXYZ(m);
//Ang3 aAng2 = RAD2DEG(aAng1);
Matrix34 camTM = rCamera.GetMatrix();
Vec3 pos = camTM.GetTranslation();
camTM.SetIdentity();
const float fScale = 0.1f;
CPNoise3* pNoise = m_pSystem->GetNoiseGen();
float fRes = pNoise->Noise1D(gEnv->pTimer->GetCurrTime() * m_fCameraNoiseFrequency);
aAng1.x += fRes * m_fCameraNoise * fScale;
pos.z -= fRes * m_fCameraNoise * fScale;
fRes = pNoise->Noise1D(17 + gEnv->pTimer->GetCurrTime() * m_fCameraNoiseFrequency);
aAng1.y -= fRes * m_fCameraNoise * fScale;
//aAng1.z+=fRes*0.025f; // left / right movement should be much less visible
camTM.SetRotationXYZ(aAng1);
camTM.SetTranslation(pos);
rCamera.SetMatrix(camTM);
}
}
m_pSystem->SetViewCamera(rCamera);
}
}
if (s_debugCamera)
{
s_debugCamera->PostUpdate();
}
// Display debug info on screen
if (m_nViewSystemDebug)
{
DebugDraw();
}
}
//------------------------------------------------------------------------
IView* CViewSystem::CreateView()
{
CView* newView = new CView(m_pSystem);
if (newView)
{
AddView(newView);
}
return newView;
}
unsigned int CViewSystem::AddView(IView* pView)
{
assert(pView);
m_views.insert(TViewMap::value_type(m_nextViewIdToAssign, pView));
return m_nextViewIdToAssign++;
}
void CViewSystem::RemoveView(IView* pView)
{
RemoveViewById(GetViewId(pView));
}
void CViewSystem::RemoveView(unsigned int viewId)
{
RemoveViewById(viewId);
}
void CViewSystem::RemoveViewById(unsigned int viewId)
{
TViewMap::iterator iter = m_views.find(viewId);
if (iter != m_views.end())
{
if (viewId == m_activeViewId)
{
m_activeViewId = 0;
}
if (viewId == m_preSequenceViewId)
{
m_preSequenceViewId = 0;
}
SAFE_RELEASE(iter->second);
m_views.erase(iter);
}
}
//------------------------------------------------------------------------
void CViewSystem::SetActiveView(IView* pView)
{
if (pView != NULL)
{
IView* const pPrevView = GetView(m_activeViewId);
if (pPrevView != pView)
{
if (pPrevView != NULL)
{
pPrevView->SetActive(false);
}
pView->SetActive(true);
m_activeViewId = GetViewId(pView);
}
}
else
{
m_activeViewId = ~0;
}
m_bActiveViewFromSequence = false;
}
//------------------------------------------------------------------------
void CViewSystem::SetActiveView(unsigned int viewId)
{
IView* const pPrevView = GetView(m_activeViewId);
if (pPrevView != NULL)
{
pPrevView->SetActive(false);
}
IView* const pView = GetView(viewId);
if (pView != NULL)
{
pView->SetActive(true);
m_activeViewId = viewId;
m_bActiveViewFromSequence = false;
}
}
//------------------------------------------------------------------------
IView* CViewSystem::GetView(unsigned int viewId)
{
TViewMap::iterator it = m_views.find(viewId);
if (it != m_views.end())
{
return it->second;
}
return NULL;
}
//------------------------------------------------------------------------
IView* CViewSystem::GetActiveView()
{
return GetView(m_activeViewId);
}
//------------------------------------------------------------------------
unsigned int CViewSystem::GetViewId(IView* pView)
{
for (TViewMap::iterator it = m_views.begin(); it != m_views.end(); ++it)
{
IView* tView = it->second;
if (tView == pView)
{
return it->first;
}
}
return 0;
}
//------------------------------------------------------------------------
unsigned int CViewSystem::GetActiveViewId()
{
// cutscene can override the games id of the active view
if (m_cutsceneCount && m_cutsceneViewId)
{
return m_cutsceneViewId;
}
return m_activeViewId;
}
//------------------------------------------------------------------------
IView* CViewSystem::GetViewByEntityId(const AZ::EntityId& id, bool forceCreate)
{
for (TViewMap::iterator it = m_views.begin(); it != m_views.end(); ++it)
{
IView* tView = it->second;
if (tView && tView->GetLinkedId() == id)
{
return tView;
}
}
if (forceCreate)
{
// Component Camera
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, id);
if (entity)
{
if (IView* pNew = CreateView())
{
pNew->LinkTo(entity);
return pNew;
}
}
}
return nullptr;
}
//------------------------------------------------------------------------
void CViewSystem::SetActiveCamera(const SCameraParams& params)
{
IView* pView = NULL;
if (params.cameraEntityId.IsValid())
{
pView = GetViewByEntityId(params.cameraEntityId, true);
if (pView)
{
SViewParams viewParams = *pView->GetCurrentParams();
viewParams.fov = params.fov;
viewParams.nearplane = params.nearZ;
if (m_bActiveViewFromSequence == false && m_preSequenceViewId == 0)
{
m_preSequenceViewId = m_activeViewId;
IView* pPrevView = GetView(m_activeViewId);
if (pPrevView && m_fBlendInPosSpeed > 0.0f && m_fBlendInRotSpeed > 0.0f)
{
viewParams.blendPosSpeed = m_fBlendInPosSpeed;
viewParams.blendRotSpeed = m_fBlendInRotSpeed;
viewParams.BlendFrom(*pPrevView->GetCurrentParams());
}
}
if (m_activeViewId != GetViewId(pView) && params.justActivated)
{
viewParams.justActivated = true;
}
pView->SetCurrentParams(viewParams);
// make this one the active view
SetActiveView(pView);
m_bActiveViewFromSequence = true;
}
}
else
{
if (m_preSequenceViewId != 0)
{
// Restore m_preSequenceViewId view
IView* pActiveView = GetView(m_activeViewId);
IView* pNewView = GetView(m_preSequenceViewId);
if (pActiveView && pNewView && m_bPerformBlendOut)
{
SViewParams activeViewParams = *pActiveView->GetCurrentParams();
SViewParams newViewParams = *pNewView->GetCurrentParams();
newViewParams.BlendFrom(activeViewParams);
newViewParams.blendPosSpeed = activeViewParams.blendPosSpeed;
newViewParams.blendRotSpeed = activeViewParams.blendRotSpeed;
if (m_activeViewId != m_preSequenceViewId && params.justActivated)
{
newViewParams.justActivated = true;
}
pNewView->SetCurrentParams(newViewParams);
SetActiveView(m_preSequenceViewId);
}
else if (pActiveView && m_activeViewId != m_preSequenceViewId && params.justActivated)
{
SViewParams activeViewParams = *pActiveView->GetCurrentParams();
activeViewParams.justActivated = true;
if (pNewView)
{
pNewView->SetCurrentParams(activeViewParams);
SetActiveView(m_preSequenceViewId);
}
}
m_preSequenceViewId = 0;
m_bActiveViewFromSequence = false;
}
}
m_cutsceneViewId = GetViewId(pView);
VS_CALL_LISTENERS(OnCameraChange(params));
}
//------------------------------------------------------------------------
void CViewSystem::BeginCutScene(IAnimSequence* pSeq, [[maybe_unused]] unsigned long dwFlags, bool bResetFX)
{
m_cutsceneCount++;
VS_CALL_LISTENERS(OnBeginCutScene(pSeq, bResetFX));
}
//------------------------------------------------------------------------
void CViewSystem::EndCutScene(IAnimSequence* pSeq, [[maybe_unused]] unsigned long dwFlags)
{
m_cutsceneCount -= (m_cutsceneCount > 0);
ClearCutsceneViews();
VS_CALL_LISTENERS(OnEndCutScene(pSeq));
}
void CViewSystem::SendGlobalEvent([[maybe_unused]] const char* pszEvent)
{
// TODO: broadcast to script system
}
//////////////////////////////////////////////////////////////////////////
void CViewSystem::SetOverrideCameraRotation(bool bOverride, Quat rotation)
{
m_bOverridenCameraRotation = bOverride;
m_overridenCameraRotation = rotation;
}
//////////////////////////////////////////////////////////////////////////
void CViewSystem::UpdateSoundListeners()
{
assert(gEnv->IsEditor() && !gEnv->IsEditorGameMode());
// In Editor we may want to control global listeners outside of the game view.
if (m_bControlsAudioListeners)
{
IView* const pActiveView = static_cast<IView*>(GetActiveView());
TViewMap::const_iterator Iter(m_views.begin());
TViewMap::const_iterator const IterEnd(m_views.end());
for (; Iter != IterEnd; ++Iter)
{
IView* const pView = Iter->second;
bool const bIsActive = (pView == pActiveView);
CCamera const& rCamera = bIsActive ? gEnv->pSystem->GetViewCamera() : pView->GetCamera();
pView->UpdateAudioListener(rCamera.GetMatrix());
}
}
}
//////////////////////////////////////////////////////////////////
void CViewSystem::OnLoadingStart([[maybe_unused]] const char* levelName)
{
//If the level is being restarted (IsSerializingFile() == 1)
//views should not be cleared, because the main view (player one) won't be recreated in this case
//Views will only be cleared when loading a new map, or loading a saved game (IsSerizlizingFile() == 2)
bool shouldClearViews = gEnv->pSystem ? (gEnv->pSystem->IsSerializingFile() != 1) : false;
if (shouldClearViews)
{
ClearAllViews();
}
}
/////////////////////////////////////////////////////////////////////
void CViewSystem::OnUnloadComplete([[maybe_unused]] const char* levelName)
{
bool shouldClearViews = gEnv->pSystem ? (gEnv->pSystem->IsSerializingFile() != 1) : false;
if (shouldClearViews)
{
ClearAllViews();
}
assert(m_listeners.empty());
stl::free_container(m_listeners);
}
/////////////////////////////////////////////////////////////////////
void CViewSystem::ClearCutsceneViews()
{
//First switch to previous camera if available
//In practice, the camera should be already restored before reaching this point, but just in case.
if (m_preSequenceViewId != 0)
{
SCameraParams camParams;
camParams.cameraEntityId.SetInvalid(); //Setting to invalid will try to switch to previous camera
camParams.fov = 60.0f;
camParams.nearZ = DEFAULT_NEAR;
camParams.justActivated = true;
SetActiveCamera(camParams);
}
}
///////////////////////////////////////////
void CViewSystem::ClearAllViews()
{
TViewMap::iterator end = m_views.end();
for (TViewMap::iterator it = m_views.begin(); it != end; ++it)
{
SAFE_RELEASE(it->second);
}
stl::free_container(m_views);
m_preSequenceViewId = 0;
m_activeViewId = 0;
}
////////////////////////////////////////////////////////////////////
void CViewSystem::DebugDraw()
{
}
//////////////////////////////////////////////////////////////////////////
void CViewSystem::GetMemoryUsage(ICrySizer* s) const
{
SIZER_SUBCOMPONENT_NAME(s, "ViewSystem");
s->Add(*this);
s->AddContainer(m_views);
}
void CViewSystem::Serialize(TSerialize ser)
{
TViewMap::iterator iter = m_views.begin();
TViewMap::iterator iterEnd = m_views.end();
while (iter != iterEnd)
{
iter->second->Serialize(ser);
++iter;
}
}
void CViewSystem::PostSerialize()
{
TViewMap::iterator iter = m_views.begin();
TViewMap::iterator iterEnd = m_views.end();
while (iter != iterEnd)
{
iter->second->PostSerialize();
++iter;
}
}
///////////////////////////////////////////////////////////////////////////
void CViewSystem::SetControlAudioListeners(bool bActive)
{
m_bControlsAudioListeners = bActive;
TViewMap::const_iterator Iter(m_views.begin());
TViewMap::const_iterator const IterEnd(m_views.end());
for (; Iter != IterEnd; ++Iter)
{
Iter->second->SetActive(bActive);
}
}
} // namespace LegacyViewSystem
@@ -0,0 +1,155 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : View System interfaces.
#pragma once
#include "View.h"
#include "IMovieSystem.h"
#include <ILevelSystem.h>
#include <AzFramework/Components/CameraBus.h>
namespace LegacyViewSystem
{
class DebugCamera;
class CViewSystem
: public IViewSystem
, public IMovieUser
, public ILevelSystemListener
, public Camera::CameraSystemRequestBus::Handler
{
private:
typedef std::map<unsigned int, IView*> TViewMap;
typedef std::vector<unsigned int> TViewIdVector;
public:
//IViewSystem
virtual IView* CreateView();
virtual unsigned int AddView(IView* pView) override;
virtual void RemoveView(IView* pView);
virtual void RemoveView(unsigned int viewId);
virtual void SetActiveView(IView* pView);
virtual void SetActiveView(unsigned int viewId);
//CameraSystemRequestBus
AZ::EntityId GetActiveCamera() override { return m_activeViewId ? GetActiveView()->GetLinkedId() : AZ::EntityId(); }
//utility functions
virtual IView* GetView(unsigned int viewId);
virtual IView* GetActiveView();
virtual unsigned int GetViewId(IView* pView);
virtual unsigned int GetActiveViewId();
virtual void Serialize(TSerialize ser);
virtual void PostSerialize();
virtual IView* GetViewByEntityId(const AZ::EntityId& id, bool forceCreate);
virtual float GetDefaultZNear() { return m_fDefaultCameraNearZ; };
virtual void SetBlendParams(float fBlendPosSpeed, float fBlendRotSpeed, bool performBlendOut) { m_fBlendInPosSpeed = fBlendPosSpeed; m_fBlendInRotSpeed = fBlendRotSpeed; m_bPerformBlendOut = performBlendOut; };
virtual void SetOverrideCameraRotation(bool bOverride, Quat rotation);
virtual bool IsPlayingCutScene() const
{
return m_cutsceneCount > 0;
}
virtual void UpdateSoundListeners();
virtual void SetDeferredViewSystemUpdate(bool const bDeferred){ m_useDeferredViewSystemUpdate = bDeferred; }
virtual bool UseDeferredViewSystemUpdate() const { return m_useDeferredViewSystemUpdate; }
virtual void SetControlAudioListeners(bool const bActive);
//~IViewSystem
//IMovieUser
virtual void SetActiveCamera(const SCameraParams& Params);
virtual void BeginCutScene(IAnimSequence* pSeq, unsigned long dwFlags, bool bResetFX);
virtual void EndCutScene(IAnimSequence* pSeq, unsigned long dwFlags);
virtual void SendGlobalEvent(const char* pszEvent);
//~IMovieUser
// ILevelSystemListener
virtual void OnLevelNotFound([[maybe_unused]] const char* levelName) {};
virtual void OnLoadingStart([[maybe_unused]] const char* levelName);
virtual void OnLoadingComplete([[maybe_unused]] const char* levelName){};
virtual void OnLoadingError([[maybe_unused]] const char* levelName, [[maybe_unused]] const char* error){};
virtual void OnLoadingProgress([[maybe_unused]] const char* levelName, [[maybe_unused]] int progressAmount){};
virtual void OnUnloadComplete([[maybe_unused]] const char* levelName);
//~ILevelSystemListener
CViewSystem(ISystem* pSystem);
~CViewSystem();
void Release() override { delete this; };
void Update(float frameTime) override;
virtual void ForceUpdate(float elapsed) { Update(elapsed); }
//void RegisterViewClass(const char *name, IView *(*func)());
bool AddListener(IViewSystemListener* pListener)
{
return stl::push_back_unique(m_listeners, pListener);
}
bool RemoveListener(IViewSystemListener* pListener)
{
return stl::find_and_erase(m_listeners, pListener);
}
void GetMemoryUsage(ICrySizer* s) const;
void ClearAllViews();
private:
void RemoveViewById(unsigned int viewId);
void ClearCutsceneViews();
void DebugDraw();
ISystem* m_pSystem;
//TViewClassMap m_viewClasses;
TViewMap m_views;
// Listeners
std::vector<IViewSystemListener*> m_listeners;
unsigned int m_activeViewId;
unsigned int m_nextViewIdToAssign; // next id which will be assigned
unsigned int m_preSequenceViewId; // viewId before a movie cam dropped in
unsigned int m_cutsceneViewId;
unsigned int m_cutsceneCount;
bool m_bActiveViewFromSequence;
bool m_bOverridenCameraRotation;
Quat m_overridenCameraRotation;
float m_fCameraNoise;
float m_fCameraNoiseFrequency;
float m_fDefaultCameraNearZ;
float m_fBlendInPosSpeed;
float m_fBlendInRotSpeed;
bool m_bPerformBlendOut;
int m_nViewSystemDebug;
bool m_useDeferredViewSystemUpdate;
bool m_bControlsAudioListeners;
public:
static DebugCamera* s_debugCamera;
};
} // namespace LegacyViewSystem
@@ -0,0 +1,132 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Support for Windows Error Reporting (WER)
#include "CrySystem_precompiled.h"
#ifdef WIN32
#include "System.h"
#include <windows.h>
#include <tchar.h>
#include "errorrep.h"
#include "ISystem.h"
#include <DbgHelp.h>
static WCHAR szPath[MAX_PATH + 1];
static WCHAR szFR[] = L"\\System32\\FaultRep.dll";
WCHAR* GetFullPathToFaultrepDll(void)
{
UINT rc = GetSystemWindowsDirectoryW(szPath, ARRAYSIZE(szPath));
if (rc == 0 || rc > ARRAYSIZE(szPath) - ARRAYSIZE(szFR) - 1)
{
return NULL;
}
wcscat_s(szPath, szFR);
return szPath;
}
typedef BOOL (WINAPI * MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
//////////////////////////////////////////////////////////////////////////
LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE DumpType)
{
// note: In debug mode, this dll is loaded on startup anyway, so this should not incur an additional load unless it crashes
// very early during startup.
fflush(nullptr); // according to MSDN on fflush, calling fflush on null flushes all buffers.
HMODULE hndDBGHelpDLL = LoadLibraryA("DBGHELP.DLL");
if (!hndDBGHelpDLL)
{
CryLogAlways("Failed to record DMP file: Could not open DBGHELP.DLL");
return EXCEPTION_CONTINUE_SEARCH;
}
MINIDUMPWRITEDUMP dumpFnPtr = (MINIDUMPWRITEDUMP)::GetProcAddress(hndDBGHelpDLL, "MiniDumpWriteDump");
if (!dumpFnPtr)
{
CryLogAlways("Failed to record DMP file: Unable to find MiniDumpWriteDump in DBGHELP.DLL");
return EXCEPTION_CONTINUE_SEARCH;
}
HANDLE hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
CryLogAlways("Failed to record DMP file: could not open file '%s' for writing - error code: %d", szDumpPath, GetLastError());
return EXCEPTION_CONTINUE_SEARCH;
}
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionPointers;
ExInfo.ClientPointers = NULL;
BOOL bOK = dumpFnPtr(GetCurrentProcess(), GetCurrentProcessId(), hFile, DumpType, &ExInfo, NULL, NULL);
::CloseHandle(hFile);
if (bOK)
{
CryLogAlways("Successfully recorded DMP file: '%s'", szDumpPath);
return EXCEPTION_EXECUTE_HANDLER; // SUCCESS! you can execute your handlers now
}
else
{
CryLogAlways("Failed to record DMP file: '%s' - error code: %d", szDumpPath, GetLastError());
}
return EXCEPTION_CONTINUE_SEARCH;
}
//////////////////////////////////////////////////////////////////////////
LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers)
{
if (g_cvars.sys_WER > 1)
{
char szScratch [_MAX_PATH];
const char* szDumpPath = gEnv->pCryPak->AdjustFileName("@log@/CE2Dump.dmp", szScratch, AZ_ARRAY_SIZE(szScratch), 0);
MINIDUMP_TYPE mdumpValue = (MINIDUMP_TYPE)(MiniDumpNormal);
if (g_cvars.sys_WER > 1)
{
mdumpValue = (MINIDUMP_TYPE)(g_cvars.sys_WER - 2);
}
return CryEngineExceptionFilterMiniDump(pExceptionPointers, szDumpPath, mdumpValue);
}
LONG lRet = EXCEPTION_CONTINUE_SEARCH;
WCHAR* psz = GetFullPathToFaultrepDll();
if (psz)
{
HMODULE hFaultRepDll = LoadLibraryW(psz);
if (hFaultRepDll)
{
pfn_REPORTFAULT pfn = (pfn_REPORTFAULT)GetProcAddress(hFaultRepDll, "ReportFault");
if (pfn)
{
pfn(pExceptionPointers, 0);
lRet = EXCEPTION_EXECUTE_HANDLER;
}
FreeLibrary(hFaultRepDll);
}
}
return lRet;
}
#endif // WIN32
File diff suppressed because it is too large Load Diff
+436
View File
@@ -0,0 +1,436 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XCONSOLE_H
#define CRYINCLUDE_CRYSYSTEM_XCONSOLE_H
#pragma once
#include <IConsole.h>
#include <CryCrc32.h>
#include "Timer.h"
#include <AzFramework/Components/ConsoleBus.h>
#include <AzFramework/CommandLine/CommandRegistrationBus.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Input/Events/InputChannelEventListener.h>
#include <AzFramework/Input/Events/InputTextEventListener.h>
//forward declaration
struct INetwork;
class CSystem;
#define MAX_HISTORY_ENTRIES 50
#define LINE_BORDER 10
enum ScrollDir
{
sdDOWN,
sdUP,
sdNONE
};
//////////////////////////////////////////////////////////////////////////
// Console command holds information about commands registered to console.
//////////////////////////////////////////////////////////////////////////
struct CConsoleCommand
{
string m_sName; // Console command name
string m_sCommand; // lua code that is executed when this command is invoked
string m_sHelp; // optional help string - can be shown in the console with "<commandname> ?"
int m_nFlags; // bitmask consist of flag starting with VF_ e.g. VF_CHEAT
ConsoleCommandFunc m_func; // Pointer to console command.
//////////////////////////////////////////////////////////////////////////
CConsoleCommand()
: m_func(0)
, m_nFlags(0) {}
size_t sizeofThis () const {return sizeof(*this) + m_sName.capacity() + 1 + m_sCommand.capacity() + 1; }
void GetMemoryUsage (class ICrySizer* pSizer) const
{
pSizer->AddObject(m_sName);
pSizer->AddObject(m_sCommand);
pSizer->AddObject(m_sHelp);
}
};
//////////////////////////////////////////////////////////////////////////
// Implements IConsoleCmdArgs.
//////////////////////////////////////////////////////////////////////////
struct CConsoleCommandArgs
: public IConsoleCmdArgs
{
CConsoleCommandArgs(string& line, std::vector<string>& args)
: m_line(line)
, m_args(args) {};
virtual int GetArgCount() const { return m_args.size(); };
// Get argument by index, nIndex must be in 0 <= nIndex < GetArgCount()
virtual const char* GetArg(int nIndex) const
{
assert(nIndex >= 0 && nIndex < GetArgCount());
if (!(nIndex >= 0 && nIndex < GetArgCount()))
{
return NULL;
}
return m_args[nIndex].c_str();
}
virtual const char* GetCommandLine() const
{
return m_line.c_str();
}
private:
std::vector<string>& m_args;
string& m_line;
};
struct string_nocase_lt
{
bool operator()(const char* s1, const char* s2) const
{
return azstricmp(s1, s2) < 0;
}
};
/* - very dangerous to use with STL containers
struct string_nocase_lt
{
bool operator()( const char *s1,const char *s2 ) const
{
return _stricmp(s1,s2) < 0;
}
bool operator()( const string &s1,const string &s2 ) const
{
return _stricmp(s1.c_str(),s2.c_str()) < 0;
}
};
*/
//forward declarations
class ITexture;
struct IRenderer;
/*! engine console implementation
@see IConsole
*/
class CXConsole
: public IConsole
, public AzFramework::InputChannelEventListener
, public AzFramework::InputTextEventListener
, public IRemoteConsoleListener
, public AzFramework::ConsoleRequestBus::Handler
, public AzFramework::CommandRegistrationBus::Handler
{
public:
typedef std::deque<string> ConsoleBuffer;
typedef ConsoleBuffer::iterator ConsoleBufferItor;
typedef ConsoleBuffer::reverse_iterator ConsoleBufferRItor;
// constructor
CXConsole();
// destructor
virtual ~CXConsole();
void SetStatus(bool bActive){ m_bConsoleActive = bActive; }
bool GetStatus() const { return m_bConsoleActive; }
//
void FreeRenderResources();
//
void Copy();
void Paste();
// interface IConsole ---------------------------------------------------------
virtual void Release();
virtual void Init(ISystem* pSystem);
virtual ICVar* RegisterString(const char* sName, const char* sValue, int nFlags, const char* help = "", ConsoleVarFunc pChangeFunc = 0);
virtual ICVar* RegisterInt(const char* sName, int iValue, int nFlags, const char* help = "", ConsoleVarFunc pChangeFunc = 0);
virtual ICVar* RegisterInt64(const char* sName, int64 iValue, int nFlags, const char* help = "", ConsoleVarFunc pChangeFunc = 0);
virtual ICVar* RegisterFloat(const char* sName, float fValue, int nFlags, const char* help = "", ConsoleVarFunc pChangeFunc = 0);
virtual ICVar* Register(const char* name, float* src, float defaultvalue, int flags = 0, const char* help = "", ConsoleVarFunc pChangeFunc = 0, bool allowModify = true);
virtual ICVar* Register(const char* name, int* src, int defaultvalue, int flags = 0, const char* help = "", ConsoleVarFunc pChangeFunc = 0, bool allowModify = true);
virtual ICVar* Register(const char* name, const char** src, const char* defaultvalue, int flags = 0, const char* help = "", ConsoleVarFunc pChangeFunc = 0, bool allowModify = true);
virtual ICVar* Register(ICVar* pVar) { RegisterVar(pVar); return pVar; }
virtual void UnregisterVariable(const char* sVarName, bool bDelete = false);
virtual void SetScrollMax(int value);
virtual void AddOutputPrintSink(IOutputPrintSink* inpSink);
virtual void RemoveOutputPrintSink(IOutputPrintSink* inpSink);
virtual void ShowConsole(bool show, int iRequestScrollMax = -1);
virtual void DumpCVars(ICVarDumpSink* pCallback, unsigned int nFlagsFilter = 0);
virtual void DumpKeyBinds(IKeyBindDumpSink* pCallback);
virtual void CreateKeyBind(const char* sCmd, const char* sRes);
virtual const char* FindKeyBind(const char* sCmd) const;
virtual void SetImage(ITexture* pImage, bool bDeleteCurrent);
virtual inline ITexture* GetImage() { return m_pImage; }
virtual void StaticBackground(bool bStatic) { m_bStaticBackground = bStatic; }
virtual bool GetLineNo(int indwLineNo, char* outszBuffer, int indwBufferSize) const;
virtual int GetLineCount() const;
virtual ICVar* GetCVar(const char* name);
virtual char* GetVariable(const char* szVarName, const char* szFileName, const char* def_val);
virtual float GetVariable(const char* szVarName, const char* szFileName, float def_val);
virtual void PrintLine(const char* s);
virtual void PrintLinePlus(const char* s);
virtual bool GetStatus();
virtual void Clear();
virtual void Update();
virtual void Draw();
virtual bool AddCommand(const char* sCommand, ConsoleCommandFunc func, int nFlags = 0, const char* sHelp = NULL);
virtual bool AddCommand(const char* sName, const char* sScriptFunc, int nFlags = 0, const char* sHelp = NULL);
virtual void RemoveCommand(const char* sName);
virtual void ExecuteString(const char* command, bool bSilentMode, bool bDeferExecution = false);
virtual void ExecuteConsoleCommand(const char* command) override;
virtual void ResetCVarsToDefaults() override;
virtual void Exit(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual bool IsOpened();
virtual int GetNumVars();
virtual int GetNumVisibleVars();
virtual size_t GetSortedVars(const char** pszArray, size_t numItems, const char* szPrefix = 0);
virtual int GetNumCheatVars();
virtual void SetCheatVarHashRange(size_t firstVar, size_t lastVar);
virtual void CalcCheatVarHash();
virtual bool IsHashCalculated();
virtual uint64 GetCheatVarHash();
virtual void FindVar(const char* substr);
virtual const char* AutoComplete(const char* substr);
virtual const char* AutoCompletePrev(const char* substr);
virtual const char* ProcessCompletion(const char* szInputBuffer);
virtual void RegisterAutoComplete(const char* sVarOrCommand, IConsoleArgumentAutoComplete* pArgAutoComplete);
virtual void UnRegisterAutoComplete(const char* sVarOrCommand);
virtual void ResetAutoCompletion();
virtual void GetMemoryUsage (ICrySizer* pSizer) const;
virtual void ResetProgressBar(int nProgressRange);
virtual void TickProgressBar();
virtual void SetLoadingImage(const char* szFilename);
virtual void AddConsoleVarSink(IConsoleVarSink* pSink);
virtual void RemoveConsoleVarSink(IConsoleVarSink* pSink);
virtual const char* GetHistoryElement(bool bUpOrDown);
virtual void AddCommandToHistory(const char* szCommand);
virtual void SetInputLine(const char* szLine);
virtual void LoadConfigVar(const char* sVariable, const char* sValue);
virtual void EnableActivationKey(bool bEnable);
virtual void SetClientDataProbeString(const char* pName, const char* pValue);
// InputChannelEventListener / InputTextEventListener
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
bool OnInputTextEventFiltered(const AZStd::string& textUTF8) override;
// interface IRemoteConsoleListener ------------------------------------------------------------------
virtual void OnConsoleCommand(const char* cmd);
// interface IConsoleVarSink ----------------------------------------------------------------------
virtual bool OnBeforeVarChange(ICVar* pVar, const char* sNewValue);
virtual void OnAfterVarChange(ICVar* pVar);
// interface CommandRegistration --------------------------------------------------------------------
bool RegisterCommand(AZStd::string_view identifier, AZStd::string_view helpText, AZ::u32 commandFlags, AzFramework::CommandFunction callback) override;
bool UnregisterCommand(AZStd::string_view identifier) override;
void ExecuteRegisteredCommand(IConsoleCmdArgs* pArg);
//////////////////////////////////////////////////////////////////////////
// Returns
// 0 if the operation failed
ICVar* RegisterCVarGroup(const char* sName, const char* szFileName);
virtual void PrintCheatVars(bool bUseLastHashRange);
virtual char* GetCheatVarAt(uint32 nOffset);
void SetProcessingGroup(bool isGroup) { m_bIsProcessingGroup = isGroup; }
bool GetIsProcessingGroup(void) const { return m_bIsProcessingGroup; }
protected: // ----------------------------------------------------------------------------------------
void DrawBuffer(int nScrollPos, const char* szEffect);
void RegisterVar(ICVar* pCVar, ConsoleVarFunc pChangeFunc = 0);
bool ProcessInput(const AzFramework::InputChannel& inputChannel);
void AddLine(const char* inputStr);
void AddLinePlus(const char* inputStr);
void AddInputUTF8(const AZStd::string& textUTF8);
void RemoveInputChar(bool bBackSpace);
void ExecuteInputBuffer();
void ExecuteCommand(CConsoleCommand& cmd, string& params, bool bIgnoreDevMode = false);
void ScrollConsole();
// CommandRegistration usage
struct CommandRegistrationEntry
{
AzFramework::CommandFunction m_callback;
AZStd::string m_id;
AZStd::string m_helpText;
};
AZStd::unordered_map<AZStd::string, CommandRegistrationEntry> m_commandRegistrationMap;
#if ALLOW_AUDIT_CVARS
void AuditCVars(IConsoleCmdArgs* pArg);
#endif // ALLOW_AUDIT_CVARS
#ifndef _RELEASE
// will be removed once the HTML version is good enough
void DumpCommandsVarsTxt(const char* prefix);
void DumpVarsTxt(const bool includeCheat);
#endif
void ConsoleLogInputResponse(const char* szFormat, ...) PRINTF_PARAMS(2, 3);
void ConsoleLogInput(const char* szFormat, ...) PRINTF_PARAMS(2, 3);
void ConsoleWarning(const char* szFormat, ...) PRINTF_PARAMS(2, 3);
void DisplayHelp(const char* help, const char* name);
void DisplayVarValue(ICVar* pVar);
// Arguments:
// bFromConsole - true=from console, false=from outside
void SplitCommands(const char* line, std::list<string>& split);
void ExecuteStringInternal(const char* command, const bool bFromConsole, const bool bSilentMode = false);
void ExecuteDeferredCommands();
static const char* GetFlagsString(const uint32 dwFlags);
static void CmdDumpAllAnticheatVars(IConsoleCmdArgs* pArgs);
static void CmdDumpLastHashedAnticheatVars(IConsoleCmdArgs* pArgs);
private: // ----------------------------------------------------------
typedef std::map<const char*, ICVar*, string_nocase_lt> ConsoleVariablesMap; // key points into string stored in ICVar or in .exe/.dll
typedef ConsoleVariablesMap::iterator ConsoleVariablesMapItor;
typedef std::vector<std::pair<const char*, ICVar*> > ConsoleVariablesVector;
void LogChangeMessage(const char* name, const bool isConst, const bool isCheat, const bool isReadOnly, const bool isDeprecated,
const char* oldValue, const char* newValue, const bool isProcessingGroup, const bool allowChange);
void AddCheckedCVar(ConsoleVariablesVector& vector, const ConsoleVariablesVector::value_type& value);
void RemoveCheckedCVar(ConsoleVariablesVector& vector, const ConsoleVariablesVector::value_type& value);
static void AddCVarsToHash(ConsoleVariablesVector::const_iterator begin, ConsoleVariablesVector::const_iterator end, CCrc32& runningNameCrc32, CCrc32& runningNameValueCrc32);
static bool CVarNameLess(const std::pair<const char*, ICVar*>& lhs, const std::pair<const char*, ICVar*>& rhs);
void PostLine(const char* lineOfText, size_t len);
typedef std::map<string, CConsoleCommand, string_nocase_lt> ConsoleCommandsMap;
typedef ConsoleCommandsMap::iterator ConsoleCommandsMapItor;
typedef std::map<string, string> ConsoleBindsMap;
typedef ConsoleBindsMap::iterator ConsoleBindsMapItor;
typedef std::map<string, IConsoleArgumentAutoComplete*, stl::less_stricmp<string> > ArgumentAutoCompleteMap;
struct SConfigVar
{
string m_value;
bool m_partOfGroup;
};
typedef std::map<string, SConfigVar, string_nocase_lt> ConfigVars;
struct SDeferredCommand
{
string command;
bool silentMode;
SDeferredCommand(const string& _command, bool _silentMode)
: command(_command)
, silentMode(_silentMode)
{}
};
typedef std::list<SDeferredCommand> TDeferredCommandList;
typedef std::list<IConsoleVarSink*> ConsoleVarSinks;
// --------------------------------------------------------------------------------
ConsoleBuffer m_dqConsoleBuffer;
ConsoleBuffer m_dqHistory;
bool m_bStaticBackground;
int m_nLoadingBackTexID;
int m_nProgress;
int m_nProgressRange;
string m_sInputBuffer;
string m_sReturnString;
string m_sPrevTab;
int m_nTabCount;
ConsoleCommandsMap m_mapCommands; //
ConsoleBindsMap m_mapBinds; //
ConsoleVariablesMap m_mapVariables; //
ConsoleVariablesVector m_randomCheckedVariables;
ConsoleVariablesVector m_alwaysCheckedVariables;
std::vector<IOutputPrintSink*> m_OutputSinks; // objects in this vector are not released
TDeferredCommandList m_deferredCommands; // A fifo of deferred commands
bool m_deferredExecution; // True when deferred commands are processed
int m_waitFrames; // A counter which is used by wait_frames command
CTimeValue m_waitSeconds; // An absolute timestamp which is used by wait_seconds command
int m_blockCounter; // This counter is incremented whenever a blocker command (VF_BLOCKFRAME) is executed.
ArgumentAutoCompleteMap m_mapArgumentAutoComplete;
ConsoleVarSinks m_consoleVarSinks;
ConfigVars m_configVars; // temporary data of cvars that haven't been created yet
int m_nScrollPos;
int m_nTempScrollMax; // for currently opened console, reset to m_nScrollMax
int m_nScrollMax; //
int m_nScrollLine;
int m_nHistoryPos;
size_t m_nCursorPos; // x position in characters
ITexture* m_pImage;
float m_fRepeatTimer; // relative, next repeat even in .. decreses over time, repeats when 0, only valid if m_nRepeatEvent.keyId != eKI_Unknown
AzFramework::InputChannelId m_nRepeatEventId; // event that will be repeated
float m_fCursorBlinkTimer; // relative, increases over time,
bool m_bDrawCursor;
ScrollDir m_sdScrollDir;
AzFramework::SystemCursorState m_previousSystemCursorState;
bool m_bConsoleActive;
bool m_bActivationKeyEnable;
bool m_bIsProcessingGroup;
bool m_bIsConsoleKeyPressed;
size_t m_nCheatHashRangeFirst;
size_t m_nCheatHashRangeLast;
bool m_bCheatHashDirty;
uint64 m_nCheatHash;
CSystem* m_pSystem;
IFFont* m_pFont;
ITimer* m_pTimer;
ICVar* m_pSysDeactivateConsole;
static int con_display_last_messages;
static int con_line_buffer_size;
static int con_showonload;
static int con_debug;
static int con_restricted;
friend void Command_SetWaitSeconds(IConsoleCmdArgs* Cmd);
friend void Command_SetWaitFrames(IConsoleCmdArgs* Cmd);
#if ALLOW_AUDIT_CVARS
friend void Command_AuditCVars(IConsoleCmdArgs* pArg);
#endif // ALLOW_AUDIT_CVARS
friend void Command_DumpCommandsVars(IConsoleCmdArgs* Cmd);
friend void Command_DumpVars(IConsoleCmdArgs* Cmd);
friend class CConsoleHelpGen;
};
#endif // CRYINCLUDE_CRYSYSTEM_XCONSOLE_H
+735
View File
@@ -0,0 +1,735 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : implementation of the CXConsoleVariable class.
#include "CrySystem_precompiled.h"
#include "XConsole.h"
#include "XConsoleVariable.h"
#include <IConsole.h>
#include <ISystem.h>
#include <algorithm>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CXConsoleVariableBase::CXConsoleVariableBase(CXConsole* pConsole, const char* sName, int nFlags, const char* help)
: m_valueMin(0.0f)
, m_valueMax(100.0f)
, m_hasCustomLimits(false)
{
assert(pConsole);
m_psHelp = (char*)help;
m_pChangeFunc = NULL;
m_pConsole = pConsole;
m_nFlags = nFlags;
if (nFlags & VF_COPYNAME)
{
m_szName = new char[strlen(sName) + 1];
azstrcpy(m_szName, strlen(sName) + 1, sName);
}
else
{
m_szName = (char*)sName;
}
if (gEnv->IsDedicated())
{
m_pDataProbeString = NULL;
}
}
//////////////////////////////////////////////////////////////////////////
CXConsoleVariableBase::~CXConsoleVariableBase()
{
if (m_nFlags & VF_COPYNAME)
{
delete[] m_szName;
}
if (gEnv->IsDedicated() && m_pDataProbeString)
{
delete[] m_pDataProbeString;
}
}
//////////////////////////////////////////////////////////////////////////
void CXConsoleVariableBase::ForceSet(const char* s)
{
int excludeFlags = (VF_CHEAT | VF_READONLY | VF_NET_SYNCED);
int oldFlags = (m_nFlags & excludeFlags);
m_nFlags &= ~(excludeFlags);
Set(s);
m_nFlags |= oldFlags;
}
//////////////////////////////////////////////////////////////////////////
void CXConsoleVariableBase::ClearFlags (int flags)
{
m_nFlags &= ~flags;
}
//////////////////////////////////////////////////////////////////////////
int CXConsoleVariableBase::GetFlags() const
{
return m_nFlags;
}
//////////////////////////////////////////////////////////////////////////
int CXConsoleVariableBase::SetFlags(int flags)
{
m_nFlags = flags;
return m_nFlags;
}
//////////////////////////////////////////////////////////////////////////
const char* CXConsoleVariableBase::GetName() const
{
return m_szName;
}
//////////////////////////////////////////////////////////////////////////
const char* CXConsoleVariableBase::GetHelp()
{
return m_psHelp;
}
void CXConsoleVariableBase::Release()
{
m_pConsole->UnregisterVariable(m_szName);
}
void CXConsoleVariableBase::SetOnChangeCallback(ConsoleVarFunc pChangeFunc)
{
m_pChangeFunc = pChangeFunc;
}
uint64 CXConsoleVariableBase::AddOnChangeFunctor(const SFunctor& pChangeFunctor)
{
static int uniqueIdGenerator = 0;
int newId = uniqueIdGenerator++;
m_changeFunctors.push_back(std::make_pair(newId, pChangeFunctor));
return newId;
}
uint64 CXConsoleVariableBase::GetNumberOfOnChangeFunctors() const
{
return m_changeFunctors.size();
}
const SFunctor& CXConsoleVariableBase::GetOnChangeFunctor(uint64 nFunctorId) const
{
auto predicate = [nFunctorId](const std::pair<int, SFunctor>& entry) -> bool { return entry.first == nFunctorId; };
auto changeFunctor = std::find_if(m_changeFunctors.begin(), m_changeFunctors.end(), predicate);
if (changeFunctor != m_changeFunctors.end())
{
return (*changeFunctor).second;
}
static SFunctor sDummyFunctor;
assert(false && "[CXConsoleVariableBase::GetOnChangeFunctor] Trying to get a functor for an id that does not exist.");
return sDummyFunctor;
}
bool CXConsoleVariableBase::RemoveOnChangeFunctor(const uint64 nFunctorId)
{
auto predicate = [nFunctorId](const std::pair<int, SFunctor>& entry) -> bool { return entry.first == nFunctorId; };
auto changeFunctor = std::find_if(m_changeFunctors.begin(), m_changeFunctors.end(), predicate);
if (changeFunctor != m_changeFunctors.end())
{
m_changeFunctors.erase(changeFunctor);
return true;
}
return false;
}
ConsoleVarFunc CXConsoleVariableBase::GetOnChangeCallback() const
{
return m_pChangeFunc;
}
void CXConsoleVariableBase::CallOnChangeFunctions()
{
if (m_pChangeFunc)
{
m_pChangeFunc(this);
}
const size_t nTotal(m_changeFunctors.size());
for (size_t nCount = 0; nCount < nTotal; ++nCount)
{
m_changeFunctors[nCount].second.Call();
}
}
void CXConsoleVariableBase::SetLimits(float min, float max)
{
m_valueMin = min;
m_valueMax = max;
// Flag to determine when this variable has custom limits set
m_hasCustomLimits = true;
}
void CXConsoleVariableBase::GetLimits(float& min, float& max)
{
min = m_valueMin;
max = m_valueMax;
}
bool CXConsoleVariableBase::HasCustomLimits()
{
return m_hasCustomLimits;
}
void CXConsoleVariableCVarGroup::OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup)
{
assert(szGroup);
assert(szKey);
assert(szValue);
bool bCheckIfInDefault = false;
SCVarGroup* pGrp = 0;
if (azstricmp(szGroup, "default") == 0) // needs to be before the other groups
{
pGrp = &m_CVarGroupDefault;
// if(azstricmp(GetName(),szKey)==0)
if (*szKey == 0)
{
m_sDefaultValue = szValue;
int iGrpValue = atoi(szValue);
// if default state is not part of the mentioned states generate this state, so GetIRealVal() can return this state as well
if (m_CVarGroupStates.find(iGrpValue) == m_CVarGroupStates.end())
{
m_CVarGroupStates[iGrpValue] = new SCVarGroup;
}
return;
}
}
else
{
int iGrp;
if (azsscanf(szGroup, "%d", &iGrp) == 1)
{
if (m_CVarGroupStates.find(iGrp) == m_CVarGroupStates.end())
{
m_CVarGroupStates[iGrp] = new SCVarGroup;
}
pGrp = m_CVarGroupStates[iGrp];
}
else
{
gEnv->pLog->LogError("[CVARS]: [MISSING] [%s] is not a registered console variable group", szGroup);
#if LOG_CVAR_INFRACTIONS_CALLSTACK
gEnv->pSystem->debug_LogCallStack();
#endif // LOG_CVAR_INFRACTIONS_CALLSTACK
return;
}
if (*szKey == 0)
{
assert(0); // =%d only expected in default section
return;
}
}
if (pGrp)
{
if (pGrp->m_KeyValuePair.find(szKey) != pGrp->m_KeyValuePair.end())
{
gEnv->pLog->LogError("[CVARS]: [DUPLICATE] [%s] specified multiple times in console variable group [%s] = [%s]", szKey, GetName(), szGroup);
bCheckIfInDefault = true;
}
pGrp->m_KeyValuePair[szKey] = szValue;
if (bCheckIfInDefault)
{
if (m_CVarGroupDefault.m_KeyValuePair.find(szKey) == m_CVarGroupDefault.m_KeyValuePair.end())
{
gEnv->pLog->LogError("[CVARS]: [MISSING] [%s] specified in console variable group [%s] = [%s], but missing from default group", szKey, GetName(), szGroup);
}
}
}
}
void CXConsoleVariableCVarGroup::OnLoadConfigurationEntry_End()
{
if (!m_sDefaultValue.empty())
{
gEnv->pConsole->LoadConfigVar(GetName(), m_sDefaultValue);
m_sDefaultValue.clear();
}
}
CXConsoleVariableCVarGroup::CXConsoleVariableCVarGroup(CXConsole* pConsole, const char* sName, const char* szFileName, int nFlags)
: CXConsoleVariableInt(pConsole, sName, 0, nFlags, 0)
{
gEnv->pSystem->LoadConfiguration(szFileName, this);
}
string CXConsoleVariableCVarGroup::GetDetailedInfo() const
{
string sRet = GetName();
sRet += " [";
{
TCVarGroupStateMap::const_iterator it, end = m_CVarGroupStates.end();
for (it = m_CVarGroupStates.begin(); it != end; ++it)
{
if (it != m_CVarGroupStates.begin())
{
sRet += "/";
}
char szNum[10];
azsprintf(szNum, "%d", it->first);
sRet += szNum;
}
}
sRet += "/default] [current]:\n";
std::map<string, string>::const_iterator it, end = m_CVarGroupDefault.m_KeyValuePair.end();
for (it = m_CVarGroupDefault.m_KeyValuePair.begin(); it != end; ++it)
{
const string& rKey = it->first;
sRet += " ... ";
sRet += rKey;
sRet += " = ";
TCVarGroupStateMap::const_iterator it2, end2 = m_CVarGroupStates.end();
for (it2 = m_CVarGroupStates.begin(); it2 != end2; ++it2)
{
sRet += GetValueSpec(rKey, &(it2->first));
sRet += "/";
}
sRet += GetValueSpec(rKey);
ICVar* pCVar = gEnv->pConsole->GetCVar(rKey);
if (pCVar)
{
sRet += " [";
sRet += pCVar->GetString();
sRet += "]";
}
sRet += "\n";
}
return sRet;
}
const char* CXConsoleVariableCVarGroup::GetHelp()
{
if (m_psHelp)
{
delete m_psHelp;
m_psHelp = NULL;
}
// create help on demand
string sRet = "Console variable group to apply settings to multiple variables\n\n";
sRet += GetDetailedInfo();
m_psHelp = new char[sRet.size() + 1];
azstrcpy(m_psHelp, sRet.size() + 1, &sRet[0]);
return m_psHelp;
}
void CXConsoleVariableCVarGroup::DebugLog(const int iExpectedValue, const ICVar::EConsoleLogMode mode) const
{
TCVarGroupStateMap::const_iterator it, end = m_CVarGroupStates.end();
SCVarGroup* pCurrentGrp = 0;
{
TCVarGroupStateMap::const_iterator itCurrentGrp = m_CVarGroupStates.find(iExpectedValue);
if (itCurrentGrp != end)
{
pCurrentGrp = itCurrentGrp->second;
}
}
// try the current state
if (TestCVars(pCurrentGrp, mode))
{
return;
}
}
int CXConsoleVariableCVarGroup::GetRealIVal() const
{
TCVarGroupStateMap::const_iterator it, end = m_CVarGroupStates.end();
int iValue = GetIVal();
SCVarGroup* pCurrentGrp = 0;
{
TCVarGroupStateMap::const_iterator itCurrentGrp = m_CVarGroupStates.find(iValue);
if (itCurrentGrp != end)
{
pCurrentGrp = itCurrentGrp->second;
}
}
// first try the current state
if (TestCVars(pCurrentGrp))
{
return iValue;
}
// then all other
for (it = m_CVarGroupStates.begin(); it != end; ++it)
{
SCVarGroup* pLocalGrp = it->second;
if (pLocalGrp == pCurrentGrp)
{
continue;
}
int iLocalState = it->first;
if (TestCVars(pLocalGrp))
{
return iLocalState;
}
}
return -1; // no state found that represent the current one
}
void CXConsoleVariableCVarGroup::Set(const int i)
{
if (i == m_iValue)
{
SCVarGroup* pCurrentGrp = 0;
TCVarGroupStateMap::const_iterator itCurrentGrp = m_CVarGroupStates.find(m_iValue);
if (itCurrentGrp != m_CVarGroupStates.end())
{
pCurrentGrp = itCurrentGrp->second;
}
if (TestCVars(pCurrentGrp))
{
// All cvars in this group match the current state - no further action is necessary
return;
}
}
char sTemp[128];
azsprintf(sTemp, "%d", i);
bool wasProcessingGroup = m_pConsole->GetIsProcessingGroup();
m_pConsole->SetProcessingGroup(true);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_iValue = i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
m_pConsole->SetProcessingGroup(wasProcessingGroup);
// Useful for debugging cvar groups
//CryLogAlways("[CVARS]: CXConsoleVariableCVarGroup::Set() Group %s in state %d (wanted %d)", GetName(), m_iValue, i);
}
CXConsoleVariableCVarGroup::~CXConsoleVariableCVarGroup()
{
TCVarGroupStateMap::iterator it, end = m_CVarGroupStates.end();
for (it = m_CVarGroupStates.begin(); it != end; ++it)
{
SCVarGroup* pGrp = it->second;
delete pGrp;
}
delete m_psHelp;
}
void CXConsoleVariableCVarGroup::OnCVarChangeFunc(ICVar* pVar)
{
CXConsoleVariableCVarGroup* pThis = (CXConsoleVariableCVarGroup*)pVar;
int iValue = pThis->GetIVal();
TCVarGroupStateMap::const_iterator itGrp = pThis->m_CVarGroupStates.find(iValue);
SCVarGroup* pGrp = 0;
if (itGrp != pThis->m_CVarGroupStates.end())
{
pGrp = itGrp->second;
}
if (pGrp)
{
pThis->ApplyCVars(*pGrp);
}
pThis->ApplyCVars(pThis->m_CVarGroupDefault, pGrp);
}
bool CXConsoleVariableCVarGroup::TestCVars(const SCVarGroup* pGroup, const ICVar::EConsoleLogMode mode) const
{
if (pGroup)
{
if (!TestCVars(*pGroup, mode))
{
return false;
}
}
if (!TestCVars(m_CVarGroupDefault, mode, pGroup))
{
return false;
}
return true;
}
bool CXConsoleVariableCVarGroup::TestCVars(const SCVarGroup& rGroup, const ICVar::EConsoleLogMode mode, const SCVarGroup* pExclude) const
{
bool bRet = true;
std::map<string, string>::const_iterator it, end = rGroup.m_KeyValuePair.end();
for (it = rGroup.m_KeyValuePair.begin(); it != end; ++it)
{
const string& rKey = it->first;
const string& rValue = it->second;
if (pExclude)
{
if (pExclude->m_KeyValuePair.find(rKey) != pExclude->m_KeyValuePair.end())
{
continue;
}
}
ICVar* pVar = gEnv->pConsole->GetCVar(rKey.c_str());
if (pVar)
{
if (pVar->GetFlags() & VF_CVARGRP_IGNOREINREALVAL) // Ignore the cvars which change often and shouldn't be used to determine state
{
continue;
}
bool bOk = true;
// compare exact type,
// simple string comparison would fail on some comparisons e.g. 2.0 == 2
// and GetString() for int and float return pointer to shared array so this
// can cause problems
switch (pVar->GetType())
{
case CVAR_INT:
{
int iVal;
if (azsscanf(rValue.c_str(), "%d", &iVal) == 1)
{
if (pVar->GetIVal() != atoi(rValue.c_str()))
{
bOk = false;
break;
}
}
if (pVar->GetIVal() != pVar->GetRealIVal())
{
bOk = false;
break;
}
}
break;
case CVAR_FLOAT:
{
float fVal;
if (azsscanf(rValue.c_str(), "%f", &fVal) == 1)
{
if (pVar->GetFVal() != fVal)
{
bOk = false;
break;
}
}
}
break;
case CVAR_STRING:
if (rValue != pVar->GetString())
{
bOk = false;
break;
}
break;
default:
assert(0);
}
if (!bOk)
{
if (mode == ICVar::eCLM_Off)
{
return false; // exit as early as possible
}
bRet = false; // exit with same return code but log all differences
if (strcmp(pVar->GetString(), rValue.c_str()) != 0)
{
switch (mode)
{
case ICVar::eCLM_ConsoleAndFile:
CryLog("[CVARS]: $3[FAIL] [%s] = $6[%s] $4(expected [%s] in group [%s] = [%s])", rKey.c_str(), pVar->GetString(), rValue.c_str(), GetName(), GetString());
break;
case ICVar::eCLM_FileOnly:
case ICVar::eCLM_FullInfo:
gEnv->pLog->LogToFile("[CVARS]: [FAIL] [%s] = [%s] (expected [%s] in group [%s] = [%s])", rKey.c_str(), pVar->GetString(), rValue.c_str(), GetName(), GetString());
break;
default:
assert(0);
}
}
else if (mode == ICVar::eCLM_FullInfo)
{
gEnv->pLog->LogToFile("[CVARS]: [FAIL] [%s] = [%s] (expected [%s] in group [%s] = [%s])", rKey.c_str(), pVar->GetString(), rValue.c_str(), GetName(), GetString());
}
pVar->DebugLog(pVar->GetIVal(), mode); // recursion
}
if (pVar->GetFlags() & (VF_CHEAT | VF_CHEAT_ALWAYS_CHECK | VF_CHEAT_NOCHECK))
{
// either VF_CHEAT should be removed or the var should be not part of the CVarGroup
gEnv->pLog->LogError("[CVARS]: [%s] is cheat protected; referenced in console variable group [%s] = [%s] ", rKey.c_str(), GetName(), GetString());
}
}
else
{
// Do not warn about D3D registered cvars, which carry the prefix "q_", as they are not actually registered with the cvar system.
if (strcmp(rKey.c_str(), "q") == -1)
{
gEnv->pLog->LogError("[CVARS]: [MISSING] [%s] is not a registered console variable; referenced when testing console variable group [%s] = [%s]", rKey.c_str(), GetName(), GetString());
}
}
}
return bRet;
}
string CXConsoleVariableCVarGroup::GetValueSpec(const string& sKey, const int* pSpec) const
{
if (pSpec)
{
TCVarGroupStateMap::const_iterator itGrp = m_CVarGroupStates.find(*pSpec);
if (itGrp != m_CVarGroupStates.end())
{
const SCVarGroup* pGrp = itGrp->second;
// check in spec
std::map<string, string>::const_iterator it = pGrp->m_KeyValuePair.find(sKey);
if (it != pGrp->m_KeyValuePair.end())
{
return it->second;
}
}
}
// check in default
std::map<string, string>::const_iterator it = m_CVarGroupDefault.m_KeyValuePair.find(sKey);
if (it != m_CVarGroupDefault.m_KeyValuePair.end())
{
return it->second;
}
assert(0); // internal error
return "";
}
void CXConsoleVariableCVarGroup::ApplyCVars(const SCVarGroup& rGroup, const SCVarGroup* pExclude)
{
std::map<string, string>::const_iterator it, end = rGroup.m_KeyValuePair.end();
bool wasProcessingGroup = m_pConsole->GetIsProcessingGroup();
m_pConsole->SetProcessingGroup(true);
for (it = rGroup.m_KeyValuePair.begin(); it != end; ++it)
{
const string& rKey = it->first;
if (pExclude)
{
if (pExclude->m_KeyValuePair.find(rKey) != pExclude->m_KeyValuePair.end())
{
continue;
}
}
// Useful for debugging cvar groups
//CryLogAlways("[CVARS]: [APPLY] ([%s]) [%s] = [%s]", GetName(), rKey.c_str(), it->second.c_str());
m_pConsole->LoadConfigVar(rKey, it->second);
}
m_pConsole->SetProcessingGroup(wasProcessingGroup);
}
+842
View File
@@ -0,0 +1,842 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XCONSOLEVARIABLE_H
#define CRYINCLUDE_CRYSYSTEM_XCONSOLEVARIABLE_H
#pragma once
#include <ISystem.h>
#include "BitFiddling.h"
#include "SFunctor.h"
class CXConsole;
inline int64 TextToInt64(const char* s, int64 nCurrent, bool bBitfield)
{
int64 nValue = 0;
if (s)
{
char* e;
if (bBitfield)
{
// Bit manipulation.
if (*s == '^')
// Bit number
#if defined(_MSC_VER)
{
nValue = 1LL << _strtoi64(++s, &e, 10);
}
#else
{
nValue = 1LL << strtoll(++s, &e, 10);
}
#endif
else
// Full number
#if defined(_MSC_VER)
{
nValue = _strtoi64(s, &e, 10);
}
#else
{
nValue = strtoll(s, &e, 10);
}
#endif
// Check letter codes.
for (; (*e >= 'a' && *e <= 'z') || (*e >= 'A' && *e <= 'Z'); e++)
{
nValue |= AlphaBit64(*e);
}
if (*e == '+')
{
nValue = nCurrent | nValue;
}
else if (*e == '-')
{
nValue = nCurrent & ~nValue;
}
else if (*e == '^')
{
nValue = nCurrent ^ nValue;
}
}
else
#if defined(_MSC_VER)
{
nValue = _strtoi64(s, &e, 10);
}
#else
{
nValue = strtoll(s, &e, 10);
}
#endif
}
return nValue;
}
inline int TextToInt(const char* s, int nCurrent, bool bBitfield)
{
return (int)TextToInt64(s, nCurrent, bBitfield);
}
class CXConsoleVariableBase
: public ICVar
{
public:
//! constructor
//! \param pConsole must not be 0
CXConsoleVariableBase(CXConsole* pConsole, const char* sName, int nFlags, const char* help);
//! destructor
virtual ~CXConsoleVariableBase();
// interface ICVar --------------------------------------------------------------------------------------
virtual void ClearFlags(int flags);
virtual int GetFlags() const;
virtual int SetFlags(int flags);
virtual const char* GetName() const;
virtual const char* GetHelp();
virtual void Release();
virtual void ForceSet(const char* s);
virtual void SetOnChangeCallback(ConsoleVarFunc pChangeFunc);
virtual uint64 AddOnChangeFunctor(const SFunctor& pChangeFunctor) override;
virtual bool RemoveOnChangeFunctor(const uint64 nFunctorId) override;
virtual uint64 GetNumberOfOnChangeFunctors() const;
virtual const SFunctor& GetOnChangeFunctor(uint64 nFunctorId) const override;
virtual ConsoleVarFunc GetOnChangeCallback() const;
virtual bool ShouldReset() const { return (m_nFlags & VF_RESETTABLE) != 0; }
virtual void Reset() override
{
if (ShouldReset())
{
ResetImpl();
}
}
virtual void ResetImpl() = 0;
virtual void SetLimits(float min, float max) override;
virtual void GetLimits(float& min, float& max) override;
virtual bool HasCustomLimits() override;
virtual int GetRealIVal() const { return GetIVal(); }
virtual bool IsConstCVar() const {return (m_nFlags & VF_CONST_CVAR) != 0; }
virtual void SetDataProbeString(const char* pDataProbeString)
{
CRY_ASSERT(m_pDataProbeString == NULL);
m_pDataProbeString = new char[ strlen(pDataProbeString) + 1 ];
azstrcpy(m_pDataProbeString, strlen(pDataProbeString) + 1, pDataProbeString);
}
virtual const char* GetDataProbeString() const
{
if (gEnv->IsDedicated() && m_pDataProbeString)
{
return m_pDataProbeString;
}
return GetOwnDataProbeString();
}
protected: // ------------------------------------------------------------------------------------------
virtual const char* GetOwnDataProbeString() const
{
return GetString();
}
void CallOnChangeFunctions();
char* m_szName; // if VF_COPYNAME then this data need to be deleteed, otherwise it's pointer to .dll/.exe
char* m_psHelp; // pointer to the help string, might be 0
char* m_pDataProbeString; // value client is required to have for data probes
int m_nFlags; // e.g. VF_CHEAT, ...
typedef std::vector<std::pair<int, SFunctor> > ChangeFunctorContainer;
ChangeFunctorContainer m_changeFunctors;
ConsoleVarFunc m_pChangeFunc; // Callback function that is called when this variable changes.
CXConsole* m_pConsole; // used for the callback OnBeforeVarChange()
float m_valueMin;
float m_valueMax;
bool m_hasCustomLimits;
};
//////////////////////////////////////////////////////////////////////////
class CXConsoleVariableString
: public CXConsoleVariableBase
{
public:
// constructor
CXConsoleVariableString(CXConsole* pConsole, const char* sName, const char* szDefault, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
{
m_sValue = szDefault;
m_sDefault = szDefault;
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return atoi(m_sValue); }
virtual int64 GetI64Val() const { return _atoi64(m_sValue); }
virtual float GetFVal() const { return (float)atof(m_sValue); }
virtual const char* GetString() const { return m_sValue; }
virtual void ResetImpl()
{
Set(m_sDefault);
}
virtual void Set(const char* s)
{
if (!s)
{
return;
}
if ((m_sValue == s) && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
if (m_pConsole->OnBeforeVarChange(this, s))
{
m_nFlags |= VF_MODIFIED;
{
m_sValue = s;
}
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(float f)
{
stack_string s;
s.Format("%g", f);
if ((m_sValue == s.c_str()) && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
m_nFlags |= VF_MODIFIED;
Set(s.c_str());
}
virtual void Set(int i)
{
stack_string s;
s.Format("%d", i);
if ((m_sValue == s.c_str()) && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
m_nFlags |= VF_MODIFIED;
Set(s.c_str());
}
virtual int GetType() { return CVAR_STRING; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
private: // --------------------------------------------------------------------------------------------
string m_sValue;
string m_sDefault; //!<
};
class CXConsoleVariableInt
: public CXConsoleVariableBase
{
public:
// constructor
CXConsoleVariableInt(CXConsole* pConsole, const char* sName, const int iDefault, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_iValue(iDefault)
, m_iDefault(iDefault)
{
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return m_iValue; }
virtual int64 GetI64Val() const { return m_iValue; }
virtual float GetFVal() const { return (float)GetIVal(); }
virtual const char* GetString() const
{
static char szReturnString[256];
sprintf_s(szReturnString, "%d", GetIVal());
return szReturnString;
}
virtual void ResetImpl() { Set(m_iDefault); }
virtual void Set(const char* s)
{
int nValue = TextToInt(s, m_iValue, (m_nFlags & VF_BITFIELD) != 0);
Set(nValue);
}
virtual void Set(float f)
{
Set((int)f);
}
virtual void Set(int i)
{
if (i == m_iValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
stack_string s;
s.Format("%d", i);
if (m_pConsole->OnBeforeVarChange(this, s.c_str()))
{
m_nFlags |= VF_MODIFIED;
m_iValue = i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual int GetType() { return CVAR_INT; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
protected: // --------------------------------------------------------------------------------------------
int m_iValue;
int m_iDefault; //!<
};
class CXConsoleVariableInt64
: public CXConsoleVariableBase
{
public:
// constructor
CXConsoleVariableInt64(CXConsole* pConsole, const char* sName, const int64 iDefault, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_iValue(iDefault)
, m_iDefault(iDefault)
{
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return (int)m_iValue; }
virtual int64 GetI64Val() const { return m_iValue; }
virtual float GetFVal() const { return (float)GetIVal(); }
virtual const char* GetString() const
{
static char szReturnString[256];
sprintf_s(szReturnString, "%lld", GetI64Val());
return szReturnString;
}
virtual void ResetImpl() { Set(m_iDefault); }
virtual void Set(const char* s)
{
int64 nValue = TextToInt64(s, m_iValue, (m_nFlags & VF_BITFIELD) != 0);
Set(nValue);
}
virtual void Set(float f)
{
Set((int)f);
}
virtual void Set(int i)
{
Set((int64)i);
}
virtual void Set(int64 i)
{
if (i == m_iValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
stack_string s;
s.Format("%lld", i);
if (m_pConsole->OnBeforeVarChange(this, s.c_str()))
{
m_nFlags |= VF_MODIFIED;
m_iValue = i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual int GetType() { return CVAR_INT; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
protected: // --------------------------------------------------------------------------------------------
int64 m_iValue;
int64 m_iDefault; //!<
};
//////////////////////////////////////////////////////////////////////////
class CXConsoleVariableFloat
: public CXConsoleVariableBase
{
public:
// constructor
CXConsoleVariableFloat(CXConsole* pConsole, const char* sName, const float fDefault, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_fValue(fDefault)
, m_fDefault(fDefault)
{
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return (int)m_fValue; }
virtual int64 GetI64Val() const { return (int64)m_fValue; }
virtual float GetFVal() const { return m_fValue; }
virtual const char* GetString() const
{
static char szReturnString[256];
sprintf_s(szReturnString, "%g", m_fValue); // %g -> "2.01", %f -> "2.01000"
return szReturnString;
}
virtual void ResetImpl() { Set(m_fDefault); }
virtual void Set(const char* s)
{
float fValue = 0;
if (s)
{
fValue = (float)atof(s);
}
if (fValue == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
if (m_pConsole->OnBeforeVarChange(this, s))
{
m_nFlags |= VF_MODIFIED;
m_fValue = fValue;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(float f)
{
if (f == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
stack_string s;
s.Format("%g", f);
if (m_pConsole->OnBeforeVarChange(this, s.c_str()))
{
m_nFlags |= VF_MODIFIED;
m_fValue = f;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(int i)
{
if ((float)i == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
char sTemp[128];
sprintf_s(sTemp, "%d", i);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_fValue = (float)i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual int GetType() { return CVAR_FLOAT; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
protected:
virtual const char* GetOwnDataProbeString() const
{
static char szReturnString[8];
sprintf_s(szReturnString, "%.1g", m_fValue);
return szReturnString;
}
private: // --------------------------------------------------------------------------------------------
float m_fValue;
float m_fDefault; //!<
};
class CXConsoleVariableIntRef
: public CXConsoleVariableBase
{
public:
//! constructor
//!\param pVar must not be 0
CXConsoleVariableIntRef(CXConsole* pConsole, const char* sName, int32* pVar, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_iValue(*pVar)
, m_iDefault(*pVar)
{
assert(pVar);
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return m_iValue; }
virtual int64 GetI64Val() const { return m_iValue; }
virtual float GetFVal() const { return (float)m_iValue; }
virtual const char* GetString() const
{
static char szReturnString[256];
sprintf_s(szReturnString, "%d", m_iValue);
return szReturnString;
}
virtual void ResetImpl() { Set(m_iDefault); }
virtual void Set(const char* s)
{
int nValue = TextToInt(s, m_iValue, (m_nFlags & VF_BITFIELD) != 0);
if (nValue == m_iValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
if (m_pConsole->OnBeforeVarChange(this, s))
{
m_nFlags |= VF_MODIFIED;
m_iValue = nValue;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(float f)
{
if ((int)f == m_iValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
char sTemp[128];
sprintf_s(sTemp, "%g", f);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_iValue = (int)f;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(int i)
{
if (i == m_iValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
char sTemp[128];
sprintf_s(sTemp, "%d", i);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_iValue = i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual int GetType() { return CVAR_INT; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
private: // --------------------------------------------------------------------------------------------
int& m_iValue;
int m_iDefault; //!<
};
class CXConsoleVariableFloatRef
: public CXConsoleVariableBase
{
public:
//! constructor
//!\param pVar must not be 0
CXConsoleVariableFloatRef(CXConsole* pConsole, const char* sName, float* pVar, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_fValue(*pVar)
, m_fDefault(*pVar)
{
assert(pVar);
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return (int)m_fValue; }
virtual int64 GetI64Val() const { return (int64)m_fValue; }
virtual float GetFVal() const { return m_fValue; }
virtual const char* GetString() const
{
static char szReturnString[256];
sprintf_s(szReturnString, "%g", m_fValue);
return szReturnString;
}
virtual void ResetImpl() { Set(m_fDefault); }
virtual void Set(const char* s)
{
float fValue = 0;
if (s)
{
fValue = (float)atof(s);
}
if (fValue == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
if (m_pConsole->OnBeforeVarChange(this, s))
{
m_nFlags |= VF_MODIFIED;
m_fValue = fValue;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(float f)
{
if (f == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
char sTemp[128];
sprintf_s(sTemp, "%g", f);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_fValue = f;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(int i)
{
if ((float)i == m_fValue && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
char sTemp[128];
sprintf_s(sTemp, "%d", i);
if (m_pConsole->OnBeforeVarChange(this, sTemp))
{
m_nFlags |= VF_MODIFIED;
m_fValue = (float)i;
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual int GetType() { return CVAR_FLOAT; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
protected:
virtual const char* GetOwnDataProbeString() const
{
static char szReturnString[8];
sprintf_s(szReturnString, "%.1g", m_fValue);
return szReturnString;
}
private: // --------------------------------------------------------------------------------------------
float& m_fValue;
float m_fDefault; //!<
};
class CXConsoleVariableStringRef
: public CXConsoleVariableBase
{
public:
//! constructor
//!\param userBuf must not be 0
CXConsoleVariableStringRef(CXConsole* pConsole, const char* sName, const char** userBuf, const char* defaultValue, int nFlags, const char* help)
: CXConsoleVariableBase(pConsole, sName, nFlags, help)
, m_sValue(defaultValue)
, m_sDefault(defaultValue)
, m_userPtr(*userBuf)
{
m_userPtr = m_sValue.c_str();
assert(userBuf);
}
// interface ICVar --------------------------------------------------------------------------------------
virtual int GetIVal() const { return atoi(m_sValue.c_str()); }
virtual int64 GetI64Val() const { return _atoi64(m_sValue.c_str()); }
virtual float GetFVal() const { return (float)atof(m_sValue.c_str()); }
virtual const char* GetString() const
{
return m_sValue.c_str();
}
virtual void ResetImpl() { Set(m_sDefault); }
virtual void Set(const char* s)
{
if ((m_sValue == s) && (m_nFlags & VF_ALWAYSONCHANGE) == 0)
{
return;
}
if (m_pConsole->OnBeforeVarChange(this, s))
{
m_nFlags |= VF_MODIFIED;
{
m_sValue = s;
m_userPtr = m_sValue.c_str();
}
CallOnChangeFunctions();
m_pConsole->OnAfterVarChange(this);
}
}
virtual void Set(float f)
{
stack_string s;
s.Format("%g", f);
Set(s.c_str());
}
virtual void Set(int i)
{
stack_string s;
s.Format("%d", i);
Set(s.c_str());
}
virtual int GetType() { return CVAR_STRING; }
virtual void GetMemoryUsage(class ICrySizer* pSizer) const { pSizer->AddObject(this, sizeof(*this)); }
private: // --------------------------------------------------------------------------------------------
string m_sValue;
string m_sDefault;
const char*& m_userPtr; //!<
};
// works like CXConsoleVariableInt but when changing it sets other console variables
// getting the value returns the last value it was set to - if that is still what was applied
// to the cvars can be tested with GetRealIVal()
class CXConsoleVariableCVarGroup
: public CXConsoleVariableInt
, public ILoadConfigurationEntrySink
{
public:
// constructor
CXConsoleVariableCVarGroup(CXConsole* pConsole, const char* sName, const char* szFileName, int nFlags);
// destructor
~CXConsoleVariableCVarGroup();
// Returns:
// part of the help string - useful to log out detailed description without additional help text
string GetDetailedInfo() const;
// interface ICVar -----------------------------------------------------------------------------------
virtual const char* GetHelp();
virtual int GetRealIVal() const;
virtual void DebugLog(const int iExpectedValue, const ICVar::EConsoleLogMode mode) const;
virtual void Set(int i);
// ConsoleVarFunc ------------------------------------------------------------------------------------
static void OnCVarChangeFunc(ICVar* pVar);
// interface ILoadConfigurationEntrySink -------------------------------------------------------------
virtual void OnLoadConfigurationEntry(const char* szKey, const char* szValue, const char* szGroup);
virtual void OnLoadConfigurationEntry_End();
virtual void GetMemoryUsage(class ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_sDefaultValue);
pSizer->AddObject(m_CVarGroupStates);
}
private: // --------------------------------------------------------------------------------------------
struct SCVarGroup
{
std::map<string, string> m_KeyValuePair; // e.g. m_KeyValuePair["r_fullscreen"]="0"
void GetMemoryUsage(class ICrySizer* pSizer) const
{
pSizer->AddObject(m_KeyValuePair);
}
};
SCVarGroup m_CVarGroupDefault;
typedef std::map<int, SCVarGroup*> TCVarGroupStateMap;
TCVarGroupStateMap m_CVarGroupStates;
string m_sDefaultValue; // used by OnLoadConfigurationEntry_End()
void ApplyCVars(const SCVarGroup& rGroup, const SCVarGroup* pExclude = 0);
// Arguments:
// sKey - must exist, at least in default
// pSpec - can be 0
string GetValueSpec(const string& sKey, const int* pSpec = 0) const;
// should only be used by TestCVars()
// Returns:
// true=all console variables match the state (excluding default state), false otherwise
bool TestCVars(const SCVarGroup& rGroup, const ICVar::EConsoleLogMode mode, const SCVarGroup* pExclude = 0) const;
// Arguments:
// pGroup - can be 0 to test if the default state is set
// Returns:
// true=all console variables match the state (including default state), false otherwise
bool TestCVars(const SCVarGroup* pGroup, const ICVar::EConsoleLogMode mode = ICVar::eCLM_Off) const;
};
#endif // CRYINCLUDE_CRYSYSTEM_XCONSOLEVARIABLE_H
+19
View File
@@ -0,0 +1,19 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
ly_add_target(
NAME CrySystem.XMLBinary STATIC
NAMESPACE Legacy
FILES_CMAKE
crysystem_xmlbinary_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PUBLIC
Legacy::CryCommon
)
@@ -0,0 +1,22 @@
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
and Clark Cooper
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
#define CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
#pragma once
#include <ISystem.h>
#include <IReadWriteXMLSink.h>
class CReadWriteXMLSink
: public IReadWriteXMLSink
{
public:
bool ReadXML(const char* definitionFile, const char* dataFile, IReadXMLSink* pSink);
bool ReadXML(const char* definitionFile, XmlNodeRef node, IReadXMLSink* pSink);
bool ReadXML(XmlNodeRef definition, const char* dataFile, IReadXMLSink* pSink);
bool ReadXML(XmlNodeRef definition, XmlNodeRef node, IReadXMLSink* pSink);
XmlNodeRef CreateXMLFromSource(const char* definitionFile, IWriteXMLSource* pSource);
bool WriteXML(const char* definitionFile, const char* dataFile, IWriteXMLSource* pSource);
};
// helper to define the if/else chain that we need in a few locations...
// types must match IReadXMLSink::TValueTypes
#define XML_SET_PROPERTY_HELPER(ELSE_LOAD_PROPERTY) \
if (false) {; } \
ELSE_LOAD_PROPERTY(Vec3); \
ELSE_LOAD_PROPERTY(int); \
ELSE_LOAD_PROPERTY(float); \
ELSE_LOAD_PROPERTY(string); \
ELSE_LOAD_PROPERTY(bool);
#endif // CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
+681
View File
@@ -0,0 +1,681 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "ReadWriteXMLSink.h"
#include <ISystem.h>
#include <stack>
typedef std::map<string, XmlNodeRef> IdTable;
struct SParseParams
{
IdTable idTable;
XmlNodeRef useAlways;
bool strict;
SParseParams()
{
strict = true;
}
};
static XmlNodeRef Clone(XmlNodeRef source);
static void CopyAttributes(const XmlNodeRef& source, XmlNodeRef& dest);
static bool IsOptionalReadXML(const SParseParams& parseParams, XmlNodeRef& definition);
static bool CheckEnum(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data);
static bool LoadTableInner(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadArray(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadProperty(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadReferencedId(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadSomething(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
static bool LoadArraySetValueTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem);
typedef bool (* LoadArraySetValue)(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem);
typedef bool (* LoadDefinitionFunction)(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
template <class T>
struct ReadPropertyTyped;
template <class T>
struct ReadPropertyTyped
{
static bool Load(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
T value;
memset(&value, 0, sizeof(T));
if (!pSink->IsCreationMode())
{
if (!data->haveAttr(name))
{
return false;
}
if (!data->getAttr(name, value))
{
return false;
}
if (!CheckEnum(parseParams, name, definition, data))
{
return false;
}
}
IReadXMLSink::TValue vvalue(value);
pSink->SetValue(name, vvalue, definition);
return true;
}
static bool LoadArray([[maybe_unused]] const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
{
T value;
memset(&value, 0, sizeof(T));
if (!pSink->IsCreationMode())
{
if (!data->haveAttr("value"))
{
return false;
}
if (!data->getAttr("value", value))
{
return false;
}
}
IReadXMLSink::TValue vvalue(value);
pSink->SetAt(elem, vvalue, definition);
return true;
}
};
template <>
struct ReadPropertyTyped<string>
{
static bool Load(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
const char* value = 0;
if (!pSink->IsCreationMode())
{
if (!data->haveAttr(name))
{
return false;
}
if (!CheckEnum(parseParams, name, definition, data))
{
return false;
}
value = data->getAttr(name);
}
IReadXMLSink::TValue vvalue(value);
pSink->SetValue(name, vvalue, definition);
return true;
}
static bool LoadArray([[maybe_unused]] const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
{
const char* value = 0;
if (!pSink->IsCreationMode())
{
if (!data->haveAttr("value"))
{
return false;
}
value = data->getAttr("value");
}
IReadXMLSink::TValue vvalue(value);
pSink->SetAt(elem, vvalue, definition);
return true;
}
};
XmlNodeRef Clone(XmlNodeRef source)
{
assert(source != (IXmlNode*)NULL);
// Can't use clone() on XmlNodeRef objects since they can contain a CXMLBinaryNode, which doesn't support
// clone(). Instead we just create a regular xml node and manually copy content, tag, attributes and children
XmlNodeRef cloned = GetISystem()->CreateXmlNode(source->getTag());
cloned->setContent(source->getContent());
CopyAttributes(source, cloned);
const int iChildCount = source->getChildCount();
for (int i = 0; i < iChildCount; ++i)
{
cloned->addChild(Clone(source->getChild(i)));
}
return cloned;
}
void CopyAttributes(const XmlNodeRef& source, XmlNodeRef& dest)
{
// Not as fast as CXmlNode::copyAttributes(), but that method will have undefined behavior if the XmlNodeRef contains
// a CBinaryXmlNode object
int nNumAttributes = source->getNumAttributes();
for (int i = 0; i < nNumAttributes; ++i)
{
const char* key = NULL;
const char* value = NULL;
if (source->getAttributeByIndex(i, &key, &value))
{
dest->setAttr(key, value);
}
}
}
bool IsOptionalReadXML(const SParseParams& parseParams, XmlNodeRef& definition)
{
// If strict mode is off, then everything is optional
if (parseParams.strict == false)
{
return true;
}
bool optional = false;
definition->getAttr("optional", optional);
return optional;
}
bool CheckEnum([[maybe_unused]] const SParseParams& parseParams, [[maybe_unused]] const char* name, XmlNodeRef& definition, [[maybe_unused]] XmlNodeRef& data)
{
if (XmlNodeRef enumNode = definition->findChild("Enum"))
{
// If strict mode is off, then no need to check the enum value
return true;
}
return true;
}
bool LoadProperty(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Property has no name");
return false;
}
const char* type = definition->getAttr("type");
if (0 == strlen(type))
{
CryLog("Property '%s' has no type", type);
return false;
}
XmlNodeRef dataToRead = data;
if (!pSink->IsCreationMode())
{
// This check is done so the data xml can specify child elements instead of attributes if desired,
// since the rest of the code assume only attributes
if (XmlNodeRef childRef = data->findChild(name))
{
if (data->haveAttr(name))
{
CryLog("Duplicate definition (attribute and element) for %s", name);
return false;
}
if (childRef->getChildCount())
{
CryLog("Property-style elements can not have children (property was %s)", name);
return false;
}
dataToRead = GetISystem()->CreateXmlNode(data->getTag());
string content = childRef->getContent();
dataToRead->setAttr(name, content.Trim().c_str());
}
if (!dataToRead->haveAttr(name))
{
if (!IsOptionalReadXML(parseParams, definition))
{
CryLog("Failed to load property %s", name);
return false;
}
return true;
}
}
bool ok = false;
#define LOAD_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) ok = ReadPropertyTyped<whichType>::Load(parseParams, name, definition, dataToRead, pSink)
XML_SET_PROPERTY_HELPER(LOAD_PROPERTY);
#undef LOAD_PROPERTY
if (!ok)
{
CryLog("Failed loading attribute %s of type %s", name, type);
}
return ok;
}
bool LoadArraySetValueTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
{
IReadXMLSinkPtr pChildSink = pSink->BeginTableAt(elem, definition);
if (pSink->IsCreationMode() && definition->haveAttr("type"))
{
if (!LoadSomething(parseParams, definition, data, &*pChildSink))
{
return false;
}
}
else
{
if (!LoadTableInner(parseParams, definition, data, &*pChildSink))
{
return false;
}
}
if (!pChildSink->EndTableAt(elem))
{
CryLog("Failed to finish table at element %d", elem);
return false;
}
return true;
}
bool LoadArray(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Array has no name");
return false;
}
const char* elementName = definition->getAttr("elementName");
if (0 == strlen(elementName))
{
elementName = "element";
}
bool validateArray = true;
if (definition->haveAttr(elementName))
{
definition->getAttr("validate", validateArray);
}
XmlNodeRef childData;
if (!pSink->IsCreationMode())
{
childData = data->findChild(name);
if (!childData)
{
bool ok = IsOptionalReadXML(parseParams, definition);
if (!ok)
{
CryLog("Failed to load child table %s", name);
}
return ok;
}
}
IReadXMLSinkPtr childSink = pSink->BeginArray(name, definition);
if (!childSink)
{
CryLog("Failed to begin array named %s", name);
return false;
}
LoadArraySetValue setter = NULL;
if (definition->haveAttr("type"))
{
setter = NULL;
const char* type = definition->getAttr("type");
#define SETTER_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) setter = ReadPropertyTyped<whichType>::LoadArray
XML_SET_PROPERTY_HELPER(SETTER_PROPERTY);
#undef SETTER_PROPERTY
if (!setter)
{
CryLog("Unknown type %s in array %s", type, name);
return false;
}
}
else
{
setter = LoadArraySetValueTable;
}
if (!pSink->IsCreationMode())
{
int numElems = childData->getChildCount();
int elem = 1;
for (int i = 0; i < numElems; i++)
{
XmlNodeRef elemData = childData->getChild(i);
if (0 == strcmp(elemData->getTag(), elementName))
{
int increment = 1;
if (elemData->haveAttr("_index"))
{
if (!elemData->getAttr("_index", elem))
{
CryLog("_index is not an integer in array %s (pos hint=%d)", name, elem);
return false;
}
}
if (!setter(parseParams, definition, elemData, &*childSink, elem))
{
CryLog("Failed loading element %d of array %s", elem, name);
return false;
}
elem += increment;
}
else if (validateArray)
{
CryLog("Invalid node %s in array %s", elemData->getTag(), name);
return false;
}
}
}
else
{
// only process array content for the array being created
if (0 == strcmp(name, pSink->GetCreationNode()->getAttr("name")))
{
if (!setter(parseParams, definition, data, &*childSink, 1))
{
CryLog("[ReadXML CreationMode]: Failed loading element %d of array %s", 1, name);
return false;
}
}
}
if (!pSink->EndArray(name))
{
CryLog("Failed to finish array named %s", name);
return false;
}
return true;
}
bool LoadTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Child-table has no name");
return false;
}
XmlNodeRef childData;
if (!pSink->IsCreationMode())
{
childData = data->findChild(name);
if (!childData)
{
bool ok = IsOptionalReadXML(parseParams, definition);
if (!ok)
{
CryLog("Failed to load child table %s", name);
}
return ok;
}
}
IReadXMLSinkPtr childSink = pSink->BeginTable(name, definition);
if (!childSink)
{
CryLog("Sink creation failed for table %s", name);
return false;
}
if (!LoadTableInner(parseParams, definition, childData, childSink))
{
CryLog("Failed to load data for child table %s", name);
return false;
}
if (!pSink->EndTable(name))
{
CryLog("Table %s failed to complete in sink", name);
return false;
}
return true;
}
bool LoadSomething(const SParseParams& parseParams, XmlNodeRef& nodeDefinition, XmlNodeRef& data, IReadXMLSink* pSink)
{
// Ignore if it's the useAlways array
if (parseParams.useAlways == nodeDefinition)
{
return true;
}
static struct
{
const char* name;
LoadDefinitionFunction loader;
} loaderTypes[] = {
{"Property", &LoadProperty},
{"Array", &LoadArray},
{"Table", &LoadTable},
{"Use", &LoadReferencedId},
};
static const int numLoaderTypes = sizeof(loaderTypes) / sizeof(*loaderTypes);
const char* nodeDefinitionTag = nodeDefinition->getTag();
bool ok = false;
int i;
for (i = 0; i < numLoaderTypes; i++)
{
if (0 == strcmp(loaderTypes[i].name, nodeDefinitionTag))
{
ok = loaderTypes[i].loader(parseParams, nodeDefinition, data, pSink);
break;
}
}
if (0 == _stricmp("Settings", nodeDefinitionTag))
{
return true;
}
if (!ok)
{
if (i == numLoaderTypes)
{
CryLog("Invalid definition node type %s, line %d", nodeDefinitionTag, nodeDefinition->getLine());
}
}
return ok;
}
bool LoadReferencedId(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
IdTable::const_iterator iter = parseParams.idTable.find(definition->getAttr("id"));
if (iter == parseParams.idTable.end())
{
CryLog("No definition with id '%s'", definition->getAttr("id"));
return false;
}
XmlNodeRef useDefinition = Clone(iter->second);
CopyAttributes(definition, useDefinition);
return LoadSomething(parseParams, useDefinition, data, pSink);
}
bool LoadTableInner(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
{
const int nChildrenDefinition = definition->getChildCount();
for (int nChildDefinition = 0; nChildDefinition < nChildrenDefinition; nChildDefinition++)
{
XmlNodeRef nodeDefinition = definition->getChild(nChildDefinition);
if (!LoadSomething(parseParams, nodeDefinition, data, pSink))
{
return false;
}
}
if (parseParams.useAlways != (IXmlNode*)NULL)
{
assert(!definition->haveAttr("type"));
const int nUseAlwaysDefCount = parseParams.useAlways->getChildCount();
for (int i = 0; i < nUseAlwaysDefCount; ++i)
{
XmlNodeRef nodeDefinition = parseParams.useAlways->getChild(i);
// Don't continue loading useAlways nodes in creation mode
if (!pSink->IsCreationMode())
{
if (!LoadSomething(parseParams, nodeDefinition, data, pSink))
{
return false;
}
}
}
}
return true;
}
bool CReadWriteXMLSink::ReadXML(XmlNodeRef rootDefinition, XmlNodeRef rootData, IReadXMLSink* pSink)
{
if (!pSink->IsCreationMode())
{
if (0 == rootData)
{
return false;
}
if (0 != strcmp(rootDefinition->getTag(), "Definition"))
{
CryLog("Root tag of definition file was %s; expected Definition", rootDefinition->getTag());
return false;
}
if (rootDefinition->haveAttr("root"))
{
if (0 != strcmp(rootDefinition->getAttr("root"), rootData->getTag()))
{
CryLog("Root data has wrong tag; was %s expected %s", rootData->getTag(), rootDefinition->getAttr("root"));
return false;
}
}
}
SParseParams parseParams;
parseParams.useAlways = rootDefinition->findChild("AllowAlways");
if (XmlNodeRef settingsParams = rootDefinition->findChild("Settings"))
{
settingsParams->getAttr("strict", parseParams.strict);
}
// scan for id's in the structure (for the Use member)
std::stack<XmlNodeRef> scanStack;
scanStack.push(rootDefinition);
while (!scanStack.empty())
{
XmlNodeRef refNode = scanStack.top();
scanStack.pop();
int numChildren = refNode->getChildCount();
const char* tag = refNode->getTag();
for (int i = 0; i < numChildren; i++)
{
const XmlNodeRef& childNodeRef = refNode->getChild(i);
if (parseParams.useAlways != childNodeRef)
{
scanStack.push(childNodeRef);
}
}
// If the element has an attribute id="" and is not a "<Use>" element add it to the idTable map
if (refNode->haveAttr("id") && 0 != strcmp("Use", tag))
{
parseParams.idTable[refNode->getAttr("id")] = refNode;
}
}
if (pSink->IsCreationMode() && rootDefinition->haveAttr("type"))
{
// if creating from a 0-child definition node, load itself
if (!LoadSomething(parseParams, rootDefinition, rootData, pSink))
{
return false;
}
}
else
{
// load content
if (!LoadTableInner(parseParams, rootDefinition, rootData, pSink))
{
return false;
}
}
bool ok = pSink->Complete();
if (!ok)
{
CryLog("Warning: sink failed to complete reading");
}
return ok;
}
bool CReadWriteXMLSink::ReadXML(XmlNodeRef definition, const char* dataFile, IReadXMLSink* pSink)
{
XmlNodeRef rootData = GetISystem()->LoadXmlFromFile(dataFile);
if (!rootData)
{
CryLog("Unable to load XML-Lua data file: %s", dataFile);
return false;
}
return ReadXML(definition, rootData, pSink);
}
bool CReadWriteXMLSink::ReadXML(const char* definitionFile, XmlNodeRef rootData, IReadXMLSink* pSink)
{
XmlNodeRef rootDefinition = GetISystem()->LoadXmlFromFile(definitionFile);
if (!rootDefinition)
{
CryLog("Unable to load XML-Lua definition file: %s", definitionFile);
return false;
}
return ReadXML(rootDefinition, rootData, pSink);
}
bool CReadWriteXMLSink::ReadXML(const char* definitionFile, const char* dataFile, IReadXMLSink* pSink)
{
XmlNodeRef rootData = GetISystem()->LoadXmlFromFile(dataFile);
if (!rootData)
{
CryLog("Unable to load XML-Lua data file: %s", dataFile);
return false;
}
if (!ReadXML(definitionFile, rootData, pSink))
{
CryLog("Unable to load file %s", dataFile);
return false;
}
return true;
}
@@ -0,0 +1,204 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "SerializeXMLReader.h"
#include <ISystem.h>
#define TAG_SCRIPT_VALUE "v"
#define TAG_SCRIPT_TYPE "t"
#define TAG_SCRIPT_NAME "n"
//#define LOG_SERIALIZE_STACK(tag,szName) CryLogAlways( "<%s> %s/%s",tag,GetStackInfo(),szName );
#define LOG_SERIALIZE_STACK(tag, szName)
CSerializeXMLReaderImpl::CSerializeXMLReaderImpl(const XmlNodeRef& nodeRef)
: m_nErrors(0)
{
//m_curTime = gEnv->pTimer->GetFrameStartTime();
assert(!!nodeRef);
m_nodeStack.push_back(CParseState());
m_nodeStack.back().Init(nodeRef);
}
bool CSerializeXMLReaderImpl::Value(const char* name, int8& value)
{
DefaultValue(value); // Set input value to default.
if (m_nErrors)
{
return false;
}
int temp;
bool bResult = Value(name, temp);
if (temp < -128 || temp > 127)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Attribute %s is out of range (%d)", name, temp);
Failed();
bResult = false;
}
else
{
value = temp;
}
return bResult;
}
bool CSerializeXMLReaderImpl::Value(const char* name, string& value)
{
DefaultValue(value); // Set input value to default.
if (m_nErrors)
{
return false;
}
if (!CurNode()->haveAttr(name))
{
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"No such attribute %s (invalid type?)", name);
//Failed();
return false;
}
else
{
value = CurNode()->getAttr(name);
}
return true;
}
bool CSerializeXMLReaderImpl::Value(const char* name, CTimeValue& value)
{
DefaultValue(value); // Set input value to default.
if (m_nErrors)
{
return false;
}
XmlNodeRef nodeRef = CurNode();
if (!nodeRef)
{
return false;
}
if (0 == strcmp("zero", nodeRef->getAttr(name)))
{
value = CTimeValue(0.0f);
}
else
{
float delta;
if (!GetAttr(nodeRef, name, delta))
{
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Failed to read time value %s", name);
//Failed();
value = gEnv->pTimer->GetFrameStartTime(); // in case we don't find the node, it was assumed to be the default value (0.0)
// 0.0 means current time, whereas "zero" really means CTimeValue(0.0), see above
return false;
}
else
{
value = CTimeValue(gEnv->pTimer->GetFrameStartTime() + delta);
}
}
return true;
}
bool CSerializeXMLReaderImpl::Value(const char* name, XmlNodeRef& value)
{
DefaultValue(value); // Set input value to default.
if (m_nErrors)
{
return false;
}
if (BeginOptionalGroup(name, true))
{
value = CurNode()->getChild(0);
EndGroup();
}
return true;
}
void CSerializeXMLReaderImpl::BeginGroup(const char* szName)
{
if (m_nErrors)
{
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"BeginGroup %s called on non-existant group", szName);
m_nErrors++;
}
else if (XmlNodeRef node = NextOf(szName))
{
m_nodeStack.push_back(CParseState());
m_nodeStack.back().Init(node);
LOG_SERIALIZE_STACK("BeginGroup:ok", szName);
}
else
{
LOG_SERIALIZE_STACK("BeginGroup:fail", szName);
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!BeginGroup( %s ) not found", szName);
m_nErrors++;
}
}
bool CSerializeXMLReaderImpl::BeginOptionalGroup(const char* szName, [[maybe_unused]] bool condition)
{
if (m_nErrors)
{
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"BeginOptionalGroup %s called on non-existant group", szName);
m_nErrors++;
}
else if (XmlNodeRef node = NextOf(szName))
{
m_nodeStack.push_back(CParseState());
m_nodeStack.back().Init(node);
LOG_SERIALIZE_STACK("BeginOptionalGroup:ok", szName);
return true;
}
LOG_SERIALIZE_STACK("BeginOptionalGroup:fail", szName);
return false;
}
void CSerializeXMLReaderImpl::EndGroup()
{
if (m_nErrors)
{
m_nErrors--;
}
else
{
LOG_SERIALIZE_STACK("EndGroup", "");
m_nodeStack.pop_back();
}
assert(!m_nodeStack.empty());
}
//////////////////////////////////////////////////////////////////////////
const char* CSerializeXMLReaderImpl::GetStackInfo() const
{
static string str;
str.assign("");
for (int i = 0; i < (int)m_nodeStack.size(); i++)
{
const char* name = m_nodeStack[i].m_node->getAttr(TAG_SCRIPT_NAME);
if (name && name[0])
{
str += name;
}
else
{
str += m_nodeStack[i].m_node->getTag();
}
if (i != m_nodeStack.size() - 1)
{
str += "/";
}
}
return str.c_str();
}
void CSerializeXMLReaderImpl::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->Add(*this);
pSizer->AddContainer(m_nodeStack);
}
@@ -0,0 +1,182 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
#define CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
#pragma once
#include "SimpleSerialize.h"
#include <stack>
#include <IXml.h>
#include <ITimer.h>
#include <IValidator.h>
#include <ISystem.h>
#include "xml.h"
class CSerializeXMLReaderImpl
: public CSimpleSerializeImpl<true, eST_SaveGame>
{
public:
CSerializeXMLReaderImpl(const XmlNodeRef& nodeRef);
template <class T_Value>
ILINE bool GetAttr(const XmlNodeRef& node, const char* name, T_Value& value)
{
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
bool bReturn = node->getAttr(name, value);
g_pXmlStrCmp = pPrevCmpFunc;
return bReturn;
}
ILINE bool GetAttr(const XmlNodeRef& node, const char* name, SSerializeString& value)
{
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
bool bReturn = node->haveAttr(name);
if (bReturn)
{
value = node->getAttr(name);
}
g_pXmlStrCmp = pPrevCmpFunc;
return bReturn;
}
ILINE bool GetAttr([[maybe_unused]] XmlNodeRef& node, [[maybe_unused]] const char* name, [[maybe_unused]] const string& value)
{
return false;
}
ILINE bool GetAttr([[maybe_unused]] const XmlNodeRef& node, [[maybe_unused]] const char* name, [[maybe_unused]] SNetObjectID& value)
{
return false;
}
template <class T_Value>
bool Value(const char* name, T_Value& value)
{
DefaultValue(value); // Set input value to default.
if (m_nErrors)
{
return false;
}
if (!GetAttr(CurNode(), name, value))
{
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Unable to read attribute %s (invalid type?)", name);
//Failed();
return false;
}
return true;
}
bool Value(const char* name, int8& value);
bool Value(const char* name, string& value);
bool Value(const char* name, CTimeValue& value);
bool Value(const char* name, XmlNodeRef& value);
template <class T_Value, class T_Policy>
bool Value(const char* name, T_Value& value, [[maybe_unused]] const T_Policy& policy)
{
return Value(name, value);
}
void BeginGroup(const char* szName);
bool BeginOptionalGroup(const char* szName, bool condition);
void EndGroup();
const char* GetStackInfo() const;
void GetMemoryUsage(ICrySizer* pSizer) const;
private:
//CTimeValue m_curTime;
XmlNodeRef CurNode() { return m_nodeStack.back().m_node; }
XmlNodeRef NextOf(const char* name)
{
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
assert(!m_nodeStack.empty());
CParseState& ps = m_nodeStack.back();
XmlNodeRef node = ps.GetNext(name);
g_pXmlStrCmp = pPrevCmpFunc;
return node;
}
class CParseState
{
public:
CParseState() {}
void Init(const XmlNodeRef& node)
{
m_node = node;
m_nCurrent = 0;
}
XmlNodeRef GetNext(const char* name)
{
int i;
int num = m_node->getChildCount();
for (i = m_nCurrent; i < num; i++)
{
XmlNodeRef child = m_node->getChild(i);
if (strcmp(child->getTag(), name) == 0)
{
m_nCurrent = i + 1;
return child;
}
}
int ncount = min(m_nCurrent, num);
// Try searching from begining.
for (i = 0; i < ncount; i++)
{
XmlNodeRef child = m_node->getChild(i);
if (strcmp(child->getTag(), name) == 0)
{
m_nCurrent = i + 1;
return child;
}
}
return XmlNodeRef();
}
public:
// TODO: make this much more efficient
int m_nCurrent;
XmlNodeRef m_node;
};
int m_nErrors;
std::vector<CParseState> m_nodeStack;
//////////////////////////////////////////////////////////////////////////
// Set Defaults.
//////////////////////////////////////////////////////////////////////////
void DefaultValue(bool& v) const { v = false; }
void DefaultValue(float& v) const { v = 0; }
void DefaultValue(double& v) const { v = 0; }
void DefaultValue(int8& v) const { v = 0; }
void DefaultValue(uint8& v) const { v = 0; }
void DefaultValue(int16& v) const { v = 0; }
void DefaultValue(uint16& v) const { v = 0; }
void DefaultValue(int32& v) const { v = 0; }
void DefaultValue(uint32& v) const { v = 0; }
void DefaultValue(int64& v) const { v = 0; }
void DefaultValue(uint64& v) const { v = 0; }
void DefaultValue(Vec2& v) const { v.x = 0; v.y = 0; }
void DefaultValue(Vec3& v) const { v.x = 0; v.y = 0; v.z = 0; }
void DefaultValue(Ang3& v) const { v.x = 0; v.y = 0; v.z = 0; }
void DefaultValue(Quat& v) const { v.w = 1.0f; v.v.x = 0; v.v.y = 0; v.v.z = 0; }
void DefaultValue(CTimeValue& v) const { v.SetValue(0); }
//void DefaultValue( char *str ) const { if (str) str[0] = 0; }
void DefaultValue(string& str) const { str = ""; }
void DefaultValue([[maybe_unused]] const string& str) const {}
void DefaultValue([[maybe_unused]] SNetObjectID& id) const {}
void DefaultValue([[maybe_unused]] SSerializeString& str) const {}
void DefaultValue(XmlNodeRef& ref) const { ref = NULL; }
//////////////////////////////////////////////////////////////////////////
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
@@ -0,0 +1,152 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "SerializeXMLWriter.h"
static const size_t MAX_NODE_STACK_DEPTH = 40;
#define TAG_SCRIPT_VALUE "v"
#define TAG_SCRIPT_TYPE "t"
#define TAG_SCRIPT_NAME "n"
CSerializeXMLWriterImpl::CSerializeXMLWriterImpl(const XmlNodeRef& nodeRef)
{
m_curTime = gEnv->pTimer->GetFrameStartTime();
assert(!!nodeRef);
m_nodeStack.push_back(nodeRef);
m_luaSaveStack.reserve(10);
}
//////////////////////////////////////////////////////////////////////////
CSerializeXMLWriterImpl::~CSerializeXMLWriterImpl()
{
if (m_nodeStack.size() != 1)
{
// Node stack is incorrect.
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!BeginGroup/EndGroup mismatch in SaveGame");
}
}
//////////////////////////////////////////////////////////////////////////
bool CSerializeXMLWriterImpl::Value(const char* name, CTimeValue value)
{
if (value == CTimeValue(0.0f))
{
AddValue(name, "zero");
}
else
{
AddValue(name, (value - m_curTime).GetSeconds());
}
return true;
}
bool CSerializeXMLWriterImpl::Value(const char* name, XmlNodeRef& value)
{
if (BeginOptionalGroup(name, value != NULL))
{
CurNode()->addChild(value);
EndGroup();
}
return true;
}
void CSerializeXMLWriterImpl::BeginGroup(const char* szName)
{
if (strchr(szName, ' ') != 0)
{
assert(0 && "Spaces in group name not supported");
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Spaces in group name not supported: %s/%s", GetStackInfo(), szName);
}
XmlNodeRef node = CreateNodeNamed(szName);
CurNode()->addChild(node);
m_nodeStack.push_back(node);
if (m_nodeStack.size() > MAX_NODE_STACK_DEPTH)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Too Deep Node Stack:\r\n%s", GetStackInfo());
}
}
bool CSerializeXMLWriterImpl::BeginOptionalGroup(const char* szName, bool condition)
{
if (condition)
{
BeginGroup(szName);
return true;
}
return condition;
}
XmlNodeRef CSerializeXMLWriterImpl::CreateNodeNamed(const char* name)
{
XmlNodeRef newNode = CurNode()->createNode(name);
return newNode;
}
void CSerializeXMLWriterImpl::EndGroup()
{
if (m_nodeStack.size() == 1)
{
//
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Misplaced EndGroup() for BeginGroup(%s)", CurNode()->getTag());
}
assert(!m_nodeStack.empty());
m_nodeStack.pop_back();
assert(!m_nodeStack.empty());
}
void CSerializeXMLWriterImpl::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->Add(*this);
pSizer->AddObject(m_nodeStack);
pSizer->AddContainer(m_luaSaveStack);
}
//////////////////////////////////////////////////////////////////////////
const char* CSerializeXMLWriterImpl::GetStackInfo() const
{
static string str;
str.assign("");
for (int i = 0; i < (int)m_nodeStack.size(); i++)
{
const char* name = m_nodeStack[i]->getAttr(TAG_SCRIPT_NAME);
if (name && name[0])
{
str += name;
}
else
{
str += m_nodeStack[i]->getTag();
}
if (i != m_nodeStack.size() - 1)
{
str += "/";
}
}
return str.c_str();
}
//////////////////////////////////////////////////////////////////////////
const char* CSerializeXMLWriterImpl::GetLuaStackInfo() const
{
static string str;
str.assign("");
for (int i = 0; i < (int)m_luaSaveStack.size(); i++)
{
const char* name = m_luaSaveStack[i];
str += name;
if (i != m_luaSaveStack.size() - 1)
{
str += ".";
}
}
return str.c_str();
}
@@ -0,0 +1,151 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
#define CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
#pragma once
#include <ISystem.h>
#include <ITimer.h>
#include <IXml.h>
#include "IValidator.h"
#include "SimpleSerialize.h"
class CSerializeXMLWriterImpl
: public CSimpleSerializeImpl<false, eST_SaveGame>
{
public:
CSerializeXMLWriterImpl(const XmlNodeRef& nodeRef);
~CSerializeXMLWriterImpl();
template <class T_Value>
bool Value(const char* name, T_Value& value)
{
AddValue(name, value);
return true;
}
template <class T_Value, class T_Policy>
bool Value(const char* name, T_Value& value, [[maybe_unused]] const T_Policy& policy)
{
return Value(name, value);
}
bool Value(const char* name, CTimeValue value);
bool Value(const char* name, XmlNodeRef& value);
void BeginGroup(const char* szName);
bool BeginOptionalGroup(const char* szName, bool condition);
void EndGroup();
void GetMemoryUsage(ICrySizer* pSizer) const;
private:
//////////////////////////////////////////////////////////////////////////
// Vars.
//////////////////////////////////////////////////////////////////////////
CTimeValue m_curTime;
std::vector<XmlNodeRef> m_nodeStack;
std::vector<const char*> m_luaSaveStack;
//////////////////////////////////////////////////////////////////////////
ILINE const XmlNodeRef& CurNode()
{
assert(!m_nodeStack.empty());
if (m_nodeStack.empty())
{
static XmlNodeRef temp = GetISystem()->CreateXmlNode("Error");
return temp;
}
return m_nodeStack.back();
}
XmlNodeRef CreateNodeNamed(const char* name);
template <class T>
void AddValue(const char* name, const T& value)
{
if (strchr(name, ' ') != 0)
{
assert(0 && "Spaces in Value name not supported");
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Spaces in Value name not supported: %s in Group %s", name, GetStackInfo());
return;
}
if (GetISystem()->IsDevMode() && CurNode())
{
// Check if this attribute already added.
if (CurNode()->haveAttr(name))
{
assert(0);
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Duplicate tag Value( \"%s\" ) in Group %s", name, GetStackInfo());
}
}
if (!IsDefaultValue(value))
{
CurNode()->setAttr(name, value);
}
}
void AddValue(const char* name, const SSerializeString& value)
{
AddValue(name, value.c_str());
}
void AddValue([[maybe_unused]] const char* name, [[maybe_unused]] const SNetObjectID& value)
{
assert(false);
}
template <class T>
void AddTypedValue(const char* name, const T& value, const char* type)
{
if (!IsDefaultValue(value))
{
XmlNodeRef newNode = CreateNodeNamed(name);
newNode->setAttr("v", value);
newNode->setAttr("t", type);
}
}
// Used for printing currebnt stack info for warnings.
const char* GetStackInfo() const;
const char* GetLuaStackInfo() const;
//////////////////////////////////////////////////////////////////////////
// Check For Defaults.
//////////////////////////////////////////////////////////////////////////
bool IsDefaultValue(bool v) const { return v == false; };
bool IsDefaultValue(float v) const { return v == 0; };
bool IsDefaultValue(double v) const { return v == 0; };
bool IsDefaultValue(int8 v) const { return v == 0; };
bool IsDefaultValue(uint8 v) const { return v == 0; };
bool IsDefaultValue(int16 v) const { return v == 0; };
bool IsDefaultValue(uint16 v) const { return v == 0; };
bool IsDefaultValue(int32 v) const { return v == 0; };
bool IsDefaultValue(uint32 v) const { return v == 0; };
bool IsDefaultValue(int64 v) const { return v == 0; };
bool IsDefaultValue(uint64 v) const { return v == 0; };
bool IsDefaultValue(const Vec2& v) const { return v.x == 0 && v.y == 0; };
bool IsDefaultValue(const Vec3& v) const { return v.x == 0 && v.y == 0 && v.z == 0; };
bool IsDefaultValue(const Ang3& v) const { return v.x == 0 && v.y == 0 && v.z == 0; };
bool IsDefaultValue(const Quat& v) const { return v.w == 1.0f && v.v.x == 0 && v.v.y == 0 && v.v.z == 0; };
bool IsDefaultValue(const CTimeValue& v) const { return v.GetValue() == 0; };
bool IsDefaultValue(const char* str) const { return !str || !*str; };
bool IsDefaultValue(const string& str) const { return str.empty(); };
bool IsDefaultValue(const SSerializeString& str) const { return str.empty(); };
//////////////////////////////////////////////////////////////////////////
/*
template <class T>
bool IsDefaultValue( const T& v ) const { return false; };
*/
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
@@ -0,0 +1,430 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "ReadWriteXMLSink.h"
#include <stack>
typedef std::map<string, XmlNodeRef> IdTable;
static bool IsOptionalWriteXML(XmlNodeRef& definition);
static bool SaveTableInner(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveReferencedId(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveSomething(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveArray(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveProperty(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveTable(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
static bool SaveArraySetValueTable(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem);
typedef bool (* SaveArraySetValue)(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem);
typedef bool (* SaveDefinitionFunction)(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
template <class T>
struct WritePropertyTyped;
template <class T>
struct WritePropertyTyped
{
static bool Save(const char* name, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
IWriteXMLSource::TValue vvalue((T()));
if (!pSource->GetValue(name, vvalue, definition))
{
return false;
}
T* pValue = AZStd::get_if<T>(&vvalue);
if (!pValue)
{
return false;
}
data->setAttr(name, *pValue);
return true;
}
static bool SaveArray([[maybe_unused]] const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem)
{
IWriteXMLSource::TValue vvalue((T()));
if (!pSource->GetAt(elem, vvalue, definition))
{
return false;
}
T* pValue = AZStd::get_if<T>(&vvalue);
if (!pValue)
{
return false;
}
data->setAttr("value", *pValue);
return true;
}
};
template <>
struct WritePropertyTyped<string>
: public WritePropertyTyped<const char*>
{
};
bool IsOptionalWriteXML(XmlNodeRef& definition)
{
bool optional = false;
definition->getAttr("optional", optional);
return optional;
}
bool SaveProperty([[maybe_unused]] const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Property has no name");
return false;
}
const char* type = definition->getAttr("type");
if (0 == strlen(type))
{
CryLog("Property '%s' has no type", type);
return false;
}
if (IsOptionalWriteXML(definition) && !pSource->HaveValue(name))
{
return true;
}
bool ok = false;
#define SAVE_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) ok = WritePropertyTyped<whichType>::Save(name, definition, data, pSource)
XML_SET_PROPERTY_HELPER(SAVE_PROPERTY);
#undef SAVE_PROPERTY
if (!ok)
{
CryLog("Failed loading attribute %s of type %s", name, type);
}
return ok;
}
bool SaveArraySetValueTable(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem)
{
IWriteXMLSourcePtr pChildSource = pSource->BeginTableAt(elem);
if (!pChildSource)
{
CryLog("Failed to find source table at %d", elem);
return false;
}
if (!SaveTableInner(idTable, definition, data, &*pChildSource))
{
return false;
}
if (!pChildSource->EndTableAt(elem))
{
CryLog("Failed to finish table at element %d", elem);
return false;
}
return true;
}
bool SaveArray(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Array has no name");
return false;
}
const char* elementName = definition->getAttr("elementName");
if (0 == strlen(elementName))
{
elementName = "element";
}
bool validateArray = true;
if (definition->haveAttr(elementName))
{
definition->getAttr("validate", validateArray);
}
size_t numElems = 0;
IWriteXMLSourcePtr childSource = pSource->BeginArray(name, &numElems, definition);
if (!childSource)
{
bool ok = IsOptionalWriteXML(definition);
if (!ok)
{
CryLog("Failed to begin array named %s", name);
}
return ok;
}
XmlNodeRef childData = data->createNode(name);
SaveArraySetValue setter = NULL;
if (definition->haveAttr("type"))
{
setter = NULL;
const char* type = definition->getAttr("type");
#define SETTER_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) setter = WritePropertyTyped<whichType>::SaveArray
XML_SET_PROPERTY_HELPER(SETTER_PROPERTY);
#undef SETTER_PROPERTY
if (!setter)
{
CryLog("Unknown type %s in array %s", type, name);
return false;
}
}
else
{
setter = SaveArraySetValueTable;
}
bool needIndex = false;
for (size_t i = 1; i <= numElems; i++)
{
if (!childSource->HaveElemAt(i))
{
needIndex = true;
}
else
{
XmlNodeRef elemData = childData->createNode(elementName);
if (needIndex)
{
elemData->setAttr("_index", i);
}
needIndex = false;
if (!setter(idTable, definition, elemData, &*childSource, i))
{
CryLog("Failed saving element %d of array %s", int(i), name);
return false;
}
childData->addChild(elemData);
}
}
if (!pSource->EndArray(name))
{
CryLog("Failed to finish array named %s", name);
return false;
}
data->addChild(childData);
return true;
}
bool SaveTable(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
const char* name = definition->getAttr("name");
if (0 == strlen(name))
{
CryLog("Child-table has no name");
return false;
}
IWriteXMLSourcePtr childSource = pSource->BeginTable(name);
if (!childSource)
{
bool ok = IsOptionalWriteXML(definition);
if (!ok)
{
CryLog("Source creation failed for table %s", name);
}
return ok;
}
XmlNodeRef childData = data->createNode(name);
if (!SaveTableInner(idTable, definition, childData, childSource))
{
CryLog("Failed to load data for child table %s", name);
return false;
}
if (!pSource->EndTable(name))
{
CryLog("Table %s failed to complete in sink", name);
return false;
}
data->addChild(childData);
return true;
}
bool SaveSomething(const IdTable& idTable, XmlNodeRef& nodeDefinition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
static struct
{
const char* name;
SaveDefinitionFunction saver;
} saverTypes[] = {
{"Property", &SaveProperty},
{"Array", &SaveArray},
{"Table", &SaveTable},
{"Use", &SaveReferencedId},
};
static const int numSaverTypes = sizeof(saverTypes) / sizeof(*saverTypes);
const char* nodeDefinitionTag = nodeDefinition->getTag();
bool ok = false;
int i;
for (i = 0; i < numSaverTypes; i++)
{
if (0 == strcmp(saverTypes[i].name, nodeDefinitionTag))
{
ok = saverTypes[i].saver(idTable, nodeDefinition, data, pSource);
break;
}
}
if (!ok)
{
if (i == numSaverTypes)
{
CryLog("Invalid definition node type %s", nodeDefinitionTag);
}
}
return ok;
}
bool SaveReferencedId(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
IdTable::const_iterator iter = idTable.find(definition->getAttr("id"));
if (iter == idTable.end())
{
CryLog("No definition with id '%s'", definition->getAttr("id"));
return false;
}
XmlNodeRef useDefinition = iter->second;
useDefinition = useDefinition->clone();
int numAttrs = definition->getNumAttributes();
for (int i = 0; i < numAttrs; i++)
{
const char* key, * value;
definition->getAttributeByIndex(i, &key, &value);
useDefinition->setAttr(key, value);
}
return SaveSomething(idTable, useDefinition, data, pSource);
}
bool SaveTableInner(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
{
const int nChildrenDefinition = definition->getChildCount();
for (int nChildDefinition = 0; nChildDefinition < nChildrenDefinition; nChildDefinition++)
{
XmlNodeRef nodeDefinition = definition->getChild(nChildDefinition);
if (!SaveSomething(idTable, nodeDefinition, data, pSource))
{
return false;
}
}
return true;
}
XmlNodeRef CReadWriteXMLSink::CreateXMLFromSource(const char* definitionFile, IWriteXMLSource* pSource)
{
XmlNodeRef rootDefinition = GetISystem()->LoadXmlFromFile(definitionFile);
if (!rootDefinition)
{
CryLog("Unable to load XML-Lua definition file: %s", definitionFile);
return 0;
}
if (0 != strcmp(rootDefinition->getTag(), "Definition"))
{
CryLog("Root tag of definition file was %s; expected Definition", rootDefinition->getTag());
return 0;
}
const char* rootNode = "Root";
if (rootDefinition->haveAttr("root"))
{
rootNode = rootDefinition->getAttr("root");
}
XmlNodeRef rootData = GetISystem()->CreateXmlNode(rootNode);
XmlNodeRef allowAlways = rootDefinition->findChild("AllowAlways");
if (allowAlways != 0)
{
rootDefinition->removeChild(allowAlways);
}
XmlNodeRef settingsParams = rootDefinition->findChild("Settings");
if (settingsParams != 0)
{
rootDefinition->removeChild(settingsParams);
}
// scan for id's in the structure (for the Use member)
IdTable idTable;
std::stack<XmlNodeRef> scanStack;
scanStack.push(rootDefinition);
while (!scanStack.empty())
{
XmlNodeRef refNode = scanStack.top();
scanStack.pop();
int numChildren = refNode->getChildCount();
const char* tag = refNode->getTag();
for (int i = 0; i < numChildren; i++)
{
scanStack.push(refNode->getChild(i));
}
if (refNode->haveAttr("id") && 0 != strcmp("Use", tag))
{
idTable[refNode->getAttr("id")] = refNode;
}
if (allowAlways != 0 && (!strcmp("Table", tag) || !strcmp("Array", tag)))
{
for (int i = 0; i < allowAlways->getChildCount(); ++i)
{
refNode->addChild(allowAlways->getChild(i)->clone());
}
}
}
if (!SaveTableInner(idTable, rootDefinition, rootData, pSource))
{
CryLog("Error createing xml using definition %s", definitionFile);
return 0;
}
bool ok = pSource->Complete();
if (!ok)
{
CryLog("Warning: sink failed to complete writing");
return 0;
}
return rootData;
}
bool CReadWriteXMLSink::WriteXML(const char* definitionFile, const char* dataFile, IWriteXMLSource* pSource)
{
XmlNodeRef data = CreateXMLFromSource(definitionFile, pSource);
if (!data)
{
CryLog("Failed creating %s", dataFile);
return false;
}
if (!data->saveToFile(dataFile))
{
CryLog("Failed saving %s", dataFile);
return false;
}
return true;
}
+381
View File
@@ -0,0 +1,381 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <platform.h>
#include "XMLBinaryNode.h"
#include <CrySizer.h>
//////////////////////////////////////////////////////////////////////////
CBinaryXmlData::CBinaryXmlData()
: pNodes(0)
, pAttributes(0)
, pChildIndices(0)
, pStringData(0)
, pFileContents(0)
, nFileSize(0)
, bOwnsFileContentsMemory(true)
, pBinaryNodes(0)
, nRefCount(0)
{
}
//////////////////////////////////////////////////////////////////////////
CBinaryXmlData::~CBinaryXmlData()
{
if (bOwnsFileContentsMemory)
{
delete [] pFileContents;
}
pFileContents = 0;
delete [] pBinaryNodes;
pBinaryNodes = 0;
}
void CBinaryXmlData::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(pFileContents, nFileSize);
const XMLBinary::BinaryFileHeader* pHeader = reinterpret_cast<const XMLBinary::BinaryFileHeader*>(pFileContents);
pSizer->AddObject(pBinaryNodes, sizeof(CBinaryXmlNode) * pHeader->nNodeCount);
}
//////////////////////////////////////////////////////////////////////////
// CBinaryXmlNode implementation.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// collect allocated memory informations
void CBinaryXmlNode::GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(m_pData);
}
//////////////////////////////////////////////////////////////////////////
XmlNodeRef CBinaryXmlNode::getParent() const
{
const XMLBinary::Node* const pNode = _node();
if (pNode->nParentIndex != (XMLBinary::NodeIndex)-1)
{
return &m_pData->pBinaryNodes[pNode->nParentIndex];
}
return XmlNodeRef();
}
XmlNodeRef CBinaryXmlNode::createNode([[maybe_unused]] const char* tag)
{
assert(0);
return 0;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::isTag(const char* tag) const
{
return g_pXmlStrCmp(tag, getTag()) == 0;
}
const char* CBinaryXmlNode::getAttr(const char* key) const
{
const char* svalue = GetValue(key);
if (svalue)
{
return svalue;
}
return "";
}
bool CBinaryXmlNode::getAttr(const char* key, const char** value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
*value = svalue;
return true;
}
else
{
*value = "";
return false;
}
}
bool CBinaryXmlNode::haveAttr(const char* key) const
{
return (GetValue(key) != 0);
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, int& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
value = atoi(svalue);
return true;
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, unsigned int& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
value = strtoul(svalue, NULL, 10);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, int64& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
azsscanf(svalue, "%" PRId64, &value);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, uint64& value, bool useHexFormat) const
{
const char* svalue = GetValue(key);
if (svalue)
{
if (useHexFormat)
{
azsscanf(svalue, "%" PRIX64, &value);
}
else
{
azsscanf(svalue, "%" PRIu64, &value);
}
return true;
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, bool& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
value = atoi(svalue) != 0;
return true;
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, float& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
value = (float)atof(svalue);
return true;
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, double& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
value = atof(svalue);
return true;
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, Ang3& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
float x, y, z;
if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
{
value(x, y, z);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec3& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
float x, y, z;
if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
{
value = Vec3(x, y, z);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec4& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
float x, y, z, w;
if (azsscanf(svalue, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
{
value = Vec4(x, y, z, w);
return true;
}
}
return false;
}
bool CBinaryXmlNode::getAttr(const char* key, Vec3d& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
double x, y, z;
if (azsscanf(svalue, "%lf,%lf,%lf", &x, &y, &z) == 3)
{
value = Vec3d(x, y, z);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec2& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
float x, y;
if (azsscanf(svalue, "%f,%f", &x, &y) == 2)
{
value = Vec2(x, y);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Vec2d& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
double x, y;
if (azsscanf(svalue, "%lf,%lf", &x, &y) == 2)
{
value = Vec2d(x, y);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, Quat& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
float w, x, y, z;
if (azsscanf(svalue, "%f,%f,%f,%f", &w, &x, &y, &z) == 4)
{
value = Quat(w, x, y, z);
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttr(const char* key, ColorB& value) const
{
const char* svalue = GetValue(key);
if (svalue)
{
unsigned int r, g, b, a = 255;
int numFound = azsscanf(svalue, "%u,%u,%u,%u", &r, &g, &b, &a);
if (numFound == 3 || numFound == 4)
{
// If we only found 3 values, a should be unchanged, and still be 255
if (r < 256 && g < 256 && b < 256 && a < 256)
{
value = ColorB(r, g, b, a);
return true;
}
}
}
return false;
}
XmlNodeRef CBinaryXmlNode::findChild(const char* tag) const
{
const XMLBinary::Node* const pNode = _node();
const uint32 nFirst = pNode->nFirstChildIndex;
const uint32 nAfterLast = pNode->nFirstChildIndex + pNode->nChildCount;
for (uint32 i = nFirst; i < nAfterLast; ++i)
{
const char* sChildTag = m_pData->pStringData + m_pData->pNodes[m_pData->pChildIndices[i]].nTagStringOffset;
if (g_pXmlStrCmp(tag, sChildTag) == 0)
{
return m_pData->pBinaryNodes + m_pData->pChildIndices[i];
}
}
return 0;
}
//! Get XML Node child nodes.
XmlNodeRef CBinaryXmlNode::getChild(int i) const
{
const XMLBinary::Node* const pNode = _node();
assert(i >= 0 && i < (int)pNode->nChildCount);
return m_pData->pBinaryNodes + m_pData->pChildIndices[pNode->nFirstChildIndex + i];
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttributeByIndex(int index, const char** key, const char** value)
{
const XMLBinary::Node* const pNode = _node();
if (index >= 0 && index < pNode->nAttributeCount)
{
const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
*key = _string(attr.nKeyStringOffset);
*value = _string(attr.nValueStringOffset);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CBinaryXmlNode::getAttributeByIndex(int index, XmlString& key, XmlString& value)
{
const XMLBinary::Node* const pNode = _node();
if (index >= 0 && index < pNode->nAttributeCount)
{
const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
key = _string(attr.nKeyStringOffset);
value = _string(attr.nValueStringOffset);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
+238
View File
@@ -0,0 +1,238 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
#pragma once
#include <algorithm>
#include "IXml.h"
#include "XMLBinaryHeaders.h"
// Compare function for string comparison, can be strcmp or _stricmp
typedef int (__cdecl * XmlStrCmpFunc)(const char* str1, const char* str2);
extern XmlStrCmpFunc g_pXmlStrCmp;
class CBinaryXmlNode;
//////////////////////////////////////////////////////////////////////////
class CBinaryXmlData
{
public:
const XMLBinary::Node* pNodes;
const XMLBinary::Attribute* pAttributes;
const XMLBinary::NodeIndex* pChildIndices;
const char* pStringData;
const char* pFileContents;
size_t nFileSize;
bool bOwnsFileContentsMemory;
CBinaryXmlNode* pBinaryNodes;
int nRefCount;
CBinaryXmlData();
~CBinaryXmlData();
void GetMemoryUsage(ICrySizer* pSizer) const;
};
// forward declaration
namespace XMLBinary
{
class XMLBinaryReader;
};
//////////////////////////////////////////////////////////////////////////
// CBinaryXmlNode class only used for fast read only binary XML import
//////////////////////////////////////////////////////////////////////////
class CBinaryXmlNode
: public IXmlNode
{
public:
// collect allocated memory informations
void GetMemoryUsage(ICrySizer* pSizer) const;
//////////////////////////////////////////////////////////////////////////
// Custom new/delete with pool allocator.
//////////////////////////////////////////////////////////////////////////
//void* operator new( size_t nSize );
//void operator delete( void *ptr );
virtual void DeleteThis() { }
//! Create new XML node.
XmlNodeRef createNode(const char* tag);
// Summary:
// Reference counting.
virtual void AddRef() { ++m_pData->nRefCount; };
// Notes:
// When ref count reach zero XML node dies.
virtual void Release()
{
if (--m_pData->nRefCount <= 0)
{
delete m_pData;
}
};
//! Get XML node tag.
const char* getTag() const { return _string(_node()->nTagStringOffset); };
void setTag([[maybe_unused]] const char* tag) { assert(0); };
//! Return true if given tag is equal to node tag.
bool isTag(const char* tag) const;
//! Get XML Node attributes.
virtual int getNumAttributes() const { return (int)_node()->nAttributeCount; };
//! Return attribute key and value by attribute index.
virtual bool getAttributeByIndex(int index, const char** key, const char** value);
//! Return attribute key and value by attribute index, string version.
virtual bool getAttributeByIndex(int index, XmlString& key, XmlString& value);
virtual void shareChildren([[maybe_unused]] const XmlNodeRef& fromNode) { assert(0); };
virtual void copyAttributes(XmlNodeRef fromNode) { assert(0); };
//! Get XML Node attribute for specified key.
const char* getAttr(const char* key) const;
//! Get XML Node attribute for specified key.
// Returns true if the attribute exists, false otherwise.
bool getAttr(const char* key, const char** value) const;
//! Check if attributes with specified key exist.
bool haveAttr(const char* key) const;
XmlNodeRef newChild([[maybe_unused]] const char* tagName) { assert(0); return 0; };
void replaceChild([[maybe_unused]] int inChild, [[maybe_unused]] const XmlNodeRef& node) { assert(0); };
void insertChild([[maybe_unused]] int inChild, [[maybe_unused]] const XmlNodeRef& node) { assert(0); };
void addChild([[maybe_unused]] const XmlNodeRef& node) { assert(0); };
void removeChild([[maybe_unused]] const XmlNodeRef& node) { assert(0); };
//! Remove all child nodes.
void removeAllChilds() { assert(0); };
//! Get number of child XML nodes.
int getChildCount() const { return (int)_node()->nChildCount; };
//! Get XML Node child nodes.
XmlNodeRef getChild(int i) const;
//! Find node with specified tag.
XmlNodeRef findChild(const char* tag) const;
void deleteChild([[maybe_unused]] const char* tag) { assert(0); };
void deleteChildAt([[maybe_unused]] int nIndex) { assert(0); };
//! Get parent XML node.
XmlNodeRef getParent() const;
//! Returns content of this node.
const char* getContent() const { return _string(_node()->nContentStringOffset); };
void setContent([[maybe_unused]] const char* str) { assert(0); };
XmlNodeRef clone() { assert(0); return 0; };
//! Returns line number for XML tag.
int getLine() const { return 0; };
//! Set line number in xml.
void setLine([[maybe_unused]] int line) { assert(0); };
//! Returns XML of this node and sub nodes.
virtual IXmlStringData* getXMLData([[maybe_unused]] int nReserveMem = 0) const { assert(0); return 0; };
XmlString getXML([[maybe_unused]] int level = 0) const { assert(0); return ""; };
bool saveToFile([[maybe_unused]] const char* fileName) { assert(0); return false; }; // saves in one huge chunk
bool saveToFile([[maybe_unused]] const char* fileName, [[maybe_unused]] size_t chunkSizeBytes, [[maybe_unused]] AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle) { assert(0); return false; }; // save in small memory chunks
//! Set new XML Node attribute (or override attribute with same key).
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const char* value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] int value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] unsigned int value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] int64 value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] uint64 value, [[maybe_unused]] bool useHexFormat = true /* ignored */) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] float value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] f64 value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec2& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec2d& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Ang3& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec3& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec4& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec3d& value) { assert(0); };
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Quat& value) { assert(0); };
void delAttr([[maybe_unused]] const char* key) { assert(0); };
void removeAllAttributes() { assert(0); };
//! Get attribute value of node.
bool getAttr(const char* key, int& value) const;
bool getAttr(const char* key, unsigned int& value) const;
bool getAttr(const char* key, int64& value) const;
bool getAttr(const char* key, uint64& value, bool useHexFormat = true /* ignored */) const;
bool getAttr(const char* key, float& value) const;
bool getAttr(const char* key, f64& value) const;
bool getAttr(const char* key, bool& value) const;
bool getAttr(const char* key, XmlString& value) const {const char* v(NULL); bool boHasAttribute(getAttr(key, &v)); value = v; return boHasAttribute; }
bool getAttr(const char* key, Vec2& value) const;
bool getAttr(const char* key, Vec2d& value) const;
bool getAttr(const char* key, Ang3& value) const;
bool getAttr(const char* key, Vec3& value) const;
bool getAttr(const char* key, Vec4& value) const;
bool getAttr(const char* key, Vec3d& value) const;
bool getAttr(const char* key, Quat& value) const;
bool getAttr(const char* key, ColorB& value) const;
// bool getAttr( const char *key,CString &value ) const { XmlString v; if (getAttr(key,v)) { value = (const char*)v; return true; } else return false; }
private:
//////////////////////////////////////////////////////////////////////////
// INTERNAL METHODS
//////////////////////////////////////////////////////////////////////////
const char* GetValue(const char* key) const
{
const XMLBinary::Attribute* const pAttributes = m_pData->pAttributes;
const char* const pStringData = m_pData->pStringData;
const int nFirst = _node()->nFirstAttributeIndex;
const int nLast = nFirst + _node()->nAttributeCount;
for (int i = nFirst; i < nLast; i++)
{
const char* const attrKey = pStringData + pAttributes[i].nKeyStringOffset;
if (g_pXmlStrCmp(key, attrKey) == 0)
{
const char* attrValue = pStringData + pAttributes[i].nValueStringOffset;
return attrValue;
}
}
return 0;
}
// Return current node in binary data.
const XMLBinary::Node* _node() const
{
return &m_pData->pNodes[this - m_pData->pBinaryNodes];
}
const char* _string(int nIndex) const
{
return m_pData->pStringData + nIndex;
}
protected:
virtual void setParent([[maybe_unused]] const XmlNodeRef& inRef) { assert(0); }
//////////////////////////////////////////////////////////////////////////
private:
CBinaryXmlData* m_pData;
friend class XMLBinary::XMLBinaryReader;
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
@@ -0,0 +1,271 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <platform.h>
#include "XMLBinaryReader.h"
#include "XMLBinaryNode.h"
#include "CryPath.h"
XMLBinary::XMLBinaryReader::XMLBinaryReader()
{
m_errorDescription[0] = 0;
}
XMLBinary::XMLBinaryReader::~XMLBinaryReader()
{
}
const char* XMLBinary::XMLBinaryReader::GetErrorDescription() const
{
return &m_errorDescription[0];
}
void XMLBinary::XMLBinaryReader::SetErrorDescription(const char* text)
{
cry_strcpy(m_errorDescription, text);
}
XmlNodeRef XMLBinary::XMLBinaryReader::LoadFromFile(
const char* filename,
XMLBinary::XMLBinaryReader::EResult& result)
{
m_errorDescription[0] = 0;
result = eResult_Error;
CCryFile xmlFile;
if (!xmlFile.Open(filename, "rb"))
{
SetErrorDescription("Can't open file.");
return 0;
}
const size_t fileSize = xmlFile.GetLength();
if (fileSize < sizeof(BinaryFileHeader))
{
result = eResult_NotBinXml;
SetErrorDescription("File is not a binary XML file (file size is too small).");
return 0;
}
// Read in the entire file - this buffer will not be deallocated immediately, since the nodes
// will contain pointers directly into it. It will be deleted once the reference count on the
// CBinaryXmlData object reaches 0 again.
char* const pFileContents = new char[fileSize];
if (!pFileContents)
{
SetErrorDescription("Can't allocate memory for binary XML file contents.");
return 0;
}
if (xmlFile.ReadRaw(pFileContents, fileSize) != fileSize)
{
delete [] pFileContents;
SetErrorDescription("Failed to read binary XML file, the file is corrupt.");
return 0;
}
Check(pFileContents, fileSize, result);
if (result != eResult_Success)
{
delete [] pFileContents;
return 0;
}
CBinaryXmlData* const pData = Create(pFileContents, fileSize, result);
if (result != eResult_Success)
{
assert(pData == 0);
delete [] pFileContents;
return 0;
}
assert(pData);
pData->bOwnsFileContentsMemory = true;
// Return first node
return &pData->pBinaryNodes[0];
}
XmlNodeRef XMLBinary::XMLBinaryReader::LoadFromBuffer(
EBufferMemoryHandling bufferMemoryHandling,
const char* buffer,
size_t size,
XMLBinary::XMLBinaryReader::EResult& result)
{
m_errorDescription[0] = 0;
result = eResult_Error;
Check(buffer, size, result);
if (result != eResult_Success)
{
return 0;
}
CBinaryXmlData* pData = 0;
if (bufferMemoryHandling == eBufferMemoryHandling_MakeCopy)
{
char* ownBuffer = new char[size];
if (!ownBuffer)
{
SetErrorDescription("Can't allocate memory for binary XML data.");
return 0;
}
memcpy(ownBuffer, buffer, size);
pData = Create(ownBuffer, size, result);
if (result != eResult_Success)
{
assert(pData == 0);
delete [] ownBuffer;
return 0;
}
}
else
{
assert(bufferMemoryHandling == eBufferMemoryHandling_TakeOwnership);
pData = Create(buffer, size, result);
if (result != eResult_Success)
{
assert(pData == 0);
return 0;
}
}
assert(pData);
pData->bOwnsFileContentsMemory = true;
// Return first node
return &pData->pBinaryNodes[0];
}
void XMLBinary::XMLBinaryReader::Check(const char* buffer, size_t size, EResult& result)
{
m_errorDescription[0] = 0;
result = eResult_Error;
if (buffer == 0)
{
SetErrorDescription("Buffer is null.");
return;
}
if (size < sizeof(BinaryFileHeader))
{
result = eResult_NotBinXml;
SetErrorDescription("Not a binary XML - data size is too small.");
return;
}
const BinaryFileHeader& header = *(reinterpret_cast<const BinaryFileHeader*>(buffer));
CheckHeader(header, size, result);
}
void XMLBinary::XMLBinaryReader::CheckHeader(const BinaryFileHeader& header, size_t size, EResult& result)
{
assert(size >= sizeof(BinaryFileHeader));
m_errorDescription[0] = 0;
// Check the signature of the file to make sure that it is a binary XML file.
{
static const char signature[] = "CryXmlB";
COMPILE_TIME_ASSERT(sizeof(signature) == sizeof(header.szSignature));
if (memcmp(header.szSignature, signature, sizeof(header.szSignature)) != 0)
{
result = eResult_NotBinXml;
SetErrorDescription("Not a binary XML - has no signature.");
return;
}
}
// Check contents of the file header.
const uint32 nNodeTableEnd = header.nNodeTablePosition + header.nNodeCount * sizeof(Node);
const uint32 nChildTableEnd = header.nChildTablePosition + header.nChildCount * sizeof(NodeIndex);
const uint32 nAttributeTableEnd = header.nAttributeTablePosition + header.nAttributeCount * sizeof(Attribute);
const uint32 nStringDataEnd = header.nStringDataPosition + header.nStringDataSize;
bool bCorrupt = false;
bCorrupt = bCorrupt || header.nXMLSize > size;
bCorrupt = bCorrupt || nNodeTableEnd > header.nChildTablePosition;
bCorrupt = bCorrupt || nChildTableEnd > header.nAttributeTablePosition;
bCorrupt = bCorrupt || nAttributeTableEnd > header.nStringDataPosition;
bCorrupt = bCorrupt || nStringDataEnd > header.nXMLSize;
if (bCorrupt)
{
result = eResult_Error;
SetErrorDescription("Binary XML data is corrupt.");
return;
}
result = eResult_Success;
}
CBinaryXmlData* XMLBinary::XMLBinaryReader::Create(const char* buffer, size_t size, EResult& result)
{
assert((buffer != 0) && (size >= sizeof(BinaryFileHeader)));
m_errorDescription[0] = 0;
result = eResult_Error;
CBinaryXmlData* const pData = new CBinaryXmlData;
if (!pData)
{
SetErrorDescription("Can't allocate memory for binary XML object.");
return 0;
}
pData->pFileContents = buffer;
pData->nFileSize = size;
pData->bOwnsFileContentsMemory = false;
const BinaryFileHeader& header = *(reinterpret_cast<const BinaryFileHeader*>(buffer));
// Create nodes
pData->pBinaryNodes = new CBinaryXmlNode[header.nNodeCount];
if (!pData->pBinaryNodes)
{
delete pData;
SetErrorDescription("Can't allocate memory for binary XML nodes.");
return 0;
}
pData->pAttributes = reinterpret_cast<const Attribute*>(buffer + header.nAttributeTablePosition);
pData->pChildIndices = reinterpret_cast<const NodeIndex*>(buffer + header.nChildTablePosition);
pData->pNodes = reinterpret_cast<const Node*>(buffer + header.nNodeTablePosition);
pData->pStringData = buffer + header.nStringDataPosition;
for (uint32 nNode = 0; nNode < header.nNodeCount; ++nNode)
{
CBinaryXmlNode* const pNode = &pData->pBinaryNodes[nNode];
pNode->m_nRefCount = 0;
pNode->m_pData = pData;
}
result = eResult_Success;
return pData;
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
#pragma once
#include "XMLBinaryHeaders.h"
#include "IXml.h"
#include "CryFile.h"
class CBinaryXmlData;
namespace XMLBinary
{
class XMLBinaryReader
{
public:
enum EResult
{
eResult_Success,
eResult_NotBinXml,
eResult_Error
};
enum EBufferMemoryHandling
{
eBufferMemoryHandling_MakeCopy,
eBufferMemoryHandling_TakeOwnership
};
public:
XMLBinaryReader();
~XMLBinaryReader();
XmlNodeRef LoadFromFile(const char* filename, EResult& result);
// Note: if bufferMemoryHandling == eBufferMemoryHandling_TakeOwnership and
// returned result is eResult_Success, then buffer's memory is owned and
// will be released by XMLBinaryReader (by a 'delete[] buffer' call).
// Otherwise, the caller is responsible for releasing buffer's memory.
XmlNodeRef LoadFromBuffer(EBufferMemoryHandling bufferMemoryHandling, const char* buffer, size_t size, EResult& result);
const char* GetErrorDescription() const;
private:
void Check(const char* buffer, size_t size, EResult& result);
void CheckHeader(const BinaryFileHeader& layout, size_t size, EResult& result);
CBinaryXmlData* Create(const char* buffer, size_t size, EResult& result);
void SetErrorDescription(const char* text);
private:
char m_errorDescription[64];
};
}
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
@@ -0,0 +1,335 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <platform.h>
#include "XMLBinaryWriter.h"
#include "CryEndian.h"
#include <string.h> // memcpy()
//////////////////////////////////////////////////////////////////////////
namespace XMLBinary
{
void SwapEndianness_Node(Node& t)
{
SwapEndian(t.nTagStringOffset, true);
SwapEndian(t.nContentStringOffset, true);
SwapEndian(t.nAttributeCount, true);
SwapEndian(t.nChildCount, true);
SwapEndian(t.nParentIndex, true);
SwapEndian(t.nFirstAttributeIndex, true);
SwapEndian(t.nFirstChildIndex, true);
}
void SwapEndianness_Attribute(Attribute& t)
{
SwapEndian(t.nKeyStringOffset, true);
SwapEndian(t.nValueStringOffset, true);
}
void SwapEndianness_Header(BinaryFileHeader& t)
{
SwapEndian(t.nXMLSize, true);
SwapEndian(t.nNodeTablePosition, true);
SwapEndian(t.nNodeCount, true);
SwapEndian(t.nAttributeTablePosition, true);
SwapEndian(t.nAttributeCount, true);
SwapEndian(t.nChildTablePosition, true);
SwapEndian(t.nChildCount, true);
SwapEndian(t.nStringDataPosition, true);
SwapEndian(t.nStringDataSize, true);
}
}
//////////////////////////////////////////////////////////////////////////
XMLBinary::CXMLBinaryWriter::CXMLBinaryWriter()
{
m_nStringDataSize = 0;
}
static void align(size_t& nPosition, const size_t nAlignment)
{
const size_t nPadSize = ((nPosition + (nAlignment - 1)) & ~(nAlignment - 1)) - nPosition;
nPosition += nPadSize;
}
static void alignWrite(XMLBinary::IDataWriter* const pFile, size_t& nPosition, const size_t nAlignment)
{
size_t nPadSize = ((nPosition + (nAlignment - 1)) & ~(nAlignment - 1)) - nPosition;
if (nPadSize > 0)
{
nPosition += nPadSize;
static const char zeroes[32] = { 0 };
while (nPadSize > 0)
{
const size_t n = (nPadSize <= sizeof(zeroes)) ? nPadSize : sizeof(zeroes);
nPadSize -= n;
pFile->Write(zeroes, n);
}
}
}
static void write(XMLBinary::IDataWriter* const pFile, size_t& nPosition, const void* const pData, const size_t nDataSize)
{
pFile->Write(pData, nDataSize);
nPosition += nDataSize;
}
//////////////////////////////////////////////////////////////////////////
bool XMLBinary::CXMLBinaryWriter::WriteNode(IDataWriter* pFile, XmlNodeRef node, bool bNeedSwapEndian, XMLBinary::IFilter* pFilter, string& error)
{
error = "";
// Scan the node tree, building a flat node list, attribute list and string table.
m_nStringDataSize = 0;
if (!CompileTables(node, pFilter, error))
{
return false;
}
static const uint nMaxNodeCount = (NodeIndex) ~0;
if (m_nodes.size() > nMaxNodeCount)
{
error.Format("XMLBinary: Too many nodes: %d (max is %i)", m_nodes.size(), nMaxNodeCount);
return false;
}
// Initialize the file header.
size_t nTheoreticalPosition = 0;
static const size_t nAlignment = sizeof(uint32);
BinaryFileHeader header;
static const char signature[] = "CryXmlB";
COMPILE_TIME_ASSERT(sizeof(signature) == sizeof(header.szSignature));
memcpy(header.szSignature, signature, sizeof(header.szSignature));
nTheoreticalPosition += sizeof(header);
align(nTheoreticalPosition, nAlignment);
header.nNodeTablePosition = nTheoreticalPosition;
header.nNodeCount = int(m_nodes.size());
nTheoreticalPosition += header.nNodeCount * sizeof(Node);
align(nTheoreticalPosition, nAlignment);
header.nChildTablePosition = nTheoreticalPosition;
header.nChildCount = int(m_childs.size());
nTheoreticalPosition += header.nChildCount * sizeof(NodeIndex);
align(nTheoreticalPosition, nAlignment);
header.nAttributeTablePosition = nTheoreticalPosition;
header.nAttributeCount = int(m_attributes.size());
nTheoreticalPosition += header.nAttributeCount * sizeof(Attribute);
align(nTheoreticalPosition, nAlignment);
header.nStringDataPosition = nTheoreticalPosition;
header.nStringDataSize = m_nStringDataSize;
nTheoreticalPosition += header.nStringDataSize;
header.nXMLSize = nTheoreticalPosition;
// Swap endianness of the data structures
if (bNeedSwapEndian)
{
SwapEndianness_Header(header);
for (size_t i = 0, iCount = m_nodes.size(); i < iCount; ++i)
{
SwapEndianness_Node(m_nodes[i]);
}
for (size_t i = 0, iCount = m_attributes.size(); i < iCount; ++i)
{
SwapEndianness_Attribute(m_attributes[i]);
}
for (size_t i = 0, iCount = m_childs.size(); i < iCount; ++i)
{
SwapEndian(m_childs[i], true);
}
}
// Write file
{
nTheoreticalPosition = 0;
// Write out the file header.
write(pFile, nTheoreticalPosition, &header, sizeof(header));
alignWrite(pFile, nTheoreticalPosition, nAlignment);
// Write out the node table.
if (!m_nodes.empty())
{
write(pFile, nTheoreticalPosition, &m_nodes[0], sizeof(m_nodes[0]) * m_nodes.size());
alignWrite(pFile, nTheoreticalPosition, nAlignment);
}
// Write out the children table.
if (!m_childs.empty())
{
write(pFile, nTheoreticalPosition, &m_childs[0], sizeof(m_childs[0]) * m_childs.size());
alignWrite(pFile, nTheoreticalPosition, nAlignment);
}
// Write out the attribute table.
if (!m_attributes.empty())
{
write(pFile, nTheoreticalPosition, &m_attributes[0], sizeof(m_attributes[0]) * m_attributes.size());
alignWrite(pFile, nTheoreticalPosition, nAlignment);
}
// Write out the data of all the m_strings.
for (size_t nString = 0; nString < m_strings.size(); ++nString)
{
pFile->Write(m_strings[nString].c_str(), m_strings[nString].size() + 1);
}
}
return true;
}
bool XMLBinary::CXMLBinaryWriter::CompileTables(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error)
{
bool ok = CompileTablesForNode(node, -1, pFilter, error);
ok = ok && CompileChildTable(node, pFilter, error);
return ok;
}
//////////////////////////////////////////////////////////////////////////
bool XMLBinary::CXMLBinaryWriter::CompileTablesForNode(XmlNodeRef node, int nParentIndex, XMLBinary::IFilter* pFilter, string& error)
{
// Add the tag to the string table.
int nTagStringOffset = AddString(node->getTag());
// Add the content string to the string table.
int nContentStringOffset = AddString(node->getContent());
// Add all the attributes to the attributes table.
const char* szKey;
const char* szValue;
const int nFirstAttributeIndex = int(m_attributes.size());
for (int i = 0, attrCount = node->getNumAttributes(); i < attrCount; ++i)
{
if (node->getAttributeByIndex(i, &szKey, &szValue) &&
(!pFilter || pFilter->IsAccepted(IFilter::eType_AttributeName, szKey)))
{
// Add the key and the value to the string table.
Attribute attribute;
attribute.nKeyStringOffset = AddString(szKey);
attribute.nValueStringOffset = AddString(szValue);
// Add the attribute to the attribute table.
m_attributes.push_back(attribute);
}
}
const int nAttributeCount = int(m_attributes.size()) - nFirstAttributeIndex;
static const int nMaxAttributeCount = (uint16) ~0;
if (nAttributeCount > nMaxAttributeCount)
{
error.Format("XMLBinary: Too many attributes in a node: %d (max is %i)", nAttributeCount, nMaxAttributeCount);
return false;
}
// Add ourselves to the node list.
const int nIndex = int(m_nodes.size());
{
Node nd;
memset(&nd, 0, sizeof(nd));
nd.nTagStringOffset = nTagStringOffset;
nd.nContentStringOffset = nContentStringOffset;
nd.nParentIndex = nParentIndex;
nd.nFirstAttributeIndex = nFirstAttributeIndex;
nd.nAttributeCount = nAttributeCount;
m_nodes.push_back(nd);
}
m_nodesMap.insert(NodesMap::value_type(node, nIndex));
// Recurse to the child nodes.
int nChildCount = 0;
static const int nMaxChildCount = (uint16) ~0;
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
{
XmlNodeRef childNode = node->getChild(nChild);
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
{
if (++nChildCount > nMaxChildCount)
{
error.Format("XMLBinary: Too many children in node '%s': %d (max is %i)", childNode->getTag(), nChildCount, nMaxChildCount);
return false;
}
if (!CompileTablesForNode(childNode, nIndex, pFilter, error))
{
return false;
}
}
}
m_nodes[nIndex].nChildCount = nChildCount;
return true;
}
//////////////////////////////////////////////////////////////////////////
bool XMLBinary::CXMLBinaryWriter::CompileChildTable(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error)
{
const int nIndex = m_nodesMap.find(node)->second; // Assume node always exist in map.
const int nFirstChildIndex = (int)m_childs.size();
Node& nd = m_nodes[nIndex];
nd.nFirstChildIndex = nFirstChildIndex;
int nChildCount = 0;
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
{
XmlNodeRef childNode = node->getChild(nChild);
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
{
++nChildCount;
const int nChildIndex = m_nodesMap.find(childNode)->second; // Assume node always exist in map.
m_childs.push_back(nChildIndex);
}
}
if (nChildCount != nd.nChildCount)
{
error.Format("XMLBinary: Internal error in CompileChildTable()");
return false;
}
// Recurse to the child nodes.
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
{
XmlNodeRef childNode = node->getChild(nChild);
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
{
if (!CompileChildTable(childNode, pFilter, error))
{
return false;
}
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
int XMLBinary::CXMLBinaryWriter::AddString(const XmlString& sString)
{
// If we have such string already, then we will re-use its data.
StringMap::const_iterator itStringEntry = m_stringMap.find(sString);
if (itStringEntry == m_stringMap.end())
{
// We don't have such string yet, so we should add it to the tables.
m_strings.push_back(sString);
itStringEntry = m_stringMap.insert(StringMap::value_type(sString, m_nStringDataSize)).first;
m_nStringDataSize += sString.length() + 1;
}
// Return offset of the string in the string data buffer.
return (*itStringEntry).second;
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
#pragma once
#include "IXml.h"
#include "XMLBinaryHeaders.h"
#include <vector>
#include <map>
class IXMLDataSink;
namespace XMLBinary
{
class CXMLBinaryWriter
{
public:
CXMLBinaryWriter();
bool WriteNode(IDataWriter* pFile, XmlNodeRef node, bool bNeedSwapEndian, XMLBinary::IFilter* pFilter, string& error);
private:
bool CompileTables(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error);
bool CompileTablesForNode(XmlNodeRef node, int nParentIndex, XMLBinary::IFilter* pFilter, string& error);
bool CompileChildTable(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error);
int AddString(const XmlString& sString);
private:
// tables.
typedef std::map<IXmlNode*, int> NodesMap;
typedef std::map<string, uint> StringMap;
std::vector<Node> m_nodes;
NodesMap m_nodesMap;
std::vector<Attribute> m_attributes;
std::vector<NodeIndex> m_childs;
std::vector<string> m_strings;
StringMap m_stringMap;
uint m_nStringDataSize;
};
}
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
+423
View File
@@ -0,0 +1,423 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include "XMLPatcher.h"
#include "StringUtils.h"
CXMLPatcher::CXMLPatcher(XmlNodeRef& patchXML)
{
m_patchXML = patchXML;
#if DATA_PATCH_DEBUG
m_pDumpFilesCVar = REGISTER_INT("g_datapatcher_dumpfiles", 0, NULL, "will dump a copy of every file data patched, before and after patching");
#endif
}
CXMLPatcher::~CXMLPatcher()
{
#if DATA_PATCH_DEBUG
if (IConsole* pIC = gEnv->pConsole)
{
pIC->UnregisterVariable(m_pDumpFilesCVar->GetName());
}
#endif
}
XmlNodeRef CXMLPatcher::DuplicateForPatching(
const XmlNodeRef& inOrig,
bool inShareChildren)
{
XmlNodeRef newNode(0);
if (m_patchXML)
{
newNode = m_patchXML->createNode(inOrig->getTag());
if (newNode)
{
// copy attributes in a safe way, copyAttributes() itself assumes the node being copied from is of the same type
int numAttr = inOrig->getNumAttributes();
for (int i = 0; i < numAttr; i++)
{
const char* pKey, * pValue;
if (inOrig->getAttributeByIndex(i, &pKey, &pValue))
{
newNode->setAttr(pKey, pValue);
}
}
if (inShareChildren)
{
newNode->shareChildren(inOrig);
}
}
}
return newNode;
}
void CXMLPatcher::PatchFail(
const char* pInReason)
{
CryLogAlways("Failed to apply data patch for file '%s' - reason '%s'", m_pFileBeingPatched, pInReason);
}
XmlNodeRef CXMLPatcher::FindPatchForFile(
const char* pInFileToPatch)
{
XmlNodeRef result;
if (m_patchXML)
{
for (int i = 0, m = m_patchXML->getChildCount(); i < m; i++)
{
XmlNodeRef child = m_patchXML->getChild(i);
if (child->isTag("patch"))
{
const char* pForFile = child->getAttr("forfile");
if (pForFile && CryStringUtils::stristr(pForFile, pInFileToPatch) != 0)
{
result = child;
break;
}
}
}
}
return result;
}
XmlNodeRef CXMLPatcher::ApplyPatchToNode(
const XmlNodeRef& inNode,
const XmlNodeRef& inPatch)
{
XmlNodeRef result = inNode;
for (int i = 0, m = inPatch->getChildCount(); i < m; i++)
{
XmlNodeRef patchNode = inPatch->getChild(i);
if (!patchNode || _stricmp(patchNode->getTag(), "patchnode") != 0)
{
continue;
}
int indexToPatch;
if (!patchNode->getAttr("index", indexToPatch))
{
PatchFail("found patchnode missing index");
continue;
}
int maxChildren = result->getChildCount();
if ((indexToPatch < 0 || indexToPatch >= maxChildren) && indexToPatch != -1)
{
PatchFail("patchnode index out of valid range");
continue;
}
XmlNodeRef childToPatch = (indexToPatch != -1) ? result->getChild(indexToPatch) : XmlNodeRef(0);
XmlNodeRef matchTag = GetMatchTag(patchNode);
if (childToPatch && matchTag && !CompareTags(matchTag, childToPatch))
{
PatchFail("patch failed to apply, data did not match what was expected");
continue;
}
// we need to apply a patch to this child, make it patchable by duplicating the node
if (inNode == result)
{
// make parent patchable if not already
result = DuplicateForPatching(inNode, true);
}
if (XmlNodeRef insertTag = GetInsertTag(patchNode))
{
// insert a new child after this node
XmlNodeRef newChild = DuplicateForPatching(insertTag, true); // have to duplicate it as we don't have an 'insert shared child' function
result->insertChild(indexToPatch + 1, newChild);
}
else
{
if (indexToPatch == -1)
{
PatchFail("child indices of -1 can only be used when inserting new nodes");
continue;
}
}
bool shouldReplaceChildren = false;
if (XmlNodeRef replaceTag = GetReplaceTag(patchNode, &shouldReplaceChildren))
{
XmlNodeRef newChild = DuplicateForPatching(replaceTag, false);
if (!shouldReplaceChildren)
{
newChild->shareChildren(childToPatch);
}
else
{
// note: this is inserting children that belong to the data patcher into the data being patched
// this is fine to do, as long as the caller doesn't make any permanent changes to the xml tree
// returned. if they did they would alter the patcher's nodes and thus affect future patches
// applied using the same patch
// as most callers are working with binary xmls they don't try and modify them - as this is not
// a supported operation
// note, if a second patch was applied to this patched tree containing the patch nodes, it
// wouldn't mess up the patch, as patching a tree never modifies it, it always returns a new tree
// that may share parts of the original tree
newChild->shareChildren(replaceTag);
}
result->replaceChild(indexToPatch, newChild);
childToPatch = newChild;
}
if (XmlNodeRef deleteTag = GetDeleteTag(patchNode))
{
result->deleteChildAt(indexToPatch);
childToPatch = 0; // deleted - don't recurse into it
}
if (childToPatch)
{
// Apply recursively
XmlNodeRef newChild = ApplyPatchToNode(childToPatch, patchNode);
// child has been patched, insert new child into parent
if (newChild != childToPatch)
{
result->replaceChild(indexToPatch, newChild);
}
}
}
return result;
}
XmlNodeRef CXMLPatcher::ApplyXMLDataPatch(
const XmlNodeRef& inNode,
const char* pInXMLFileName)
{
XmlNodeRef result = inNode;
if (m_patchingEnabled)
{
if (m_patchXML)
{
XmlNodeRef patchForFile = FindPatchForFile(pInXMLFileName);
if (patchForFile)
{
m_pFileBeingPatched = pInXMLFileName;
CryLog("Applying game data patch to %s", pInXMLFileName);
XmlNodeRef containerNode = m_patchXML->createNode("");
containerNode->addChild(inNode);
containerNode = ApplyPatchToNode(containerNode, patchForFile);
result = containerNode->getChild(0);
m_pFileBeingPatched = NULL;
#if DATA_PATCH_DEBUG
if (inNode != result)
{
DumpFiles(pInXMLFileName, inNode, result);
}
#endif
}
}
}
return result;
}
XmlNodeRef CXMLPatcher::GetMatchTag(
const XmlNodeRef& inNode)
{
XmlNodeRef result;
XmlNodeRef nr = inNode->findChild("match");
if (nr && nr->getChildCount() == 1)
{
result = nr->getChild(0);
}
return result;
}
XmlNodeRef CXMLPatcher::GetReplaceTag(
const XmlNodeRef& inNode,
bool* outShouldReplaceChildren)
{
XmlNodeRef result;
XmlNodeRef nr = inNode->findChild("replacewith");
if (nr && nr->getChildCount() == 1)
{
if (!nr->getAttr("replaceChildren", *outShouldReplaceChildren))
{
*outShouldReplaceChildren = false;
}
result = nr->getChild(0);
}
return result;
}
XmlNodeRef CXMLPatcher::GetInsertTag(
const XmlNodeRef& inNode)
{
XmlNodeRef result;
XmlNodeRef nr = inNode->findChild("insertAfter");
if (nr && nr->getChildCount() == 1)
{
result = nr->getChild(0);
}
return result;
}
XmlNodeRef CXMLPatcher::GetDeleteTag(
const XmlNodeRef& inNode)
{
XmlNodeRef result = inNode->findChild("delete");
return result;
}
// compares the two tags for equality of tag and attributes
// used to ensure the source data being patched meets the patches expectations
// only compares tag and attribs, doesn't do deep compare of children
bool CXMLPatcher::CompareTags(
const XmlNodeRef& inA,
const XmlNodeRef& inB)
{
bool result = true;
if (inA != inB)
{
result = false;
if (_stricmp(inA->getTag(), inB->getTag()) == 0)
{
if (inA->getNumAttributes() == inB->getNumAttributes())
{
result = true;
for (int i = 0, m = inA->getNumAttributes(); i < m; i++)
{
const char* pAKey, * pBKey;
const char* pAValue, * pBValue;
inA->getAttributeByIndex(i, &pAKey, &pAValue);
inB->getAttributeByIndex(i, &pBKey, &pBValue);
if (_stricmp(pAKey, pBKey) || _stricmp(pAValue, pBValue))
{
result = false;
break;
}
}
}
}
}
return result;
}
#if DATA_PATCH_DEBUG
static const char* k_lotsOfTabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
#define INDENT() \
if (inIndent > 0) \
{ \
pPak->FWrite(k_lotsOfTabs, inIndent, inFileHandle); \
}
void CXMLPatcher::DumpXMLNodes(
AZ::IO::HandleType inFileHandle,
int inIndent,
const XmlNodeRef& inNode,
CryFixedStringT<512>* ioTempString)
{
auto pPak = gEnv->pCryPak;
inIndent = min(inIndent, int(sizeof(k_lotsOfTabs) - 1));
INDENT();
ioTempString->Format("<%s ", inNode->getTag());
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
for (int i = 0, m = inNode->getNumAttributes(); i < m; i++)
{
const char* pKey, * pVal;
inNode->getAttributeByIndex(i, &pKey, &pVal);
ioTempString->Format("%s=\"%s\" ", pKey, pVal);
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
}
pPak->FWrite(">\n", 2, inFileHandle);
for (int i = 0, m = inNode->getChildCount(); i < m; i++)
{
DumpXMLNodes(inFileHandle, inIndent + 1, inNode->getChild(i), ioTempString);
}
INDENT();
ioTempString->Format("</%s>\n", inNode->getTag());
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
}
void CXMLPatcher::DumpFiles(
const char* pInXMLFileName,
const XmlNodeRef& inBefore,
const XmlNodeRef& inAfter)
{
if (m_pDumpFilesCVar->GetIVal())
{
CryLog("Dumping before and after data files for '%s'", pInXMLFileName);
const char* pOrigFileName = strrchr(pInXMLFileName, '/');
if (pOrigFileName)
{
pOrigFileName++;
DumpXMLFile(string().Format("PATCH_%s", pOrigFileName), inBefore);
CryFixedStringT<128> newFileName(pOrigFileName);
newFileName.replace(".xml", "_patched.xml");
DumpXMLFile(string().Format("PATCH_%s", newFileName.c_str()), inAfter);
}
else
{
CryLog("Couldn't determine file name for path '%s' can't output diffs", pInXMLFileName);
}
}
}
void CXMLPatcher::DumpXMLFile(
const char* pInFilePath,
const XmlNodeRef& inNode)
{
auto pIPak = GetISystem()->GetIPak();
AZ::IO::HandleType fileHandle = pIPak->FOpen(pInFilePath, "wb");
if (fileHandle != AZ::IO::InvalidHandle)
{
CryFixedStringT<512> tempStr;
DumpXMLNodes(fileHandle, 0, inNode, &tempStr);
pIPak->FClose(fileHandle);
}
}
#endif
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
#define CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
#pragma once
#if defined(WIN32) && !defined(_RELEASE)
#define DATA_PATCH_DEBUG 1
#else
#define DATA_PATCH_DEBUG 0
#endif
class CXMLPatcher
{
protected:
#if DATA_PATCH_DEBUG
ICVar * m_pDumpFilesCVar;
#endif
XmlNodeRef m_patchXML;
const char* m_pFileBeingPatched;
bool m_patchingEnabled;
void PatchFail(
const char* pInReason);
XmlNodeRef ApplyPatchToNode(
const XmlNodeRef& inNode,
const XmlNodeRef& inPatch);
XmlNodeRef DuplicateForPatching(
const XmlNodeRef& inOrig,
bool inShareChildren);
bool CompareTags(
const XmlNodeRef& inA,
const XmlNodeRef& inB);
XmlNodeRef GetMatchTag(
const XmlNodeRef& inNode);
XmlNodeRef GetReplaceTag(
const XmlNodeRef& inNode,
bool* outShouldReplaceChildren);
XmlNodeRef GetInsertTag(
const XmlNodeRef& inNode);
XmlNodeRef GetDeleteTag(
const XmlNodeRef& inNode);
XmlNodeRef FindPatchForFile(
const char* pInFileToPatch);
#if DATA_PATCH_DEBUG
void DumpXMLNodes(
AZ::IO::HandleType inFileHandle,
int inIndent,
const XmlNodeRef& inNode,
CryFixedStringT<512>* ioTempString);
void DumpFiles(
const char* pInXMLFileName,
const XmlNodeRef& inBefore,
const XmlNodeRef& inAfter);
void DumpXMLFile(
const char* pInFilePath,
const XmlNodeRef& inNode);
#endif
public:
CXMLPatcher(XmlNodeRef& patchXML);
~CXMLPatcher();
XmlNodeRef ApplyXMLDataPatch(
const XmlNodeRef& inNode,
const char* pInXMLFileName);
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
+712
View File
@@ -0,0 +1,712 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "CrySystem_precompiled.h"
#include <IXml.h>
#include "xml.h"
#include "XmlUtils.h"
#include "ReadWriteXMLSink.h"
#include "../SimpleStringPool.h"
#include "SerializeXMLReader.h"
#include "SerializeXMLWriter.h"
#include "XMLBinaryWriter.h"
#include "XMLBinaryReader.h"
#include "XMLPatcher.h"
#include <md5.h>
//////////////////////////////////////////////////////////////////////////
CXmlNode_PoolAlloc* g_pCXmlNode_PoolAlloc = 0;
#ifdef CRY_COLLECT_XML_NODE_STATS
SXmlNodeStats* g_pCXmlNode_Stats = 0;
#endif
extern bool g_bEnableBinaryXmlLoading;
//////////////////////////////////////////////////////////////////////////
CXmlUtils::CXmlUtils(ISystem* pSystem)
{
m_pSystem = pSystem;
m_pSystem->GetISystemEventDispatcher()->RegisterListener(this);
// create IReadWriteXMLSink object
m_pReadWriteXMLSink = new CReadWriteXMLSink();
g_pCXmlNode_PoolAlloc = new CXmlNode_PoolAlloc;
#ifdef CRY_COLLECT_XML_NODE_STATS
g_pCXmlNode_Stats = new SXmlNodeStats();
#endif
m_pStatsXmlNodePool = 0;
#ifndef _RELEASE
m_statsThreadOwner = CryGetCurrentThreadId();
#endif
m_pXMLPatcher = NULL;
}
//////////////////////////////////////////////////////////////////////////
CXmlUtils::~CXmlUtils()
{
m_pSystem->GetISystemEventDispatcher()->RemoveListener(this);
delete g_pCXmlNode_PoolAlloc;
#ifdef CRY_COLLECT_XML_NODE_STATS
delete g_pCXmlNode_Stats;
#endif
SAFE_DELETE(m_pStatsXmlNodePool);
SAFE_DELETE(m_pXMLPatcher);
}
//////////////////////////////////////////////////////////////////////////
IXmlParser* CXmlUtils::CreateXmlParser()
{
const bool bReuseStrings = false; //TODO: do we ever want to reuse strings here?
return new XmlParser(bReuseStrings);
}
//////////////////////////////////////////////////////////////////////////
XmlNodeRef CXmlUtils::LoadXmlFromFile(const char* sFilename, bool bReuseStrings, bool bEnablePatching)
{
XmlParser parser(bReuseStrings);
XmlNodeRef node = parser.ParseFile(sFilename, true);
// XmlParser is supposed to log warnings and errors (if any),
// so we don't need to call parser.getErrorString(),
// CryLog() etc here.
if (node && bEnablePatching && m_pXMLPatcher)
{
node = m_pXMLPatcher->ApplyXMLDataPatch(node, sFilename);
}
return node;
}
//////////////////////////////////////////////////////////////////////////
XmlNodeRef CXmlUtils::LoadXmlFromBuffer(const char* buffer, size_t size, bool bReuseStrings, bool bSuppressWarnings)
{
XmlParser parser(bReuseStrings);
XmlNodeRef node = parser.ParseBuffer(buffer, size, true, bSuppressWarnings);
return node;
}
void GetMD5(const char* pSrcBuffer, int nSrcSize, char signatureMD5[16])
{
MD5Context md5c;
MD5Init(&md5c);
MD5Update(&md5c, (unsigned char*)pSrcBuffer, nSrcSize);
MD5Final((unsigned char*)signatureMD5, &md5c);
}
//////////////////////////////////////////////////////////////////////////
const char* CXmlUtils::HashXml(XmlNodeRef node)
{
static char signature[16 * 2 + 1];
static char temp[16];
static const char* hex = "0123456789abcdef";
XmlString str = node->getXML();
GetMD5(str.data(), str.length(), temp);
for (int i = 0; i < 16; i++)
{
signature[2 * i + 0] = hex[((uint8)temp[i]) >> 4];
signature[2 * i + 1] = hex[((uint8)temp[i]) & 0xf];
}
signature[16 * 2] = 0;
return signature;
}
//////////////////////////////////////////////////////////////////////////
IReadWriteXMLSink* CXmlUtils::GetIReadWriteXMLSink()
{
return m_pReadWriteXMLSink;
}
//////////////////////////////////////////////////////////////////////////
class CXmlSerializer
: public IXmlSerializer
{
public:
CXmlSerializer()
: m_nRefCount(0)
, m_pReaderImpl(NULL)
, m_pReaderSer(NULL)
, m_pWriterSer(NULL)
, m_pWriterImpl(NULL)
{
}
~CXmlSerializer()
{
ClearAll();
}
void ClearAll()
{
SAFE_DELETE(m_pReaderSer);
SAFE_DELETE(m_pReaderImpl);
SAFE_DELETE(m_pWriterSer);
SAFE_DELETE(m_pWriterImpl);
}
//////////////////////////////////////////////////////////////////////////
virtual void AddRef() { ++m_nRefCount; }
virtual void Release()
{
if (--m_nRefCount <= 0)
{
delete this;
}
}
virtual ISerialize* GetWriter(XmlNodeRef& node)
{
ClearAll();
m_pWriterImpl = new CSerializeXMLWriterImpl(node);
m_pWriterSer = new CSimpleSerializeWithDefaults<CSerializeXMLWriterImpl>(*m_pWriterImpl);
return m_pWriterSer;
}
virtual ISerialize* GetReader(XmlNodeRef& node)
{
ClearAll();
m_pReaderImpl = new CSerializeXMLReaderImpl(node);
m_pReaderSer = new CSimpleSerializeWithDefaults<CSerializeXMLReaderImpl>(*m_pReaderImpl);
return m_pReaderSer;
}
virtual void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->Add(*this);
pSizer->AddObject(m_pReaderImpl);
pSizer->AddObject(m_pWriterImpl);
}
//////////////////////////////////////////////////////////////////////////
private:
int m_nRefCount;
CSerializeXMLReaderImpl* m_pReaderImpl;
CSimpleSerializeWithDefaults<CSerializeXMLReaderImpl>* m_pReaderSer;
CSerializeXMLWriterImpl* m_pWriterImpl;
CSimpleSerializeWithDefaults<CSerializeXMLWriterImpl>* m_pWriterSer;
};
//////////////////////////////////////////////////////////////////////////
IXmlSerializer* CXmlUtils::CreateXmlSerializer()
{
return new CXmlSerializer;
}
//////////////////////////////////////////////////////////////////////////
void CXmlUtils::GetMemoryUsage(ICrySizer* pSizer)
{
{
SIZER_COMPONENT_NAME(pSizer, "Nodes");
g_pCXmlNode_PoolAlloc->GetMemoryUsage(pSizer);
}
#ifdef CRY_COLLECT_XML_NODE_STATS
// yes, slow
std::vector<const CXmlNode*> rootNodes;
{
TXmlNodeSet::const_iterator iter = g_pCXmlNode_Stats->nodeSet.begin();
TXmlNodeSet::const_iterator iterEnd = g_pCXmlNode_Stats->nodeSet.end();
while (iter != iterEnd)
{
const CXmlNode* pNode = *iter;
if (pNode->getParent() == 0)
{
rootNodes.push_back(pNode);
}
++iter;
}
}
// use the following to log to console
#if 0
CryLogAlways("NumXMLRootNodes=%d NumXMLNodes=%d TotalAllocs=%d TotalFrees=%d",
rootNodes.size(), g_pCXmlNode_Stats->nodeSet.size(),
g_pCXmlNode_Stats->nAllocs, g_pCXmlNode_Stats->nFrees);
#endif
// use the following to debug the nodes in the system
#if 0
{
std::vector<const CXmlNode*>::const_iterator iter = rootNodes.begin();
std::vector<const CXmlNode*>::const_iterator iterEnd = rootNodes.end();
while (iter != iterEnd)
{
const CXmlNode* pNode = *iter;
CryLogAlways("Node 0x%p Tag='%s'", pNode, pNode->getTag());
++iter;
}
}
#endif
// only for debugging, add it as pseudo numbers to the CrySizer.
// shift it by 10, so we get the actual number
{
SIZER_COMPONENT_NAME(pSizer, "#NumTotalNodes");
pSizer->Add("#NumTotalNodes", g_pCXmlNode_Stats->nodeSet.size() << 10);
}
{
SIZER_COMPONENT_NAME(pSizer, "#NumRootNodes");
pSizer->Add("#NumRootNodes", rootNodes.size() << 10);
}
#endif
}
//////////////////////////////////////////////////////////////////////////
void CXmlUtils::OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
case ESYSTEM_EVENT_LEVEL_LOAD_END:
g_pCXmlNode_PoolAlloc->FreeMemoryIfEmpty();
break;
}
}
//////////////////////////////////////////////////////////////////////////
class CXmlBinaryDataWriterFile
: public XMLBinary::IDataWriter
{
public:
CXmlBinaryDataWriterFile(const char* file)
{
m_fileHandle = gEnv->pCryPak->FOpen(file, "wb");
}
~CXmlBinaryDataWriterFile()
{
if (m_fileHandle != AZ::IO::InvalidHandle)
{
gEnv->pCryPak->FClose(m_fileHandle);
}
};
virtual bool IsOk()
{
return m_fileHandle != AZ::IO::InvalidHandle;
}
;
virtual void Write(const void* pData, size_t size)
{
if (m_fileHandle != AZ::IO::InvalidHandle)
{
gEnv->pCryPak->FWrite(pData, size, 1, m_fileHandle);
}
}
private:
AZ::IO::HandleType m_fileHandle;
};
//////////////////////////////////////////////////////////////////////////
bool CXmlUtils::SaveBinaryXmlFile(const char* filename, XmlNodeRef root)
{
CXmlBinaryDataWriterFile fileSink(filename);
if (!fileSink.IsOk())
{
return false;
}
XMLBinary::CXMLBinaryWriter writer;
string error;
return writer.WriteNode(&fileSink, root, false, 0, error);
}
//////////////////////////////////////////////////////////////////////////
XmlNodeRef CXmlUtils::LoadBinaryXmlFile(const char* filename, bool bEnablePatching)
{
XMLBinary::XMLBinaryReader reader;
XMLBinary::XMLBinaryReader::EResult result;
XmlNodeRef root = reader.LoadFromFile(filename, result);
if (result == XMLBinary::XMLBinaryReader::eResult_Success && bEnablePatching == true && m_pXMLPatcher != NULL)
{
root = m_pXMLPatcher->ApplyXMLDataPatch(root, filename);
}
return root;
}
//////////////////////////////////////////////////////////////////////////
bool CXmlUtils::EnableBinaryXmlLoading(bool bEnable)
{
bool bPrev = g_bEnableBinaryXmlLoading;
g_bEnableBinaryXmlLoading = bEnable;
return bPrev;
}
//////////////////////////////////////////////////////////////////////////
class CXmlTableReader
: public IXmlTableReader
{
public:
CXmlTableReader();
virtual ~CXmlTableReader();
virtual void Release();
virtual bool Begin(XmlNodeRef rootNode);
virtual int GetEstimatedRowCount();
virtual bool ReadRow(int& rowIndex);
virtual bool ReadCell(int& columnIndex, const char*& pContent, size_t& contentSize);
float GetCurrentRowHeight() override;
private:
bool m_bExcel;
XmlNodeRef m_tableNode;
XmlNodeRef m_rowNode;
float m_currentRowHeight;
int m_rowNodeIndex;
int m_row;
int m_columnNodeIndex; // used if m_bExcel == true
int m_column;
size_t m_rowTextSize; // used if m_bExcel == false
size_t m_rowTextPos; // used if m_bExcel == false
};
//////////////////////////////////////////////////////////////////////////
CXmlTableReader::CXmlTableReader()
{
}
//////////////////////////////////////////////////////////////////////////
CXmlTableReader::~CXmlTableReader()
{
}
//////////////////////////////////////////////////////////////////////////
void CXmlTableReader::Release()
{
delete this;
}
//////////////////////////////////////////////////////////////////////////
bool CXmlTableReader::Begin(XmlNodeRef rootNode)
{
m_tableNode = 0;
if (!rootNode)
{
return false;
}
XmlNodeRef worksheetNode = rootNode->findChild("Worksheet");
if (worksheetNode)
{
m_bExcel = true;
m_tableNode = worksheetNode->findChild("Table");
}
else
{
m_bExcel = false;
m_tableNode = rootNode->findChild("Table");
}
m_rowNode = 0;
m_rowNodeIndex = -1;
m_row = -1;
return (m_tableNode != 0);
}
//////////////////////////////////////////////////////////////////////////
int CXmlTableReader::GetEstimatedRowCount()
{
if (!m_tableNode)
{
return -1;
}
return m_tableNode->getChildCount();
}
//////////////////////////////////////////////////////////////////////////
bool CXmlTableReader::ReadRow(int& rowIndex)
{
m_currentRowHeight = 0.0f;
if (!m_tableNode)
{
return false;
}
m_columnNodeIndex = -1;
m_column = -1;
const int rowNodeCount = m_tableNode->getChildCount();
if (m_bExcel)
{
for (;; )
{
if (++m_rowNodeIndex >= rowNodeCount)
{
m_rowNodeIndex = rowNodeCount;
return false;
}
m_rowNode = m_tableNode->getChild(m_rowNodeIndex);
if (!m_rowNode)
{
m_rowNodeIndex = rowNodeCount;
return false;
}
if (!m_rowNode->isTag("Row"))
{
m_rowNode = 0;
continue;
}
++m_row;
int index = 0;
if (m_rowNode->getAttr("ss:Index", index))
{
--index; // one-based -> zero-based
if (index < m_row)
{
m_rowNodeIndex = rowNodeCount;
m_rowNode = 0;
return false;
}
m_row = index;
}
float height;
if (m_rowNode->getAttr("ss:Height", height))
{
m_currentRowHeight = height;
}
rowIndex = m_row;
return true;
}
}
{
m_rowTextSize = 0;
m_rowTextPos = 0;
if (++m_rowNodeIndex >= rowNodeCount)
{
m_rowNodeIndex = rowNodeCount;
return false;
}
m_rowNode = m_tableNode->getChild(m_rowNodeIndex);
if (!m_rowNode)
{
m_rowNodeIndex = rowNodeCount;
return false;
}
const char* const pContent = m_rowNode->getContent();
if (pContent)
{
m_rowTextSize = strlen(pContent);
}
m_row = m_rowNodeIndex;
rowIndex = m_rowNodeIndex;
return true;
}
}
//////////////////////////////////////////////////////////////////////////
bool CXmlTableReader::ReadCell(int& columnIndex, const char*& pContent, size_t& contentSize)
{
pContent = 0;
contentSize = 0;
if (!m_tableNode)
{
return false;
}
if (!m_rowNode)
{
return false;
}
if (m_bExcel)
{
const int columnNodeCount = m_rowNode->getChildCount();
for (;; )
{
if (++m_columnNodeIndex >= columnNodeCount)
{
m_columnNodeIndex = columnNodeCount;
return false;
}
XmlNodeRef columnNode = m_rowNode->getChild(m_columnNodeIndex);
if (!columnNode)
{
m_columnNodeIndex = columnNodeCount;
return false;
}
if (!columnNode->isTag("Cell"))
{
continue;
}
++m_column;
int index = 0;
if (columnNode->getAttr("ss:Index", index))
{
--index; // one-based -> zero-based
if (index < m_column)
{
m_columnNodeIndex = columnNodeCount;
return false;
}
m_column = index;
}
columnIndex = m_column;
XmlNodeRef dataNode = columnNode->findChild("Data");
if (dataNode)
{
pContent = dataNode->getContent();
if (pContent)
{
contentSize = strlen(pContent);
}
}
return true;
}
}
{
if (m_rowTextPos >= m_rowTextSize)
{
return false;
}
const char* const pRowContent = m_rowNode->getContent();
if (!pRowContent)
{
m_rowTextPos = m_rowTextSize;
return false;
}
pContent = &pRowContent[m_rowTextPos];
columnIndex = ++m_column;
for (;; )
{
char c = pRowContent[m_rowTextPos++];
if ((c == '\n') || (c == '\0'))
{
return true;
}
if (c == '\r')
{
// ignore all '\r' chars
for (;; )
{
c = pRowContent[m_rowTextPos++];
if ((c == '\n') || (c == '\0'))
{
return true;
}
if (c != '\r')
{
// broken data. '\r' expected to be followed by '\n' or '\0'.
contentSize = 0;
m_rowTextPos = m_rowTextSize;
return false;
}
}
}
++contentSize;
}
}
}
float CXmlTableReader::GetCurrentRowHeight()
{
return m_currentRowHeight;
}
//////////////////////////////////////////////////////////////////////////
IXmlTableReader* CXmlUtils::CreateXmlTableReader()
{
return new CXmlTableReader;
}
//////////////////////////////////////////////////////////////////////////
// Init xml stats nodes pool
void CXmlUtils::InitStatsXmlNodePool(uint32 nPoolSize)
{
CHECK_STATS_THREAD_OWNERSHIP();
if (0 == m_pStatsXmlNodePool)
{
// create special xml node pools for game statistics
const bool bReuseStrings = true; // TODO parameterise?
m_pStatsXmlNodePool = new CXmlNodePool(nPoolSize, bReuseStrings);
assert(m_pStatsXmlNodePool);
}
else
{
CryLog("[CXmlNodePool]: Xml stats nodes pool already initialized");
}
}
//////////////////////////////////////////////////////////////////////////
// Creates new xml node for statistics.
XmlNodeRef CXmlUtils::CreateStatsXmlNode(const char* sNodeName)
{
CHECK_STATS_THREAD_OWNERSHIP();
if (0 == m_pStatsXmlNodePool)
{
CryLog("[CXmlNodePool]: Xml stats nodes pool isn't initialized. Perform default initialization.");
InitStatsXmlNodePool();
}
return m_pStatsXmlNodePool->GetXmlNode(sNodeName);
}
void CXmlUtils::SetStatsOwnerThread([[maybe_unused]] threadID threadId)
{
#ifndef _RELEASE
m_statsThreadOwner = threadId;
#endif
}
void CXmlUtils::FlushStatsXmlNodePool()
{
CHECK_STATS_THREAD_OWNERSHIP();
if (m_pStatsXmlNodePool)
{
if (m_pStatsXmlNodePool->empty())
{
SAFE_DELETE(m_pStatsXmlNodePool);
}
}
}
void CXmlUtils::SetXMLPatcher(XmlNodeRef* pPatcher)
{
SAFE_DELETE(m_pXMLPatcher);
if (pPatcher != NULL)
{
m_pXMLPatcher = new CXMLPatcher(*pPatcher);
}
}
+101
View File
@@ -0,0 +1,101 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
#define CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
#pragma once
#include "ISystem.h"
#ifdef _RELEASE
#define CHECK_STATS_THREAD_OWNERSHIP()
#else
#define CHECK_STATS_THREAD_OWNERSHIP() if (m_statsThreadOwner != CryGetCurrentThreadId()) {__debugbreak(); }
#endif
class CXmlNodePool;
class CXMLPatcher;
//////////////////////////////////////////////////////////////////////////
// Implements IXmlUtils interface.
//////////////////////////////////////////////////////////////////////////
class CXmlUtils
: public IXmlUtils
, public ISystemEventListener
{
public:
CXmlUtils(ISystem* pSystem);
virtual ~CXmlUtils();
//////////////////////////////////////////////////////////////////////////
// IXmlUtils
//////////////////////////////////////////////////////////////////////////
virtual IXmlParser* CreateXmlParser();
// Load xml from file, returns 0 if load failed.
virtual XmlNodeRef LoadXmlFromFile(const char* sFilename, bool bReuseStrings = false, bool bEnablePatching = true);
// Load xml from memory buffer, returns 0 if load failed.
virtual XmlNodeRef LoadXmlFromBuffer(const char* buffer, size_t size, bool bReuseStrings = false, bool bSuppressWarnings = false);
// create an MD5 hash of an XML file
virtual const char* HashXml(XmlNodeRef node);
// Get an object that can read a xml into a IReadXMLSink
// and write a xml from a IWriteXMLSource
virtual IReadWriteXMLSink* GetIReadWriteXMLSink();
virtual IXmlSerializer* CreateXmlSerializer();
virtual bool SaveBinaryXmlFile(const char* sFilename, XmlNodeRef root);
virtual XmlNodeRef LoadBinaryXmlFile(const char* sFilename, bool bEnablePatching = true);
virtual bool EnableBinaryXmlLoading(bool bEnable);
// Create XML Table reader.
virtual IXmlTableReader* CreateXmlTableReader();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// ISystemEventListener
//////////////////////////////////////////////////////////////////////////
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
virtual void GetMemoryUsage(ICrySizer* pSizer);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Init xml stats nodes pool
virtual void InitStatsXmlNodePool(uint32 nPoolSize = 1024*1024);
// Create new xml node for statistics
virtual XmlNodeRef CreateStatsXmlNode(const char* sNodeName = "");
// Set owner thread
virtual void SetStatsOwnerThread(threadID threadId);
// Free memory if stats xml node pool is empty
virtual void FlushStatsXmlNodePool();
// Set the XML Patcher. This is an XML object that modifies named XML files as they are loaded
// EXCEPT for xml files loaded from a buffer, for which names aren't passed in
virtual void SetXMLPatcher(XmlNodeRef* pPatcher);
private:
ISystem* m_pSystem;
IReadWriteXMLSink* m_pReadWriteXMLSink;
CXmlNodePool* m_pStatsXmlNodePool;
CXMLPatcher* m_pXMLPatcher; //If set, applies data patches to any XML file that is loaded by this class
#ifndef _RELEASE
threadID m_statsThreadOwner;
#endif
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
@@ -0,0 +1,15 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(FILES
XMLBinaryNode.cpp
XMLBinaryNode.h
XMLBinaryReader.cpp
XMLBinaryReader.h
XMLBinaryWriter.cpp
XMLBinaryWriter.h
)
File diff suppressed because it is too large Load Diff
+438
View File
@@ -0,0 +1,438 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XML_H
#define CRYINCLUDE_CRYSYSTEM_XML_XML_H
#pragma once
#include <algorithm>
#include <PoolAllocator.h>
#include <stack>
#include "IXml.h"
// track some XML stats. only to find persistent XML nodes in the system
// slow, so disable by default
//#define CRY_COLLECT_XML_NODE_STATS
//#undef CRY_COLLECT_XML_NODE_STATS
struct IXmlStringPool
{
public:
IXmlStringPool() { m_refCount = 0; }
virtual ~IXmlStringPool() {};
void AddRef() { m_refCount++; };
void Release()
{
if (--m_refCount <= 0)
{
delete this;
}
};
virtual const char* AddString(const char* str) = 0;
virtual void GetMemoryUsage(ICrySizer* pSizer) const = 0;
private:
int m_refCount;
};
/************************************************************************/
/* XmlParser class, Parse xml and return root xml node if success. */
/************************************************************************/
class XmlParser
: public IXmlParser
{
public:
explicit XmlParser(bool bReuseStrings);
~XmlParser();
void AddRef()
{
++m_nRefCount;
}
void Release()
{
if (--m_nRefCount <= 0)
{
delete this;
}
}
virtual XmlNodeRef ParseFile(const char* filename, bool bCleanPools);
virtual XmlNodeRef ParseBuffer(const char* buffer, int nBufLen, bool bCleanPools, bool bSuppressWarnings = false);
const char* getErrorString() const { return m_errorString; }
void GetMemoryUsage(ICrySizer* pSizer) const;
private:
int m_nRefCount;
XmlString m_errorString;
class XmlParserImp* m_pImpl;
};
// Compare function for string comparasion, can be strcmp or _stricmp
typedef int (__cdecl * XmlStrCmpFunc)(const char* str1, const char* str2);
extern XmlStrCmpFunc g_pXmlStrCmp;
//////////////////////////////////////////////////////////////////////////
// XmlAttribute class
//////////////////////////////////////////////////////////////////////////
struct XmlAttribute
{
const char* key;
const char* value;
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const{}
bool operator<(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) < 0; }
bool operator>(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) > 0; }
bool operator==(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) == 0; }
bool operator!=(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) != 0; }
};
//! Xml node attributes class.
typedef std::vector<XmlAttribute> XmlAttributes;
typedef XmlAttributes::iterator XmlAttrIter;
typedef XmlAttributes::const_iterator XmlAttrConstIter;
/**
******************************************************************************
* CXmlNode class
* Never use CXmlNode directly instead use reference counted XmlNodeRef.
******************************************************************************
*/
class CXmlNode
: public IXmlNode
{
public:
//! Constructor.
CXmlNode();
CXmlNode(const char* tag, bool bReuseStrings, bool bIsProcessingInstruction = false);
//! Destructor.
~CXmlNode();
// collect allocated memory informations
void GetMemoryUsage(ICrySizer* pSizer) const;
//////////////////////////////////////////////////////////////////////////
// Custom new/delete with pool allocator.
//////////////////////////////////////////////////////////////////////////
//void* operator new( size_t nSize );
//void operator delete( void *ptr );
virtual void DeleteThis();
//! Create new XML node.
XmlNodeRef createNode(const char* tag);
//! Get XML node tag.
const char* getTag() const { return m_tag; };
void setTag(const char* tag);
//! Return true if given tag equal to node tag.
bool isTag(const char* tag) const;
//! Get XML Node attributes.
virtual int getNumAttributes() const { return m_pAttributes ? (int)m_pAttributes->size() : 0; };
//! Return attribute key and value by attribute index.
virtual bool getAttributeByIndex(int index, const char** key, const char** value);
//! Return attribute key and value by attribute index, string version.
virtual bool getAttributeByIndex(int index, XmlString& key, XmlString& value);
virtual void copyAttributes(XmlNodeRef fromNode);
virtual void shareChildren(const XmlNodeRef& fromNode);
//! Get XML Node attribute for specified key.
const char* getAttr(const char* key) const;
//! Get XML Node attribute for specified key.
// Returns true if the attribute existes, alse otherwise.
bool getAttr(const char* key, const char** value) const;
//! Check if attributes with specified key exist.
bool haveAttr(const char* key) const;
//! Creates new xml node and add it to childs list.
XmlNodeRef newChild(const char* tagName);
//! Adds new child node.
void addChild(const XmlNodeRef& node);
//! Remove child node.
void removeChild(const XmlNodeRef& node);
void insertChild(int nIndex, const XmlNodeRef& node);
void replaceChild(int nIndex, const XmlNodeRef& node);
//! Remove all child nodes.
void removeAllChilds();
//! Get number of child XML nodes.
int getChildCount() const { return m_pChilds ? (int)m_pChilds->size() : 0; };
//! Get XML Node child nodes.
XmlNodeRef getChild(int i) const;
//! Find node with specified tag.
XmlNodeRef findChild(const char* tag) const;
void deleteChild(const char* tag);
void deleteChildAt(int nIndex);
//! Get parent XML node.
XmlNodeRef getParent() const { return m_parent; }
void setParent(const XmlNodeRef& inRef);
//! Returns content of this node.
const char* getContent() const { return m_content; };
void setContent(const char* str);
XmlNodeRef clone();
//! Returns line number for XML tag.
int getLine() const { return m_line; };
//! Set line number in xml.
void setLine(int line) { m_line = line; };
//! Returns XML of this node and sub nodes.
virtual IXmlStringData* getXMLData(int nReserveMem = 0) const;
XmlString getXML(int level = 0) const;
XmlString getXMLUnsafe(int level, char* tmpBuffer, uint32 sizeOfTmpBuffer) const;
bool saveToFile(const char* fileName); // saves in one huge chunk
bool saveToFile(const char* fileName, size_t chunkSizeBytes, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle); // save in small memory chunks
//! Set new XML Node attribute (or override attribute with same key).
void setAttr(const char* key, const char* value);
void setAttr(const char* key, int value);
void setAttr(const char* key, unsigned int value);
void setAttr(const char* key, int64 value);
void setAttr(const char* key, uint64 value, bool useHexFormat = true);
void setAttr(const char* key, float value);
void setAttr(const char* key, double value);
void setAttr(const char* key, const Vec2& value);
void setAttr(const char* key, const Vec2d& value);
void setAttr(const char* key, const Ang3& value);
void setAttr(const char* key, const Vec3& value);
void setAttr(const char* key, const Vec4& value);
void setAttr(const char* key, const Vec3d& value);
void setAttr(const char* key, const Quat& value);
//! Delete attrbute.
void delAttr(const char* key);
//! Remove all node attributes.
void removeAllAttributes();
//! Get attribute value of node.
bool getAttr(const char* key, int& value) const;
bool getAttr(const char* key, unsigned int& value) const;
bool getAttr(const char* key, int64& value) const;
bool getAttr(const char* key, uint64& value, bool useHexFormat = true /*ignored*/) const;
bool getAttr(const char* key, float& value) const;
bool getAttr(const char* key, double& value) const;
bool getAttr(const char* key, bool& value) const;
bool getAttr(const char* key, XmlString& value) const {const char* v(NULL); bool boHasAttribute(getAttr(key, &v)); value = v; return boHasAttribute; }
bool getAttr(const char* key, Vec2& value) const;
bool getAttr(const char* key, Vec2d& value) const;
bool getAttr(const char* key, Ang3& value) const;
bool getAttr(const char* key, Vec3& value) const;
bool getAttr(const char* key, Vec4& value) const;
bool getAttr(const char* key, Vec3d& value) const;
bool getAttr(const char* key, Quat& value) const;
bool getAttr(const char* key, ColorB& value) const;
protected:
private:
CXmlNode(const CXmlNode&);
CXmlNode& operator = (const CXmlNode&);
private:
void ReleaseChild(IXmlNode* pChild);
void removeAllChildsImpl();
void AddToXmlString(XmlString& xml, int level, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
char* AddToXmlStringUnsafe(char* xml, int level, char* endPtr, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
XmlString MakeValidXmlString(const XmlString& xml) const;
bool IsValidXmlString(const char* str) const;
XmlAttrConstIter GetAttrConstIterator(const char* key) const
{
assert(m_pAttributes);
XmlAttribute tempAttr;
tempAttr.key = key;
XmlAttributes::const_iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
return it;
/*
XmlAttributes::const_iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
return it;
return m_attributes.end();
*/
}
XmlAttrIter GetAttrIterator(const char* key)
{
assert(m_pAttributes);
XmlAttribute tempAttr;
tempAttr.key = key;
XmlAttributes::iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
return it;
// XmlAttributes::iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
//if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
//return it;
//return m_attributes.end();
}
const char* GetValue(const char* key) const
{
if (m_pAttributes)
{
XmlAttrConstIter it = GetAttrConstIterator(key);
if (it != m_pAttributes->end())
{
return it->value;
}
}
return 0;
}
protected:
// String pool used by this node.
IXmlStringPool* m_pStringPool;
//! Tag of XML node.
const char* m_tag;
private:
//! Content of XML node.
const char* m_content;
//! Parent XML node.
IXmlNode* m_parent;
//typedef DynArray<CXmlNode*,XmlDynArrayAlloc> XmlNodes;
typedef std::vector<IXmlNode*> XmlNodes;
//XmlNodes m_childs;
XmlNodes* m_pChilds;
//! Xml node attributes.
//XmlAttributes m_attributes;
XmlAttributes* m_pAttributes;
//! Line in XML file where this node firstly appeared (useful for debugging).
int m_line;
bool m_isProcessingInstruction;
friend class XmlParserImp;
};
typedef stl::PoolAllocatorNoMT<sizeof(CXmlNode)> CXmlNode_PoolAlloc;
extern CXmlNode_PoolAlloc* g_pCXmlNode_PoolAlloc;
#ifdef CRY_COLLECT_XML_NODE_STATS
typedef std::set<CXmlNode*> TXmlNodeSet; // yes, slow, but really only for one-shot debugging
struct SXmlNodeStats
{
SXmlNodeStats()
: nAllocs(0)
, nFrees(0) {}
TXmlNodeSet nodeSet;
uint32 nAllocs;
uint32 nFrees;
};
extern SXmlNodeStats* g_pCXmlNode_Stats;
#endif
/*
//////////////////////////////////////////////////////////////////////////
inline void* CXmlNode::operator new( size_t nSize )
{
void *ptr = g_pCXmlNode_PoolAlloc->Allocate();
if (ptr)
{
memset( ptr,0,nSize ); // Clear objects memory.
#ifdef CRY_COLLECT_XML_NODE_STATS
g_pCXmlNode_Stats->nodeSet.insert(reinterpret_cast<CXmlNode*> (ptr));
++g_pCXmlNode_Stats->nAllocs;
#endif
}
return ptr;
}
//////////////////////////////////////////////////////////////////////////
inline void CXmlNode::operator delete( void *ptr )
{
if (ptr)
{
g_pCXmlNode_PoolAlloc->Deallocate(ptr);
#ifdef CRY_COLLECT_XML_NODE_STATS
g_pCXmlNode_Stats->nodeSet.erase(reinterpret_cast<CXmlNode*> (ptr));
++g_pCXmlNode_Stats->nFrees;
#endif
}
}
*/
//////////////////////////////////////////////////////////////////////////
//
// Reusable XmlNode for XmlNode pool with shared xml string pool
//
//////////////////////////////////////////////////////////////////////////
class CXmlNodePool;
class CXmlNodeReuse
: public CXmlNode
{
public:
CXmlNodeReuse(const char* tag, CXmlNodePool* pPool);
virtual void Release();
protected:
CXmlNodePool* m_pPool;
};
//////////////////////////////////////////////////////////////////////////
//
// Pool of reusable XML nodes with shared string pool
//
//////////////////////////////////////////////////////////////////////////
class CXmlNodePool
{
public:
CXmlNodePool(unsigned int nBlockSize, bool bReuseStrings);
virtual ~CXmlNodePool();
XmlNodeRef GetXmlNode(const char* sNodeName);
bool empty() const { return (m_nAllocated == 0); }
protected:
virtual void OnRelease(int iRefCount, void* pThis);
IXmlStringPool* GetStringPool() { return m_pStringPool; }
private:
friend class CXmlNodeReuse;
IXmlStringPool* m_pStringPool;
unsigned int m_nAllocated;
std::stack<CXmlNodeReuse*> m_pNodePool;
};
#endif // CRYINCLUDE_CRYSYSTEM_XML_XML_H
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
#define CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
#pragma once
typedef string xml_string;
#endif // CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
@@ -0,0 +1,75 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(FILES
AZCrySystemInitLogSink.cpp
CmdLine.cpp
CmdLineArg.cpp
ConsoleBatchFile.cpp
ConsoleHelpGen.cpp
DebugCallStack.cpp
IDebugCallStack.cpp
Log.cpp
System.cpp
SystemCFG.cpp
SystemEventDispatcher.cpp
SystemInit.cpp
SystemWin32.cpp
Timer.cpp
XConsole.cpp
XConsoleVariable.cpp
XML/ReadWriteXMLSink.h
AZCrySystemInitLogSink.h
AZCoreLogSink.h
CmdLine.h
CmdLineArg.h
ConsoleBatchFile.h
ConsoleHelpGen.h
DebugCallStack.h
IDebugCallStack.h
Log.h
SimpleStringPool.h
CrySystem_precompiled.h
System.h
SystemCFG.h
SystemEventDispatcher.h
Timer.h
XConsole.h
XConsoleVariable.h
XML/SerializeXMLReader.cpp
XML/SerializeXMLWriter.cpp
XML/xml.cpp
XML/XMLPatcher.cpp
XML/XmlUtils.cpp
XML/SerializeXMLReader.h
XML/SerializeXMLWriter.h
XML/xml.h
XML/XMLPatcher.h
XML/xml_string.h
XML/XmlUtils.h
XML/ReadXMLSink.cpp
XML/WriteXMLSource.cpp
LocalizedStringManager.cpp
LocalizedStringManager.h
Huffman.cpp
Huffman.h
RemoteConsole/RemoteConsole.cpp
RemoteConsole/RemoteConsole.h
RemoteConsole/RemoteConsole_impl.inl
RemoteConsole/RemoteConsole_none.inl
LevelSystem/LevelSystem.cpp
LevelSystem/LevelSystem.h
LevelSystem/SpawnableLevelSystem.cpp
LevelSystem/SpawnableLevelSystem.h
ViewSystem/DebugCamera.cpp
ViewSystem/DebugCamera.h
ViewSystem/View.cpp
ViewSystem/View.h
ViewSystem/ViewSystem.cpp
ViewSystem/ViewSystem.h
WindowsErrorReporting.cpp
)
@@ -0,0 +1,10 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
set(FILES
DllMain.cpp
)