From: Andrew Udvare <audvare@gmail.com>
Subject: [PATCH] Resolve the languages directory the same way as the files in it

Translate_Init() calls Translate_Load("Languages/") and that relative path is
handed straight to std::filesystem::directory_iterator, so it only resolves
when the program is run from its own directory. Translate_Read() already puts
the individual .lng file through setBasePath(); this does the same for the
directory listing.

directory_iterator is also given an error_code, so a missing directory now
leaves English selected rather than throwing filesystem_error out of
Translate_Load(), which nothing catches and which aborts the program.

diff --git a/Source/Utility/Translate.cpp b/Source/Utility/Translate.cpp
index 9c2b65a..46ed8b5 100644
--- a/Source/Utility/Translate.cpp
+++ b/Source/Utility/Translate.cpp
@@ -1,302 +1,310 @@
-/*
-Copyright (C) 2012 Salvy6735
-Copyright (C) 2012 StrmnNrmn
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-*/
-
-
-#include "Base/Types.h"
-
-
-#include <vector>
-#include <iostream>
-#include <string>
-
-#include <fstream> 
-#include <sstream>
-
-#include "Utility/Paths.h"
-#include "Utility/StringUtil.h"
-#include "Utility/Translate.h"
-#include "Utility/VolatileMem.h"
-
-
-
-#include "Base/Macros.h"
-#define TRANSLATE_DUMP_VALUE 0xDAEDDAED
-//*****************************************************************************
-//
-//*****************************************************************************
-struct pTranslate
-{
-	u32		hash;			// hash that corresponds to string
-//	u32		len;			// lenght of the translated string
-	char	*translated;	// Translated string
-};
-
-pTranslate				 text[180];
-std::vector<std::string> gLanguage;
-//
-// Hash was taken from http://stackoverflow.com/questions/98153/whats-the-best-hashing-algorithm-to-use-on-a-stl-string-when-using-hash-map
-//
-//*****************************************************************************
-//
-//*****************************************************************************
-u32 HashString(const std::string& s) {
-    u32 hash = 0;
-    for (char c : s) {
-        hash = hash * 101 + c;
-    }
-    return hash;
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-const char * Translate_Strings(const std::string& original, u32 & len)
-{
-	u32 hash = HashString(original);
-	if( hash == 0 )
-		return original.data();
-
-	for( u32 i=0; i < std::size(text); i++ )
-	{
-		// ToDo..
-		//DAEDALUS_ASSERT( text[i].translated != original, " String already translated" );
-
-		if( text[i].hash == hash )
-		{
-			if( text[i].translated )
-			{
-				len =  strlen( text[i].translated );
-				return text[i].translated;
-			}
-			else
-				return original.data();
-		}
-	}
-	return original.data();
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-const char * Translate_String(const char *original)
-{
-	u32 dummy;
-	return Translate_Strings( original, dummy );
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-void Translate_Unload()
-{
-	// Clear translations
-	for( u32 i = 0; i < std::size(text); ++i )
-	{
-		if( text[i].translated != NULL )
-		{
-			free_volatile(text[i].translated);
-			text[i].translated = NULL;
-		}
-	}
-}
-//*****************************************************************************
-//
-//*****************************************************************************
-bool	Translate_Init()
-{
-	// Init translations if available
-	Translate_Load("Languages/" );
-
-	return /*gLanguage.empty() == 0*/ true;
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-void	Translate_Load( const std::filesystem::path& p_dir )
-{
-	// Set default language
-	gLanguage.push_back("English");
-
-	for (auto const& dir_entry : std::filesystem::directory_iterator(p_dir))
-	{
-		if (dir_entry.is_regular_file() && dir_entry.path().extension() == ".lng")
-		{
-			std::string filename = dir_entry.path().stem().string();
-			gLanguage.push_back(filename);
-		}
-	}
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-const char * Translate_Name(u32 idx)
-{
-	if( idx < gLanguage.size())
-	{
-		return gLanguage[ idx ].c_str();
-	}
-
-	return "?";
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-u32 Translate_Number()
-{
-	return gLanguage.size()-1;
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-u32	Translate_IndexFromName( const char * name )
-{
-	for( u32 i = 0; i < gLanguage.size(); ++i )
-	{
-		if( strcasecmp(  gLanguage[ i ].c_str(), name ) == 0 )
-		{
-			return i;
-		}
-	}
-
-	// Default language (English)
-	return 0;
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-const char * Translate_NameFromIndex( u32 idx )
-{
-	if( idx < gLanguage.size())
-	{
-		return gLanguage[ idx ].c_str();
-	}
-
-	return "?";
-}
-
-//*****************************************************************************
-// Restores escape characters which were removed when parsing
-// Which are needed by line-breaking and back-slash
-//*****************************************************************************
-std::string Restore(std::string s, size_t len) {
-    for (size_t i = 0; i < len; i++) {
-        if (s[i] == '\\') {
-            if (s[i + 1] == 'n') {
-                s[i + 1] = '\b'; 
-                s[i] = '\n';
-                i++;
-            } else if (s[i + 1] == '\\') {
-                s[i + 1] = '\b'; 
-                s[i] = '\\';
-                i++;
-            }
-        }
-    }
-    return s;
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-void Translate_Dump(const std::string string, bool dump)
-{
-	if(dump)
-	{
-	std::fstream fh("hash.txt", std::ios::in);
-
-	if (fh.is_open())
-		{
-		fh << FORMAT_NAMESPACE::format("{:08x},{}\n", HashString(string), string);
-			
-		}
-	}
-}
-
-//*****************************************************************************
-//
-//*****************************************************************************
-bool Translate_Read(u32 idx, const std::filesystem::path& dir)
-{
-	/// Always unload previous language file if available
-	Translate_Unload();
-
-	if( idx > gLanguage.size() )
-		return false;
-
-	std::string line;
-
-	char *string;
-
-	u32 count = 0;
-	u32 hash  = 0;
-	u32	len   = 0;
-
-	// Build path where we'll load the translation file(s)
-	
-	const std::string languageFile = "Languages/" + gLanguage[idx] + ".lng";
-	const std::filesystem::path& path = setBasePath(languageFile);
-	
-
-	std::cout << "Language Path: " << path << std::endl;
-	std::fstream stream(path, std::ios::in);
-
-	if (!stream.is_open())
-	{
-		return false;
-	}
-
-
-  while (std::getline(stream, line)) {
-        // Strip spaces from end of lines
-        line.erase(line.find_last_not_of(" \t\n\r\f\v") + 1);
-
-        // Handle comments
-        if (line.empty() || line[0] == '/')
-            continue;
-
-        size_t commaPos = line.find(',');
-        if (commaPos != std::string::npos) {
-            std::string hashString = line.substr(0, commaPos);
-            std::string string = line.substr(commaPos + 1);
-
-            unsigned int hash;
-            std::stringstream ss;
-            ss << std::hex << hashString;
-            ss >> hash;
-			constexpr size_t TEXT_ARRAY_SIZE = 1024; 
-            size_t len = string.length();
-            if (count < TEXT_ARRAY_SIZE) {
-                // Write translated string and hash to array
-                text[count].hash = hash;
-                Translate_Dump(string, hash == TRANSLATE_DUMP_VALUE);
-
-                text[count].translated = (char*)malloc(len + 1); // Leave space for terminator
-                strcpy(text[count].translated, Restore(string, len).c_str());
-                count++;
-            }
-        }
-    }
-
-	return true;
-}
+/*
+Copyright (C) 2012 Salvy6735
+Copyright (C) 2012 StrmnNrmn
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+
+#include "Base/Types.h"
+
+
+#include <vector>
+#include <iostream>
+#include <string>
+
+#include <fstream> 
+#include <sstream>
+
+#include "Utility/Paths.h"
+#include "Utility/StringUtil.h"
+#include "Utility/Translate.h"
+#include "Utility/VolatileMem.h"
+
+
+
+#include "Base/Macros.h"
+#define TRANSLATE_DUMP_VALUE 0xDAEDDAED
+//*****************************************************************************
+//
+//*****************************************************************************
+struct pTranslate
+{
+	u32		hash;			// hash that corresponds to string
+//	u32		len;			// lenght of the translated string
+	char	*translated;	// Translated string
+};
+
+pTranslate				 text[180];
+std::vector<std::string> gLanguage;
+//
+// Hash was taken from http://stackoverflow.com/questions/98153/whats-the-best-hashing-algorithm-to-use-on-a-stl-string-when-using-hash-map
+//
+//*****************************************************************************
+//
+//*****************************************************************************
+u32 HashString(const std::string& s) {
+    u32 hash = 0;
+    for (char c : s) {
+        hash = hash * 101 + c;
+    }
+    return hash;
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+const char * Translate_Strings(const std::string& original, u32 & len)
+{
+	u32 hash = HashString(original);
+	if( hash == 0 )
+		return original.data();
+
+	for( u32 i=0; i < std::size(text); i++ )
+	{
+		// ToDo..
+		//DAEDALUS_ASSERT( text[i].translated != original, " String already translated" );
+
+		if( text[i].hash == hash )
+		{
+			if( text[i].translated )
+			{
+				len =  strlen( text[i].translated );
+				return text[i].translated;
+			}
+			else
+				return original.data();
+		}
+	}
+	return original.data();
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+const char * Translate_String(const char *original)
+{
+	u32 dummy;
+	return Translate_Strings( original, dummy );
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+void Translate_Unload()
+{
+	// Clear translations
+	for( u32 i = 0; i < std::size(text); ++i )
+	{
+		if( text[i].translated != NULL )
+		{
+			free_volatile(text[i].translated);
+			text[i].translated = NULL;
+		}
+	}
+}
+//*****************************************************************************
+//
+//*****************************************************************************
+bool	Translate_Init()
+{
+	// Init translations if available
+	Translate_Load("Languages/" );
+
+	return /*gLanguage.empty() == 0*/ true;
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+void	Translate_Load( const std::filesystem::path& p_dir )
+{
+	// Set default language
+	gLanguage.push_back("English");
+
+	// The caller passes a relative directory, which only resolves when the
+	// program is run from its own directory. Route it through setBasePath()
+	// like the rest of the translation code already does, and use the
+	// error_code overload so a missing directory leaves English selected
+	// instead of throwing std::filesystem::filesystem_error out of here.
+	std::error_code error;
+	const std::filesystem::path dir = setBasePath(p_dir);
+
+	for (auto const& dir_entry : std::filesystem::directory_iterator(dir, error))
+	{
+		if (dir_entry.is_regular_file() && dir_entry.path().extension() == ".lng")
+		{
+			std::string filename = dir_entry.path().stem().string();
+			gLanguage.push_back(filename);
+		}
+	}
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+const char * Translate_Name(u32 idx)
+{
+	if( idx < gLanguage.size())
+	{
+		return gLanguage[ idx ].c_str();
+	}
+
+	return "?";
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+u32 Translate_Number()
+{
+	return gLanguage.size()-1;
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+u32	Translate_IndexFromName( const char * name )
+{
+	for( u32 i = 0; i < gLanguage.size(); ++i )
+	{
+		if( strcasecmp(  gLanguage[ i ].c_str(), name ) == 0 )
+		{
+			return i;
+		}
+	}
+
+	// Default language (English)
+	return 0;
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+const char * Translate_NameFromIndex( u32 idx )
+{
+	if( idx < gLanguage.size())
+	{
+		return gLanguage[ idx ].c_str();
+	}
+
+	return "?";
+}
+
+//*****************************************************************************
+// Restores escape characters which were removed when parsing
+// Which are needed by line-breaking and back-slash
+//*****************************************************************************
+std::string Restore(std::string s, size_t len) {
+    for (size_t i = 0; i < len; i++) {
+        if (s[i] == '\\') {
+            if (s[i + 1] == 'n') {
+                s[i + 1] = '\b'; 
+                s[i] = '\n';
+                i++;
+            } else if (s[i + 1] == '\\') {
+                s[i + 1] = '\b'; 
+                s[i] = '\\';
+                i++;
+            }
+        }
+    }
+    return s;
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+void Translate_Dump(const std::string string, bool dump)
+{
+	if(dump)
+	{
+	std::fstream fh("hash.txt", std::ios::in);
+
+	if (fh.is_open())
+		{
+		fh << FORMAT_NAMESPACE::format("{:08x},{}\n", HashString(string), string);
+			
+		}
+	}
+}
+
+//*****************************************************************************
+//
+//*****************************************************************************
+bool Translate_Read(u32 idx, const std::filesystem::path& dir)
+{
+	/// Always unload previous language file if available
+	Translate_Unload();
+
+	if( idx > gLanguage.size() )
+		return false;
+
+	std::string line;
+
+	char *string;
+
+	u32 count = 0;
+	u32 hash  = 0;
+	u32	len   = 0;
+
+	// Build path where we'll load the translation file(s)
+	
+	const std::string languageFile = "Languages/" + gLanguage[idx] + ".lng";
+	const std::filesystem::path& path = setBasePath(languageFile);
+	
+
+	std::cout << "Language Path: " << path << std::endl;
+	std::fstream stream(path, std::ios::in);
+
+	if (!stream.is_open())
+	{
+		return false;
+	}
+
+
+  while (std::getline(stream, line)) {
+        // Strip spaces from end of lines
+        line.erase(line.find_last_not_of(" \t\n\r\f\v") + 1);
+
+        // Handle comments
+        if (line.empty() || line[0] == '/')
+            continue;
+
+        size_t commaPos = line.find(',');
+        if (commaPos != std::string::npos) {
+            std::string hashString = line.substr(0, commaPos);
+            std::string string = line.substr(commaPos + 1);
+
+            unsigned int hash;
+            std::stringstream ss;
+            ss << std::hex << hashString;
+            ss >> hash;
+			constexpr size_t TEXT_ARRAY_SIZE = 1024; 
+            size_t len = string.length();
+            if (count < TEXT_ARRAY_SIZE) {
+                // Write translated string and hash to array
+                text[count].hash = hash;
+                Translate_Dump(string, hash == TRANSLATE_DUMP_VALUE);
+
+                text[count].translated = (char*)malloc(len + 1); // Leave space for terminator
+                strcpy(text[count].translated, Restore(string, len).c_str());
+                count++;
+            }
+        }
+    }
+
+	return true;
+}
