Browse Source

Handle corrupted H3 data - show message box instead of silent crash

Ivan Savenko 1 year ago
parent
commit
95d761bbb8
4 changed files with 48 additions and 10 deletions
  1. 23 9
      client/CMT.cpp
  2. 8 1
      lib/CArtHandler.cpp
  3. 1 0
      lib/CMakeLists.txt
  4. 16 0
      lib/ExceptionsCommon.h

+ 23 - 9
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<bool> headlessQuit = false;
+static std::optional<std::string> 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);
 }

+ 8 - 1
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<JsonNode> 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();

+ 1 - 0
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

+ 16 - 0
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;
+};