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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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(<ime);
|
||||
tm today;
|
||||
localtime_s(&today, <ime);
|
||||
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
@@ -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
|
||||
@@ -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(<ime);
|
||||
tm* today = localtime(<ime);
|
||||
|
||||
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)
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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(<ime);
|
||||
|
||||
#ifdef AZ_COMPILER_MSVC
|
||||
tm today;
|
||||
localtime_s(&today, <ime);
|
||||
char s[1024];
|
||||
strftime(s, 128, "%d %b %y (%H %M %S)", &today);
|
||||
#else
|
||||
char s[1024];
|
||||
auto today = localtime(<ime);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
Reference in New Issue
Block a user