diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 77a42d468..c527a6af9 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -8720,7 +8720,11 @@ bool CEditor::Save(const char *pFilename) if(std::any_of(std::begin(m_WriterFinishJobs), std::end(m_WriterFinishJobs), [pFilename](const std::shared_ptr &Job) { return str_comp(pFilename, Job->GetRealFileName()) == 0; })) return false; - return m_Map.Save(pFilename); + const auto &&ErrorHandler = [this](const char *pErrorMessage) { + ShowFileDialogError("%s", pErrorMessage); + Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor/save", pErrorMessage); + }; + return m_Map.Save(pFilename, ErrorHandler); } bool CEditor::HandleMapDrop(const char *pFileName, int StorageType) diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 86521f561..39cf313b7 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -200,7 +200,8 @@ public: void CreateDefault(IGraphics::CTextureHandle EntitiesTexture); // io - bool Save(const char *pFilename); + bool Save(const char *pFilename, const std::function &ErrorHandler); + bool PerformPreSaveSanityChecks(const std::function &ErrorHandler); bool Load(const char *pFilename, int StorageType, const std::function &ErrorHandler); void PerformSanityChecks(const std::function &ErrorHandler); diff --git a/src/game/editor/mapitems/map_io.cpp b/src/game/editor/mapitems/map_io.cpp index 2ad4c6966..30f85c676 100644 --- a/src/game/editor/mapitems/map_io.cpp +++ b/src/game/editor/mapitems/map_io.cpp @@ -34,7 +34,7 @@ struct CSoundSource_DEPRECATED int m_SoundEnvOffset; }; -bool CEditorMap::Save(const char *pFileName) +bool CEditorMap::Save(const char *pFileName, const std::function &ErrorHandler) { char aFileNameTmp[IO_MAX_PATH_LENGTH]; IStorage::FormatTmpPath(aFileNameTmp, sizeof(aFileNameTmp), pFileName); @@ -42,11 +42,17 @@ bool CEditorMap::Save(const char *pFileName) char aBuf[IO_MAX_PATH_LENGTH + 64]; str_format(aBuf, sizeof(aBuf), "saving to '%s'...", aFileNameTmp); m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor", aBuf); + + if(!PerformPreSaveSanityChecks(ErrorHandler)) + { + return false; + } + CDataFileWriter Writer; if(!Writer.Open(m_pEditor->Storage(), aFileNameTmp)) { - str_format(aBuf, sizeof(aBuf), "failed to open file '%s'...", aFileNameTmp); - m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "editor", aBuf); + str_format(aBuf, sizeof(aBuf), "Error: Failed to open file '%s' for writing.", aFileNameTmp); + ErrorHandler(aBuf); return false; } @@ -402,11 +408,42 @@ bool CEditorMap::Save(const char *pFileName) return true; } +bool CEditorMap::PerformPreSaveSanityChecks(const std::function &ErrorHandler) +{ + bool Success = true; + char aErrorMessage[256]; + + for(const std::shared_ptr &pImage : m_vpImages) + { + if(!pImage->m_External && pImage->m_pData == nullptr) + { + str_format(aErrorMessage, sizeof(aErrorMessage), "Error: Saving is not possible because the image '%s' could not be loaded. Remove or replace this image.", pImage->m_aName); + ErrorHandler(aErrorMessage); + Success = false; + } + } + + for(const std::shared_ptr &pSound : m_vpSounds) + { + if(pSound->m_pData == nullptr) + { + str_format(aErrorMessage, sizeof(aErrorMessage), "Error: Saving is not possible because the sound '%s' could not be loaded. Remove or replace this sound.", pSound->m_aName); + ErrorHandler(aErrorMessage); + Success = false; + } + } + + return Success; +} + bool CEditorMap::Load(const char *pFileName, int StorageType, const std::function &ErrorHandler) { CDataFileReader DataFile; if(!DataFile.Open(m_pEditor->Storage(), pFileName, StorageType)) + { + ErrorHandler("Error: Failed to open map file. See local console for details."); return false; + } // check version const CMapItemVersion *pItemVersion = static_cast(DataFile.FindItem(MAPITEMTYPE_VERSION, 0)); @@ -518,6 +555,11 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio pImg->m_External = 1; pImg->m_Texture = m_pEditor->Graphics()->LoadTextureRaw(*pImg, TextureLoadFlag, aBuf); } + else + { + str_format(aBuf, sizeof(aBuf), "Error: Failed to load external image '%s'.", pImg->m_aName); + ErrorHandler(aBuf); + } } else { @@ -578,6 +620,11 @@ bool CEditorMap::Load(const char *pFileName, int StorageType, const std::functio { pSound->m_SoundId = m_pEditor->Sound()->LoadOpusFromMem(pSound->m_pData, pSound->m_DataSize, true); } + else + { + str_format(aBuf, sizeof(aBuf), "Error: Failed to load external sound '%s'.", pSound->m_aName); + ErrorHandler(aBuf); + } } else { diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index e251ff240..8f13151d1 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1655,6 +1655,11 @@ CUi::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, { if(pEditor->DoButton_MenuItem(&s_ExternalButton, "Embed", 0, &Slot, 0, "Embeds the image into the map file.")) { + if(pImg->m_pData == nullptr) + { + pEditor->ShowFileDialogError("Embedding is not possible because the image could not be loaded."); + return CUi::POPUP_KEEP_OPEN; + } pImg->m_External = 0; return CUi::POPUP_CLOSE_CURRENT; } @@ -1730,6 +1735,11 @@ CUi::EPopupMenuFunctionResult CEditor::PopupImage(void *pContext, CUIRect View, View.HSplitTop(RowHeight, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ExportButton, "Export", 0, &Slot, 0, "Export the image")) { + if(pImg->m_pData == nullptr) + { + pEditor->ShowFileDialogError("Exporting is not possible because the image could not be loaded."); + return CUi::POPUP_KEEP_OPEN; + } pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_IMG, "Save image", "Save", "mapres", false, CallbackSaveImage, pEditor); pEditor->m_FileDialogFileNameInput.Set(pImg->m_aName); return CUi::POPUP_CLOSE_CURRENT; @@ -1825,6 +1835,11 @@ CUi::EPopupMenuFunctionResult CEditor::PopupSound(void *pContext, CUIRect View, View.HSplitTop(RowHeight, &Slot, &View); if(pEditor->DoButton_MenuItem(&s_ExportButton, "Export", 0, &Slot, 0, "Export sound")) { + if(pSound->m_pData == nullptr) + { + pEditor->ShowFileDialogError("Exporting is not possible because the sound could not be loaded."); + return CUi::POPUP_KEEP_OPEN; + } pEditor->InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_SOUND, "Save sound", "Save", "mapres", false, CallbackSaveSound, pEditor); pEditor->m_FileDialogFileNameInput.Set(pSound->m_aName); return CUi::POPUP_CLOSE_CURRENT;