From 0e4f174f786c74a75720d90e0abfca1ef15331f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Wed, 18 Oct 2023 21:21:26 +0200 Subject: [PATCH] Check for all file errors in demo player, show demo error popup Handle the return values of all uses of the `io_read`, `io_skip` and `io_tell` functions in `CDemoPlayer`, to handle truncated demo files and other unexpected file reading and seeking errors during demo playback. Show more detailed error message popups when demos cannot be loaded due to errors and when demo playback is stopped unexpectedly due to errors. Previously, only a generic message "Error loading demo" was shown when loading failed and no error message was shown when demo playback is stopped due to errors. --- src/engine/client/client.cpp | 28 ++- src/engine/demo.h | 6 +- src/engine/shared/demo.cpp | 276 +++++++++++----------- src/engine/shared/demo.h | 8 +- src/game/client/components/menus.cpp | 2 +- src/game/client/components/menus_demo.cpp | 6 +- 6 files changed, 172 insertions(+), 154 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 7f76aeafc47..90862b31398 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -609,6 +609,9 @@ void CClient::Connect(const char *pAddress, const char *pPassword) void CClient::DisconnectWithReason(const char *pReason) { + if(pReason != nullptr && pReason[0] == '\0') + pReason = nullptr; + char aBuf[512]; str_format(aBuf, sizeof(aBuf), "disconnecting. reason='%s'", pReason ? pReason : "unknown"); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, gs_ClientNetworkPrintColor); @@ -2437,7 +2440,13 @@ void CClient::Update() else { // disconnect on error - Disconnect(); + DisconnectWithReason(m_DemoPlayer.ErrorMessage()); + if(m_DemoPlayer.ErrorMessage()[0] != '\0') + { + SWarning Warning(Localize("Error playing demo"), m_DemoPlayer.ErrorMessage()); + Warning.m_AutoHide = false; + m_vWarnings.emplace_back(Warning); + } } } else if(State() == IClient::STATE_ONLINE) @@ -3581,27 +3590,23 @@ void CClient::DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void { if(m_DemoPlayer.IsPlaying()) { - const char *pDemoFileName = m_DemoPlayer.GetDemoFileName(); - m_DemoEditor.Slice(pDemoFileName, pDstPath, g_Config.m_ClDemoSliceBegin, g_Config.m_ClDemoSliceEnd, pfnFilter, pUser); + m_DemoEditor.Slice(m_DemoPlayer.Filename(), pDstPath, g_Config.m_ClDemoSliceBegin, g_Config.m_ClDemoSliceEnd, pfnFilter, pUser); } } const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType) { - IOHANDLE File = Storage()->OpenFile(pFilename, IOFLAG_READ, StorageType); - if(!File) - return "error opening demo file"; - - io_close(File); + // Don't disconnect unless the file exists (only for play command) + if(!Storage()->FileExists(pFilename, StorageType)) + return "No demo with this filename exists"; Disconnect(); m_aNetClient[CONN_MAIN].ResetErrorString(); // try to start playback m_DemoPlayer.SetListener(this); - if(m_DemoPlayer.Load(Storage(), m_pConsole, pFilename, StorageType)) - return "error loading demo"; + return m_DemoPlayer.ErrorMessage(); // load map const CMapInfo *pMapInfo = m_DemoPlayer.GetMapInfo(); @@ -3669,8 +3674,7 @@ const char *CClient::DemoPlayer_Render(const char *pFilename, int StorageType, c { m_DemoPlayer.Pause(); } - //m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "demo_recorder", "demo eof"); - return 0; + return nullptr; } #endif diff --git a/src/engine/demo.h b/src/engine/demo.h index 2bd27eefe6b..8e5da1b390a 100644 --- a/src/engine/demo.h +++ b/src/engine/demo.h @@ -58,8 +58,8 @@ struct CMapInfo { char m_aName[MAX_MAP_LENGTH]; SHA256_DIGEST m_Sha256; - int m_Crc; - int m_Size; + unsigned m_Crc; + unsigned m_Size; }; class IDemoPlayer : public IInterface @@ -100,7 +100,7 @@ class IDemoPlayer : public IInterface virtual bool IsPlaying() const = 0; virtual const CInfo *BaseInfo() const = 0; virtual void GetDemoName(char *pBuffer, size_t BufferSize) const = 0; - virtual bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo) const = 0; + virtual bool GetDemoInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile = nullptr, char *pErrorMessage = nullptr, size_t ErrorMessageSize = 0) const = 0; }; class IDemoRecorder : public IInterface diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index e62089f307d..6666752a309 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -411,6 +411,9 @@ void CDemoPlayer::Construct(class CSnapshotDelta *pSnapshotDelta, bool UseVideo) m_LastSnapshotDataSize = -1; m_pListener = nullptr; m_UseVideo = UseVideo; + + m_aFilename[0] = '\0'; + m_aErrorMessage[0] = '\0'; } void CDemoPlayer::SetListener(IListener *pListener) @@ -487,11 +490,18 @@ bool CDemoPlayer::ScanFile() { const long StartPos = io_tell(m_File); m_vKeyFrames.clear(); + if(StartPos < 0) + return false; int ChunkTick = -1; while(true) { const long CurrentPos = io_tell(m_File); + if(CurrentPos < 0) + { + m_vKeyFrames.clear(); + return false; + } int ChunkType, ChunkSize; const EReadChunkHeaderResult Result = ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick); @@ -517,10 +527,20 @@ bool CDemoPlayer::ScanFile() m_Info.m_Info.m_LastTick = ChunkTick; } else if(ChunkSize) - io_skip(m_File, ChunkSize); + { + if(io_skip(m_File, ChunkSize) != 0) + { + m_vKeyFrames.clear(); + return false; + } + } } - io_seek(m_File, StartPos, IOSEEK_START); + if(io_seek(m_File, StartPos, IOSEEK_START) != 0) + { + m_vKeyFrames.clear(); + return false; + } return true; } @@ -545,29 +565,26 @@ void CDemoPlayer::DoTick() { int ChunkType, ChunkSize; const EReadChunkHeaderResult Result = ReadChunkHeader(&ChunkType, &ChunkSize, &ChunkTick); - if(Result != CHUNKHEADER_SUCCESS) + if(Result == CHUNKHEADER_EOF) { - // stop on error or eof - if(m_pConsole) - { - if(Result == CHUNKHEADER_EOF) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", "end of file"); - else - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", "error reading chunk header"); - } - -#if defined(CONF_VIDEORECORDER) - if(m_UseVideo && IVideo::Current()) - Stop(); -#endif if(m_Info.m_PreviousTick == -1) { - if(m_pConsole) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", "empty demo"); - Stop(); + Stop("Empty demo"); } else + { Pause(); + // Stop rendering when reaching end of file +#if defined(CONF_VIDEORECORDER) + if(m_UseVideo && IVideo::Current()) + Stop(); +#endif + } + break; + } + else if(Result == CHUNKHEADER_ERROR) + { + Stop("Error reading chunk header"); break; } @@ -577,30 +594,21 @@ void CDemoPlayer::DoTick() { if(io_read(m_File, m_aCompressedSnapshotData, ChunkSize) != (unsigned)ChunkSize) { - // stop on error or eof - if(m_pConsole) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", "error reading chunk"); - Stop(); + Stop("Error reading chunk data"); break; } DataSize = CNetBase::Decompress(m_aCompressedSnapshotData, ChunkSize, m_aDecompressedSnapshotData, sizeof(m_aDecompressedSnapshotData)); if(DataSize < 0) { - // stop on error or eof - if(m_pConsole) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", "error during network decompression"); - Stop(); + Stop("Error during network decompression"); break; } DataSize = CVariableInt::Decompress(m_aDecompressedSnapshotData, DataSize, m_aCurrentSnapshotData, sizeof(m_aCurrentSnapshotData)); - if(DataSize < 0) { - if(m_pConsole) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", "error during intpack decompression"); - Stop(); + Stop("Error during intpack decompression"); break; } } @@ -615,8 +623,8 @@ void CDemoPlayer::DoTick() { if(m_pConsole) { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "error during unpacking of delta, err=%d", DataSize); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "Error unpacking snapshot delta. DataSize=%d", DataSize); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", aBuf); } } @@ -624,8 +632,8 @@ void CDemoPlayer::DoTick() { if(m_pConsole) { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "snapshot delta invalid. DataSize=%d", DataSize); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "Snapshot delta invalid. DataSize=%d", DataSize); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", aBuf); } } @@ -647,8 +655,8 @@ void CDemoPlayer::DoTick() { if(m_pConsole) { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "snapshot invalid. DataSize=%d", DataSize); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "Snapshot invalid. DataSize=%d", DataSize); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", aBuf); } } @@ -709,21 +717,16 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const dbg_assert(m_File == 0, "Demo player already playing"); m_pConsole = pConsole; - m_File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType); - if(!m_File) + str_copy(m_aFilename, pFilename); + str_copy(m_aErrorMessage, ""); + + if(m_pConsole) { - if(m_pConsole) - { - char aBuf[32 + IO_MAX_PATH_LENGTH]; - str_format(aBuf, sizeof(aBuf), "could not open '%s'", pFilename); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); - } - return -1; + char aBuf[32 + IO_MAX_PATH_LENGTH]; + str_format(aBuf, sizeof(aBuf), "Loading demo '%s'", pFilename); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); } - // store the filename - str_copy(m_aFilename, pFilename); - // clear the playback info mem_zero(&m_Info, sizeof(m_Info)); m_Info.m_Info.m_FirstTick = -1; @@ -733,66 +736,21 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const m_Info.m_PreviousTick = -1; m_Info.m_Info.m_Speed = 1; m_SpeedIndex = 4; - m_LastSnapshotDataSize = -1; - // read the header - if(io_read(m_File, &m_Info.m_Header, sizeof(m_Info.m_Header)) != sizeof(m_Info.m_Header) || !m_Info.m_Header.Valid()) + if(!GetDemoInfo(pStorage, m_pConsole, pFilename, StorageType, &m_Info.m_Header, &m_Info.m_TimelineMarkers, &m_MapInfo, &m_File, m_aErrorMessage, sizeof(m_aErrorMessage))) { - if(m_pConsole) - { - char aBuf[32 + IO_MAX_PATH_LENGTH]; - str_format(aBuf, sizeof(aBuf), "'%s' is not a valid demo file", pFilename); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); - } - io_close(m_File); - m_File = 0; + str_copy(m_aFilename, ""); return -1; } - if(m_Info.m_Header.m_Version < gs_OldVersion) + // save byte offset of map for later use + m_MapOffset = io_tell(m_File); + if(m_MapOffset < 0 || io_skip(m_File, m_MapInfo.m_Size) != 0) { - if(m_pConsole) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "demo version %d is not supported", m_Info.m_Header.m_Version); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); - } - io_close(m_File); - m_File = 0; + Stop("Error skipping map data"); return -1; } - else if(m_Info.m_Header.m_Version > gs_OldVersion) - io_read(m_File, &m_Info.m_TimelineMarkers, sizeof(m_Info.m_TimelineMarkers)); - - SHA256_DIGEST Sha256 = SHA256_ZEROED; - if(m_Info.m_Header.m_Version >= gs_Sha256Version) - { - CUuid ExtensionUuid = {}; - io_read(m_File, &ExtensionUuid.m_aData, sizeof(ExtensionUuid.m_aData)); - - if(ExtensionUuid == SHA256_EXTENSION) - { - io_read(m_File, &Sha256, sizeof(SHA256_DIGEST)); // need a safe read - } - else - { - // This hopes whatever happened during the version increment didn't add something here - dbg_msg("demo", "demo version incremented, but not by ddnet"); - io_seek(m_File, -(int)sizeof(ExtensionUuid.m_aData), IOSEEK_CUR); - } - } - - // save byte offset of map for later use - const unsigned MapSize = bytes_be_to_uint(m_Info.m_Header.m_aMapSize); - m_MapOffset = io_tell(m_File); - io_skip(m_File, MapSize); - - // store map information - m_MapInfo.m_Crc = bytes_be_to_uint(m_Info.m_Header.m_aMapCrc); - m_MapInfo.m_Sha256 = Sha256; - m_MapInfo.m_Size = MapSize; - str_copy(m_MapInfo.m_aName, m_Info.m_Header.m_aMapName); if(m_Info.m_Header.m_Version > gs_OldVersion) { @@ -808,12 +766,7 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const // scan the file for interesting points if(!ScanFile()) { - if(m_pConsole) - { - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", "Error scanning demo file"); - } - io_close(m_File); - m_File = 0; + Stop("Error scanning demo file"); return -1; } @@ -828,15 +781,18 @@ int CDemoPlayer::Load(class IStorage *pStorage, class IConsole *pConsole, const unsigned char *CDemoPlayer::GetMapData(class IStorage *pStorage) { if(!m_MapInfo.m_Size) - return 0; + return nullptr; - long CurSeek = io_tell(m_File); - - // get map data - io_seek(m_File, m_MapOffset, IOSEEK_START); + const long CurSeek = io_tell(m_File); + if(CurSeek < 0 || io_seek(m_File, m_MapOffset, IOSEEK_START) != 0) + return nullptr; unsigned char *pMapData = (unsigned char *)malloc(m_MapInfo.m_Size); - io_read(m_File, pMapData, m_MapInfo.m_Size); - io_seek(m_File, CurSeek, IOSEEK_START); + if(io_read(m_File, pMapData, m_MapInfo.m_Size) != m_MapInfo.m_Size || + io_seek(m_File, CurSeek, IOSEEK_START) != 0) + { + free(pMapData); + return nullptr; + } return pMapData; } @@ -970,7 +926,11 @@ int CDemoPlayer::SetPos(int WantedTick) KeyFrame--; // seek to the correct key frame - io_seek(m_File, m_vKeyFrames[KeyFrame].m_Filepos, IOSEEK_START); + if(io_seek(m_File, m_vKeyFrames[KeyFrame].m_Filepos, IOSEEK_START) != 0) + { + Stop("Error seeking keyframe position"); + return -1; + } m_Info.m_NextTick = -1; m_Info.m_Info.m_CurrentTick = -1; @@ -1043,7 +1003,7 @@ int CDemoPlayer::Update(bool RealTime) return 0; } -int CDemoPlayer::Stop() +void CDemoPlayer::Stop(const char *pErrorMessage) { #if defined(CONF_VIDEORECORDER) if(m_UseVideo && IVideo::Current()) @@ -1051,15 +1011,23 @@ int CDemoPlayer::Stop() #endif if(!m_File) - return -1; + return; if(m_pConsole) - m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", "Stopped playback"); + { + char aBuf[256]; + if(pErrorMessage[0] == '\0') + str_copy(aBuf, "Stopped playback"); + else + str_format(aBuf, sizeof(aBuf), "Stopped playback due to error: %s", pErrorMessage); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demo_player", aBuf); + } + io_close(m_File); m_File = 0; m_vKeyFrames.clear(); str_copy(m_aFilename, ""); - return 0; + str_copy(m_aErrorMessage, pErrorMessage); } void CDemoPlayer::GetDemoName(char *pBuffer, size_t BufferSize) const @@ -1067,7 +1035,7 @@ void CDemoPlayer::GetDemoName(char *pBuffer, size_t BufferSize) const IStorage::StripPathAndExtension(m_aFilename, pBuffer, BufferSize); } -bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo) const +bool CDemoPlayer::GetDemoInfo(IStorage *pStorage, IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile, char *pErrorMessage, size_t ErrorMessageSize) const { mem_zero(pDemoHeader, sizeof(CDemoHeader)); mem_zero(pTimelineMarkers, sizeof(CTimelineMarkers)); @@ -1075,45 +1043,87 @@ bool CDemoPlayer::GetDemoInfo(class IStorage *pStorage, const char *pFilename, i IOHANDLE File = pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType); if(!File) + { + if(pErrorMessage != nullptr) + str_copy(pErrorMessage, "Could not open demo file", ErrorMessageSize); return false; + } - if(io_read(File, pDemoHeader, sizeof(CDemoHeader)) != sizeof(CDemoHeader) || !pDemoHeader->Valid() || pDemoHeader->m_Version < gs_OldVersion) + if(io_read(File, pDemoHeader, sizeof(CDemoHeader)) != sizeof(CDemoHeader) || !pDemoHeader->Valid()) { + if(pErrorMessage != nullptr) + str_copy(pErrorMessage, "Error reading demo header", ErrorMessageSize); mem_zero(pDemoHeader, sizeof(CDemoHeader)); io_close(File); return false; } - if(pDemoHeader->m_Version > gs_OldVersion) + if(pDemoHeader->m_Version < gs_OldVersion) { - io_read(File, pTimelineMarkers, sizeof(CTimelineMarkers)); + if(pErrorMessage != nullptr) + str_format(pErrorMessage, ErrorMessageSize, "Demo version '%d' is not supported", pDemoHeader->m_Version); + mem_zero(pDemoHeader, sizeof(CDemoHeader)); + io_close(File); + return false; + } + else if(pDemoHeader->m_Version > gs_OldVersion) + { + if(io_read(File, pTimelineMarkers, sizeof(CTimelineMarkers)) != sizeof(CTimelineMarkers)) + { + if(pErrorMessage != nullptr) + str_copy(pErrorMessage, "Error reading timeline markers", ErrorMessageSize); + mem_zero(pDemoHeader, sizeof(CDemoHeader)); + io_close(File); + return false; + } } - - str_copy(pMapInfo->m_aName, pDemoHeader->m_aMapName); - pMapInfo->m_Crc = bytes_be_to_uint(pDemoHeader->m_aMapCrc); SHA256_DIGEST Sha256 = SHA256_ZEROED; if(pDemoHeader->m_Version >= gs_Sha256Version) { CUuid ExtensionUuid = {}; - io_read(File, &ExtensionUuid.m_aData, sizeof(ExtensionUuid.m_aData)); - - if(ExtensionUuid == SHA256_EXTENSION) + const unsigned ExtensionUuidSize = io_read(File, &ExtensionUuid.m_aData, sizeof(ExtensionUuid.m_aData)); + if(ExtensionUuidSize == sizeof(ExtensionUuid.m_aData) && ExtensionUuid == SHA256_EXTENSION) { - io_read(File, &Sha256, sizeof(SHA256_DIGEST)); // need a safe read + if(io_read(File, &Sha256, sizeof(SHA256_DIGEST)) != sizeof(SHA256_DIGEST)) + { + if(pErrorMessage != nullptr) + str_copy(pErrorMessage, "Error reading SHA256", ErrorMessageSize); + mem_zero(pDemoHeader, sizeof(CDemoHeader)); + mem_zero(pTimelineMarkers, sizeof(CTimelineMarkers)); + io_close(File); + return false; + } } else { // This hopes whatever happened during the version increment didn't add something here - dbg_msg("demo", "demo version incremented, but not by ddnet"); - io_seek(File, -(int)sizeof(ExtensionUuid.m_aData), IOSEEK_CUR); + if(pConsole) + { + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "demo_player", "Demo version incremented, but not by DDNet"); + } + if(io_seek(File, -(int)ExtensionUuidSize, IOSEEK_CUR) != 0) + { + if(pErrorMessage != nullptr) + str_copy(pErrorMessage, "Error rewinding SHA256 extension UUID", ErrorMessageSize); + mem_zero(pDemoHeader, sizeof(CDemoHeader)); + mem_zero(pTimelineMarkers, sizeof(CTimelineMarkers)); + io_close(File); + return false; + } } } - pMapInfo->m_Sha256 = Sha256; + str_copy(pMapInfo->m_aName, pDemoHeader->m_aMapName); + pMapInfo->m_Sha256 = Sha256; + pMapInfo->m_Crc = bytes_be_to_uint(pDemoHeader->m_aMapCrc); pMapInfo->m_Size = bytes_be_to_uint(pDemoHeader->m_aMapSize); - io_close(File); + if(pFile == nullptr) + io_close(File); + else + *pFile = File; + return true; } diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index 14b127d0e58..4cb18304f7d 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -107,6 +107,7 @@ class CDemoPlayer : public IDemoPlayer IOHANDLE m_File; long m_MapOffset; char m_aFilename[IO_MAX_PATH_LENGTH]; + char m_aErrorMessage[256]; std::vector m_vKeyFrames; CMapInfo m_MapInfo; int m_SpeedIndex; @@ -150,7 +151,7 @@ class CDemoPlayer : public IDemoPlayer int Play(); void Pause() override; void Unpause() override; - int Stop(); + void Stop(const char *pErrorMessage = ""); void SetSpeed(float Speed) override; void SetSpeedIndex(int SpeedIndex) override; void AdjustSpeedIndex(int Offset) override; @@ -160,8 +161,9 @@ class CDemoPlayer : public IDemoPlayer int SetPos(int WantedTick) override; const CInfo *BaseInfo() const override { return &m_Info.m_Info; } void GetDemoName(char *pBuffer, size_t BufferSize) const override; - bool GetDemoInfo(class IStorage *pStorage, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo) const override; - const char *GetDemoFileName() { return m_aFilename; } + bool GetDemoInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile = nullptr, char *pErrorMessage = nullptr, size_t ErrorMessageSize = 0) const override; + const char *Filename() { return m_aFilename; } + const char *ErrorMessage() { return m_aErrorMessage; } int Update(bool RealTime = true); diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 1d9aeb4bfee..a227ea3fc79 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -1839,7 +1839,7 @@ void CMenus::PopupConfirmDemoReplaceVideo() if(pError) { m_DemoRenderInput.Clear(); - PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok")); + PopupMessage(Localize("Error loading demo"), pError, Localize("Ok")); } } #endif diff --git a/src/game/client/components/menus_demo.cpp b/src/game/client/components/menus_demo.cpp index e579af1292a..0e70cb3f944 100644 --- a/src/game/client/components/menus_demo.cpp +++ b/src/game/client/components/menus_demo.cpp @@ -1041,7 +1041,7 @@ bool CMenus::FetchHeader(CDemoItem &Item) { char aBuffer[IO_MAX_PATH_LENGTH]; str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aCurrentDemoFolder, Item.m_aFilename); - Item.m_Valid = DemoPlayer()->GetDemoInfo(Storage(), aBuffer, Item.m_StorageType, &Item.m_Info, &Item.m_TimelineMarkers, &Item.m_MapInfo); + Item.m_Valid = DemoPlayer()->GetDemoInfo(Storage(), nullptr, aBuffer, Item.m_StorageType, &Item.m_Info, &Item.m_TimelineMarkers, &Item.m_MapInfo); Item.m_InfosLoaded = true; } return Item.m_Valid; @@ -1505,7 +1505,9 @@ void CMenus::RenderDemoBrowserButtons(CUIRect ButtonsView, bool WasListboxItemAc m_LastPauseChange = -1.0f; m_LastSpeedChange = -1.0f; if(pError) - PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok")); + { + PopupMessage(Localize("Error loading demo"), pError, Localize("Ok")); + } else { UI()->SetActiveItem(nullptr);