From 95d761bbb8dd054862b2a2e69ef8f37895fd1520 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 13 May 2024 15:41:47 +0000 Subject: [PATCH] Handle corrupted H3 data - show message box instead of silent crash --- client/CMT.cpp | 32 +++++++++++++++++++++++--------- lib/CArtHandler.cpp | 9 ++++++++- lib/CMakeLists.txt | 1 + lib/ExceptionsCommon.h | 16 ++++++++++++++++ 4 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 lib/ExceptionsCommon.h diff --git a/client/CMT.cpp b/client/CMT.cpp index 10c4ea893..be8e262d5 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -31,6 +31,7 @@ #include "../lib/CConfigHandler.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CThreadHelper.h" +#include "../lib/ExceptionsCommon.h" #include "../lib/VCMIDirs.h" #include "../lib/VCMI_Lib.h" #include "../lib/filesystem/Filesystem.h" @@ -56,6 +57,7 @@ namespace po = boost::program_options; namespace po_style = boost::program_options::command_line_style; static std::atomic headlessQuit = false; +static std::optional criticalInitializationError; #ifndef VCMI_IOS void processCommand(const std::string &message); @@ -69,9 +71,16 @@ static CBasicLogConfigurator *logConfig; void init() { CStopWatch tmh; - - loadDLLClasses(); - CGI->setFromLib(); + try + { + loadDLLClasses(); + CGI->setFromLib(); + } + catch (const DataLoadingException & e) + { + criticalInitializationError = e.what(); + return; + } logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff()); @@ -322,6 +331,11 @@ int main(int argc, char * argv[]) #endif // ANDROID #endif // THREADED + if (criticalInitializationError.has_value()) + { + handleFatalError(criticalInitializationError.value(), false); + } + if(!settings["session"]["headless"].Bool()) { pomtime.getDiff(); @@ -412,7 +426,7 @@ static void mainLoop() } } -[[noreturn]] static void quitApplicationImmediately() +[[noreturn]] static void quitApplicationImmediately(int error_code) { // Perform quick exit without executing static destructors and let OS cleanup anything that we did not // We generally don't care about them and this leads to numerous issues, e.g. @@ -420,9 +434,9 @@ static void mainLoop() // Android - std::quick_exit is available only starting from API level 21 // Mingw, macOS and iOS - std::quick_exit is unavailable (at least in current version of CI) #if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE) - ::exit(0); + ::exit(error_code); #else - std::quick_exit(0); + std::quick_exit(error_code); #endif } @@ -476,7 +490,7 @@ static void mainLoop() } std::cout << "Ending...\n"; - quitApplicationImmediately(); + quitApplicationImmediately(0); } void handleQuit(bool ask) @@ -500,7 +514,7 @@ void handleQuit(bool ask) // proper solution would be to abort init thread (or wait for it to finish) if (!CCS->curh) { - quitApplicationImmediately(); + quitApplicationImmediately(0); } if (LOCPLINT) @@ -521,5 +535,5 @@ void handleFatalError(const std::string & message, bool terminate) if (terminate) throw std::runtime_error(message); else - exit(1); + quitApplicationImmediately(1); } diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index c85d67918..9cdd430e9 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -12,6 +12,7 @@ #include "ArtifactUtils.h" #include "CGeneralTextHandler.h" +#include "ExceptionsCommon.h" #include "GameSettings.h" #include "mapObjects/MapObjects.h" #include "constants/StringConstants.h" @@ -354,7 +355,13 @@ std::vector CArtHandler::loadLegacyData() artData["slot"].Vector().emplace_back(artSlot); } } - artData["class"].String() = classes.at(parser.readString()[0]); + + std::string artClass = parser.readString(); + + if (classes.count(artClass[0])) + artData["class"].String() = classes.at(artClass[0]); + else + throw DataLoadingException("File ARTRAITS.TXT is invalid or corrupted! Please reinstall Heroes III data files"); artData["text"]["description"].String() = parser.readString(); parser.endLine(); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7ddfd46d5..b00c41afe 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -660,6 +660,7 @@ set(lib_MAIN_HEADERS CStack.h CStopWatch.h CTownHandler.h + ExceptionsCommon.h ExtraOptionsInfo.h FunctionList.h GameCallbackHolder.h diff --git a/lib/ExceptionsCommon.h b/lib/ExceptionsCommon.h new file mode 100644 index 000000000..b33d78106 --- /dev/null +++ b/lib/ExceptionsCommon.h @@ -0,0 +1,16 @@ +/* + * ExceptionsCommon.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +class DLL_LINKAGE DataLoadingException: public std::runtime_error +{ +public: + using std::runtime_error::runtime_error; +};