!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / CryEngine / CrySchematyc2 / Services / Log.cpp
blob7fcf7035931f6dba6571f4de51173e725c75a57d
1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "Log.h"
6 #include <CryCore/Platform/CryWindows.h>
7 #include <CryEntitySystem/IEntitySystem.h>
8 #include <CrySerialization/IArchiveHost.h>
9 #include <CrySerialization/STL.h>
10 #include <CrySchematyc2/TemplateUtils/TemplateUtils_ScopedConnection.h>
11 #include <CrySystem/File/ICryPak.h>
12 #include <CrySystem/ConsoleRegistration.h>
14 #include "CVars.h"
16 SERIALIZATION_ENUM_BEGIN_NESTED(Schematyc2, ELogMessageType, "Schematyc Log Message Type")
17 SERIALIZATION_ENUM(Schematyc2::ELogMessageType::Comment, "Comment", "Comment")
18 SERIALIZATION_ENUM(Schematyc2::ELogMessageType::Warning, "Warning", "Warning")
19 SERIALIZATION_ENUM(Schematyc2::ELogMessageType::Error, "Error", "Error")
20 SERIALIZATION_ENUM(Schematyc2::ELogMessageType::CriticalError, "CriticalError", "Critical Error")
21 SERIALIZATION_ENUM_END()
23 namespace Schematyc2
25 namespace LogUtils
27 template <size_t SIZE> inline void FormatMessage(char (&messageBuffer)[SIZE], const char* szFormat, va_list va_args)
29 const size_t pos = strlen(messageBuffer);
30 vsnprintf(messageBuffer + pos, sizeof(messageBuffer) - pos - 1, szFormat, va_args);
33 inline ECriticalErrorStatus DisplayCriticalErrorMessage(const char* szMessage)
35 string message = szMessage;
36 message.append("\r\n\r\n");
37 message.append("Would you like to continue?\r\nClick 'Yes' to continue, 'No' to break or 'Cancel' to continue and ignore this error.");
39 switch (CryMessageBox(message.c_str(), "Schematyc - Critical Error!", eMB_YesNoCancel))
41 case eQR_Yes:
43 return ECriticalErrorStatus::Continue;
45 case eQR_Cancel:
47 return ECriticalErrorStatus::Ignore;
50 return ECriticalErrorStatus::Break;
53 void CriticalErrorCommand(IConsoleCmdArgs* pArgs)
55 const char* szMessage = "";
56 if(pArgs->GetArgCount() == 2)
58 szMessage = pArgs->GetArg(1);
60 SCHEMATYC2_SYSTEM_CRITICAL_ERROR(szMessage);
63 void FatalErrorCommand(IConsoleCmdArgs* pArgs)
65 const char* szMessage = "";
66 if(pArgs->GetArgCount() == 2)
68 szMessage = pArgs->GetArg(1);
70 SCHEMATYC2_SYSTEM_FATAL_ERROR(szMessage);
73 class CFileOutput : public ILogOutput, public IErrorObserver
75 private:
77 typedef CryStackStringT<char, 512> MessageString;
78 typedef std::vector<MessageString> MessageQueue;
79 typedef std::vector<LogStreamId> StreamIds;
80 typedef std::vector<ELogMessageType> MessageTypes;
82 public:
84 CFileOutput(LogMessageSignal& messageSignal, const char* szFileName)
85 : m_pFile(nullptr)
86 , m_bErrorObserverRegistered(false)
87 , m_bWriteToFile(true)
88 , m_bForwardToStandardLog(false)
89 , m_fileName(szFileName)
91 Initialize();
92 messageSignal.Connect(LogMessageSignal::Delegate::FromMemberFunction<CFileOutput, &CFileOutput::OnLogMessage>(*this), m_connectionScope);
95 ~CFileOutput()
97 FlushAndClose();
100 // ILogOutput
102 virtual void ConfigureFileOutput(bool bWriteToFile, bool bForwardToStandardLog) override // Hack! Allows game to decide where messages are forwarded.
104 m_bWriteToFile = bWriteToFile;
105 m_bForwardToStandardLog = bForwardToStandardLog;
108 virtual void EnableStream(const LogStreamId& streamId) override
110 stl::push_back_unique(m_streamIds, streamId);
113 virtual void ClearStreams() override
115 m_streamIds.clear();
118 virtual void EnableMessageType(ELogMessageType messageType) override
120 stl::push_back_unique(m_messageTypes, messageType);
123 virtual void ClearMessageTypes() override
125 m_messageTypes.clear();
128 virtual void Update() override
130 FlushMessages();
133 // ~ILogOutput
135 // IErrorObserver
137 virtual void OnAssert(const char* szCondition, const char* szMessage, const char* szFileName, unsigned int fileLineNumber) override
139 FlushMessages();
142 virtual void OnFatalError(const char* szMessage) override
144 FlushMessages();
147 // ~IErrorObserver
149 void OnLogMessage(const SLogMessageData& logMessageData)
151 if(m_streamIds.empty() || std::find(m_streamIds.begin(), m_streamIds.end(), logMessageData.streamId) != m_streamIds.end())
153 if(m_messageTypes.empty() || std::find(m_messageTypes.begin(), m_messageTypes.end(), logMessageData.messageType) != m_messageTypes.end())
155 EnqueueMessage(logMessageData);
160 void FlushMessages()
162 CRY_PROFILE_FUNCTION(PROFILE_GAME);
164 bool bFlush = false;
166 CryAutoCriticalSection lock(m_criticalSection);
167 if (m_pFile && !m_messagQueue.empty())
169 const size_t valueSize = sizeof(MessageString::CharTraits::value_type);
170 for (const MessageString& message : m_messagQueue)
172 fwrite(message.c_str(), valueSize, message.size(), m_pFile);
174 m_messagQueue.clear();
175 bFlush = true;
178 if (bFlush)
180 fflush(m_pFile);
184 void FlushAndClose()
186 CryAutoCriticalSection lock(m_criticalSection);
188 FlushMessages();
190 if(m_pFile)
192 fclose(m_pFile);
193 m_pFile = nullptr;
196 if(m_bErrorObserverRegistered)
198 m_bErrorObserverRegistered = false;
199 gEnv->pSystem->UnregisterErrorObserver(this);
202 m_messagQueue.shrink_to_fit();
205 private:
207 void Initialize()
209 if(!m_pFile)
211 m_bErrorObserverRegistered = gEnv->pSystem->RegisterErrorObserver(this);
213 FILE *pFile = fxopen(m_fileName.c_str(), "rb");
214 if(pFile)
216 fclose(pFile);
218 stack_string backupFileName("LogBackups/");
219 backupFileName.append(m_fileName.c_str());
220 #if CRY_PLATFORM_DURANGO
221 CRY_VERIFY(CopyFile2(CryStringUtils::UTF8ToWStrSafe(m_fileName), CryStringUtils::UTF8ToWStrSafe(backupFileName), nullptr) == S_OK, "Error copying schematyc log backup file");
222 #else
223 CopyFile(m_fileName.c_str(), backupFileName.c_str(), true);
224 #endif
227 if (m_pFile = fxopen(m_fileName.c_str(), "wtc"))
229 setvbuf(m_pFile, m_writeBuffer, _IOFBF, s_writeBufferSize);
231 else
233 CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "Failed to write Schematyc2 log to disk!");
236 m_messagQueue.reserve(100);
240 void EnqueueMessage(const SLogMessageData& logMessageData)
242 MessageString output;
245 LogUtils::TimeStringBuffer stringBuffer = "";
246 output.append("<");
247 output.append(LogUtils::FormatTime(stringBuffer));
248 output.append("> ");
251 const string cryLinkUri = logMessageData.metaInfo.GetUri();
252 if(!cryLinkUri.empty())
254 output.append("[");
255 output.append(logMessageData.metaInfo.GetUri().c_str());
256 output.append("]");
259 output.append(logMessageData.szMessage);
260 output.append("\n");
262 if(m_bWriteToFile)
264 CryAutoCriticalSection lock(m_criticalSection);
265 m_messagQueue.push_back(output);
268 if (m_bForwardToStandardLog)
270 CryLog("SCHEMATYC: %s", output.c_str());
274 private:
276 static const size_t s_writeBufferSize = 1024 * 1024;
278 MessageQueue m_messagQueue;
279 StreamIds m_streamIds;
280 MessageTypes m_messageTypes;
281 FILE * volatile m_pFile;
282 string m_fileName;
283 CryCriticalSection m_criticalSection;
284 char m_writeBuffer[s_writeBufferSize];
285 bool m_bErrorObserverRegistered;
286 bool m_bWriteToFile;
287 bool m_bForwardToStandardLog;
288 TemplateUtils::CConnectionScope m_connectionScope;
292 const LogStreamId CLog::FIRST_DYNAMIC_STREAM_ID(0xffff0000);
294 //////////////////////////////////////////////////////////////////////////
295 CLog::CLog()
296 : m_nextDynamicStreamId(FIRST_DYNAMIC_STREAM_ID)
298 m_pSettings = SSettingsPtr(new SSettings(SettingsModifiedCallback::FromMemberFunction<CLog, &CLog::OnSettingsModified>(*this)));
301 //////////////////////////////////////////////////////////////////////////
302 void CLog::Init()
304 CreateStream("Default", LOG_STREAM_DEFAULT);
305 CreateStream("System", LOG_STREAM_SYSTEM);
306 CreateStream("Compiler", LOG_STREAM_COMPILER);
307 CreateStream("Game", LOG_STREAM_GAME);
308 gEnv->pSchematyc2->GetEnvRegistry().RegisterSettings("log_settings", m_pSettings);
309 REGISTER_COMMAND("sc2_CriticalError", LogUtils::CriticalErrorCommand, VF_NULL, "Trigger Schematyc critical error");
310 REGISTER_COMMAND("sc2_FatalError", LogUtils::FatalErrorCommand, VF_NULL, "Trigger Schematyc fatal error");
313 //////////////////////////////////////////////////////////////////////////
314 ELogMessageType CLog::GetMessageType(const char* szMessageType)
316 SCHEMATYC2_SYSTEM_ASSERT(szMessageType);
317 if(szMessageType)
319 const Serialization::EnumDescription& enumDescription = Serialization::getEnumDescription<ELogMessageType>();
320 for(int logMessageTypeIdx = 0, logMessageTypeCount = enumDescription.count(); logMessageTypeIdx < logMessageTypeCount; ++ logMessageTypeIdx)
322 if(stricmp(enumDescription.labelByIndex(logMessageTypeIdx), szMessageType) == 0)
324 return static_cast<ELogMessageType>(enumDescription.valueByIndex(logMessageTypeIdx));
328 return ELogMessageType::Invalid;
331 //////////////////////////////////////////////////////////////////////////
332 const char* CLog::GetMessageTypeName(ELogMessageType messageType)
334 const Serialization::EnumDescription& enumDescription = Serialization::getEnumDescription<ELogMessageType>();
335 return enumDescription.labelByIndex(enumDescription.indexByValue(static_cast<int>(messageType)));
338 //////////////////////////////////////////////////////////////////////////
339 LogStreamId CLog::CreateStream(const char* szName, const LogStreamId& staticStreamId)
341 SCHEMATYC2_SYSTEM_ASSERT(szName);
342 if(szName && (FindStream(szName) == INVALID_INDEX))
344 LogStreamId streamId;
345 if(staticStreamId == LogStreamId::s_invalid)
347 streamId = m_nextDynamicStreamId ++;
349 else if(staticStreamId < FIRST_DYNAMIC_STREAM_ID)
351 if(FindStream(staticStreamId) == INVALID_INDEX)
353 streamId = staticStreamId;
356 SCHEMATYC2_SYSTEM_ASSERT(streamId != LogStreamId::s_invalid);
357 if(streamId != LogStreamId::s_invalid)
359 m_streams.push_back(SStream(szName, streamId));
360 return streamId;
363 return LogStreamId::s_invalid;
366 //////////////////////////////////////////////////////////////////////////
367 void CLog::DestroyStream(const LogStreamId& streamId)
369 for(Streams::iterator itStream = m_streams.begin(), itEndStream = m_streams.end(); itStream != itEndStream; ++ itStream)
371 if(itStream->id == streamId)
373 m_streams.erase(itStream);
374 break;
379 //////////////////////////////////////////////////////////////////////////
380 LogStreamId CLog::GetStreamId(const char* szName) const
382 const size_t streamIdx = FindStream(szName);
383 return streamIdx != INVALID_INDEX ? m_streams[streamIdx].id : LogStreamId::s_invalid;
386 //////////////////////////////////////////////////////////////////////////
387 const char* CLog::GetStreamName(const LogStreamId& streamId) const
389 const size_t streamIdx = FindStream(streamId);
390 return streamIdx != INVALID_INDEX ? m_streams[streamIdx].name.c_str() : "";
393 //////////////////////////////////////////////////////////////////////////
394 void CLog::VisitStreams(const LogStreamVisitor& visitor) const
396 SCHEMATYC2_SYSTEM_ASSERT(visitor);
397 if(visitor)
399 for(const SStream& stream : m_streams)
401 visitor(stream.name.c_str(), stream.id);
406 //////////////////////////////////////////////////////////////////////////
407 ILogOutputPtr CLog::CreateFileOutput(const char* szFileName)
409 ILogOutputPtr pFileOutput(new LogUtils::CFileOutput(m_signals.message, szFileName));
410 m_outputs.push_back(pFileOutput);
411 return pFileOutput;
414 //////////////////////////////////////////////////////////////////////////
415 void CLog::Comment(const LogStreamId& streamId, CLogMessageMetaInfo metaInfo, const char* szFormat, ...)
417 CRY_PROFILE_FUNCTION(PROFILE_GAME);
419 va_list va_args;
420 va_start(va_args, szFormat);
421 char messageBuffer[1024] = "";
422 LogUtils::FormatMessage(messageBuffer, szFormat, va_args);
423 va_end(va_args);
424 m_signals.message.Send(SLogMessageData(streamId, ELogMessageType::Comment, metaInfo, messageBuffer));
427 //////////////////////////////////////////////////////////////////////////
428 void CLog::Warning(const LogStreamId& streamId, CLogMessageMetaInfo metaInfo, const char* szFormat, ...)
430 CRY_PROFILE_FUNCTION(PROFILE_GAME);
432 va_list va_args;
433 va_start(va_args, szFormat);
434 char messageBuffer[1024] = "";
435 LogUtils::FormatMessage(messageBuffer, szFormat, va_args);
436 va_end(va_args);
437 m_signals.message.Send(SLogMessageData(streamId, ELogMessageType::Warning, metaInfo, messageBuffer));
440 //////////////////////////////////////////////////////////////////////////
441 void CLog::Error(const LogStreamId& streamId, CLogMessageMetaInfo metaInfo, const char* szFormat, ...)
443 CRY_PROFILE_FUNCTION(PROFILE_GAME);
445 va_list va_args;
446 va_start(va_args, szFormat);
447 char messageBuffer[1024] = "";
448 LogUtils::FormatMessage(messageBuffer, szFormat, va_args);
449 va_end(va_args);
450 m_signals.message.Send(SLogMessageData(streamId, ELogMessageType::Error, metaInfo, messageBuffer));
453 //////////////////////////////////////////////////////////////////////////
454 ECriticalErrorStatus CLog::CriticalError(const LogStreamId& streamId, CLogMessageMetaInfo metaInfo, const char* szFormat, ...)
456 va_list va_args;
457 va_start(va_args, szFormat);
458 char messageBuffer[1024] = "";
459 LogUtils::FormatMessage(messageBuffer, szFormat, va_args);
460 va_end(va_args);
461 m_signals.message.Send(SLogMessageData(streamId, ELogMessageType::CriticalError, metaInfo, messageBuffer));
462 if(CVars::sc2_DisplayCriticalErrors)
464 return LogUtils::DisplayCriticalErrorMessage(messageBuffer);
466 else
468 return ECriticalErrorStatus::Continue;
472 //////////////////////////////////////////////////////////////////////////
473 void CLog::FatalError(const LogStreamId& streamId, CLogMessageMetaInfo metaInfo, const char* szFormat, ...)
475 va_list va_args;
476 va_start(va_args, szFormat);
477 char messageBuffer[1024] = "";
478 LogUtils::FormatMessage(messageBuffer, szFormat, va_args);
479 va_end(va_args);
480 m_signals.message.Send(SLogMessageData(streamId, ELogMessageType::FatalError, metaInfo, messageBuffer));
483 //////////////////////////////////////////////////////////////////////////
484 void CLog::Update()
486 for(ILogOutputPtr& pOutput : m_outputs)
488 pOutput->Update();
492 //////////////////////////////////////////////////////////////////////////
493 SLogSignals& CLog::Signals()
495 return m_signals;
498 //////////////////////////////////////////////////////////////////////////
499 CLog::SStream::SStream(const char* _szName, const LogStreamId& _id)
500 : name(_szName)
501 , id(_id)
504 //////////////////////////////////////////////////////////////////////////
505 void CLog::OnSettingsModified()
507 for(const string& userStream : m_pSettings->userStreams)
509 const char* szStreamName = userStream.c_str();
510 if(szStreamName[0] && (FindStream(szStreamName) == INVALID_INDEX))
512 CreateStream(szStreamName);
517 //////////////////////////////////////////////////////////////////////////
518 size_t CLog::FindStream(const char* szName) const
520 SCHEMATYC2_SYSTEM_ASSERT(szName);
521 if(szName)
523 for(size_t streamIdx = 0, steamCount = m_streams.size(); streamIdx < steamCount; ++ streamIdx)
525 if(stricmp(m_streams[streamIdx].name.c_str(), szName) == 0)
527 return streamIdx;
531 return INVALID_INDEX;
534 //////////////////////////////////////////////////////////////////////////
535 size_t CLog::FindStream(const LogStreamId& streamId) const
537 for(size_t streamIdx = 0, steamCount = m_streams.size(); streamIdx < steamCount; ++ streamIdx)
539 if(m_streams[streamIdx].id == streamId)
541 return streamIdx;
544 return INVALID_INDEX;
547 //////////////////////////////////////////////////////////////////////////
548 void CLog::GetUserStreamsFileName(stack_string& fileName) const
550 fileName = gEnv->pSystem->GetIPak()->GetGameFolder();
551 fileName.append("/");
552 fileName.append(CVars::GetStringSafe(CVars::sc2_RootFolder));
553 fileName.append("/");
554 fileName.append("log_user_streams.xml");
557 //////////////////////////////////////////////////////////////////////////
558 bool CLog::IsLoggingEnabled() const
560 return CVars::sc2_LogToFile != nullptr && CVars::sc2_LogToFile->GetIVal() != 0;
563 //////////////////////////////////////////////////////////////////////////
564 CLog::SSettings::SSettings(const SettingsModifiedCallback& _modifiedCallback)
565 : modifiedCallback(_modifiedCallback)
568 //////////////////////////////////////////////////////////////////////////
569 void CLog::SSettings::Serialize(Serialization::IArchive& archive)
571 archive(userStreams, "userStreams", "User Streams");
572 if(archive.isInput() && modifiedCallback)
574 modifiedCallback();
578 //////////////////////////////////////////////////////////////////////////
579 CLogRecorder::~CLogRecorder()
581 End();
584 //////////////////////////////////////////////////////////////////////////
585 void CLogRecorder::Begin()
587 m_recordedMessages.reserve(1024);
588 gEnv->pSchematyc2->GetLog().Signals().message.Connect(LogMessageSignal::Delegate::FromMemberFunction<CLogRecorder, &CLogRecorder::OnLogMessage>(*this), m_connectionScope);
591 //////////////////////////////////////////////////////////////////////////
592 void CLogRecorder::End()
594 m_connectionScope.Release();
597 //////////////////////////////////////////////////////////////////////////
598 void CLogRecorder::VisitMessages(const LogMessageVisitor& visitor)
600 SCHEMATYC2_SYSTEM_ASSERT(visitor);
601 if(visitor)
603 for(auto recordedMessage : m_recordedMessages)
605 if(visitor(SLogMessageData(recordedMessage.streamId, recordedMessage.messageType, recordedMessage.metaInfo, recordedMessage.message.c_str())) != EVisitStatus::Continue)
607 return;
613 //////////////////////////////////////////////////////////////////////////
614 void CLogRecorder::Clear()
616 m_recordedMessages.clear();
619 //////////////////////////////////////////////////////////////////////////
620 CLogRecorder::SRecordedMessage::SRecordedMessage(const SLogMessageData& _logMessageData)
621 : streamId(_logMessageData.streamId)
622 , messageType(_logMessageData.messageType)
623 , metaInfo(_logMessageData.metaInfo)
624 , message(_logMessageData.szMessage)
627 //////////////////////////////////////////////////////////////////////////
628 void CLogRecorder::OnLogMessage(const SLogMessageData& logMessageData)
630 m_recordedMessages.push_back(SRecordedMessage(logMessageData));